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