diff --git a/CardStack/src/main/java/com/wenchao/cardstack/CardAnimator.java b/CardStack/src/main/java/com/wenchao/cardstack/CardAnimator.java index 5e570f4..8c3d68b 100644 --- a/CardStack/src/main/java/com/wenchao/cardstack/CardAnimator.java +++ b/CardStack/src/main/java/com/wenchao/cardstack/CardAnimator.java @@ -1,92 +1,108 @@ package com.wenchao.cardstack; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Random; - -import com.wenchao.animation.RelativeLayoutParamsEvaluator; - import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; -import android.graphics.Color; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; +import com.wenchao.animation.RelativeLayoutParamsEvaluator; -import static com.wenchao.cardstack.CardUtils.*; +import java.util.ArrayList; +import java.util.HashMap; + +import static com.wenchao.cardstack.CardUtils.cloneParams; +import static com.wenchao.cardstack.CardUtils.getMoveParams; +import static com.wenchao.cardstack.CardUtils.move; +import static com.wenchao.cardstack.CardUtils.scale; -public class CardAnimator{ +public class CardAnimator { private static final String DEBUG_TAG = "CardAnimator"; + + public static final int TOP = 48; + public static final int BOTTOM = 80; + + private static final int REMOTE_DISTANCE = 1000; private int mBackgroundColor; public ArrayList mCardCollection; private float mRotation; - private HashMap mLayoutsMap; + private HashMap mLayoutsMap; private RelativeLayout.LayoutParams[] mRemoteLayouts = new RelativeLayout.LayoutParams[4]; private RelativeLayout.LayoutParams baseLayout; - private int mStackMargin=20; + private int mStackMargin; + private int mGravity = BOTTOM; + private boolean mEnableRotation; // 是否允许旋转 - public CardAnimator(ArrayList viewCollection, int backgroundColor){ + public CardAnimator(ArrayList viewCollection, int backgroundColor, int margin) { mCardCollection = viewCollection; mBackgroundColor = backgroundColor; + mStackMargin = margin; setup(); - } - private void setup(){ - mLayoutsMap = new HashMap(); - for(View v : mCardCollection){ + private void setup() { + mLayoutsMap = new HashMap(); + + for (View v : mCardCollection) { //setup basic layout RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams(); params.addRule(RelativeLayout.ALIGN_PARENT_TOP); params.width = LayoutParams.MATCH_PARENT; - params.height = LayoutParams.MATCH_PARENT; + params.height = LayoutParams.WRAP_CONTENT; - if(mBackgroundColor != -1) { + if (mBackgroundColor != -1) { v.setBackgroundColor(mBackgroundColor); } v.setLayoutParams(params); } - baseLayout = (RelativeLayout.LayoutParams)mCardCollection.get(0).getLayoutParams(); + baseLayout = (RelativeLayout.LayoutParams) mCardCollection.get(0).getLayoutParams(); baseLayout = cloneParams(baseLayout); - initLayout(); - - for (View v : mCardCollection){ - RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams(); - RelativeLayout.LayoutParams paramsCopy = cloneParams(params); - mLayoutsMap.put(v, paramsCopy); - } - - setupRemotes(); - } - public void initLayout(){ + public void initLayout() { int size = mCardCollection.size(); - for(View v : mCardCollection){ - int index = mCardCollection.indexOf(v); - if(index!=0){ - index-=1; + for (View v : mCardCollection) { + int index = mCardCollection.indexOf(v); + if (index != 0) { + index -= 1; } LayoutParams params = cloneParams(baseLayout); v.setLayoutParams(params); - scale(v, -(size - index - 1) * 5); - move(v, index * mStackMargin, 0); + scale(v, -(size - index - 1) * 5, mGravity); + + int margin = index * mStackMargin; + move(v, mGravity == TOP ? -margin : margin, 0); v.setRotation(0); + + RelativeLayout.LayoutParams paramsCopy = + cloneParams((RelativeLayout.LayoutParams) v.getLayoutParams()); + mLayoutsMap.put(v, paramsCopy); } + + setupRemotes(); + } + + /** + * 设置方向,支持上、下。 + * 设置后调用{@link #initLayout()} 来重新初始化布局 + * + * @param gravity {@link #TOP} 向上 {@link #BOTTOM} 向下,默认值 + */ + public void setGravity(int gravity) { + mGravity = gravity; } - private void setupRemotes(){ + private void setupRemotes() { View topView = getTopView(); mRemoteLayouts[0] = getMoveParams(topView, REMOTE_DISTANCE, -REMOTE_DISTANCE); mRemoteLayouts[1] = getMoveParams(topView, REMOTE_DISTANCE, REMOTE_DISTANCE); @@ -95,42 +111,43 @@ private void setupRemotes(){ } - private View getTopView(){ - return mCardCollection.get(mCardCollection.size()-1); + private View getTopView() { + return mCardCollection.get(mCardCollection.size() - 1); } - private void moveToBack(View child) - { - final ViewGroup parent = (ViewGroup)child.getParent(); + private void moveToBack(View child) { + final ViewGroup parent = (ViewGroup) child.getParent(); if (null != parent) { parent.removeView(child); - parent.addView(child, 0); + parent.addView(child, 0); // 移到最后一个 } } - private void reorder(){ + // 卡片排序,抽出一个,底部上来一个 + private void reorder() { + View temp = getTopView(); //RelativeLayout.LayoutParams tempLp = mLayoutsMap.get(mCardCollection.get(0)); //mLayoutsMap.put(temp,tempLp); moveToBack(temp); - for(int i=(mCardCollection.size()-1); i>0; i--){ + for (int i = (mCardCollection.size() - 1); i > 0; i--) { //View next = mCardCollection.get(i); //RelativeLayout.LayoutParams lp = mLayoutsMap.get(next); //mLayoutsMap.remove(next); - View current = mCardCollection.get(i-1); + View current = mCardCollection.get(i - 1); + //current replace next - mCardCollection.set(i,current); + mCardCollection.set(i, current); //mLayoutsMap.put(current,lp); } - mCardCollection.set(0,temp); - - temp = getTopView(); + mCardCollection.set(0, temp); } - public void discard(int direction,final AnimatorListener al){ + // 销毁卡片 + public void discard(int direction, final AnimatorListener al) { AnimatorSet as = new AnimatorSet(); ArrayList aCollection = new ArrayList(); @@ -138,49 +155,49 @@ public void discard(int direction,final AnimatorListener al){ final View topView = getTopView(); RelativeLayout.LayoutParams topParams = (RelativeLayout.LayoutParams) topView.getLayoutParams(); RelativeLayout.LayoutParams layout = cloneParams(topParams); - ValueAnimator discardAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(),layout, mRemoteLayouts[direction]); + ValueAnimator discardAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(), layout, mRemoteLayouts[direction]); discardAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator value) { - topView.setLayoutParams((LayoutParams)value.getAnimatedValue()); + topView.setLayoutParams((LayoutParams) value.getAnimatedValue()); } }); discardAnim.setDuration(250); aCollection.add(discardAnim); - for(int i = 0; i< mCardCollection.size();i++){ + for (int i = 0; i < mCardCollection.size(); i++) { final View v = mCardCollection.get(i); - if(v==topView) continue; - final View nv = mCardCollection.get(i+1); + if (v == topView) continue; + final View nv = mCardCollection.get(i + 1); RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) v.getLayoutParams(); RelativeLayout.LayoutParams endLayout = cloneParams(layoutParams); - ValueAnimator layoutAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(),endLayout,mLayoutsMap.get(nv)); + ValueAnimator layoutAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(), endLayout, mLayoutsMap.get(nv)); layoutAnim.setDuration(250); layoutAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator value) { - v.setLayoutParams((LayoutParams)value.getAnimatedValue()); + v.setLayoutParams((LayoutParams) value.getAnimatedValue()); } }); aCollection.add(layoutAnim); } - as.addListener(new AnimatorListenerAdapter(){ + as.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { reorder(); - if(al != null){ + if (al != null) { al.onAnimationEnd(animation); } - mLayoutsMap = new HashMap(); - for (View v : mCardCollection){ + mLayoutsMap = new HashMap(); + for (View v : mCardCollection) { RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams(); - RelativeLayout.LayoutParams paramsCopy = cloneParams(params); + RelativeLayout.LayoutParams paramsCopy = cloneParams(params); mLayoutsMap.put(v, paramsCopy); } @@ -193,8 +210,11 @@ public void onAnimationEnd(Animator animation) { as.start(); } - public void reverse(MotionEvent e1, MotionEvent e2){ - final View topView = getTopView(); + /** + * 还原卡片位置 + */ + public void reverse(MotionEvent e1, MotionEvent e2) { + final View topView = getTopView(); ValueAnimator rotationAnim = ValueAnimator.ofFloat(mRotation, 0f); rotationAnim.setDuration(250); rotationAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @@ -206,15 +226,15 @@ public void onAnimationUpdate(ValueAnimator v) { rotationAnim.start(); - for(final View v : mCardCollection){ + for (final View v : mCardCollection) { RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) v.getLayoutParams(); RelativeLayout.LayoutParams endLayout = cloneParams(layoutParams); - ValueAnimator layoutAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(),endLayout,mLayoutsMap.get(v)); - layoutAnim.setDuration(250); + ValueAnimator layoutAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(), endLayout, mLayoutsMap.get(v)); + layoutAnim.setDuration(100); layoutAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator value) { - v.setLayoutParams((LayoutParams)value.getAnimatedValue()); + v.setLayoutParams((LayoutParams) value.getAnimatedValue()); } }); layoutAnim.start(); @@ -223,41 +243,45 @@ public void onAnimationUpdate(ValueAnimator value) { } public void drag(MotionEvent e1, MotionEvent e2, float distanceX, - float distanceY){ - - View topView = getTopView(); + float distanceY) { + View topView = getTopView(); + int x_diff = (int) ((e2.getRawX() - e1.getRawX())); + int y_diff = (int) ((e2.getRawY() - e1.getRawY())); float rotation_coefficient = 20f; - RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) topView.getLayoutParams(); RelativeLayout.LayoutParams topViewLayouts = mLayoutsMap.get(topView); - int x_diff = (int)((e2.getRawX()-e1.getRawX())); - int y_diff = (int)((e2.getRawY()-e1.getRawY())); - - layoutParams.leftMargin = topViewLayouts.leftMargin+ x_diff; + layoutParams.leftMargin = topViewLayouts.leftMargin + x_diff; layoutParams.rightMargin = topViewLayouts.rightMargin - x_diff; - layoutParams.topMargin = topViewLayouts.topMargin + y_diff; - layoutParams.bottomMargin = topViewLayouts.bottomMargin - y_diff; + layoutParams.topMargin = topViewLayouts.topMargin + y_diff; + layoutParams.bottomMargin = topViewLayouts.bottomMargin - y_diff; - mRotation = (x_diff/rotation_coefficient); - topView.setRotation(mRotation); - topView.setLayoutParams(layoutParams); + if (mEnableRotation) { + mRotation = (x_diff / rotation_coefficient); + topView.setRotation(mRotation); + topView.setLayoutParams(layoutParams); + } //animate secondary views. - for(View v : mCardCollection){ - int index = mCardCollection.indexOf(v); - if(v!=getTopView() && index != 0){ - LayoutParams l = scaleFrom(v, mLayoutsMap.get(v), (int) (Math.abs(x_diff) * 0.05)); - moveFrom(v, l, 0, (int) (Math.abs(x_diff) * 0.1)); + for (View v : mCardCollection) { + int index = mCardCollection.indexOf(v); + if (v != getTopView() && index != 0) { + LayoutParams l = CardUtils.scaleFrom(v, mLayoutsMap.get(v), (int) (Math.abs(x_diff) * 0.05), mGravity); + CardUtils.moveFrom(v, l, 0, (int) (Math.abs(x_diff) * index * 0.05), mGravity); } } } public void setStackMargin(int margin) { mStackMargin = margin; - initLayout(); } + public boolean isEnableRotation() { + return mEnableRotation; + } + public void setEnableRotation(boolean enableRotation) { + mEnableRotation = enableRotation; + } } diff --git a/CardStack/src/main/java/com/wenchao/cardstack/CardStack.java b/CardStack/src/main/java/com/wenchao/cardstack/CardStack.java index ffd984d..e9f4050 100644 --- a/CardStack/src/main/java/com/wenchao/cardstack/CardStack.java +++ b/CardStack/src/main/java/com/wenchao/cardstack/CardStack.java @@ -1,24 +1,29 @@ package com.wenchao.cardstack; -import android.content.res.TypedArray; -import java.util.ArrayList; -import java.util.Queue; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; +import android.content.res.TypedArray; +import android.database.DataSetObserver; import android.util.AttributeSet; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.RelativeLayout; -import android.database.DataSetObserver; + +import java.util.ArrayList; public class CardStack extends RelativeLayout { + + private boolean mEnableRotation; + private int mGravity; private int mColor = -1; private int mIndex = 0; private int mNumVisible = 4; @@ -26,26 +31,33 @@ public class CardStack extends RelativeLayout { private ArrayAdapter mAdapter; private OnTouchListener mOnTouchListener; private CardAnimator mCardAnimator; + private boolean mEnableLoop; // 是否允许循环滚动 + private CardEventListener mEventListener = new DefaultStackEventListener(300); private int mContentResource = 0; + private int mMargin; - public interface CardEventListener{ + public interface CardEventListener { //section // 0 | 1 //-------- // 2 | 3 // swipe distance, most likely be used with height and width of a view ; - boolean swipeEnd(int section,float distance); - boolean swipeStart(int section,float distance); - boolean swipeContinue(int section, float distanceX,float distanceY ); + boolean swipeEnd(int section, float distance); + + boolean swipeStart(int section, float distance); + + boolean swipeContinue(int section, float distanceX, float distanceY); + void discarded(int mIndex, int direction); + void topCardTapped(); } - public void discardTop(final int direction){ + public void discardTop(final int direction) { mCardAnimator.discard(direction, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator arg0) { @@ -60,7 +72,65 @@ public void onAnimationEnd(Animator arg0) { }); } - public int getCurrIndex(){ + /** + * 设置方向,支持上、下。 + * 设置后调用{@link #reset(boolean)} 来重新初始化布局 + * + * @param gravity {@link CardAnimator#TOP} 向上 {@link CardAnimator#BOTTOM} 向下,默认值 + */ + public void setStackGravity(int gravity) { + mGravity = gravity; + } + + /** + * 获取当前方向 + * + * @return + */ + public int getStackGravity() { + return mGravity; + } + + /** + * 是否允许旋转 + *

+ * 设置后调用{@link #reset(boolean)} 来重新初始化布局 + * + * @param enableRotation + */ + public void setEnableRotation(boolean enableRotation) { + mEnableRotation = enableRotation; + } + + /** + * 是否循环滚动 + * 设置后调用{@link #reset(boolean)} 来重新初始化布局 + * + * @param enableLoop + */ + public void setEnableLoop(boolean enableLoop) { + mEnableLoop = enableLoop; + } + + /** + * 是否允许旋转 + * + * @return + */ + public boolean isEnableRotation() { + return mEnableRotation; + } + + /** + * 是否循环滚动 + * + * @return + */ + public boolean isEnableLoop() { + return mEnableLoop; + } + + public int getCurrIndex() { //sync? return mIndex; } @@ -71,29 +141,45 @@ public CardStack(Context context, AttributeSet attrs) { if (attrs != null) { TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CardStack); - mColor = array.getColor(R.styleable.CardStack_backgroundColor, mColor); + + mColor = array.getColor(R.styleable.CardStack_card_backgroundColor, mColor); + mGravity = array.getInteger(R.styleable.CardStack_card_gravity, Gravity.BOTTOM); + mEnableRotation = array.getBoolean(R.styleable.CardStack_card_enable_rotation, false); + mNumVisible = array.getInteger(R.styleable.CardStack_card_stack_size, mNumVisible); + mEnableLoop = array.getBoolean(R.styleable.CardStack_card_enable_loop, mEnableLoop); + mMargin = array.getDimensionPixelOffset(R.styleable.CardStack_card_margin, 20); array.recycle(); } //get attrs assign minVisiableNum - for(int i = 0; i= mAdapter.getCount()) { + mNumVisible = mAdapter.getCount(); + } reset(false); } - public void setThreshold(int t){ + public void setThreshold(int t) { mEventListener = new DefaultStackEventListener(t); } - public void setListener(CardEventListener cel){ + public void setListener(CardEventListener cel) { mEventListener = cel; } - private void setupAnimation(){ - final View cardView = viewCollection.get(viewCollection.size()-1); - mCardAnimator = new CardAnimator(viewCollection, mColor); + + private void setupAnimation() { + final View cardView = viewCollection.get(viewCollection.size() - 1); + mCardAnimator = new CardAnimator(viewCollection, mColor, mMargin); + mCardAnimator.setGravity(mGravity); + mCardAnimator.setEnableRotation(mEnableRotation); + //mCardAnimator.setStackMargin(mMargin); mCardAnimator.initLayout(); - final DragGestureDetector dd = new DragGestureDetector(CardStack.this.getContext(),new DragGestureDetector.DragListener(){ + final DragGestureDetector dd = new DragGestureDetector(CardStack.this.getContext(), new DragGestureDetector.DragListener() { @Override - public boolean onDragStart(MotionEvent e1, MotionEvent e2, - float distanceX, float distanceY) { + public boolean onDragStart(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { if (canSwipe) { mCardAnimator.drag(e1, e2, distanceX, distanceY); } @@ -154,26 +251,26 @@ public boolean onDragContinue(MotionEvent e1, MotionEvent e2, float y1 = e1.getRawY(); float x2 = e2.getRawX(); float y2 = e2.getRawY(); - final int direction = CardUtils.direction(x1,y1,x2,y2); + final int direction = CardUtils.direction(x1, y1, x2, y2); if (canSwipe) { mCardAnimator.drag(e1, e2, distanceX, distanceY); } - mEventListener.swipeContinue(direction, Math.abs(x2-x1), Math.abs(y2-y1)); + mEventListener.swipeContinue(direction, Math.abs(x2 - x1), Math.abs(y2 - y1)); return true; } @Override - public boolean onDragEnd(MotionEvent e1, MotionEvent e2) { + public boolean onDragEnd(MotionEvent e1, MotionEvent e2) { //reverse(e1,e2); float x1 = e1.getRawX(); float y1 = e1.getRawY(); float x2 = e2.getRawX(); float y2 = e2.getRawY(); - float distance = CardUtils.distance(x1,y1,x2,y2); - final int direction = CardUtils.direction(x1,y1,x2,y2); + float distance = CardUtils.distance(x1, y1, x2, y2); + final int direction = CardUtils.direction(x1, y1, x2, y2); boolean discard = mEventListener.swipeEnd(direction, distance); - if(discard){ + if (discard) { if (canSwipe) { mCardAnimator.discard(direction, new AnimatorListenerAdapter() { @@ -193,8 +290,9 @@ public void onAnimationEnd(Animator arg0) { }); } - }else{ + } else { if (canSwipe) { + mCardAnimator.reverse(e1, e2); } } @@ -211,6 +309,7 @@ public boolean onTapUp() { mOnTouchListener = new OnTouchListener() { private static final String DEBUG_TAG = "MotionEvents"; + @Override public boolean onTouch(View arg0, MotionEvent event) { dd.onTouchEvent(event); @@ -220,9 +319,9 @@ public boolean onTouch(View arg0, MotionEvent event) { cardView.setOnTouchListener(mOnTouchListener); } - private DataSetObserver mOb = new DataSetObserver(){ + private DataSetObserver mOb = new DataSetObserver() { @Override - public void onChanged(){ + public void onChanged() { reset(false); } }; @@ -231,12 +330,13 @@ public void onChanged(){ //ArrayList ArrayList viewCollection = new ArrayList(); + public CardStack(Context context) { super(context); } - public void setAdapter(final ArrayAdapter adapter){ - if(mAdapter != null){ + public void setAdapter(final ArrayAdapter adapter) { + if (mAdapter != null) { mAdapter.unregisterDataSetObserver(mOb); } mAdapter = adapter; @@ -253,13 +353,14 @@ public View getTopView() { return ((ViewGroup) viewCollection.get(viewCollection.size() - 1)).getChildAt(0); } - private void loadData(){ - for(int i=mNumVisible-1 ; i>=0 ; i--) { + private void loadData() { + for (int i = mNumVisible - 1; i >= 0; i--) { ViewGroup parent = (ViewGroup) viewCollection.get(i); int index = (mIndex + mNumVisible - 1) - i; if (index > mAdapter.getCount() - 1) { parent.setVisibility(View.GONE); - }else{ + + } else { View child = mAdapter.getView(index, getContentView(), this); parent.addView(child); parent.setVisibility(View.VISIBLE); @@ -267,23 +368,30 @@ private void loadData(){ } } - private View getContentView(){ + private View getContentView() { View contentView = null; - if(mContentResource != 0) { + if (mContentResource != 0) { LayoutInflater lf = LayoutInflater.from(getContext()); - contentView = lf.inflate(mContentResource,null); + contentView = lf.inflate(mContentResource, null); } return contentView; } - private void loadLast(){ - ViewGroup parent = (ViewGroup)viewCollection.get(0); - - int lastIndex = (mNumVisible - 1)+ mIndex; - if(lastIndex > mAdapter.getCount() -1 ){ - parent.setVisibility(View.GONE); - return; + // 加载下一个 + private void loadLast() { + ViewGroup parent = (ViewGroup) viewCollection.get(0); + int lastIndex = ((mNumVisible - 1) + mIndex); + + // 超出索引 + if (lastIndex > mAdapter.getCount() - 1) { + if (mEnableLoop) { + // 循环处理 + lastIndex = lastIndex % mAdapter.getCount(); + } else { + parent.setVisibility(View.GONE); + return; + } } View child = mAdapter.getView(lastIndex, getContentView(), parent); @@ -291,7 +399,18 @@ private void loadLast(){ parent.addView(child); } - public int getStackSize() { + /** + * 获取可见卡片个数 + * + * @return + */ + public int getVisibleCardNum() { return mNumVisible; } + + public void undo() { + if (mIndex == 0) return; + mIndex --; + reset(false, true); + } } diff --git a/CardStack/src/main/java/com/wenchao/cardstack/CardUtils.java b/CardStack/src/main/java/com/wenchao/cardstack/CardUtils.java index 49276b9..472cb06 100644 --- a/CardStack/src/main/java/com/wenchao/cardstack/CardUtils.java +++ b/CardStack/src/main/java/com/wenchao/cardstack/CardUtils.java @@ -11,19 +11,24 @@ public class CardUtils { final static int DIRECTION_BOTTOM_LEFT = 2; final static int DIRECTION_BOTTOM_RIGHT = 3; - public static void scale(View v, int pixel){ - RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)v.getLayoutParams(); - params.leftMargin -= pixel; - params.rightMargin -= pixel; - params.topMargin -= pixel; + public static void scale(View v, int pixel, int gravity) { + RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams(); + params.leftMargin -= pixel * 3; + params.rightMargin -= pixel * 3; + if (gravity == CardAnimator.TOP) { + params.topMargin += pixel; + } else { + params.topMargin -= pixel; + } params.bottomMargin -= pixel; v.setLayoutParams(params); } - public static LayoutParams getMoveParams(View v, int upDown,int leftRight){ - RelativeLayout.LayoutParams original = (RelativeLayout.LayoutParams)v.getLayoutParams(); + public static LayoutParams getMoveParams(View v, int upDown, int leftRight) { + RelativeLayout.LayoutParams original = (RelativeLayout.LayoutParams) v.getLayoutParams(); //RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(original); RelativeLayout.LayoutParams params = cloneParams(original); + params.leftMargin += leftRight; params.rightMargin -= leftRight; params.topMargin -= upDown; @@ -31,45 +36,54 @@ public static LayoutParams getMoveParams(View v, int upDown,int leftRight){ return params; } - public static void move(View v, int upDown,int leftRight){ - RelativeLayout.LayoutParams params = getMoveParams(v,upDown,leftRight); + public static void move(View v, int upDown, int leftRight) { + RelativeLayout.LayoutParams params = getMoveParams(v, upDown, leftRight); v.setLayoutParams(params); } - public static LayoutParams scaleFrom(View v, LayoutParams params, int pixel) { + public static LayoutParams scaleFrom(View v, LayoutParams params, int pixel, int gravity) { Log.d("pixel", "onScroll: " + pixel); params = cloneParams(params); params.leftMargin -= pixel; params.rightMargin -= pixel; - params.topMargin -= pixel; + if (gravity == CardAnimator.TOP) { + params.topMargin += pixel; + } else { + params.topMargin -= pixel; + } params.bottomMargin -= pixel; - Log.d("pixel", "onScroll: " + pixel); v.setLayoutParams(params); return params; } - public static LayoutParams moveFrom(View v, LayoutParams params, int leftRight, int upDown) { + public static LayoutParams moveFrom(View v, LayoutParams params, int leftRight, int upDown, int gravity) { params = cloneParams(params); params.leftMargin += leftRight; params.rightMargin -= leftRight; - params.topMargin -= upDown; - params.bottomMargin += upDown; + + if (gravity == CardAnimator.BOTTOM) { + params.bottomMargin += upDown; + params.topMargin -= upDown; + } else { + params.bottomMargin -= upDown; + params.topMargin += upDown; + } v.setLayoutParams(params); return params; } //a copy method for RelativeLayout.LayoutParams for backward compartibility - public static RelativeLayout.LayoutParams cloneParams(RelativeLayout.LayoutParams params){ - RelativeLayout.LayoutParams copy = new RelativeLayout.LayoutParams(params.width,params.height); + public static RelativeLayout.LayoutParams cloneParams(RelativeLayout.LayoutParams params) { + RelativeLayout.LayoutParams copy = new RelativeLayout.LayoutParams(params.width, params.height); copy.leftMargin = params.leftMargin; copy.topMargin = params.topMargin; copy.rightMargin = params.rightMargin; copy.bottomMargin = params.bottomMargin; int[] rules = params.getRules(); - for (int i = 0 ; i< rules.length; i++ ){ - copy.addRule(i,rules[i]); + for (int i = 0; i < rules.length; i++) { + copy.addRule(i, rules[i]); } //copy.setMarginStart(params.getMarginStart()); //copy.setMarginEnd(params.getMarginEnd()); @@ -79,20 +93,20 @@ public static RelativeLayout.LayoutParams cloneParams(RelativeLayout.LayoutParam public static float distance(float x1, float y1, float x2, float y2) { - return (float) Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); + return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } public static int direction(float x1, float y1, float x2, float y2) { - if(x2>x1){//RIGHT - if(y2>y1){//BOTTOM + if (x2 > x1) {//RIGHT + if (y2 > y1) {//BOTTOM return DIRECTION_BOTTOM_RIGHT; - }else{//TOP + } else {//TOP return DIRECTION_TOP_RIGHT; } - }else{//LEFT - if(y2>y1){//BOTTOM + } else {//LEFT + if (y2 > y1) {//BOTTOM return DIRECTION_BOTTOM_LEFT; - }else{//TOP + } else {//TOP return DIRECTION_TOP_LEFT; } } diff --git a/CardStack/src/main/java/com/wenchao/cardstack/DefaultStackEventListener.java b/CardStack/src/main/java/com/wenchao/cardstack/DefaultStackEventListener.java index b4da3ff..f8065aa 100644 --- a/CardStack/src/main/java/com/wenchao/cardstack/DefaultStackEventListener.java +++ b/CardStack/src/main/java/com/wenchao/cardstack/DefaultStackEventListener.java @@ -1,5 +1,7 @@ package com.wenchao.cardstack; +import android.util.Log; + public class DefaultStackEventListener implements CardStack.CardEventListener { private float mThreshold; @@ -10,27 +12,36 @@ public DefaultStackEventListener(int i) { @Override public boolean swipeEnd(int section, float distance) { + + Log.d("rae", "swipeEnd:" + section + "-" + distance); + return distance > mThreshold; } @Override public boolean swipeStart(int section, float distance) { + + Log.d("rae", "swipeStart:" + section + "-" + distance); return false; } @Override public boolean swipeContinue(int section, float distanceX, float distanceY) { + + Log.d("rae", "swipeContinue:" + section + "-" + distanceX + "-" + distanceY); + return false; } @Override - public void discarded(int mIndex,int direction) { + public void discarded(int mIndex, int direction) { + Log.d("rae", "discarded:" + mIndex + "-" + direction); } @Override public void topCardTapped() { - + Log.d("rae", "topCardTapped"); } diff --git a/CardStack/src/main/res/anim/undo_anim.xml b/CardStack/src/main/res/anim/undo_anim.xml new file mode 100644 index 0000000..9eb39e8 --- /dev/null +++ b/CardStack/src/main/res/anim/undo_anim.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/CardStack/src/main/res/values/attrs.xml b/CardStack/src/main/res/values/attrs.xml index c789a7e..fbbdabe 100644 --- a/CardStack/src/main/res/values/attrs.xml +++ b/CardStack/src/main/res/values/attrs.xml @@ -1,7 +1,21 @@ - + + + + + + + + + + + + + + + diff --git a/DemoApp/src/main/AndroidManifest.xml b/DemoApp/src/main/AndroidManifest.xml index dbc78ff..a3cba02 100644 --- a/DemoApp/src/main/AndroidManifest.xml +++ b/DemoApp/src/main/AndroidManifest.xml @@ -1,21 +1,22 @@ + package="wxj.swipeablecardstack"> + android:theme="@style/AppTheme"> + android:label="@string/app_name"> - + - + + diff --git a/DemoApp/src/main/java/wxj/swipeablecardstack/MyActivity.java b/DemoApp/src/main/java/wxj/swipeablecardstack/MyActivity.java index 617eef0..193050d 100644 --- a/DemoApp/src/main/java/wxj/swipeablecardstack/MyActivity.java +++ b/DemoApp/src/main/java/wxj/swipeablecardstack/MyActivity.java @@ -6,6 +6,7 @@ import android.view.Menu; import android.view.MenuItem; +import com.wenchao.cardstack.CardAnimator; import com.wenchao.cardstack.CardStack; @@ -18,10 +19,10 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); - mCardStack = (CardStack)findViewById(R.id.container); + mCardStack = (CardStack) findViewById(R.id.container); mCardStack.setContentResource(R.layout.card_content); - mCardStack.setStackMargin(20); +// mCardStack.setStackMargin(20); mCardAdapter = new CardsDataAdapter(getApplicationContext()); mCardAdapter.add("test1"); @@ -29,10 +30,12 @@ protected void onCreate(Bundle savedInstanceState) { mCardAdapter.add("test3"); mCardAdapter.add("test4"); mCardAdapter.add("test5"); + mCardAdapter.add("test6"); + mCardAdapter.add("test7"); mCardStack.setAdapter(mCardAdapter); - if(mCardStack.getAdapter() != null) { + if (mCardStack.getAdapter() != null) { Log.i("MyActivity", "Card Stack size: " + mCardStack.getAdapter().getCount()); } } @@ -45,15 +48,54 @@ public boolean onCreateOptionsMenu(Menu menu) { return true; } + /** + * @param item + * @return + */ @Override public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); - if (id == R.id.action_settings) { + // 重置 + if (id == R.id.action_reset) { + mCardStack.reset(true); + return true; + } + + // 底部 + if (id == R.id.action_bottom) { + mCardStack.setStackGravity(mCardStack.getStackGravity() == CardAnimator.TOP ? CardAnimator.BOTTOM : CardAnimator.TOP); + mCardStack.reset(true); return true; } + + // 循环 + if (id == R.id.action_loop) { + mCardStack.setEnableLoop(!mCardStack.isEnableLoop()); + mCardStack.reset(true); + } + + // 是否允许旋转 + if (id == R.id.action_rotation) { + mCardStack.setEnableRotation(!mCardStack.isEnableRotation()); + mCardStack.reset(true); + } + + // 可见个数 + if (id == R.id.action_visibly_size) { + mCardStack.setVisibleCardNum(mCardStack.getVisibleCardNum() + 1); + } + + // 间隔 + if (id == R.id.action_span) { + mCardStack.setStackMargin(mCardStack.getStackMargin() + 10); + } + + if (id == R.id.action_settings) { + mCardStack.undo(); + } + + return super.onOptionsItemSelected(item); } } diff --git a/DemoApp/src/main/res/layout/activity_my.xml b/DemoApp/src/main/res/layout/activity_my.xml index 00cd5a1..e33d91c 100644 --- a/DemoApp/src/main/res/layout/activity_my.xml +++ b/DemoApp/src/main/res/layout/activity_my.xml @@ -1,20 +1,22 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MyActivity"> + android:gravity="center" + android:padding="10dp" + app:card_enable_loop="false" + app:card_enable_rotation="true" + app:card_gravity="top" + app:card_margin="20dp" + app:card_stack_size="4"/> diff --git a/DemoApp/src/main/res/layout/card_content.xml b/DemoApp/src/main/res/layout/card_content.xml index 464aba9..f6f250a 100644 --- a/DemoApp/src/main/res/layout/card_content.xml +++ b/DemoApp/src/main/res/layout/card_content.xml @@ -1,24 +1,31 @@ - + + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:minHeight="200dp" + android:orientation="vertical"> + + + android:id="@+id/content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + tools:text="Test Text"/> \ No newline at end of file diff --git a/DemoApp/src/main/res/menu/my.xml b/DemoApp/src/main/res/menu/my.xml index b1c1801..789131c 100644 --- a/DemoApp/src/main/res/menu/my.xml +++ b/DemoApp/src/main/res/menu/my.xml @@ -1,9 +1,45 @@

+ xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + tools:context=".MyActivity"> + + + + + + + + + + + + + android:title="stack orientation" + app:showAsAction="never"/> diff --git a/README.md b/README.md index 8b9307c..6d9d927 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,18 @@ AndroidSwipeableCardStack Change log: -Now it is compatible with api level 14 -Investigating compatibility with api level 13 +- provide option to infinitly swipe in a loop +- card rotation setting +- card gravity setting +- undo animation +Thanks for contributions from: +https://github.com/raee +https://github.com/rebus007 + -![image](https://raw.githubusercontent.com/wenchaojiang/AndroidSwipeableCardStack/master/pics/image2.png) +![image](https://github.com/raee/AndroidSwipeableCardStack/raw/RAE-DEV/pics/demo.gif) @@ -48,7 +54,7 @@ repositories { } dependencies { - compile(name:'android-card-stack-0.1.0', ext:'aar') + compile(name:'android-card-stack-0.*.*', ext:'aar') } ``` @@ -59,14 +65,19 @@ Configuration Put CardStack in your layout file ```xml - + android:gravity="center" + android:padding="10dp" + app:card_enable_loop="true" + app:card_enable_rotation="true" + app:card_gravity="top" + app:card_margin="10dp" + app:card_stack_size="4"/> ``` Create your card view layout file. @@ -178,9 +189,3 @@ Class YourListener extends CardStack.CardEventListener{ } ``` - -TODO ----- - -1. deploy to maven central -2. compatibility with api level 13 diff --git a/build.gradle b/build.gradle index e4b8ece..6fbf45f 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.novoda:bintray-release:0.3.4' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3eaab5e..594e41c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Oct 22 08:45:38 BST 2015 +#Tue Jul 05 11:52:26 CST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/pics/demo.gif b/pics/demo.gif new file mode 100644 index 0000000..1c588bd Binary files /dev/null and b/pics/demo.gif differ