android自定义动画

时间:2023-02-09 20:45:45

前一篇说了实现过程,这次来写一个自己简单实现的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>

 

动画类的代码

android自定义动画android自定义动画
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;

}
}
View Code

 

配置文件加载和动态构造两种方式创建对话实例以及调用

android自定义动画android自定义动画
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);
}
});

}
}
View Code

 

 

ok 基本完成,希望没有什么遗漏