Android Custom Animation-3D Rotation Animation
There are several types of animation in Android:
(1) AlphaAnimation: an animation with a changed transparency.
(2) ScaleAnimation: A scaled animation.
(3) TranslateAnimation: an animation with displacement changes.
(4) RotateAnimation: rotation animation.
However, in actual projects, transparency, scaling, displacement, and rotation cannot meet our needs. For example, we need a 3D Rotation animation similar to the following.
In this case, you need to use a Custom Animation. The Custom Animation must inherit the Animation, and rewrite the applyTransformation (float interpolatedTime, Transformation t) method and initialize method.
Two Parameters in the applyTransformation method are described as follows:
InterpolatedTime: this parameter indicates the progress of the time (for example, the time you set is 1000 ms,
InterploatedTime starts from 0 to 1. When this parameter is set to 1, the animation ends)
Transformation:
Represents the degree of deformation of the animation at different times. This object encapsulates a Matrix object. Transformation controls the corresponding image or view for Transformation when the object contains Matrix objects such as displacement, skew, and rotation.
Initialize (int width, int height, int parentWidth, int parentHeight) function. This is a callback function that tells Animation the size parameter of the target View. Here we can initialize some related parameters, for example, set the animation duration, set the Interpolator, and set the animation reference point.
In order to control the conversion of images or views in 3D space, a Camera class provided by Android is also required. This class is a space conversion tool, which is similar to Matrix and provides the following common methods.
GetMatrix (Matrix matrix): Apply the transformation made by Camera to the specified maxtrix.
RotateX (float deg): rotate the target component along the X axis
RotateY (float deg ),
RotateZ (float deg)
Translate (float x, float y, float z): Transform the displacement of the target component in the 3D space class.
ApplyToCanvas: Apply the transformation made by Camera to the Canvas.
Basic Application-create an animation in code
Next, let's take a simple implementation. We only create an animation in the activity, instead of using an xml file to create an animation.
The specific implementation is as follows:
Custom rotate3dAnimation inherits from Animation and overwrites the applyTransformation (float interpolatedTime, Transformation t) method.
Public class extends Animation {// The default rotation point type is ABSOLUTE private int mPivotXType = ABSOLUTE; private int mPivotYType = ABSOLUTE; private float mPivotXValue = 0.0f; private float mPivotYValue = 0.0f; private float mFromDegrees; private float mToDegrees; private float mPivotX; private float mPivotY; private Camera mCamera; private int mRollType;/*** rotation axis */public static final int ROLL_BY_X = 0; public static final int ROLL_BY_Y = 1; public static final int ROLL_BY_Z = 2; public round (int rollType, float fromDegrees, float toDegrees) {mRollType = rollType; mFromDegrees = fromDegrees; topology = toDegrees; mPivotX = 0.0f; mPivotY = 0.0f;} public topology (int rollType, float fromDegrees, float toDegrees, float limit Tx, float limit ty) {mRollType = rollType; counts = Percentage; keys = toDegrees; keys = ABSOLUTE; mPivotYType = ABSOLUTE; mPivotXValue = 0000tx; mPivotYValue = 0000ty; gradient ();} public keys (int rollType, float values, float toDegrees, int limit txtype, float partial txvalue, int partial tytype, float partial tyvalue) {mRollType = rollType; partial = partial; counts = toDegrees; mPivotXValue = partial txvalue; counts = partial txtype; mPivotYValue = partial tyvalue; counts = partial tytype; callback ();} private void upload () {if (mPivotXType = ABSOLUTE) {mPivotX = mPivotXValue;} if (mPivotYType = ABSOLUTE) {mPivotY = mPivotYValue ;}} // The Initialization Method in the Animation class is somewhat similar to onMeasure @ Override public void initialize (int width, int height, int parentWidth, int parentHeight) {super. initialize (width, height, parentWidth, parentHeight); mCamera = new Camera (); mPivotX = resolveSize (values, mPivotXValue, width, parentWidth); mPivotY = resolveSize (values, height, parentHeight) ;}@ Override protected void applyTransformation (float interpolatedTime, Transformation t) {final float duration = inflow; float degrees = inflow + (response-interval) * interpolatedTime ); final Matrix matrix = t. getMatrix (); mCamera. save (); switch (mRollType) {case ROLL_BY_X: // rotate mCamera around the X axis. rotateX (degrees); break; case ROLL_BY_Y: // rotate mCamera around the Y axis. rotateY (degrees); break; case ROLL_BY_Z: // rotate mCamera around the Z axis. rotateZ (degrees); break;} mCamera. getMatrix (matrix); mCamera. restore (); matrix. preTranslate (-mPivotX,-mPivotY); matrix. postTranslate (mPivotX, mPivotY );}}
The method used in the activity is no different from the method used for scaling animation.
The code in the activity is as follows:
public class MainActivity extends ActionBarActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ImageView img = (ImageView) findViewById(R.id.img); Rotate3dAnimation animation = new Rotate3dAnimation(Rotate3dAnimation.ROLL_BY_X,0f,360f); animation.setFillAfter(true); animation.setDuration(1000); img.startAnimation(animation); }}
Now we have a preliminary understanding of the use of custom animation classes, but it is not enough to create an animation only in code. In many cases, we also need to create an animation in the xml file. What should we do?
Advanced Application-XML animation creation
The process of creating an animation using XML is somewhat similar to the use of custom controls.
(1) set custom attributes in the attrs. xml file
Attrs. xml
(2) retrieving Custom Attributes
Next, we need to modify our rotate3dAnimation class to get and parse the Custom Attributes declared in the xml file.
Rotate3dAnimation. class
Package com. demo. customanimation; import android. content. context; import android. content. res. typedArray; import android. graphics. camera; import android. graphics. matrix; import android. util. attributeSet; import android. util. typedValue; import android. view. animation. animation; import android. view. animation. transformation; public class Rotate3dAnimation extends Animation {// The default rotation point type is ABSOLUTE private int mPiv. OtXType = ABSOLUTE; private int rows = ABSOLUTE; private float mPivotXValue = 0.0f; private float mPivotYValue = 0.0f; private float rows; private float mPivotX; private flompivoty; private Camera mCamera; private int mRollType;/*** rotation axis */public static final int ROLL_BY_X = 0; public static final int ROLL_BY_Y = 1; public static final int ROLL_BY_Z = 2; // get and parse the custom attributes, which are the same as the public Rotate3dAnimation (Context context, AttributeSet attrs) {super (context, attrs) in the custom control ); typedArray a = context. obtainStyledAttributes (attrs, R. styleable. rotate3dAnimation); mFromDegrees =. getFloat (R. styleable. rotate3dAnimation_fromDeg, 0.0f); mToDegrees =. getFloat (R. styleable. rotate3dAnimation_toDeg, 0.0f); mRollType =. getInt (R. styleable. rotate3dAnimation_rollTyp E, ROLL_BY_X); Description d = parseValue (. peekValue (R. styleable. rotate3danimation_0000tx); mPivotXType = d. type; mPivotXValue = d. value; d = parseValue (. peekValue (R. styleable. rotate3danimation_policty); mPivotYType = d. type; mPivotYValue = d. value;. recycle (); // initialize the rotation point initializePivotPoint ();} public Rotate3dAnimation (int rollType, float fromDegrees, float toDegrees) {mRollType = rollType; Volumes = fromDegrees; volumes = toDegrees; mPivotX = 0.0f; mPivotY = 0.0f;} public blocks (int rollType, float fromDegrees, float toDegrees, float limit Tx, float limit ty) {mRollType = rollType; counts = fromDegrees; counts = toDegrees; mPivotXType = ABSOLUTE; mPivotYType = ABSOLUTE; mPivotXValue = Ready TX; mPivotYValue = Ready ty; Rotate ();} public Rotate 3 dAnimation (int rollType, float fromDegrees, float toDegrees, int partial txtype, float partial txvalue, int partial tytype, float partial tyvalue) {mRollType = rollType; vertex = fromDegrees; vertex = toDegrees; mPivotXValue = effectxvalue; mPivotXType = effectxtype; mPivotYValue = effectyvalue; mPivotYType = effectytype; initializePivotPoint ();} private void callback () {if (mPivotXType = BSOLUTE) {mPivotX = mPivotXValue;} if (mPivotYType = ABSOLUTE) {mPivotY = mPivotYValue ;}} // The Initialization Method in the Animation class is somewhat similar to onMeasure @ Override public void initialize (int width, int height, int parentWidth, int parentHeight) {super. initialize (width, height, parentWidth, parentHeight); mCamera = new Camera (); mPivotX = resolveSize (mPivotXType, mPivotXValue, width, parentWidth); mPivotY = resolveSize (MPivotYType, mPivotYValue, height, parentHeight);} protected static class Description {public int type; public float value;} Description parseValue (TypedValue value) {Description d = new Description (); if (value = null) {d. type = ABSOLUTE; d. value = 0;} else {if (value. type = TypedValue. TYPE_FRACTION) {d. type = (value. data & TypedValue. COMPLEX_UNIT_MASK) = TypedValue. COMPLEX_UNIT_FRACT ION_PARENT? RELATIVE_TO_PARENT: RELATIVE_TO_SELF; d. value = TypedValue. complexToFloat (value. data); return d;} else if (value. type = TypedValue. TYPE_FLOAT) {d. type = ABSOLUTE; d. value = value. getFloat (); return d;} else if (value. type> = TypedValue. TYPE_FIRST_INT & value. type <= TypedValue. TYPE_LAST_INT) {d. type = ABSOLUTE; d. value = value. data; return d ;}} d. type = ABSOLUTE; d. value = 0.0f; return d ;}@ Override protected void applyTransformation (float interpolatedTime, Transformation t) {final float response = response; float degrees = fromDegrees + (response-fromDegrees) * interpolatedTime); final Matrix matrix = t. getMatrix (); mCamera. save (); switch (mRollType) {case ROLL_BY_X: mCamera. rotateX (degrees); break; case ROLL_BY_Y: mCamera. rotateY (degrees); break; case ROLL_BY_Z: mCamera. rotateZ (degrees); break;} mCamera. getMatrix (matrix); mCamera. restore (); matrix. preTranslate (-mPivotX,-mPivotY); matrix. postTranslate (mPivotX, mPivotY );}}
Finally, we can use our custom animation classes and attributes in the anim animation file.
Note: When Using Custom Animation classes in xml, You need to customize our namespace and add the namecontrol: package name when using animation labels.
Rotate3d. xml
Next, use the loadAnimation method of AnimationUtil in the activity to load our xml animation file.
public class MainActivity extends ActionBarActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ImageView img = (ImageView) findViewById(R.id.img); Animation animation = AnimationUtils.loadAnimation(this, R.anim.rotate3d); animation.setFillAfter(true); animation.setDuration(1000); img.startAnimation(animation); }}
Okay, "Success!" (Is that true ..)! Run it !!!
Ah? What's going on? An error occurred while running ????!!!!
Why?
Modify the source code of AnimationUtils
View AnimationUtils. loadAnimation source code we know that when loading an animation class from xml, we only recognize the animation classes included in the alpha, scale, rotate, and translate sdks, the Custom animation class Rotate3dAnimation we write will cause it to report an Unknown animation name exception. The official SDK does not provide other API methods to solve this problem. How can this problem be solved? It is very simple. You only need to change a line in the original AnimationUtils. loadAnimation source code, and load the custom animation class from ClassLoader by the package name through the reflection mechanism in java. Copy the source code to implement the loadAnimation method as follows:
Package com. demo. customanimation; import java. io. IOException; import org. xmlpull. v1.XmlPullParser; import org. xmlpull. v1.XmlPullParserException; import android. content. context; import android. content. res. resources. notFoundException; import android. content. res. xmlResourceParser; import android. OS. systemClock; import android. util. attributeSet; import android. util. xml; import android. view. animation. alphaAnimation; import android. view. animation. animation; import android. view. animation. animationSet; import android. view. animation. gridLayoutAnimationController; import android. view. animation. layoutAnimationController; import android. view. animation. rotateAnimation; import android. view. animation. scaleAnimation; import android. view. animation. translateAnimation; public class MyAnimationUtil {/*** These flags are used when parsing AnimatorSet objects */private static final int TOGETHER = 0; private static final int SEQUENTIALLY = 1; /*** Returns the current animation time in milliseconds. this time shoshould be * used when invoking {@ link Animation # setStartTime (long )}. refer to * {@ link android. OS. systemClock} for more information about the different * available clocks. the clock used by this method isNotThe * wall clock (it is not {@ link System # currentTimeMillis }). ** @ return the current animation time in milliseconds ** @ see android. OS. systemClock */public static long currentAnimationTimeMillis () {return SystemClock. uptimeMillis ();} /*** Loads an {@ link Animation} object from a resource ** @ param context * Application context used to access resources * @ param id * The resource id of the Imation to load * @ return The animation object reference by the specified id * @ throws NotFoundException * when the animation cannot be loaded */public static Animation loadAnimation (Context context, int id) throws NotFoundException {XmlResourceParser parser = null; try {parser = context. getResources (). getAnimation (id); return createAnimationFromXml (context, parser);} catch (XmlPullParserExcep Tion ex) {NotFoundException rnf = new NotFoundException (Can't load animation resource ID # 0x + Integer. toHexString (id); rnf. initCause (ex); throw rnf;} catch (IOException ex) {NotFoundException rnf = new NotFoundException (Can't load animation resource ID # 0x + Integer. toHexString (id); rnf. initCause (ex); throw rnf;} finally {if (parser! = Null) parser. close () ;}} private static Animation createAnimationFromXml (Context c, XmlPullParser parser) throws XmlPullParserException, IOException {return createAnimationFromXml (c, parser, null, Xml. asAttributeSet (parser);} // create an Animation private static Animation createAnimationFromXml (Context c, XmlPullParser parser, AnimationSet parent, AttributeSet attrs) throws XmlPullParserException from the Animation XML file, IOException {Animation anim = null; // Make sure we are on a start tag. int type; int depth = parser. getDepth (); while (type = parser. next ())! = XmlPullParser. END_TAG | parser. getDepth ()> depth) & type! = XmlPullParser. END_DOCUMENT) {if (type! = XmlPullParser. START_TAG) {continue;} // start tag name String name = parser. getName ();/*** create an animation set for the set tag and call the c context attrs Attribute set recursively * to represent attributes such as duration startOffset */if (name. equals (set) {anim = new AnimationSet (c, attrs); createAnimationFromXml (c, parser, (AnimationSet) anim, attrs ); /*** if it is an alpha tag, create the AlphaAnimation animation Set * parameter c: context * parameter attrs: attribute set represents duration, startOffset, and other attributes */} Else if (name. equals (alpha) {anim = new AlphaAnimation (c, attrs);/*** if it is a scale tag, create a ScaleAnimation animation Set * parameter c: context * parameter attrs: attribute Set represents duration, startOffset, and other attributes */} else if (name. equals (scale) {anim = new ScaleAnimation (c, attrs);/*** if it is a rotate tag, create a RotateAnimation animation Set * parameter c: context * parameter attrs: attribute Set represents duration, startOffset, and other attributes */} else if (name. equals (rotate) {anim = new RotateAnimation (C, attrs);/*** if it is a translate tag, create a TranslateAnimation animation Set * parameter c: context * parameter attrs: attribute Set represents duration, startOffset, and other attributes */} else if (name. equals (translate) {anim = new TranslateAnimation (c, attrs);} else {try {anim = (Animation) Class. forName (name ). getConstructor (Context. class, AttributeSet. class ). newInstance (c, attrs);} catch (Exception te) {throw new RuntimeException (Unknown animation name: + Parser. getName () + error: + te. getMessage () ;}} if (parent! = Null) {parent. addAnimation (anim) ;}return anim ;}}
Then, modify the code in our activity by replacing the system's AnimationUtils with our own MyAnimationUtils.
Animation animation = MyAnimationUtil.loadAnimation(this, R.anim.rotate3d); animation.setFillAfter(true); animation.setDuration(1000); img.startAnimation(animation);
This is the real success !!!
**