業(yè)余時(shí)間寫(xiě)了一個(gè)類(lèi)似stackview的控件,可以循環(huán)抽取.還不是很完善,算是給有需要的朋友提供個(gè)基本思路吧.有更好的建議請(qǐng)告知.
github地址:https://github.com/X-FAN/SwipeCardView
先上效果圖
SwipeCardView.gif
源碼作了簡(jiǎn)單注釋
public class SwipeCardView extends ViewGroup {
private int mInitX = 0;//最頂層view相對(duì)父view左上角x坐標(biāo)
private int mOffSet = 50;
private int mRecordCount = 0;
private int mRealOffset = 0;
private int mDuration;
private float mScale = 0.05f;
private boolean mReLayout = false;//是否再次重新布局
private BindData mBindData;
private LayoutInflater mInflater;
private View mTopView;//最頂上的View
private View mRemovedView;
private OnTopClickListener mOnTopClickListener;
public SwipeCardView(Context context) {
this(context, null);
}
public SwipeCardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeCardView, defStyleAttr, 0);
mRealOffset = a.getDimensionPixelSize(R.styleable.SwipeCardView_offset, 20);
mDuration = a.getInteger(R.styleable.SwipeCardView_animatorDuration, 500);
mScale = a.getFloat(R.styleable.SwipeCardView_scale, 0.05f);
a.recycle();
mInflater = LayoutInflater.from(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
int defaultWidth = 200;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(defaultWidth, widthSize);
} else {
width = defaultWidth;
}
int defaultHeight = 200;
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(defaultHeight, heightSize);
} else {
height = defaultHeight;
}
setMeasuredDimension(width, height);
int count = getChildCount();
for (int i = 0; i < count; i++) {//測(cè)量子view
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
layoutChildren(l, t, r, b);
}
private void layoutChildren(int left, final int top, int right, int bottom) {
if (mReLayout) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
View view = getChildAt(i);
scaleUpChildView(view, count);
}
mReLayout = false;
resetBottomView(count);
addView(mRemovedView, 0);//將刪除的View重新添加到最底端
mRemovedView = null;
} else {
int count = getChildCount();
mTopView = getChildAt(count - 1);
mTopView.setTag(true);//開(kāi)始默認(rèn)可以滑動(dòng)
int width = mTopView.getMeasuredWidth();
int height = mTopView.getMeasuredHeight();
mOffSet = (int) (width * mScale / 2 + mRealOffset);//需要向左移動(dòng)距離
float totalWidth = width + mRealOffset * (count - 1);//整個(gè)子view加起來(lái)所占的寬度
mInitX = (int) (getMeasuredWidth() - totalWidth) / 2;
int initY = (getMeasuredHeight() - height) / 2;
for (int i = 0; i < count; i++) {
View view = getChildAt(i);
view.layout(mInitX, initY, width + mInitX, height + initY);
scaleChildView(view, count - 1 - i);
}
}
setTopView();
}
/**
* 配置頂層view
*/
private void setTopView() {
mTopView = getChildAt(getChildCount() - 1);//獲取最上層的View
if (mTopView != null) {
mTopView.setOnTouchListener(new SwipeCardListener(mTopView, mInitX) {
@Override
void leftOut(View view) {
mRemovedView = view;
mReLayout = true;
removeView(view);
}
@Override
void onClick(View view) {
if (mOnTopClickListener != null) {
mOnTopClickListener.onTopClickListener(view);
}
}
});
}
}
/**
* 給最底層的View重新配置合適的值
*
* @param count
*/
private void resetBottomView(int count) {
mRemovedView.setX(mInitX);
mRemovedView.offsetLeftAndRight(count * mOffSet);
mRemovedView.setScaleX(1 - count * mScale);
mRemovedView.setScaleY(1 - count * mScale);
}
/**
* 初始化SwipeCard
*
* @param layoutId
* @param datas
* @param <T>
*/
public <T> void initSwipeCard(@LayoutRes int layoutId, List<T> datas) {
int count = datas.size();
for (int i = 0; i < count; i++) {//添加view并綁定數(shù)據(jù)
View view = mInflater.inflate(layoutId, this, false);
mBindData.bindData(view, datas.get(i));
addView(view, 0);//添加到最低端
}
}
public interface BindData<T> {
void bindData(View view, T data);
}
/**
* 縮放并平移子view
*/
private void scaleChildView(View view, int index) {
view.offsetLeftAndRight(mOffSet * index);
view.setScaleX(1 - index * mScale);
view.setScaleY(1 - index * mScale);
}
/**
* 慢慢放到上層view的位置
*
* @param view
*/
private void scaleUpChildView(final View view, final int count) {
float scaleX = view.getScaleX();
float scaleY = view.getScaleY();
view.animate().scaleX(scaleX + mScale)
.scaleY(scaleY + mScale)
.x(view.getX() - mOffSet)
.setDuration(mDuration)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mRecordCount++;
if (count == mRecordCount) {
mTopView.setTag(true);//設(shè)置為可以滑動(dòng)
mRecordCount = 0;
}
}
})
.start();
}
public <T> void setBindDataListener(BindData bindData) {
mBindData = bindData;
}
public void setOnTopClickListener(OnTopClickListener onTopClickListener) {
mOnTopClickListener = onTopClickListener;
}
public interface OnTopClickListener {
void onTopClickListener(View view);
}
}
abstract class SwipeCardListener implements View.OnTouchListener {
private final float mOutDistance;//定義滑動(dòng)多少距離后,觸發(fā)view從界面左面離開(kāi)動(dòng)作
private int mWidth;//view的寬度
private float mInitX;//view初始的x坐標(biāo)
private float mTouchDownX;//按下時(shí)的手指x坐標(biāo)
private float mRecordX;//記錄移動(dòng)后view的x坐標(biāo)
private View mView;
private GestureDetectorCompat mGestureDetector;
SwipeCardListener(View view, int initX) {
mView = view;
mInitX = initX;
mWidth = view.getWidth();
mOutDistance = mWidth / 4;
mGestureDetector = new GestureDetectorCompat(view.getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
onClick(mView);
return super.onSingleTapUp(e);
}
});
}
@Override
public boolean onTouch(final View v, MotionEvent event) {
mGestureDetector.onTouchEvent(event);
if (mView.getTag() == null || !(boolean) mView.getTag()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTouchDownX = event.getRawX();
return true;
case MotionEvent.ACTION_MOVE:
float d = event.getRawX() - mTouchDownX;
if (Math.abs(d) > 0) {
mTouchDownX = event.getRawX();
mRecordX += d;
mView.setX(mInitX + mRecordX);//移動(dòng)View
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mRecordX < 0 && Math.abs(mRecordX) > mOutDistance) {
mView.animate().x(-mWidth).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (mView != null) {
mView.setTag(false);
mView.setOnTouchListener(null);
mView.clearAnimation();
leftOut(mView);
mView = null;
}
}
}).start();//滑出父view的范圍
} else {
mView.animate().x(mInitX).start();//讓View回滾到初始位置
}
mRecordX = 0;
break;
}
return false;
}
abstract void leftOut(View view);
abstract void onClick(View view);
}