├── AndroidManifest.xml
├── README.md
├── ic_launcher-web.png
├── libs
└── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── layout
│ └── activity_main.xml
├── menu
│ └── main.xml
├── values-sw600dp
│ └── dimens.xml
├── values-sw720dp-land
│ └── dimens.xml
├── values-v11
│ └── styles.xml
├── values-v14
│ └── styles.xml
└── values
│ ├── attrs.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── src
└── io
└── carlol
└── test
└── cardflip
├── MainActivity.java
└── ui
└── CardFlip.java
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AndroidCardFlip
2 | ===============
3 |
4 | CardFlipView is a simple Compound View to perform flip animation for ALL android versions.
5 |
6 | Flip animation is performed by single tap on the compound view, but could be triggered using a specific method programmatically:
7 |
8 | public void flipCard();
9 |
10 | CardFlipView supplies two xml attributes to refer both side views (front/back).
11 |
12 | Event on flip event could be managed by an useful listener:
13 |
14 | public interface OnCardFlipListener {
15 |
16 | public void onFrontFlip(View front);
17 |
18 | public void onBackFlip(View back, boolean isFirstTime);
19 |
20 | }
21 |
22 | A simple xml sample:
23 |
24 |
30 |
31 |
38 |
39 |
49 |
50 |
51 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlol/AndroidCardFlip/6143fbde5664b82b21542bfa293b011da8d65dbf/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlol/AndroidCardFlip/6143fbde5664b82b21542bfa293b011da8d65dbf/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/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.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-19
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlol/AndroidCardFlip/6143fbde5664b82b21542bfa293b011da8d65dbf/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlol/AndroidCardFlip/6143fbde5664b82b21542bfa293b011da8d65dbf/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlol/AndroidCardFlip/6143fbde5664b82b21542bfa293b011da8d65dbf/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlol/AndroidCardFlip/6143fbde5664b82b21542bfa293b011da8d65dbf/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
15 |
16 |
26 |
27 |
28 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | io.carlol.test.cardflip
5 | Settings
6 | Hello world!
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/io/carlol/test/cardflip/MainActivity.java:
--------------------------------------------------------------------------------
1 | package io.carlol.test.cardflip;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.view.Menu;
6 |
7 | public class MainActivity extends Activity {
8 |
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | setContentView(R.layout.activity_main);
13 | }
14 |
15 | @Override
16 | public boolean onCreateOptionsMenu(Menu menu) {
17 | getMenuInflater().inflate(R.menu.main, menu);
18 | return true;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/io/carlol/test/cardflip/ui/CardFlip.java:
--------------------------------------------------------------------------------
1 | package io.carlol.test.cardflip.ui;
2 |
3 | import io.carlol.test.cardflip.R;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Camera;
7 | import android.graphics.Matrix;
8 | import android.util.AttributeSet;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.view.animation.AccelerateDecelerateInterpolator;
12 | import android.view.animation.Animation;
13 | import android.view.animation.Animation.AnimationListener;
14 | import android.view.animation.Transformation;
15 | import android.widget.RelativeLayout;
16 |
17 | /**
18 | *
19 | * @author c.luchessa
20 | *
21 | */
22 | public class CardFlip extends RelativeLayout {
23 |
24 | private static final int DEFAULT_ANIMATION_DURATION = 500;
25 |
26 | private int mFrontViewId;
27 | private int mBackViewId;
28 | private int mAnimationDuration;
29 |
30 | private ViewGroup mFrontView;
31 | private ViewGroup mBackView;
32 |
33 | private boolean isAnimPerforming = false;
34 |
35 |
36 | private OnClickListener mOnClickListener = new OnClickListener() {
37 | @Override
38 | public void onClick(View v) {
39 | if ( ! isAnimPerforming ) {
40 | flipCard();
41 | }
42 | }
43 | };
44 |
45 | private AnimationListener mAnimationListener = new AnimationListener() {
46 | @Override
47 | public void onAnimationStart(Animation animation) {
48 | isAnimPerforming = true;
49 | }
50 |
51 | @Override
52 | public void onAnimationRepeat(Animation animation) { /* ignored */ }
53 |
54 | @Override
55 | public void onAnimationEnd(Animation animation) {
56 | isAnimPerforming = false;
57 | }
58 | };
59 |
60 |
61 |
62 | public CardFlip(Context context, AttributeSet attrs) {
63 | super(context, attrs);
64 | init(attrs);
65 | }
66 |
67 | private void init(AttributeSet attrs) {
68 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CardFlip);
69 |
70 | mFrontViewId = a.getResourceId(R.styleable.CardFlip_cf_front, 0);
71 | mBackViewId = a.getResourceId(R.styleable.CardFlip_cf_back, 0);
72 | mAnimationDuration = a.getInt(R.styleable.CardFlip_cf_animationDuration, DEFAULTA_ANIMATION_DURATION);
73 |
74 | RuntimeException e = null;
75 | if (mFrontViewId == 0) {
76 | e = new IllegalArgumentException(a.getPositionDescription() +
77 | ": The front attribute is required and must refer to a valid child.");
78 | }
79 | if (mBackViewId == 0) {
80 | e = new IllegalArgumentException(a.getPositionDescription() +
81 | ": The back attribute is required and must refer to a valid child.");
82 | }
83 | a.recycle();
84 | if (e != null) throw e;
85 | }
86 |
87 | @Override
88 | protected void onFinishInflate() {
89 | super.onFinishInflate();
90 |
91 | mFrontView = (ViewGroup) this.findViewById(mFrontViewId);
92 | mBackView = (ViewGroup) this.findViewById(mBackViewId);
93 |
94 | mFrontView.setOnClickListener(mOnClickListener);
95 | mBackView.setOnClickListener(mOnClickListener);
96 |
97 | this.setBackgroundColor(getResources().getColor(android.R.color.transparent));
98 | }
99 |
100 | private void flipCard() {
101 |
102 | FlipAnimation flipAnimation = new FlipAnimation(mFrontView, mBackView, mAnimationDuration);
103 |
104 | flipAnimation.setAnimationListener(mAnimationListener);
105 |
106 | if (mFrontView.getVisibility() == View.GONE) {
107 | flipAnimation.reverse();
108 | }
109 | this.startAnimation(flipAnimation);
110 | }
111 |
112 |
113 | /**
114 | *
115 | * @link http://stackoverflow.com/questions/16030667/displaying-card-flip-animation-on-old-android
116 | *
117 | */
118 | public static class FlipAnimation extends Animation
119 | {
120 | private Camera camera;
121 |
122 | private View fromView;
123 | private View toView;
124 |
125 | private float centerX;
126 | private float centerY;
127 |
128 | private boolean forward = true;
129 |
130 | /**
131 | * Creates a 3D flip animation between two views.
132 | *
133 | * @param fromView First view in the transition.
134 | * @param toView Second view in the transition.
135 | * @param mAnimationDuration
136 | */
137 | public FlipAnimation(View fromView, View toView, int mAnimationDuration)
138 | {
139 | this.fromView = fromView;
140 | this.toView = toView;
141 |
142 | setDuration(mAnimationDuration);
143 | setFillAfter(false);
144 | setInterpolator(new AccelerateDecelerateInterpolator());
145 | }
146 |
147 | public void reverse()
148 | {
149 | forward = false;
150 | View switchView = toView;
151 | toView = fromView;
152 | fromView = switchView;
153 | }
154 |
155 | @Override
156 | public void initialize(int width, int height, int parentWidth, int parentHeight)
157 | {
158 | super.initialize(width, height, parentWidth, parentHeight);
159 | centerX = width/2;
160 | centerY = height/2;
161 | camera = new Camera();
162 | }
163 |
164 | @Override
165 | protected void applyTransformation(float interpolatedTime, Transformation t)
166 | {
167 | // Angle around the y-axis of the rotation at the given time
168 | // calculated both in radians and degrees.
169 | final double radians = Math.PI * interpolatedTime;
170 | float degrees = (float) (180.0 * radians / Math.PI);
171 |
172 | // Once we reach the midpoint in the animation, we need to hide the
173 | // source view and show the destination view. We also need to change
174 | // the angle by 180 degrees so that the destination does not come in
175 | // flipped around
176 | if (interpolatedTime >= 0.5f)
177 | {
178 | degrees -= 180.f;
179 | fromView.setVisibility(View.GONE);
180 | toView.setVisibility(View.VISIBLE);
181 | }
182 |
183 | if (forward)
184 | degrees = -degrees; //determines direction of rotation when flip begins
185 |
186 | final Matrix matrix = t.getMatrix();
187 | camera.save();
188 | camera.rotateY(degrees);
189 | camera.getMatrix(matrix);
190 | camera.restore();
191 | matrix.preTranslate(-centerX, -centerY);
192 | matrix.postTranslate(centerX, centerY);
193 | }
194 | }
195 |
196 | }
197 |
--------------------------------------------------------------------------------