1.先看下实现的效果图
页面失效效果图
运行的动态效果图
2.具体实现的方法和步骤
(1).代码架构图
代码结构图
(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资源文件主要存放一些图片资源
4.以上是卡片控件CardView的一个简单的实现,有不足的地方请大家多多指教,谢谢
Android开发之实现CardView翻转和切换 转载https://www.codesocang.com/appboke/36369.html
热门源码