android自定义动画
前一篇说了实现过程,这次来写一个自己简单实现的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 基本完成,希望没有什么遗漏