├── 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 | 2 | 3 | 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 | --------------------------------------------------------------------------------