前一篇說了實現過程,這次來寫一個自己簡單實現的3d動畫
先來屬性聲明配置,方便使用xml 文件來定制動畫
<!-- 有些類型其實是沒必要的,只是實例代碼,為了更具有代表性 --> <declare-styleable name="CubeAnimation"> <attr name="fromX" format="dimension|fraction|float"/> <attr name="toX" format="dimension|fraction|float"/> <attr name="fromDegree" format="float"/> <attr name="toDegree" format="float"/> <attr name="axisY" format="float|integer"/> <attr name="positive" format="boolean"/> </declare-styleable>
配置參數相關的一些解釋
dimension 像素值類型,包括有"px", "dip", "sp", "pt", "in", "mm", 一般用TypedValue.complexToDimension解析
fraction 分數,一般用來表示占的百分比,"%", "%p"。 一般用TypedValue.complexToFraction解析 有時候和float類型功能通用
float 浮點數。當確定是這個類型的時候,用TypedValue.getFloat解析
integer 整數,TypedValue.data 就是這個值。
后兩者,如果參數只有確定的一個類型,直接用TypedArray 的 getInteger 或者 getFloat方法就可以獲取
動畫配置
<!-- 命名空間神馬的就不說了 --> <?xml version="1.0" encoding="utf-8"?> <cube xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cs="http://schemas.android.com/apk/res/com.example.testwifi" android:duration="2000" android:repeatCount="5" cs:fromDegree="0" cs:toDegree="1440" cs:fromX="50" cs:toX="90%p" cs:axisY="0.5" cs:positive="true"/>
包含在集合內的動畫配置
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cs="http://schemas.android.com/apk/res/com.example.testwifi"> <cube cs:fromDegree="0" cs:toDegree="1440" cs:fromX="50" cs:toX="90%p" cs:axisY="0.5" cs:positive="true/> <scale android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:fromXScale="1.0" android:toXScale="1.4" android:fromYScale="1.0" android:toYScale="0.6" android:pivotX="50%" android:pivotY="50%" android:fillAfter="false" android:duration="700" /> </set>
動畫類的代碼

public class CubeAnimation extends Animation { private float mFromDegrees; private float mToDegrees; private int mFromXType = ABSOLUTE;; private float mFromX = 0; private int mFromXdata = 0; private int mToXType = ABSOLUTE; private float mToX = 0; private int mToXData = 0; private Camera mCamera; private Resources mResources; private float mAxisY = 0; private int mAxisYType = ABSOLUTE; public CubeAnimation(float fromX,float toX,float fromDegree,float toDegree,float axisY) { this.mFromX = fromX; this.mToX = toX; this.mFromDegrees = fromDegree; this.mToDegrees = toDegree; this.mAxisY = axisY; mFromXType = TypedValue.TYPE_FLOAT; mToXType = TypedValue.TYPE_FLOAT; mAxisYType = ABSOLUTE; } public CubeAnimation(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CubeAnimation); mResources = context.getResources(); TypedValue value = a.peekValue(R.styleable.CubeAnimation_fromX); if(value.type==TypedValue.TYPE_FLOAT){ this.mFromX = value.getFloat(); this.mFromXType = value.type; }else{ this.mFromXType = value.type; this.mFromXdata = value.data; } value = a.peekValue(R.styleable.CubeAnimation_toX); if(value.type==TypedValue.TYPE_FLOAT){//FLOAT 類型的,必須在這里解析了,因為下邊的resolveData 方法拿不到TypedValue,沒法解析 this.mToX = value.getFloat(); this.mToXType = value.type; }else{ this.mToXType = value.type; this.mToXData = value.data; } boolean t = a.getBoolean(R.styleable.CubeAnimation_positive, true); if (!(t)) { this.mToDegrees = 0.0F; this.mFromDegrees = 90.0F; } this.mFromDegrees = a.getFloat(R.styleable.CubeAnimation_fromDegree, 0); this.mToDegrees = a.getFloat(R.styleable.CubeAnimation_toDegree, 90); value = a.peekValue(R.styleable.CubeAnimation_axisY); this.mAxisYType = value.type; //參數不同類型用來做什么用,按自己需求來設定和解析,我這里配置文件屬性要求是兩種 <attr name="axisY" format="float|integer"/> //如果是float類型,則做用來做組件的比例 如果是int型,認為是像素值 if(this.mAxisYType==TypedValue.TYPE_FLOAT){ this.mAxisY = value.getFloat(); this.mAxisYType = RELATIVE_TO_SELF; }else{ this.mAxisY = value.data; } a.recycle(); } public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); if(this.mFromXType!=TypedValue.TYPE_FLOAT){//這里Float類型代表固定值,且已經解析過,不再解析 下同 this.mFromX = resolveData(this.mFromXType,this.mFromXdata, width, parentWidth); } if(mToXType!=TypedValue.TYPE_FLOAT){ this.mToX = resolveData(this.mToXType,this.mToXData,width,parentWidth); } this.mCamera = new Camera(); if(mAxisYType==RELATIVE_TO_SELF) {//如果是相對自身的大小比例,則按比例計算獲取對應值。否則,則為固定像素值 mAxisY = mAxisY*height; } System.out.println("mFromX="+mFromX+",mToX=="+mToX); } float resolveData( int type, int data, int size, int psize) { float value = 0; if (type == TypedValue.TYPE_FRACTION) { value = TypedValue.complexToFraction(data, size, psize); } else if (type == TypedValue.TYPE_DIMENSION) { value = TypedValue.complexToDimension(data, mResources.getDisplayMetrics()); } else{//如果是由代碼設置成的ABSOLUTE類型或者 配置文件本身就是int的固定值 value= data; } return value; } // 自定義動畫主要要實現的方法 protected void applyTransformation(float interpolatedTime, Transformation t) { float fromDegrees = this.mFromDegrees; float degrees = fromDegrees + (this.mToDegrees - fromDegrees) * interpolatedTime; Camera camera = this.mCamera; Matrix matrix = t.getMatrix(); camera.save(); camera.rotateX(degrees); camera.getMatrix(matrix); camera.restore(); matrix.postTranslate(mFromX+(mToX-mFromX)*interpolatedTime, this.mAxisY); } // 因為用AnimationUtils無法解析出這個動畫的屬性,所以所有CubeAnimation的配置文件或者包含這個動畫的set配置文件,必須用這個方法加載 public static Animation loadAnimation(Context context, int id) throws NotFoundException { XmlResourceParser parser = null; try { parser = context.getResources().getAnimation(id); return createAnimationFromXml(context, parser, null, Xml.asAttributeSet(parser)); } catch (XmlPullParserException 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, AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, 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; } String name = parser.getName(); if (name.equals("set")) { anim = new AnimationSet(c, attrs); createAnimationFromXml(c, parser, (AnimationSet) anim, attrs); } else if (name.equals("alpha")) { anim = new AlphaAnimation(c, attrs); } else if (name.equals("scale")) { anim = new ScaleAnimation(c, attrs); } else if (name.equals("rotate")) { anim = new RotateAnimation(c, attrs); } else if (name.equals("translate")) { anim = new TranslateAnimation(c, attrs); } else if (name.equals("cube")) { anim = new CubeAnimation(c, attrs); } else { throw new RuntimeException( "not a cubeanimation animation name: " + parser.getName()); } } if (parent != null) { parent.addAnimation(anim); } return anim; } }
配置文件加載和動態構造兩種方式創建對話實例以及調用

public class AnimateActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.activity_main); View view = this.findViewById(R.id.tv); view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Animation animation; if(v.getTag()==null||(Boolean)v.getTag()){ ((TextView)v).setText("配置文件加載"); animation = CubeAnimation.loadAnimation(getApplicationContext(), R.anim.cubeanimation); v.setTag(false); }else{ ((TextView)v).setText("動態初始化"); animation = new CubeAnimation(0, 400, 0, 360, 100); animation.setDuration(8000); v.setTag(true); } v.startAnimation(animation); } }); } }
ok 基本完成,希望沒有什么遺漏