├── tinder-swipe.gif
├── ic_launcher-web.png
├── libs
└── android-support-v4.jar
├── res
├── drawable-xhdpi
│ ├── card.png
│ ├── card2.png
│ ├── ic_launcher.png
│ ├── collect_fore.png
│ └── recommend_app_icon.png
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── values-v21
│ └── styles.xml
├── values
│ ├── styles.xml
│ ├── strings.xml
│ ├── dimens.xml
│ ├── attrs.xml
│ └── colors.xml
├── values-sw600dp
│ └── dimens.xml
├── menu
│ ├── main.xml
│ └── my.xml
├── values-sw720dp-land
│ └── dimens.xml
├── values-v11
│ └── styles.xml
├── values-w820dp
│ └── dimens.xml
├── values-v14
│ └── styles.xml
└── layout
│ ├── activity_main.xml
│ ├── no_circle_item.xml
│ ├── box_card_layout.xml
│ ├── item.xml
│ └── activity_my.xml
├── README.md
├── src
└── com
│ └── yiqivr
│ └── tinderswipe
│ ├── widget
│ ├── OnBaseFlingListener.java
│ ├── OnLeftRightFlingListener.java
│ ├── OnTopBottomFlingListener.java
│ ├── OnTopBottomFlingWithProportionListener.java
│ ├── HelperFlingWithProportionListener.java
│ ├── BaseFlingAdapterView.java
│ ├── MultiViewPager.java
│ ├── CircleProgress.java
│ ├── LinearRegression.java
│ ├── SwipeFlingAdapterView.java
│ ├── BoxLayout.java
│ └── FlingCardListener.java
│ └── MyActivity.java
├── .gitignore
├── .classpath
├── project.properties
├── proguard-project.txt
├── .project
└── AndroidManifest.xml
/tinder-swipe.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/tinder-swipe.gif
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/res/drawable-xhdpi/card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/res/drawable-xhdpi/card.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/card2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/res/drawable-xhdpi/card2.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/collect_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/res/drawable-xhdpi/collect_fore.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/recommend_app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lvning/TinderSwipe/HEAD/res/drawable-xhdpi/recommend_app_icon.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | TinderSwipe
2 | ===========
3 | tinder-like effect
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/OnBaseFlingListener.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | public interface OnBaseFlingListener {
4 | void removeFirstObjectInAdapter();
5 |
6 | void onAdapterAboutToEmpty(int itemsInAdapter);
7 | }
8 |
--------------------------------------------------------------------------------
/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Swipe cards
5 | Hello world!
6 | Settings
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/OnLeftRightFlingListener.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | public interface OnLeftRightFlingListener extends OnBaseFlingListener {
4 | void onLeftCardExit(Object dataObject);
5 | void onRightCardExit(Object dataObject);
6 | }
7 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/OnTopBottomFlingListener.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 |
4 | public interface OnTopBottomFlingListener extends OnBaseFlingListener {
5 | void onTopCardExit(Object dataObject);
6 |
7 | void onBottomCardExit(Object dataObject);
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 10dp
6 |
7 |
--------------------------------------------------------------------------------
/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/menu/my.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/OnTopBottomFlingWithProportionListener.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * @author lvning
7 | * @version create time:2014-9-25_上午10:20:00
8 | * @Description TODO
9 | */
10 | public interface OnTopBottomFlingWithProportionListener extends OnTopBottomFlingListener {
11 |
12 | public void onFlingSuccessPercent(View selfView, int percent, boolean flingTop);
13 |
14 | public void onFlingResetOrigin(View selfView);
15 | }
16 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/HelperFlingWithProportionListener.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | import android.view.View;
4 |
5 | public interface HelperFlingWithProportionListener {
6 | public void onCardExited();
7 |
8 | public void leftExit(Object dataObject);
9 |
10 | public void rightExit(Object dataObject);
11 |
12 | public void topExit(Object dataObject);
13 |
14 | public void bottomExit(Object dataObject);
15 |
16 | public void flingResetOrigin(View selfView);
17 |
18 | public void flingOccurPercent(View selfView, int percent, boolean swipeTop);
19 | }
20 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-17
15 |
--------------------------------------------------------------------------------
/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 | #00ff00
21 | #ff0000
22 |
23 |
--------------------------------------------------------------------------------
/res/layout/no_circle_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | TinderSwipe
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/BaseFlingAdapterView.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.AdapterView;
7 |
8 | public abstract class BaseFlingAdapterView extends AdapterView {
9 |
10 | private int heightMeasureSpec;
11 | private int widthMeasureSpec;
12 |
13 | public BaseFlingAdapterView(Context context) {
14 | super(context);
15 | }
16 |
17 | public BaseFlingAdapterView(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | }
20 |
21 | public BaseFlingAdapterView(Context context, AttributeSet attrs, int defStyle) {
22 | super(context, attrs, defStyle);
23 | }
24 |
25 | @Override
26 | public View getSelectedView() {
27 | throw new UnsupportedOperationException("Not supported");
28 | }
29 |
30 | @Override
31 | public void setSelection(int i) {
32 | throw new UnsupportedOperationException("Not supported");
33 | }
34 |
35 | @Override
36 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
37 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
38 | this.widthMeasureSpec = widthMeasureSpec;
39 | this.heightMeasureSpec = heightMeasureSpec;
40 | }
41 |
42 | public int getWidthMeasureSpec() {
43 | return widthMeasureSpec;
44 | }
45 |
46 | public int getHeightMeasureSpec() {
47 | return heightMeasureSpec;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/res/layout/box_card_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
17 |
18 |
30 |
31 |
43 |
44 |
--------------------------------------------------------------------------------
/res/layout/item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
31 |
32 |
42 |
43 |
--------------------------------------------------------------------------------
/res/layout/activity_my.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
14 |
15 |
21 |
22 |
28 |
29 |
37 |
38 |
45 |
46 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/MultiViewPager.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Point;
6 | import android.support.v4.view.ViewPager;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 |
10 | import com.yiqivr.tinderswipe.R;
11 |
12 | /**
13 | * @author lvning
14 | * @version create time:2014-10-5_下午12:52:01
15 | * @Description TODO
16 | */
17 | public class MultiViewPager extends ViewPager {
18 |
19 | /**
20 | * Maximum size.
21 | */
22 | private int mMaxWidth = -1;
23 | /**
24 | * Maximum size.
25 | */
26 | private int mMaxHeight = -1;
27 | /**
28 | * Child view inside a page to match the page size against.
29 | */
30 | private int mMatchWidthChildResId;
31 |
32 | /**
33 | * Internal state to schedule a new measurement pass.
34 | */
35 | private boolean mNeedsMeasurePage;
36 |
37 | private static void constrainTo(Point size, Point maxSize) {
38 | if (maxSize.x >= 0) {
39 | if (size.x > maxSize.x) {
40 | size.x = maxSize.x;
41 | }
42 | }
43 | if (maxSize.y >= 0) {
44 | if (size.y > maxSize.y) {
45 | size.y = maxSize.y;
46 | }
47 | }
48 | }
49 |
50 | public MultiViewPager(Context context) {
51 | super(context);
52 | }
53 |
54 | public MultiViewPager(Context context, AttributeSet attrs) {
55 | super(context, attrs);
56 | init(context, attrs);
57 | }
58 |
59 | private void init(Context context, AttributeSet attrs) {
60 | setClipChildren(false);
61 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MultiViewPager);
62 | setMaxWidth(ta.getDimensionPixelSize(R.styleable.MultiViewPager_android_maxWidth, -1));
63 | setMaxHeight(ta.getDimensionPixelSize(R.styleable.MultiViewPager_android_maxHeight, -1));
64 | setMatchChildWidth(ta.getResourceId(R.styleable.MultiViewPager_matchChildWidth, 0));
65 | ta.recycle();
66 | }
67 |
68 | @Override
69 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
70 | Point size = new Point(
71 | MeasureSpec.getSize(widthMeasureSpec),
72 | MeasureSpec.getSize(heightMeasureSpec));
73 | if (mMaxWidth >= 0 || mMaxHeight >= 0) {
74 | Point maxSize = new Point(mMaxWidth, mMaxHeight);
75 | constrainTo(size, maxSize);
76 | widthMeasureSpec = MeasureSpec.makeMeasureSpec(
77 | size.x,
78 | MeasureSpec.EXACTLY);
79 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(
80 | size.y,
81 | MeasureSpec.EXACTLY);
82 | }
83 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
84 | onMeasurePage(widthMeasureSpec, heightMeasureSpec);
85 | }
86 |
87 | protected void onMeasurePage(int widthMeasureSpec, int heightMeasureSpec) {
88 | // Only measure if a measurement pass was scheduled
89 | if (!mNeedsMeasurePage) {
90 | return;
91 | }
92 | if (mMatchWidthChildResId == 0) {
93 | mNeedsMeasurePage = false;
94 | } else if (getChildCount() > 0) {
95 | View child = getChildAt(0);
96 | child.measure(widthMeasureSpec, heightMeasureSpec);
97 | int pageWidth = child.getMeasuredWidth();
98 | View match = child.findViewById(mMatchWidthChildResId);
99 | if (match == null) {
100 | throw new NullPointerException(
101 | "MatchWithChildResId did not find that ID in the first fragment of the ViewPager; "
102 | + "is that view defined in the child view's layout? Note that MultiViewPager "
103 | + "only measures the child for index 0.");
104 | }
105 | int childWidth = match.getMeasuredWidth();
106 | // Check that the measurement was successful
107 | if (childWidth > 0) {
108 | mNeedsMeasurePage = false;
109 | int difference = pageWidth - childWidth;
110 | setPageMargin(-difference);
111 | int offscreen = (int) Math.ceil((float) pageWidth / (float) childWidth) + 1;
112 | setOffscreenPageLimit(offscreen);
113 | requestLayout();
114 | }
115 | }
116 | }
117 |
118 | @Override
119 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
120 | super.onSizeChanged(w, h, oldw, oldh);
121 | // Schedule a new measurement pass as the dimensions have changed
122 | mNeedsMeasurePage = true;
123 | }
124 |
125 | /**
126 | * Sets the child view inside a page to match the page size against.
127 | *
128 | * @param matchChildWidthResId
129 | */
130 | public void setMatchChildWidth(int matchChildWidthResId) {
131 | if (mMatchWidthChildResId != matchChildWidthResId) {
132 | mMatchWidthChildResId = matchChildWidthResId;
133 | mNeedsMeasurePage = true;
134 | }
135 | }
136 |
137 | /**
138 | * Sets the maximum size.
139 | *
140 | * @param width
141 | */
142 | public void setMaxWidth(int width) {
143 | mMaxWidth = width;
144 | }
145 |
146 | /**
147 | * Sets the maximum size.
148 | *
149 | * @param height
150 | */
151 | public void setMaxHeight(int height) {
152 | mMaxHeight = height;
153 | }
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/CircleProgress.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.graphics.RectF;
11 | import android.util.AttributeSet;
12 | import android.view.View;
13 |
14 | import com.yiqivr.tinderswipe.R;
15 |
16 | /**
17 | * @author lvning
18 | * @version create time:2014-9-15_下午4:13:50
19 | * @Description 圆形进度
20 | */
21 | public class CircleProgress extends View {
22 |
23 | private CircleAttribute mCircleAttribute;
24 | private int mMaxProgress = 100;
25 | private int mSubCurProgress;
26 | private int backColor, foreColor;
27 | private Bitmap centerImg;
28 |
29 | public CircleProgress(Context paramContext) {
30 | this(paramContext, null);
31 | }
32 |
33 | public CircleProgress(Context paramContext, AttributeSet paramAttributeSet) {
34 | super(paramContext, paramAttributeSet);
35 | init(paramContext, paramAttributeSet);
36 | defaultParam(paramContext);
37 | }
38 |
39 | private void init(Context paramContext, AttributeSet paramAttributeSet) {
40 | final TypedArray ta = paramContext.obtainStyledAttributes(paramAttributeSet, R.styleable.CircleProgress);
41 | backColor = ta.getColor(R.styleable.CircleProgress_backColor, Color.parseColor("#898989"));
42 | foreColor = ta.getColor(R.styleable.CircleProgress_foreColor, Color.parseColor("#13DEFF"));
43 | int centerImgId = ta.getResourceId(R.styleable.CircleProgress_centerPic, android.R.drawable.star_on);
44 | centerImg = BitmapFactory.decodeResource(getResources(), centerImgId);
45 | ta.recycle();
46 | }
47 |
48 | private void defaultParam(Context paramContext) {
49 | this.mCircleAttribute = new CircleAttribute();
50 | this.mMaxProgress = 100;
51 | this.mSubCurProgress = 0;
52 | }
53 |
54 | @Override
55 | public void onDraw(Canvas canvas) {
56 | super.onDraw(canvas);
57 | float f1 = 360.0F * this.mSubCurProgress / this.mMaxProgress;
58 | canvas.drawArc(this.mCircleAttribute.inRoundOval, 0.0F, 360.0F, this.mCircleAttribute.mBRoundPaintsFill,
59 | this.mCircleAttribute.circlePaint);
60 | canvas.drawArc(this.mCircleAttribute.mRoundOval, 0.0F, 360.0F, this.mCircleAttribute.mBRoundPaintsFill,
61 | this.mCircleAttribute.mBottomPaint);
62 | canvas.drawArc(this.mCircleAttribute.mRoundOval, this.mCircleAttribute.mDrawPos, f1,
63 | this.mCircleAttribute.mBRoundPaintsFill, this.mCircleAttribute.mSubPaint);
64 | canvas.drawBitmap(centerImg, this.mCircleAttribute.inRoundOval.centerX() - centerImg.getWidth() / 2,
65 | this.mCircleAttribute.inRoundOval.top + centerImg.getHeight() / 2, null);
66 | }
67 |
68 | @Override
69 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
70 | int i = View.MeasureSpec.getSize(widthMeasureSpec);
71 | View.MeasureSpec.getSize(heightMeasureSpec);
72 | setMeasuredDimension(resolveSize(i, widthMeasureSpec), resolveSize(i, heightMeasureSpec));
73 | }
74 |
75 | @Override
76 | protected void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {
77 | super.onSizeChanged(paramInt1, paramInt2, paramInt3, paramInt4);
78 | this.mCircleAttribute.autoFix(paramInt1, paramInt2);
79 | }
80 |
81 | /**
82 | * 设置进度
83 | *
84 | * @param progress
85 | * 取值范围0-100
86 | */
87 | public void setCurProgress(int progress) {
88 | this.mSubCurProgress = progress;
89 | invalidate();
90 | }
91 |
92 | public int getCurProgress() {
93 | return mSubCurProgress;
94 | }
95 |
96 | private class CircleAttribute {
97 | public boolean mBRoundPaintsFill = true;
98 | public Paint mBottomPaint;
99 | public Paint circlePaint;
100 | public int mDrawPos = -90;
101 | public int mSubPaintColor = foreColor;
102 | public int mBottomPaintColor = backColor;
103 | public int mPaintWidth = 0;
104 | public RectF mRoundOval = new RectF();
105 | public RectF inRoundOval = new RectF();
106 | public int mSidePaintInterval = 4;
107 | public Paint mSubPaint;
108 |
109 | public CircleAttribute() {
110 | this.mSubPaint = new Paint();
111 | this.mSubPaint.setAntiAlias(true);
112 | this.mSubPaint.setStyle(Paint.Style.FILL);
113 | this.mSubPaint.setStrokeWidth(this.mPaintWidth);
114 | this.mSubPaint.setColor(this.mSubPaintColor);
115 |
116 | this.mBottomPaint = new Paint();
117 | this.mBottomPaint.setAntiAlias(true);
118 | this.mBottomPaint.setStyle(Paint.Style.FILL);
119 | this.mBottomPaint.setStrokeWidth(this.mPaintWidth);
120 | this.mBottomPaint.setColor(this.mBottomPaintColor);
121 |
122 | this.circlePaint = new Paint();
123 | this.circlePaint.setAntiAlias(true);
124 | this.circlePaint.setStyle(Paint.Style.FILL);
125 | this.circlePaint.setStrokeWidth(this.mPaintWidth);
126 | this.circlePaint.setColor(this.mBottomPaintColor);
127 | }
128 |
129 | public void autoFix(int width, int height) {
130 | int left = CircleProgress.this.getPaddingLeft();
131 | int right = CircleProgress.this.getPaddingRight();
132 | int top = CircleProgress.this.getPaddingTop();
133 | int bottom = CircleProgress.this.getPaddingBottom();
134 | this.mRoundOval.set(left + this.mPaintWidth / 2 + mSidePaintInterval, top + this.mPaintWidth / 2
135 | + mSidePaintInterval, width - right - this.mPaintWidth / 2 - mSidePaintInterval, height - bottom
136 | - this.mPaintWidth / 2 - mSidePaintInterval);
137 | this.inRoundOval.set(left + this.mPaintWidth / 2, top + this.mPaintWidth / 2, width - right
138 | - this.mPaintWidth / 2, height - bottom - this.mPaintWidth / 2);
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/LinearRegression.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 |
4 | /*************************************************************************
5 | * Compilation: javac LinearRegression.java
6 | * Execution: java LinearRegression
7 | *
8 | * Compute least squares solution to y = beta * x + alpha.
9 | * Simple linear regression.
10 | *
11 | *************************************************************************/
12 |
13 |
14 | /**
15 | * The LinearRegression class performs a simple linear regression
16 | * on an set of N data points (yi, xi).
17 | * That is, it fits a straight line y = α + β x,
18 | * (where y is the response variable, x is the predictor variable,
19 | * α is the y-intercept, and β is the slope)
20 | * that minimizes the sum of squared residuals of the linear regression model.
21 | * It also computes associated statistics, including the coefficient of
22 | * determination R2 and the standard deviation of the
23 | * estimates for the slope and y-intercept.
24 | *
25 | * @author Robert Sedgewick
26 | * @author Kevin Wayne
27 | */
28 | public class LinearRegression {
29 | private final int N;
30 | private final double alpha, beta;
31 | private final double R2;
32 | private final double svar, svar0, svar1;
33 |
34 | /**
35 | * Performs a linear regression on the data points (y[i], x[i]).
36 | * @param x the values of the predictor variable
37 | * @param y the corresponding values of the response variable
38 | * @throws java.lang.IllegalArgumentException if the lengths of the two arrays are not equal
39 | */
40 | public LinearRegression(float[] x, float[] y) {
41 | if (x.length != y.length) {
42 | throw new IllegalArgumentException("array lengths are not equal");
43 | }
44 | N = x.length;
45 |
46 | // first pass
47 | double sumx = 0.0, sumy = 0.0;
48 | for (int i = 0; i < N; i++) sumx += x[i];
49 | for (int i = 0; i < N; i++) {
50 | }
51 | for (int i = 0; i < N; i++) sumy += y[i];
52 | double xbar = sumx / N;
53 | double ybar = sumy / N;
54 |
55 | // second pass: compute summary statistics
56 | double xxbar = 0.0, yybar = 0.0, xybar = 0.0;
57 | for (int i = 0; i < N; i++) {
58 | xxbar += (x[i] - xbar) * (x[i] - xbar);
59 | yybar += (y[i] - ybar) * (y[i] - ybar);
60 | xybar += (x[i] - xbar) * (y[i] - ybar);
61 | }
62 | beta = xybar / xxbar;
63 | alpha = ybar - beta * xbar;
64 |
65 | // more statistical analysis
66 | double rss = 0.0; // residual sum of squares
67 | double ssr = 0.0; // regression sum of squares
68 | for (int i = 0; i < N; i++) {
69 | double fit = beta*x[i] + alpha;
70 | rss += (fit - y[i]) * (fit - y[i]);
71 | ssr += (fit - ybar) * (fit - ybar);
72 | }
73 |
74 | int degreesOfFreedom = N-2;
75 | R2 = ssr / yybar;
76 | svar = rss / degreesOfFreedom;
77 | svar1 = svar / xxbar;
78 | svar0 = svar/N + xbar*xbar*svar1;
79 | }
80 |
81 | /**
82 | * Returns the y-intercept α of the best of the best-fit line y = α + β x.
83 | * @return the y-intercept α of the best-fit line y = α + β x
84 | */
85 | public double intercept() {
86 | return alpha;
87 | }
88 |
89 | /**
90 | * Returns the slope β of the best of the best-fit line y = α + β x.
91 | * @return the slope β of the best-fit line y = α + β x
92 | */
93 | public double slope() {
94 | return beta;
95 | }
96 |
97 | /**
98 | * Returns the coefficient of determination R2.
99 | * @return the coefficient of determination R2, which is a real number between 0 and 1
100 | */
101 | public double R2() {
102 | return R2;
103 | }
104 |
105 | /**
106 | * Returns the standard error of the estimate for the intercept.
107 | * @return the standard error of the estimate for the intercept
108 | */
109 | public double interceptStdErr() {
110 | return Math.sqrt(svar0);
111 | }
112 |
113 | /**
114 | * Returns the standard error of the estimate for the slope.
115 | * @return the standard error of the estimate for the slope
116 | */
117 | public double slopeStdErr() {
118 | return Math.sqrt(svar1);
119 | }
120 |
121 | /**
122 | * Returns the expected response y given the value of the predictor
123 | * variable x.
124 | * @param x the value of the predictor variable
125 | * @return the expected response y given the value of the predictor
126 | * variable x
127 | */
128 | public double predict(double x) {
129 | return beta*x + alpha;
130 | }
131 |
132 | /**
133 | * Returns a string representation of the simple linear regression model.
134 | * @return a string representation of the simple linear regression model,
135 | * including the best-fit line and the coefficient of determination R2
136 | */
137 | public String toString() {
138 | String s = "";
139 | s += String.format("%.2f N + %.2f", slope(), intercept());
140 | return s + " (R^2 = " + String.format("%.3f", R2()) + ")";
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/SwipeFlingAdapterView.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | import android.content.Context;
4 | import android.database.DataSetObserver;
5 | import android.util.AttributeSet;
6 | import android.view.Gravity;
7 | import android.view.View;
8 | import android.widget.Adapter;
9 | import android.widget.FrameLayout;
10 |
11 | import com.yiqivr.tinderswipe.widget.FlingCardListener.SWIPEMODE;
12 |
13 | public class SwipeFlingAdapterView extends BaseFlingAdapterView implements HelperFlingWithProportionListener {
14 |
15 | private Adapter mAdapter;
16 | private int maxVisible = 4;
17 | private int minAdapterStack = 6;
18 | private int lastObjectInStack = 0;
19 | private OnLeftRightFlingListener lrFlingListener;
20 | private OnTopBottomFlingListener tbFlingListener;
21 | private SWIPEMODE swipeMode = SWIPEMODE.LEFT_RIGHT;
22 |
23 | public SwipeFlingAdapterView(Context context) {
24 | super(context);
25 | }
26 |
27 | public SwipeFlingAdapterView(Context context, AttributeSet attrs) {
28 | super(context, attrs);
29 | }
30 |
31 | public SwipeFlingAdapterView(Context context, AttributeSet attrs, int defStyle) {
32 | super(context, attrs, defStyle);
33 | }
34 |
35 | private final DataSetObserver mDataSetObserver = new DataSetObserver() {
36 | @Override
37 | public void onChanged() {
38 | observedChange();
39 | }
40 |
41 | @Override
42 | public void onInvalidated() {
43 | observedChange();
44 | }
45 |
46 | private void observedChange() {
47 |
48 | final int adapterCount = mAdapter.getCount();
49 |
50 | if (adapterCount <= minAdapterStack) {
51 | if (swipeMode == SWIPEMODE.LEFT_RIGHT) {
52 | if (lrFlingListener != null)
53 | lrFlingListener.onAdapterAboutToEmpty(adapterCount);
54 | } else {
55 | if (tbFlingListener != null)
56 | tbFlingListener.onAdapterAboutToEmpty(adapterCount);
57 | }
58 | }
59 |
60 | if (adapterCount == 0) {
61 | removeAllViewsInLayout();
62 | } else if (adapterCount == 1) {
63 | removeAllViewsInLayout();
64 | layoutChildren(0);
65 | setTopView();
66 | } else if (adapterCount <= maxVisible) {
67 | try {
68 | removeViewsInLayout(0, lastObjectInStack);
69 | } catch (NullPointerException e) {
70 | e.printStackTrace();
71 | }
72 | layoutChildren(1);
73 | }
74 | }
75 | };
76 |
77 | @Override
78 | public Adapter getAdapter() {
79 | return mAdapter;
80 | }
81 |
82 | @Override
83 | public void setAdapter(Adapter adapter) {
84 | if (mAdapter != null) {
85 | mAdapter.unregisterDataSetObserver(mDataSetObserver);
86 | }
87 | this.mAdapter = adapter;
88 | if (mAdapter != null) {
89 | mAdapter.registerDataSetObserver(mDataSetObserver);
90 | }
91 | requestLayout();
92 | }
93 |
94 | @Override
95 | public void onCardExited() {
96 | if (swipeMode == SWIPEMODE.LEFT_RIGHT) {
97 | if (lrFlingListener != null)
98 | lrFlingListener.removeFirstObjectInAdapter();
99 | } else {
100 | if (tbFlingListener != null)
101 | tbFlingListener.removeFirstObjectInAdapter();
102 | }
103 | removeAllViewsInLayout();
104 | layoutChildren(0);
105 | setTopView();
106 | }
107 |
108 | @Override
109 | public void leftExit(Object dataObject) {
110 | if (lrFlingListener != null) {
111 | lrFlingListener.onLeftCardExit(dataObject);
112 | }
113 | }
114 |
115 | @Override
116 | public void rightExit(Object dataObject) {
117 | if (lrFlingListener != null) {
118 | lrFlingListener.onRightCardExit(dataObject);
119 | }
120 | }
121 |
122 | @Override
123 | public void topExit(Object dataObject) {
124 | if (tbFlingListener != null) {
125 | tbFlingListener.onTopCardExit(dataObject);
126 | }
127 | }
128 |
129 | @Override
130 | public void bottomExit(Object dataObject) {
131 | if (tbFlingListener != null) {
132 | tbFlingListener.onBottomCardExit(dataObject);
133 | }
134 | }
135 |
136 | public void setSwipeMode(SWIPEMODE swipeMode) {
137 | this.swipeMode = swipeMode;
138 | }
139 |
140 | public void setLeftRightFlingListener(OnLeftRightFlingListener onFlingListener) {
141 | this.lrFlingListener = onFlingListener;
142 | }
143 |
144 | public void setTopBottomFlingListener(OnTopBottomFlingListener tbFlingListener) {
145 | this.tbFlingListener = tbFlingListener;
146 | }
147 |
148 | @Override
149 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
150 | super.onLayout(changed, left, top, right, bottom);
151 | if (mAdapter == null) {
152 | return;
153 | }
154 | removeAllViewsInLayout();
155 | layoutChildren(0);
156 | setTopView();
157 | }
158 |
159 | private void layoutChildren(final int index) {
160 | int position = index;
161 | while (position < mAdapter.getCount() && position < maxVisible) {
162 | View newBottomChild = mAdapter.getView(position, null, this);
163 | if (newBottomChild.getVisibility() != GONE) {
164 | addAndMeasureChild(newBottomChild);
165 | lastObjectInStack = position;
166 | }
167 | position++;
168 | }
169 |
170 | positionItems(index);
171 | }
172 |
173 | private void addAndMeasureChild(View child) {
174 |
175 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
176 | if (lp == null) {
177 | lp = new FrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
178 | android.widget.FrameLayout.LayoutParams.WRAP_CONTENT);
179 | lp.gravity = Gravity.CENTER;
180 | }
181 |
182 | addViewInLayout(child, 0, lp, true);
183 |
184 | int childWidthSpec = getChildMeasureSpec(getWidthMeasureSpec(), getPaddingLeft() + getPaddingRight()
185 | + lp.leftMargin + lp.rightMargin, lp.width);
186 |
187 | int childHeightSpec = getChildMeasureSpec(getHeightMeasureSpec(), getPaddingTop() + getPaddingBottom()
188 | + lp.topMargin + lp.bottomMargin, lp.height);
189 |
190 | child.measure(childWidthSpec, childHeightSpec);
191 | }
192 |
193 | private void positionItems(int firstIteration) {
194 | for (int index = firstIteration; index < getChildCount(); index++) {
195 | View child = getChildAt(index);
196 |
197 | int width = child.getMeasuredWidth();
198 | int height = child.getMeasuredHeight();
199 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
200 |
201 | int childLeft;
202 | int childTop;
203 |
204 | int gravity = lp.gravity;
205 | if (gravity == -1) {
206 | gravity = Gravity.TOP | Gravity.START;
207 | }
208 |
209 | final int layoutDirection = getLayoutDirection();
210 | final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
211 | final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
212 |
213 | switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
214 | case Gravity.CENTER_HORIZONTAL:
215 | childLeft = (getWidth() + getPaddingLeft() - getPaddingRight() - width) / 2 + lp.leftMargin
216 | - lp.rightMargin;
217 | break;
218 | case Gravity.RIGHT:
219 | childLeft = getWidth() + getPaddingRight() - width - lp.rightMargin;
220 | break;
221 | case Gravity.LEFT:
222 | default:
223 | childLeft = getPaddingLeft() + lp.leftMargin;
224 | break;
225 | }
226 |
227 | switch (verticalGravity) {
228 | case Gravity.CENTER_VERTICAL:
229 | childTop = (getHeight() + getPaddingTop() - getPaddingBottom() - height) / 2 + lp.topMargin
230 | - lp.bottomMargin;
231 | break;
232 | case Gravity.BOTTOM:
233 | childTop = getHeight() - getPaddingBottom() - height - lp.bottomMargin;
234 | break;
235 | case Gravity.TOP:
236 | default:
237 | childTop = getPaddingTop() + lp.topMargin;
238 | break;
239 | }
240 |
241 | child.layout(childLeft, childTop, childLeft + width, childTop + height);
242 | }
243 | }
244 |
245 | private void setTopView() {
246 | if (getChildCount() > 0) {
247 | // Log.d("", "lastObjectInStack = " + lastObjectInStack);
248 | View tv = getChildAt(lastObjectInStack);
249 | View nextTv = null;
250 | if (getChildCount() > 1) {
251 | nextTv = getChildAt(lastObjectInStack - 1);
252 | }
253 |
254 | if (tv != null) {
255 | FlingCardListener flingCardListener = new FlingCardListener(swipeMode, tv, nextTv, getWidth(),
256 | getHeight(), tv.getX(), tv.getY(), tv.getHeight(), tv.getWidth(), mAdapter.getItem(0), this);
257 | if(bottomFinalPosX != 0 && bottomFinalPosY != 0)
258 | flingCardListener.setBottomFinalPos(bottomFinalPosX, bottomFinalPosY);
259 | tv.setOnTouchListener(flingCardListener);
260 | }
261 | }
262 | }
263 |
264 | private float bottomFinalPosX, bottomFinalPosY;
265 |
266 | public void setBottomFinalPos(float bottomFinalPosX, float bottomFinalPosY) {
267 | this.bottomFinalPosX = bottomFinalPosX;
268 | this.bottomFinalPosY = bottomFinalPosY;
269 | }
270 |
271 | public void setMaxVisible(int maxVisible) {
272 | this.maxVisible = maxVisible;
273 | }
274 |
275 | public void setMinStackInAdapter(int minAdapterStack) {
276 | this.minAdapterStack = minAdapterStack;
277 | }
278 |
279 | @Override
280 | public LayoutParams generateLayoutParams(AttributeSet attrs) {
281 | return new FrameLayout.LayoutParams(getContext(), attrs);
282 | }
283 |
284 | @Override
285 | public void flingOccurPercent(View selfView, int percent, boolean swipeTop) {
286 | if (tbFlingListener != null && tbFlingListener instanceof OnTopBottomFlingWithProportionListener) {
287 | ((OnTopBottomFlingWithProportionListener) tbFlingListener).onFlingSuccessPercent(selfView, percent,
288 | swipeTop);
289 | }
290 | }
291 |
292 | @Override
293 | public void flingResetOrigin(View selfView) {
294 | if (tbFlingListener != null && tbFlingListener instanceof OnTopBottomFlingWithProportionListener) {
295 | ((OnTopBottomFlingWithProportionListener) tbFlingListener).onFlingResetOrigin(selfView);
296 | }
297 | }
298 |
299 | }
300 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/BoxLayout.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.Animator.AnimatorListener;
5 | import android.animation.AnimatorSet;
6 | import android.animation.ObjectAnimator;
7 | import android.content.Context;
8 | import android.util.AttributeSet;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.animation.AccelerateInterpolator;
12 | import android.view.animation.DecelerateInterpolator;
13 | import android.view.animation.OvershootInterpolator;
14 | import android.widget.LinearLayout;
15 |
16 | import com.yiqivr.tinderswipe.R;
17 |
18 | /**
19 | * @author lvning
20 | * @version create time:2014-10-17_下午4:17:03
21 | * @Description 收藏盒子
22 | */
23 | public class BoxLayout extends LinearLayout {
24 |
25 | // 收藏单个动画
26 | public static final int STACK_INDEX_BACK = 0;
27 | public static final int STACK_INDEX_CENTER = 1;
28 | public static final int STACK_INDEX_FORE = 2;
29 | // 查看收藏动画
30 | public static final int ONE_COLLECT_ANIM = 3;
31 | public static final int TWO_COLLECT_ANIM = 4;
32 | public static final int MORE_COLLECT_ANIM = 5;
33 | // 收回收藏动画
34 | public static final int ONE_PAGER_ANIM = 6;
35 |
36 | public static final int TWO_PAGER_ANIM_LEFT = 7;// 两个,左边在中间
37 | public static final int TWO_PAGER_ANIM_RIGHT = 8;// 两个,右边在中间
38 | public static final int MORE_PAGER_ANIM_LEFT = 9;// 多个,左边在中间
39 | public static final int MORE_PAGER_ANIM_RIGHT = 10;// 多个,右边在中间
40 |
41 | public static final int MORE_PAGER_ANIM_OTHER = 11;// 其他
42 |
43 | private View foreView, centerView, backView;
44 | private boolean foreVisible, centerVisible, backVisible;
45 |
46 | private static final long ANIM_TIME = 600l;
47 |
48 | public BoxLayout(Context context, AttributeSet attrs) {
49 | super(context, attrs);
50 | init(context);
51 | }
52 |
53 | public BoxLayout(Context context) {
54 | super(context);
55 | init(context);
56 | }
57 |
58 | private void init(Context context) {
59 | LayoutInflater.from(context).inflate(R.layout.box_card_layout, this);
60 | foreView = findViewById(R.id.boxpic_out);
61 | centerView = findViewById(R.id.boxpic_out1);
62 | backView = findViewById(R.id.boxpic_out2);
63 | setGone();
64 | }
65 |
66 | protected void setGone() {
67 | foreView.setAlpha(0);
68 | centerView.setAlpha(0);
69 | backView.setAlpha(0);
70 | }
71 |
72 | protected void setVisible() {
73 | if (foreVisible)
74 | foreView.setAlpha(1);
75 | if (centerVisible)
76 | centerView.setAlpha(1);
77 | if (backVisible)
78 | backView.setAlpha(1);
79 | }
80 |
81 | public void setForeImage(int resId) {
82 | foreView.setBackgroundResource(resId);
83 | if (foreView.getAlpha() == 0) {
84 | foreView.setAlpha(1);
85 | }
86 | startAnim(STACK_INDEX_FORE);
87 | }
88 |
89 | public void setCenterImage(int resId) {
90 | centerView.setBackgroundResource(resId);
91 | centerView.setAlpha(1);
92 | startAnim(STACK_INDEX_CENTER);
93 | }
94 |
95 | public void setBackImage(int resId) {
96 | backView.setBackgroundResource(resId);
97 | backView.setAlpha(1);
98 | startAnim(STACK_INDEX_BACK);
99 | }
100 |
101 | public float getChildTop() {
102 | return backView.getTop();
103 | }
104 |
105 | public float getChildLeft() {
106 | return backView.getLeft();
107 | }
108 |
109 | private void startAnim(int index) {
110 | View v = null;
111 | switch (index) {
112 | case STACK_INDEX_BACK:
113 | v = backView;
114 | break;
115 | case STACK_INDEX_CENTER:
116 | v = centerView;
117 | break;
118 | case STACK_INDEX_FORE:
119 | v = foreView;
120 | break;
121 | default:
122 | break;
123 | }
124 | ObjectAnimator transAnim = ObjectAnimator.ofFloat(v, "translationY", 150f, 0f);
125 | transAnim.setInterpolator(new OvershootInterpolator());
126 | transAnim.setDuration(250);
127 | transAnim.start();
128 | }
129 |
130 | public void setReadyPullIn(int animType, float transByY, final float backTransByX, final Runnable endAction) {
131 | setVisible();
132 | ObjectAnimator transAnim = ObjectAnimator.ofFloat(this, "translationY", transByY, 0);
133 | transAnim.setDuration(ANIM_TIME).setInterpolator(new DecelerateInterpolator());
134 |
135 | ObjectAnimator centerRotateAnim = ObjectAnimator.ofFloat(centerView, "rotation", 0f, -5);
136 | centerRotateAnim.setDuration(ANIM_TIME);
137 |
138 | ObjectAnimator foreRotateAnim = ObjectAnimator.ofFloat(foreView, "rotation", 0f, -5);
139 | foreRotateAnim.setDuration(ANIM_TIME);
140 |
141 | ObjectAnimator centerTransAnim = ObjectAnimator.ofFloat(centerView, "translationX", backTransByX, 0);
142 | centerTransAnim.setDuration(ANIM_TIME).setInterpolator(new OvershootInterpolator());
143 |
144 | ObjectAnimator backTransAnim = ObjectAnimator.ofFloat(backView, "translationX", backTransByX, 0);
145 | backTransAnim.setDuration(ANIM_TIME).setInterpolator(new OvershootInterpolator());
146 |
147 | ObjectAnimator backTransAnim2 = ObjectAnimator.ofFloat(backView, "translationX", 2 * backTransByX, 0);
148 | backTransAnim2.setDuration(ANIM_TIME).setInterpolator(new OvershootInterpolator());
149 |
150 | ObjectAnimator centerTransAnimN1 = ObjectAnimator.ofFloat(centerView, "translationX", -backTransByX, 0);
151 | centerTransAnimN1.setDuration(ANIM_TIME).setInterpolator(new OvershootInterpolator());
152 |
153 | ObjectAnimator foreTransAnimN2 = ObjectAnimator.ofFloat(foreView, "translationX", -2 * backTransByX, 0);
154 | foreTransAnimN2.setDuration(ANIM_TIME).setInterpolator(new OvershootInterpolator());
155 |
156 | ObjectAnimator foreTransAnimN1 = ObjectAnimator.ofFloat(foreView, "translationX", -backTransByX, 0);
157 | foreTransAnimN1.setDuration(ANIM_TIME).setInterpolator(new OvershootInterpolator());
158 |
159 | AnimatorSet set = new AnimatorSet();
160 | switch (animType) {
161 | case ONE_PAGER_ANIM:
162 | set.play(transAnim);
163 | break;
164 | case TWO_PAGER_ANIM_LEFT:
165 | set.play(transAnim).before(centerRotateAnim).before(backTransAnim);
166 | break;
167 | case TWO_PAGER_ANIM_RIGHT:
168 | centerView.setTranslationX(-backTransByX);
169 | backView.setTranslationX(0);
170 | set.play(centerTransAnimN1).with(centerRotateAnim).after(transAnim);
171 | break;
172 | case MORE_PAGER_ANIM_LEFT:
173 | set.play(transAnim).before(foreRotateAnim).before(centerTransAnim).before(centerRotateAnim)
174 | .before(backTransAnim2);
175 | break;
176 | case MORE_PAGER_ANIM_RIGHT:
177 | foreView.setTranslationX(-2 * backTransByX);
178 | centerView.setTranslationX(-backTransByX);
179 | backView.setTranslationX(0);
180 | set.play(foreTransAnimN2).with(foreRotateAnim).with(centerTransAnimN1).with(centerRotateAnim)
181 | .after(transAnim);
182 | break;
183 | case MORE_PAGER_ANIM_OTHER:
184 | foreView.setTranslationX(-backTransByX);
185 | centerView.setTranslationX(0);
186 | backView.setTranslationX(backTransByX);
187 | set.play(transAnim).before(foreTransAnimN1).before(foreRotateAnim).before(backTransAnim)
188 | .before(centerRotateAnim);
189 | break;
190 | default:
191 | break;
192 | }
193 | set.start();
194 | set.addListener(new AnimatorListener() {
195 |
196 | @Override
197 | public void onAnimationStart(Animator animation) {
198 |
199 | }
200 |
201 | @Override
202 | public void onAnimationRepeat(Animator animation) {
203 |
204 | }
205 |
206 | @Override
207 | public void onAnimationEnd(Animator animation) {
208 | endAction.run();
209 |
210 | }
211 |
212 | @Override
213 | public void onAnimationCancel(Animator animation) {
214 |
215 | }
216 | });
217 | }
218 |
219 | public void popOut(int animType, float transByY, final float backTransByX, final Runnable endAction) {
220 | ObjectAnimator transAnim = ObjectAnimator.ofFloat(this, "translationY", 0, transByY);
221 | transAnim.setDuration(ANIM_TIME).setInterpolator(new OvershootInterpolator());
222 |
223 | ObjectAnimator centerRotateAnim = ObjectAnimator.ofFloat(centerView, "rotation", -5, 0f);
224 | centerRotateAnim.setDuration(ANIM_TIME);
225 |
226 | ObjectAnimator foreRotateAnim = ObjectAnimator.ofFloat(foreView, "rotation", -5, 0f);
227 | foreRotateAnim.setDuration(ANIM_TIME);
228 |
229 | ObjectAnimator centerTransAnim = ObjectAnimator.ofFloat(centerView, "translationX", 0, backTransByX);
230 | centerTransAnim.setDuration(ANIM_TIME).setInterpolator(new AccelerateInterpolator());
231 |
232 | ObjectAnimator backTransAnim = ObjectAnimator.ofFloat(backView, "translationX", 0, backTransByX);
233 | backTransAnim.setDuration(ANIM_TIME).setInterpolator(new AccelerateInterpolator());
234 |
235 | ObjectAnimator backTransAnim2 = ObjectAnimator.ofFloat(backView, "translationX", 0, 2 * backTransByX);
236 | backTransAnim2.setDuration(ANIM_TIME).setInterpolator(new AccelerateInterpolator());
237 |
238 | AnimatorSet set = new AnimatorSet();
239 | switch (animType) {
240 | case ONE_COLLECT_ANIM:
241 | set.play(transAnim);
242 | backVisible = true;
243 | centerVisible = false;
244 | foreVisible = false;
245 | break;
246 | case TWO_COLLECT_ANIM:
247 | set.play(transAnim).with(centerRotateAnim).before(backTransAnim);
248 | backVisible = true;
249 | centerVisible = true;
250 | foreVisible = false;
251 | break;
252 | case MORE_COLLECT_ANIM:
253 | set.play(transAnim).with(centerRotateAnim).with(foreRotateAnim).before(backTransAnim2)
254 | .before(centerTransAnim);
255 | backVisible = true;
256 | centerVisible = true;
257 | foreVisible = true;
258 | break;
259 | default:
260 | break;
261 | }
262 | set.start();
263 | set.addListener(new AnimatorListener() {
264 |
265 | @Override
266 | public void onAnimationStart(Animator animation) {
267 |
268 | }
269 |
270 | @Override
271 | public void onAnimationRepeat(Animator animation) {
272 |
273 | }
274 |
275 | @Override
276 | public void onAnimationEnd(Animator animation) {
277 | setGone();
278 | endAction.run();
279 | }
280 |
281 | @Override
282 | public void onAnimationCancel(Animator animation) {
283 |
284 | }
285 | });
286 | }
287 |
288 | }
289 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/widget/FlingCardListener.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.util.Log;
6 | import android.view.MotionEvent;
7 | import android.view.VelocityTracker;
8 | import android.view.View;
9 | import android.view.animation.AccelerateInterpolator;
10 | import android.view.animation.OvershootInterpolator;
11 |
12 | public class FlingCardListener implements View.OnTouchListener {
13 |
14 | private final float originalX;
15 | private final float originalY;
16 | private final int originalHeight;
17 | private final int originalWidth;
18 | private final int halfWidth, halfHeight;
19 | private final int parentWidth, parentHeight;
20 | private final HelperFlingWithProportionListener helperFlingListener;
21 | private final Object dataObject;
22 | private float BASE_ROTATION_DEGREES = 15.f;
23 |
24 | private float aPosX;
25 | private float aPosY;
26 | private float aDownTouchX;
27 | private float aDownTouchY;
28 | private static final int INVALID_POINTER_ID = -1;
29 |
30 | private int mActivePointerId = INVALID_POINTER_ID;
31 | private View frame = null;
32 | private View nextFrame = null;
33 |
34 | private final int TOUCH_ABOVE = 0;
35 | private final int TOUCH_BELOW = 1;
36 | private final int TOUCH_LEFT = 2;
37 | private final int TOUCH_RIGHT = 3;
38 | private int touchPosition;
39 |
40 | public enum SWIPEMODE {
41 | LEFT_RIGHT, UP_DOWN
42 | }
43 |
44 | private SWIPEMODE swipeMode;
45 |
46 | private VelocityTracker vTracker;
47 | private boolean velocityReach = false;
48 |
49 | private float bottomFinalPosX, bottomFinalPosY;
50 |
51 | public FlingCardListener(SWIPEMODE swipeMode, View frame, View nextFrame, int parentWidth, int parentHeight,
52 | float originalX, float originalY, int originalHeight, int originalWidth, Object itemAtPosition,
53 | HelperFlingWithProportionListener helperFlingListener) {
54 | super();
55 | this.swipeMode = swipeMode;
56 | this.frame = frame;
57 | this.nextFrame = nextFrame;
58 | this.parentWidth = parentWidth;
59 | this.parentHeight = parentHeight;
60 | this.originalX = originalX;
61 | this.originalY = originalY;
62 | this.originalHeight = originalHeight;
63 | this.originalWidth = originalWidth;
64 | this.halfWidth = this.originalWidth / 2;
65 | this.halfHeight = this.originalHeight / 2;
66 | this.dataObject = itemAtPosition;
67 | this.helperFlingListener = helperFlingListener;
68 | }
69 |
70 | @Override
71 | public boolean onTouch(View view, MotionEvent event) {
72 |
73 | switch (event.getAction() & MotionEvent.ACTION_MASK) {
74 | case MotionEvent.ACTION_DOWN:
75 |
76 | mActivePointerId = event.getPointerId(0);
77 | final float x = event.getX(mActivePointerId);
78 | final float y = event.getY(mActivePointerId);
79 |
80 | aDownTouchX = x;
81 | aDownTouchY = y;
82 | if (aPosX == 0) {
83 | aPosX = frame.getX();
84 | }
85 | if (aPosY == 0) {
86 | aPosY = frame.getY();
87 | }
88 |
89 | if (swipeMode == SWIPEMODE.LEFT_RIGHT) {
90 | if (y < originalHeight / 2) {
91 | touchPosition = TOUCH_ABOVE;
92 | } else {
93 | touchPosition = TOUCH_BELOW;
94 | }
95 | } else {
96 | if (x < originalWidth / 2) {
97 | touchPosition = TOUCH_LEFT;
98 | } else {
99 | touchPosition = TOUCH_RIGHT;
100 | }
101 | }
102 |
103 | if (vTracker == null) {
104 | vTracker = VelocityTracker.obtain();
105 | } else {
106 | vTracker.clear();
107 | }
108 | vTracker.addMovement(event);
109 |
110 | break;
111 |
112 | case MotionEvent.ACTION_UP:
113 | mActivePointerId = INVALID_POINTER_ID;
114 | if (velocityReach) {
115 | helperFlingListener.flingOccurPercent(frame, 100, false);
116 | bottomSelectedEvent();
117 | } else {
118 | resetCardViewOnStack();
119 | }
120 | try {
121 | vTracker.recycle();
122 | } catch (Exception e) {
123 | e.printStackTrace();
124 | }
125 | velocityReach = false;
126 | break;
127 |
128 | case MotionEvent.ACTION_POINTER_DOWN:
129 | break;
130 |
131 | case MotionEvent.ACTION_POINTER_UP:
132 | // Extract the index of the pointer that left the touch sensor
133 | final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
134 | final int pointerId = event.getPointerId(pointerIndex);
135 | if (pointerId == mActivePointerId) {
136 | // This was our active pointer going up. Choose a new
137 | // active pointer and adjust accordingly.
138 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
139 | mActivePointerId = event.getPointerId(newPointerIndex);
140 | }
141 | break;
142 | case MotionEvent.ACTION_MOVE:
143 |
144 | // Find the index of the active pointer and fetch its position
145 | final int pointerIndexMove = event.findPointerIndex(mActivePointerId);
146 | final float xMove = event.getX(pointerIndexMove);
147 | final float yMove = event.getY(pointerIndexMove);
148 |
149 | // Calculate the distance moved
150 | final float dx = xMove - aDownTouchX;
151 | final float dy = yMove - aDownTouchY;
152 |
153 | // Move the frame
154 | aPosX += dx;
155 | aPosY += dy;
156 |
157 | // calculate the rotation degrees
158 | float distOriginalX = aPosX - originalX;
159 | float distOriginalY = aPosY - originalY;
160 | // float rotation = BASE_ROTATION_DEGREES * 2.f * distOriginalX / parentWidth;
161 | // if (touchPosition == TOUCH_BELOW) {
162 | // rotation = -rotation;
163 | // }
164 |
165 | // Log.v("", "distOriginalX = " + distOriginalX);
166 | // Log.e("", "distOriginalY = " + distOriginalY);
167 |
168 | double moveDis = Math.sqrt(Math.pow(distOriginalX, 2) + Math.pow(distOriginalY, 2));
169 |
170 | float scalePercent = (float) (moveDis / (originalHeight / 2.5));
171 | // Log.d("", "scalePercent = " + scalePercent);
172 | if (scalePercent > 1)
173 | scalePercent = 1;
174 | setUpNextFrame(scalePercent, false);
175 |
176 | // no rotation for a while.
177 | // float degree = (float) (Math.atan((aPosY - originalY) / (aPosX - originalX)) / Math.PI * 180);
178 | // Log.d("", "degree = " + degree + ", distOriginalX = " + distOriginalX);
179 | // float result;
180 | // if (degree >= -90 && degree < -45) {
181 | // result = degree + 90;
182 | // } else if (degree >= 45 && degree <= 90) {
183 | // result = 90 - degree;
184 | // } else {
185 | // result = degree;
186 | // }
187 |
188 | frame.setX(aPosX);
189 | frame.setY(aPosY);
190 |
191 | helperFlingListener.flingOccurPercent(frame, getMoveDisPercent(), distOriginalY < 0);
192 |
193 | vTracker.addMovement(event);
194 | vTracker.computeCurrentVelocity(100);
195 | float yVelocity = vTracker.getYVelocity();
196 | if (yVelocity > 550f) {
197 | velocityReach = true;
198 | }
199 | break;
200 |
201 | case MotionEvent.ACTION_CANCEL: {
202 | mActivePointerId = INVALID_POINTER_ID;
203 | try {
204 | vTracker.recycle();
205 | } catch (Exception e) {
206 | e.printStackTrace();
207 | }
208 | velocityReach = false;
209 | break;
210 | }
211 | }
212 | return true;
213 | }
214 |
215 | protected void setUpNextFrame(float scalePercent, boolean withAnim) {
216 | if (nextFrame == null)
217 | return;
218 | if (!withAnim) {
219 | nextFrame.setScaleX(scalePercent);
220 | nextFrame.setScaleY(scalePercent);
221 | nextFrame.setAlpha(scalePercent);
222 | } else {
223 | nextFrame.animate().scaleX(scalePercent).scaleY(scalePercent).alpha(scalePercent).setDuration(50).start();
224 | }
225 | }
226 |
227 | private void resetCardViewOnStack() {
228 | float distOriginalY = aPosY - originalY;
229 | if (swipeMode == SWIPEMODE.LEFT_RIGHT) {
230 | if (aPosX + halfWidth > rightBorder()) {
231 | setUpNextFrame(1f, true);
232 | onRightSelected();
233 | } else if (aPosX + halfWidth < leftBorder()) {
234 | setUpNextFrame(1f, true);
235 | onLeftSelected();
236 | } else {
237 | resetOrigin();
238 | }
239 | } else {
240 | if (distOriginalY > originalHeight / 2) {
241 | bottomSelectedEvent();
242 | } else if (distOriginalY < 0 && Math.abs(distOriginalY) > originalHeight / 2) {
243 | setUpNextFrame(1f, true);
244 | onTopSelected();
245 | } else {
246 | resetOrigin();
247 | }
248 | }
249 | }
250 |
251 | protected void bottomSelectedEvent() {
252 | setUpNextFrame(1f, true);
253 | onBottomSelected();
254 | }
255 |
256 | public int getMoveDisPercent() {
257 | return (int) (Math.min(1f, Math.abs((aPosY - originalY)) / (originalHeight / 2)) * 100);
258 | }
259 |
260 | protected void resetOrigin() {
261 | aPosX = 0;
262 | aPosY = 0;
263 | aDownTouchX = 0;
264 | aDownTouchY = 0;
265 | frame.animate().setDuration(100).setInterpolator(new OvershootInterpolator()).x(originalX).y(originalY)
266 | .rotation(0);
267 | helperFlingListener.flingResetOrigin(frame);
268 | }
269 |
270 | public float leftBorder() {
271 | return parentWidth / 4.f;
272 | }
273 |
274 | public float rightBorder() {
275 | return 3 * parentWidth / 4.f;
276 | }
277 |
278 | public void setRotationDegrees(float degrees) {
279 | this.BASE_ROTATION_DEGREES = degrees;
280 | }
281 |
282 | public void onLeftSelected() {
283 | this.frame.animate().setDuration(100).setInterpolator(new AccelerateInterpolator()).x(-originalWidth)
284 | .y(getExitPoint(-originalWidth)).setListener(new AnimatorListenerAdapter() {
285 | @Override
286 | public void onAnimationEnd(Animator animation) {
287 | helperFlingListener.onCardExited();
288 | helperFlingListener.leftExit(dataObject);
289 | }
290 | }).rotation(-getExitRotation());
291 | }
292 |
293 | public void onTopSelected() {
294 | this.frame.animate().setDuration(100).setInterpolator(new AccelerateInterpolator()).x(originalX)
295 | .y(-originalHeight).setListener(new AnimatorListenerAdapter() {
296 | @Override
297 | public void onAnimationEnd(Animator animation) {
298 | helperFlingListener.onCardExited();
299 | helperFlingListener.topExit(dataObject);
300 | }
301 | });
302 | }
303 |
304 | public void onRightSelected() {
305 | this.frame.animate().setDuration(100).setInterpolator(new AccelerateInterpolator()).x(parentWidth)
306 | .y(getExitPoint(parentWidth)).setListener(new AnimatorListenerAdapter() {
307 | @Override
308 | public void onAnimationEnd(Animator animation) {
309 | helperFlingListener.onCardExited();
310 | helperFlingListener.rightExit(dataObject);
311 | }
312 | }).rotation(getExitRotation());
313 | }
314 |
315 | public void setBottomFinalPos(float bottomFinalPosX, float bottomFinalPosY) {
316 | this.bottomFinalPosX = bottomFinalPosX;
317 | this.bottomFinalPosY = bottomFinalPosY;
318 | }
319 |
320 | public void onBottomSelected() {
321 | this.frame.animate().setDuration(100).setInterpolator(new AccelerateInterpolator())
322 | .x(bottomFinalPosX == 0 ? originalX : bottomFinalPosX)
323 | .y(bottomFinalPosY == 0 ? 3 * originalHeight : bottomFinalPosY)
324 | .setListener(new AnimatorListenerAdapter() {
325 | @Override
326 | public void onAnimationEnd(Animator animation) {
327 |
328 | helperFlingListener.onCardExited();
329 | helperFlingListener.bottomExit(dataObject);
330 | }
331 | });
332 | }
333 |
334 | private float getExitPoint(int exitXPoint) {
335 | float[] x = new float[2];
336 | x[0] = originalX;
337 | x[1] = aPosX;
338 |
339 | float[] y = new float[2];
340 | y[0] = originalY;
341 | y[1] = aPosY;
342 |
343 | LinearRegression regression = new LinearRegression(x, y);
344 |
345 | float result = (float) regression.slope() * exitXPoint + (float) regression.intercept();
346 | Log.e("", "getExitPoint = " + result);
347 |
348 | return result;
349 | }
350 |
351 | private float getExitRotation() {
352 | float rotation = BASE_ROTATION_DEGREES * 2.f * (parentWidth - originalX) / parentWidth;
353 | if (touchPosition == TOUCH_BELOW) {
354 | rotation = -rotation;
355 | }
356 | return rotation;
357 | }
358 |
359 | }
360 |
--------------------------------------------------------------------------------
/src/com/yiqivr/tinderswipe/MyActivity.java:
--------------------------------------------------------------------------------
1 | package com.yiqivr.tinderswipe;
2 |
3 | import java.util.ArrayList;
4 |
5 | import android.annotation.SuppressLint;
6 | import android.app.Activity;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.support.v4.view.PagerAdapter;
10 | import android.support.v4.view.ViewPager;
11 | import android.util.Log;
12 | import android.view.LayoutInflater;
13 | import android.view.View;
14 | import android.view.View.OnClickListener;
15 | import android.view.ViewGroup;
16 | import android.widget.BaseAdapter;
17 | import android.widget.TextView;
18 | import android.widget.Toast;
19 |
20 | import com.yiqivr.tinderswipe.widget.BoxLayout;
21 | import com.yiqivr.tinderswipe.widget.CircleProgress;
22 | import com.yiqivr.tinderswipe.widget.FlingCardListener.SWIPEMODE;
23 | import com.yiqivr.tinderswipe.widget.OnLeftRightFlingListener;
24 | import com.yiqivr.tinderswipe.widget.OnTopBottomFlingWithProportionListener;
25 | import com.yiqivr.tinderswipe.widget.SwipeFlingAdapterView;
26 |
27 | public class MyActivity extends Activity implements OnLeftRightFlingListener, OnTopBottomFlingWithProportionListener {
28 |
29 | private ArrayList al;
30 | private ArrayList pagerView;
31 | private MyAdapter adapter;
32 | private MyPagerAdapter pagerAdapter;
33 | private BoxLayout boxLayout;
34 |
35 | private boolean addMoreExcuting;
36 |
37 | private ViewPager pager;
38 | private View shadow;
39 | private final int duration = 500;
40 | private final int OutInduration = 500;
41 |
42 | private float transByY, transByX;
43 | private View box;
44 | private SwipeFlingAdapterView flingContainer;
45 |
46 | @Override
47 | protected void onCreate(Bundle savedInstanceState) {
48 | super.onCreate(savedInstanceState);
49 | setContentView(R.layout.activity_my);
50 |
51 | flingContainer = (SwipeFlingAdapterView) findViewById(R.id.frame);
52 | boxLayout = (BoxLayout) findViewById(R.id.boxlayout);
53 | pager = (ViewPager) findViewById(R.id.viewpager);
54 | shadow = findViewById(R.id.shadow);
55 | box = findViewById(R.id.collect_box);
56 |
57 | al = new ArrayList();
58 | pagerView = new ArrayList();
59 | for (int i = 1; i < 10; i++) {
60 | al.add(i + "");
61 | }
62 |
63 | flingContainer.setSwipeMode(SWIPEMODE.UP_DOWN);
64 | flingContainer.setMaxVisible(2);
65 | flingContainer.setTopBottomFlingListener(this);
66 | // flingContainer.setLeftRightFlingListener(this);
67 | adapter = new MyAdapter();
68 | flingContainer.setAdapter(adapter);
69 |
70 | pager.setVisibility(View.GONE);
71 |
72 | box.setOnClickListener(boxClickListener);
73 |
74 | shadow.setOnClickListener(shadowClickListener);
75 |
76 | }
77 |
78 | private OnClickListener boxClickListener = new View.OnClickListener() {
79 |
80 | @Override
81 | public void onClick(View v) {
82 | if (pagerView.size() == 0) {
83 | return;
84 | }
85 | box.setOnClickListener(null);
86 | pager.setAlpha(0);
87 | shadow.setAlpha(0);
88 | shadow.setVisibility(View.VISIBLE);
89 | pager.setVisibility(View.VISIBLE);
90 | pagerAdapter = new MyPagerAdapter();
91 | pager.setPageMargin(-200);
92 | pager.setAdapter(pagerAdapter);
93 |
94 | boxLayout.bringToFront();
95 | box.bringToFront();
96 |
97 | pager.post(new Runnable() {
98 |
99 | @Override
100 | public void run() {
101 | transByY = pager.getTop() - boxLayout.getTop() - boxLayout.getChildTop();
102 | int collectItemsSize = pagerView.size();
103 | int animType = BoxLayout.ONE_COLLECT_ANIM;
104 | switch (collectItemsSize) {
105 | case 1:
106 | animType = BoxLayout.ONE_COLLECT_ANIM;
107 | break;
108 | case 2:
109 | animType = BoxLayout.TWO_COLLECT_ANIM;
110 | transByX = pager.getChildAt(1).getLeft() - pager.getChildAt(0).getLeft();
111 | break;
112 | default:
113 | animType = BoxLayout.MORE_COLLECT_ANIM;
114 | transByX = pager.getChildAt(1).getLeft() - pager.getChildAt(0).getLeft();
115 | break;
116 | }
117 |
118 | boxLayout.popOut(animType, transByY, transByX, new Runnable() {
119 |
120 | @Override
121 | public void run() {
122 | flingContainer.bringToFront();
123 | shadow.bringToFront();
124 | pager.setAlpha(1);
125 | pager.bringToFront();
126 | box.bringToFront();
127 | findViewById(R.id.rl_content).requestLayout();
128 | shadow.setOnClickListener(shadowClickListener);
129 | pager.setOnClickListener(shadowClickListener);
130 | }
131 | });
132 |
133 | shadow.animate().alpha(1).setDuration(2 * OutInduration);
134 | }
135 | });
136 |
137 | }
138 | };
139 |
140 | private OnClickListener shadowClickListener = new View.OnClickListener() {
141 |
142 | @SuppressLint("NewApi")
143 | @Override
144 | public void onClick(View v) {
145 | shadow.setOnClickListener(null);
146 | shadow.animate().alpha(0).setDuration(2 * duration).withEndAction(new Runnable() {
147 |
148 | @Override
149 | public void run() {
150 | shadow.setVisibility(View.GONE);
151 | }
152 | });
153 | Log.e("", "pager.getChildCount() = " + pager.getChildCount());
154 |
155 | int animType = BoxLayout.MORE_PAGER_ANIM_LEFT;
156 | int pagerChildCount = pager.getChildCount();
157 | pager.setVisibility(View.GONE);
158 | shadow.bringToFront();
159 | boxLayout.bringToFront();
160 | box.bringToFront();
161 | switch (pagerChildCount) {
162 | case 1:
163 | animType = BoxLayout.ONE_PAGER_ANIM;
164 | break;
165 | case 2:
166 | boolean leftCenter = (pager.getCurrentItem() == 0);
167 | if (pagerView.size() > 2) {
168 | animType = leftCenter ? BoxLayout.MORE_PAGER_ANIM_LEFT : BoxLayout.MORE_PAGER_ANIM_RIGHT;
169 | } else {
170 | animType = leftCenter ? BoxLayout.TWO_PAGER_ANIM_LEFT : BoxLayout.TWO_PAGER_ANIM_RIGHT;
171 | }
172 | break;
173 | default:
174 | animType = BoxLayout.MORE_PAGER_ANIM_OTHER;
175 | break;
176 | }
177 | boxLayout.setReadyPullIn(animType, transByY, transByX, new Runnable() {
178 |
179 | @Override
180 | public void run() {
181 | boxLayout.bringToFront();
182 | flingContainer.bringToFront();
183 | shadow.bringToFront();
184 | box.bringToFront();
185 | findViewById(R.id.rl_content).requestLayout();
186 | box.setOnClickListener(boxClickListener);
187 | }
188 | });
189 |
190 | }
191 |
192 | };
193 |
194 | @Override
195 | public void removeFirstObjectInAdapter() {
196 | Log.d("LIST", "removed object!");
197 | al.remove(0);
198 | adapter.notifyDataSetChanged();
199 | }
200 |
201 | @Override
202 | public void onLeftCardExit(Object dataObject) {
203 | Toast.makeText(this, "Left!" + dataObject.toString(), Toast.LENGTH_SHORT).show();
204 | }
205 |
206 | @Override
207 | public void onRightCardExit(Object dataObject) {
208 | Toast.makeText(this, "Right!" + dataObject.toString(), Toast.LENGTH_SHORT).show();
209 | }
210 |
211 | @Override
212 | public void onAdapterAboutToEmpty(int itemsInAdapter) {
213 | // Ask for more data here
214 | System.out.println("Almost empty!!! itemsInAdapter = " + itemsInAdapter);
215 | netWorkSimulation();
216 | }
217 |
218 | private void netWorkSimulation() {
219 | if (addMoreExcuting)
220 | return;
221 | addMoreExcuting = true;
222 | new Handler().postDelayed(new Runnable() {
223 |
224 | @Override
225 | public void run() {
226 | ArrayList moreData = new ArrayList();
227 | for (int i = 10; i < 20; i++) {
228 | moreData.add(i + "");
229 | }
230 |
231 | al.addAll(moreData);
232 | adapter.notifyDataSetChanged();
233 | addMoreExcuting = false;
234 | }
235 | }, 1000);
236 | }
237 |
238 | @Override
239 | public void onTopCardExit(Object dataObject) {
240 | }
241 |
242 | @Override
243 | public void onBottomCardExit(Object dataObject) {
244 | if (pagerView.size() == 0) {
245 | boxLayout
246 | .setBackImage(Integer.valueOf(dataObject.toString()) % 2 == 0 ? R.drawable.card : R.drawable.card2);
247 | } else if (pagerView.size() == 1) {
248 | boxLayout.setCenterImage(Integer.valueOf(dataObject.toString()) % 2 == 0 ? R.drawable.card
249 | : R.drawable.card2);
250 | } else {
251 | boxLayout
252 | .setForeImage(Integer.valueOf(dataObject.toString()) % 2 == 0 ? R.drawable.card : R.drawable.card2);
253 | }
254 | View v = LayoutInflater.from(MyActivity.this).inflate(R.layout.no_circle_item, null);
255 | TextView tv = (TextView) v.findViewById(R.id.helloText);
256 | tv.setText(dataObject.toString());
257 | pagerView.add(v);
258 | if (pagerAdapter != null)
259 | pagerAdapter.notifyDataSetChanged();
260 | }
261 |
262 | private class MyAdapter extends BaseAdapter {
263 |
264 | @Override
265 | public int getCount() {
266 | // TODO Auto-generated method stub
267 | return al.size();
268 | }
269 |
270 | @Override
271 | public Object getItem(int position) {
272 | // TODO Auto-generated method stub
273 | return al.get(position);
274 | }
275 |
276 | @Override
277 | public long getItemId(int position) {
278 | // TODO Auto-generated method stub
279 | return 0;
280 | }
281 |
282 | @Override
283 | public View getView(int position, View convertView, ViewGroup parent) {
284 | Log.v("", "getView--------");
285 | View v = LayoutInflater.from(MyActivity.this).inflate(R.layout.item, null);
286 | TextView tv = (TextView) v.findViewById(R.id.helloText);
287 | tv.setBackgroundResource(Integer.valueOf(al.get(position)) % 2 == 0 ? R.drawable.card : R.drawable.card2);
288 | tv.setText(al.get(position));
289 | CircleProgress top = (CircleProgress) v.findViewById(R.id.top_progress);
290 | CircleProgress bottom = (CircleProgress) v.findViewById(R.id.bottom_progress);
291 | top.setTag(0);
292 | bottom.setTag(0);
293 | top.setAlpha(0);
294 | bottom.setAlpha(0);
295 | return v;
296 | }
297 |
298 | }
299 |
300 | private class MyPagerAdapter extends PagerAdapter {
301 |
302 | @Override
303 | public int getCount() {
304 | return pagerView.size();
305 | }
306 |
307 | @Override
308 | public boolean isViewFromObject(View arg0, Object arg1) {
309 | return arg0 == arg1;
310 | }
311 |
312 | @Override
313 | public void destroyItem(ViewGroup container, int position, Object object) {
314 | container.removeView(pagerView.get(position));
315 | }
316 |
317 | @Override
318 | public Object instantiateItem(ViewGroup container, int position) {
319 | container.addView(pagerView.get(position));
320 | return pagerView.get(position);
321 | }
322 |
323 | }
324 |
325 | @Override
326 | public void onFlingSuccessPercent(View selfView, int percent, boolean flingTop) {
327 | CircleProgress top = (CircleProgress) selfView.findViewById(R.id.top_progress);
328 | CircleProgress bottom = (CircleProgress) selfView.findViewById(R.id.bottom_progress);
329 | if (top != null && bottom != null) {
330 | if (flingTop) {
331 | if ((Integer) top.getTag() == 0) {
332 | top.setTag(1);
333 | top.animate().alpha(1).setDuration(500l);
334 | }
335 | if ((Integer) bottom.getTag() == 1) {
336 | bottom.setTag(0);
337 | bottom.setCurProgress(0);
338 | bottom.animate().alpha(0).setDuration(500l);
339 | }
340 | top.setCurProgress(percent);
341 | } else {
342 | if ((Integer) top.getTag() == 1) {
343 | top.setTag(0);
344 | top.setCurProgress(0);
345 | top.animate().alpha(0).setDuration(500l);
346 | }
347 | if ((Integer) bottom.getTag() == 0) {
348 | bottom.setTag(1);
349 | bottom.animate().alpha(1).setDuration(500l);
350 | }
351 | bottom.setCurProgress(percent);
352 | }
353 | }
354 |
355 | }
356 |
357 | @Override
358 | public void onFlingResetOrigin(View selfView) {
359 | CircleProgress top = (CircleProgress) selfView.findViewById(R.id.top_progress);
360 | CircleProgress bottom = (CircleProgress) selfView.findViewById(R.id.bottom_progress);
361 | if ((Integer) top.getTag() == 1) {
362 | top.setTag(0);
363 | top.animate().alpha(0).setDuration(500l);
364 | }
365 | if ((Integer) bottom.getTag() == 1) {
366 | bottom.setTag(0);
367 | bottom.animate().alpha(0).setDuration(500l);
368 | }
369 | }
370 |
371 | }
372 |
--------------------------------------------------------------------------------