继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android开发之实现CardView翻转和切换

米脂
关注TA
已关注
手记 492
粉丝 88
获赞 592

1.先看下实现的效果图

Android开发之实现CardView翻转和切换

页面失效效果图

Android开发之实现CardView翻转和切换

运行的动态效果图

2.具体实现的方法和步骤

(1).代码架构图

Android开发之实现CardView翻转和切换

代码结构图

(2).在这里需要引入第三方的jar包

nineoldandroids-2.4.0.jar这个可以在网上下载到

(3).实现自定义的卡片控件CardView,继承FrameLayout控件类,具体实现的代码如下

public class CardView extends FrameLayout {

private static final int ITEM_SPACE = 40;

private static final int DEF_MAX_VISIBLE = 4;

private int mMaxVisible = DEF_MAX_VISIBLE;

private int itemSpace = ITEM_SPACE;

private float mTouchSlop;

private ListAdapter mListAdapter;

private int mNextAdapterPosition;

private SparseArray<View> viewHolder = new SparseArray<View>();

private OnCardClickListener mListener;

private int topPosition;

private Rect topRect;

public interface OnCardClickListener {

void onCardClick(View view, int position);

}

public CardView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

init();

}

public CardView(Context context, AttributeSet attrs) {

super(context, attrs);

init();

}

public CardView(Context context) {

super(context);

init();

}

private void init() {

topRect = new Rect();

ViewConfiguration con = ViewConfiguration.get(getContext());

mTouchSlop = con.getScaledTouchSlop();

}

public void setMaxVisibleCount(int count) {

mMaxVisible = count;

}

public int getMaxVisibleCount() {

return mMaxVisible;

}

public void setItemSpace(int itemSpace) {

this.itemSpace = itemSpace;

}

public int getItemSpace() {

return itemSpace;

}

public ListAdapter getAdapter() {

return mListAdapter;

}

public void setAdapter(ListAdapter adapter) {

if (mListAdapter != null) {

mListAdapter.unregisterDataSetObserver(mDataSetObserver);

}

mNextAdapterPosition = 0;

mListAdapter = adapter;

adapter.registerDataSetObserver(mDataSetObserver);

removeAllViews();

ensureFull();

}

public void setOnCardClickListener(OnCardClickListener listener) {

mListener = listener;

}

private void ensureFull() {

while (mNextAdapterPosition < mListAdapter.getCount()

&& getChildCount() < mMaxVisible) {

int index = mNextAdapterPosition % mMaxVisible;

View convertView = viewHolder.get(index);

final View view = mListAdapter.getView(mNextAdapterPosition,

convertView, this);

view.setOnClickListener(null);

viewHolder.put(index, view);

// 添加剩余的View时,始终处在最后

index = Math.min(mNextAdapterPosition, mMaxVisible - 1);

ViewHelper.setScaleX(view,((mMaxVisible - index - 1) / (float) mMaxVisible) * 0.2f + 0.8f);

int topMargin = (mMaxVisible - index - 1) * itemSpace;

ViewHelper.setTranslationY(view, topMargin);

ViewHelper.setAlpha(view, mNextAdapterPosition == 0 ? 1 : 0.5f);

LayoutParams params = (LayoutParams) view.getLayoutParams();

if (params == null) {

params = new LayoutParams(LayoutParams.MATCH_PARENT,

LayoutParams.WRAP_CONTENT);

}

addViewInLayout(view, 0, params);

mNextAdapterPosition += 1;

}

// requestLayout();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int widthMode = MeasureSpec.getMode(widthMeasureSpec);

int heightMode = MeasureSpec.getMode(heightMeasureSpec);

int widthSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSize = MeasureSpec.getSize(heightMeasureSpec);

int childCount = getChildCount();

int maxHeight = 0;

int maxWidth = 0;

for (int i = 0; i < childCount; i++) {

View child = getChildAt(i);

this.measureChild(child, widthMeasureSpec, heightMeasureSpec);

int height = child.getMeasuredHeight();

int width = child.getMeasuredWidth();

if (height > maxHeight) {

maxHeight = height;

}

if (width > maxWidth) {

maxWidth = width;

}

}

int desireWidth = widthSize;

int desireHeight = heightSize;

if (widthMode == MeasureSpec.AT_MOST) {

desireWidth = maxWidth + getPaddingLeft() + getPaddingRight();

}

if (heightMode == MeasureSpec.AT_MOST) {

desireHeight = maxHeight + (mMaxVisible - 1) * itemSpace + getPaddingTop() + getPaddingBottom();

}

setMeasuredDimension(desireWidth, desireHeight);

}

@Override

protected void onLayout(boolean changed, int left, int top, int right,

int bottom) {

super.onLayout(changed, left, top, right, bottom);

View topView = getChildAt(getChildCount() - 1);

if (topView != null) {

topView.setOnClickListener(listener);

}

}

float downX, downY;

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_MOVE:

if (goDown()) {

downY = -1;

}

break;

}

return super.onTouchEvent(event);

}

/**

* 下移所有视图

*/

private boolean goDown() {

final View topView = getChildAt(getChildCount() - 1);

if(!topView.isEnabled()){

return false;

}

// topView.getHitRect(topRect); 在4.3以前有bug,用以下方法代替

topRect = getHitRect(topRect, topView);

// 如果按下的位置不在顶部视图上,则不移动

if (!topRect.contains((int) downX, (int) downY)) {

return false;

}

topView.setEnabled(false);

ViewPropertyAnimator anim = ViewPropertyAnimator

.animate(topView)

.translationY(

ViewHelper.getTranslationY(topView)

+ topView.getHeight()).alpha(0).scaleX(1)

.setListener(null).setDuration(200);

anim.setListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

topView.setEnabled(true);

removeView(topView);

ensureFull();

final int count = getChildCount();

for (int i = 0; i < count; i++) {

final View view = getChildAt(i);

float scaleX = ViewHelper.getScaleX(view)

+ ((float) 1 / mMaxVisible) * 0.2f;

float tranlateY = ViewHelper.getTranslationY(view)

+ itemSpace;

if (i == count - 1) {

bringToTop(view);

} else {

if ((count == mMaxVisible && i != 0)

|| count < mMaxVisible) {

ViewPropertyAnimator

.animate(view)

.translationY(tranlateY)

.setInterpolator(

new AccelerateInterpolator())

.setListener(null).scaleX(scaleX)

.setDuration(200);

}

}

}

}

});

return true;

}

/**

* 将下一个视图移到前边

*

* @param view

*/

private void bringToTop(final View view) {

topPosition++;

float scaleX = ViewHelper.getScaleX(view) + ((float) 1 / mMaxVisible)

* 0.2f;

float tranlateY = ViewHelper.getTranslationY(view) + itemSpace;

ViewPropertyAnimator.animate(view).translationY(tranlateY)

.scaleX(scaleX).setDuration(200).alpha(1)

.setInterpolator(new AccelerateInterpolator());

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

float currentY = ev.getY();

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

downX = ev.getX();

downY = ev.getY();

break;

case MotionEvent.ACTION_MOVE:

float distance = currentY - downY;

if (distance > mTouchSlop) {

return true;

}

break;

}

return false;

}

public static Rect getHitRect(Rect rect, View child) {

rect.left = child.getLeft();

rect.right = child.getRight();

rect.top = (int) (child.getTop() + ViewHelper.getTranslationY(child));

rect.bottom = (int) (child.getBottom() + ViewHelper

.getTranslationY(child));

return rect;

}

private final DataSetObserver mDataSetObserver = new DataSetObserver() {

@Override

public void onChanged() {

super.onChanged();

}

@Override

public void onInvalidated() {

super.onInvalidated();

}

};

private OnClickListener listener = new OnClickListener() {

@Override

public void onClick(View v) {

if (mListener != null) {

mListener.onCardClick(v, topPosition);

}

}

};

}

(4).实现自定义的卡片控件之后,下面需要为这个CardView实现适配器Adapter了,在此我们定义的是一个抽象Adapter类,需要用户根据自己的实际业务场景继承这个抽象类即可,具体代码如下:

public abstract class CardAdapter<T> extends BaseAdapter {

private final Context mContext;

private ArrayList<T> mData;

public CardAdapter(Context context) {

mContext = context;

mData = new ArrayList<T>();

}

public CardAdapter(Context context, Collection<? extends T> items) {

mContext = context;

mData = new ArrayList<T>(items);

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

FrameLayout wrapper = (FrameLayout) convertView;

View cardView;

View convertedCardView;

if (wrapper == null) {

wrapper = new FrameLayout(mContext);

wrapper.setBackgroundResource(R.drawable.card_background_shadow);

cardView = getCardView(position, null, wrapper);

wrapper.addView(cardView);

} else {

cardView = wrapper.getChildAt(0);

convertedCardView = getCardView(position, cardView, wrapper);

//要先删除,然后再添加,否则界面不更新

wrapper.removeView(cardView);

wrapper.addView(convertedCardView);

if (convertedCardView != cardView) {

}

}

return wrapper;

}

protected abstract View getCardView(int position, View convertView, ViewGroup parent);

public void addAll(List<T> items){

mData.addAll(items);

}

@Override

public T getItem(int position) {

return mData.get(position);

}

@Override

public int getCount() {

return mData.size();

}

@Override

public long getItemId(int position) {

return getItem(position).hashCode();

}

public Context getContext() {

return mContext;

}

public void clear(){

if(mData != null){

mData.clear();

}

}

}

(5).在这个卡片控件实现过程中,需要根据手机屏幕大小来自适应卡片控件的大小,所以需要一个方法来计算手机屏幕大小,我们抽象成一个工具类来实现,代码如下:

public class Utils {

public float convertPixelsToDp(Context ctx, float px) {

DisplayMetrics metrics = ctx.getResources().getDisplayMetrics();

float dp = px / (metrics.densityDpi / 160f);

return dp;

}

public static int convertDpToPixelInt(Context context, float dp) {

DisplayMetrics metrics = context.getResources().getDisplayMetrics();

int px = (int) (dp * (metrics.densityDpi / 160f));

return px;

}

public static float convertDpToPixel(Context context, float dp) {

DisplayMetrics metrics = context.getResources().getDisplayMetrics();

float px = (float) (dp * (metrics.densityDpi / 160f));

return px;

}

}

(6).在Activity来实现卡片布局的显示了,很快就能看到具体的实现效果了。

public class MainActivity extends FragmentActivity implements OnCardClickListener{

List<String> list;

private TestFragment frag;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initUI();

}

private void initUI() {

CardView cardView = (CardView) findViewById(R.id.cardView1);

cardView.setOnCardClickListener(this);

cardView.setItemSpace(Utils.convertDpToPixelInt(this, 20));

MyCardAdapter adapter = new MyCardAdapter(this);

adapter.addAll(initData());

cardView.setAdapter(adapter);

FragmentManager manager = getSupportFragmentManager();

frag = new TestFragment();

manager.beginTransaction().add(R.id.contentView, frag).commit();

}

@Override

public void onCardClick(final View view, final int position) {

Toast.makeText(MainActivity.this, position + "", Toast.LENGTH_SHORT).show();

Bundle bundle = new Bundle();

bundle.putString("text", list.get(position%list.size()));

frag.show(view,bundle);

}

private List<String> initData() {

list = new ArrayList<String>();

list.add("七仔:");

list.add("楠楠:");

list.add("小编:");

list.add("陈儿:");

list.add("小小:");

list.add("Zz:");

list.add("牵着蜗牛去西藏:");

return list;

}

public class MyCardAdapter extends CardAdapter<String>{

public MyCardAdapter(Context context) {

super(context);

}

@Override

public int getCount() {

return Integer.MAX_VALUE;

}

@Override

protected View getCardView(int position,

View convertView, ViewGroup parent) {

if(convertView == null) {

LayoutInflater inflater = LayoutInflater.from(MainActivity.this);

convertView = inflater.inflate(R.layout.item_layout, parent, false);

}

TextView tv = (TextView) convertView.findViewById(R.id.textView1);

String text = getItem(position%list.size());

tv.setText(text);

return convertView;

}

}

}

(7).在点击每个卡片的时候有个翻转的页面,我们是用Fragment来实现的,具体的Fragment代码如下:

public class TestFragment extends Fragment{

private TextView tv;

private View root;

private View view;

@Override

public View onCreateView(LayoutInflater inflater,

@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

root = inflater.inflate(R.layout.frag_layout, container,false);

initUI(root);

return root;

}

private void initUI(final View root) {

root.setClickable(true);

root.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {}

});

tv = (TextView) root.findViewById(R.id.textView);

root.findViewById(R.id.button).setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

root.setClickable(false);

ViewPropertyAnimator.animate(root)

.rotationY(-90).setDuration(200)

.setListener(new AnimatorListenerAdapter(){

@Override

public void onAnimationEnd(Animator animation) {

root.clearAnimation();

root.setVisibility(View.INVISIBLE);

view.setEnabled(true);

}

});

}

});

}

public void show(final View view,Bundle bundle){

view.setEnabled(false);

this.view = view;

String text = bundle.getString("text");

tv.setText(text);

ViewHelper.setRotationY(view, 0);

ViewHelper.setRotationY(root, -90);

root.setVisibility(View.VISIBLE);

ViewPropertyAnimator.animate(view).rotationY(90)

.setDuration(300).setListener(null)

.setInterpolator(new AccelerateInterpolator());

ViewPropertyAnimator.animate(root)

.rotationY(0).setDuration(200).setStartDelay(300)

.setListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

ViewHelper.setRotationY(view, 0);

}

});

}

}

3.下面给出具体的资源文件

(1).layout布局文件

item_layout.xml的具体布局代码如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

>

<TextView

android:id="@+id/textView1"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:gravity="center"

android:padding="20dp"

android:textAppearance="?android:attr/textAppearanceMedium"

/>

<TextView

android:id="@+id/imageView1"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_below="@+id/textView1"

android:text="@string/text"

android:textAppearance="?android:attr/textAppearanceMedium"

android:gravity="center"

android:layout_marginTop="30dp"

android:layout_marginBottom="30dp"

/>

</RelativeLayout>

activity_main的布局文件代码如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context="com.DevStoreDemo.MainActivity"

android:background="#00688B"

android:clipChildren="false" >

<com.DevStoreDemo.CardView

android:id="@+id/cardView1"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:clipChildren="false"/>

<LinearLayout

android:id="@+id/contentView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"/>

</RelativeLayout>

frag_layout.xml布局文件代码如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@android:color/white"

android:visibility="invisible"

>

<TextView

android:id="@+id/textView"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerHorizontal="true"

android:textAppearance="?android:attr/textAppearanceLarge"

/>

<ImageView

android:id="@+id/button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentLeft="true"

android:layout_alignParentTop="true"

android:src="@drawable/close_bg"

android:padding="10dp"

/>

<ImageView

android:id="@+id/imageView1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerHorizontal="true"

android:layout_centerVertical="true"

android:src="@drawable/a" />

</RelativeLayout>

(2)values资源文件内容

carcontainer_attrs.xml资源文件代码如下

<?xml version="1.0" encoding="utf-8"?>

<resources>

<declare-styleable name="CardContainer">

<attr name="android:gravity"/>

<attr name="orientation" format="enum">

<enum name="ordered" value="0"/>

<enum name="disordered" value="1"/>

</attr>

</declare-styleable>

</resources>

color.xml资源文件代码如下

<?xml version="1.0" encoding="utf-8"?>

<resources>

<color name="card_bg">#EBEBEB</color>

<color name="card_outline">#D0D0CE</color>

</resources>

(3)drawable资源文件主要存放一些图片资源

Android开发之实现CardView翻转和切换

Android开发之实现CardView翻转和切换

Android开发之实现CardView翻转和切换

4.以上是卡片控件CardView的一个简单的实现,有不足的地方请大家多多指教,谢谢

原文链接:http://www.apkbus.com/blog-822415-68053.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP