Android 自定义View----属性动画
程序员文章站
2022-05-29 18:14:55
...
自定义view难免会接触到动画,不然很多效果做不出来,动画分为几种,今天主要用到的是属性动画;
上面这个效果分上下两部分完成,在绘制的时候先绘制下半部分,具体代码如下,里面有详细注释:
public class CameraAnimatorView extends View {
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 图片左上padding值
private int mPadding = 300;
// 图片宽高
private int mLength = 800;
// 上面翻的度数 (因为要做动画,所以先全部设置为0,然后设置get,set方法为动画做准备)
float topFlip = 0;
// 下面翻的度数
float bottomFlip = 0;
// 旋转度数
float flipRotation = 0;
/*
android.graphics.Camera ( 是这个并不是相机,相机是android.hardware.Camera )
官方介绍:A camera instance can be used to compute 3D transformations and generate a matrix that can be applied, for instance, on aCanvas.
(自己随便翻译吧)
Camera对象可以用来计算3D变换,并将计算结果封装进一个Matrix矩阵,之后便进行应用
*/
Camera camera = new Camera();
public CameraAnimatorView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
{
/*
默认0,0,-8 ( 8 英寸 = 8 * 72 像素 )
每个手机分辨率不同,所以需要做适配 ,getResources().getDisplayMetrics().density
*/
camera.setLocation(0, 0, -6 * getResources().getDisplayMetrics().density);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 先绘制下半部分,再绘制上半部分(直接复制)
/*********************************绘制上半部分*******************************************/
canvas.save();
canvas.translate(mPadding + mLength / 2, mPadding + mLength / 2);
canvas.rotate(-flipRotation);
camera.save();
// 以X轴为轴心旋转*°
camera.rotateX(topFlip);
camera.applyToCanvas(canvas);
camera.restore();
canvas.clipRect(-mLength , -mLength , mLength, 0);
canvas.rotate(flipRotation);
canvas.translate(-(mPadding + mLength / 2), -(mPadding + mLength / 2));
canvas.drawBitmap(getAvatar(mLength), mPadding, mPadding, mPaint);
canvas.restore();
/**********************************绘制下半部分(倒着绘制)******************************/
canvas.save();
/*
投射原点再0,0位置,如果不移动画布,映射出来的是变形的图片(歪的),所以需要先移动再旋转再挪回来
挪动位置:padding + 图片宽高的一半
*/
canvas.translate(mPadding + mLength / 2, mPadding + mLength / 2);
canvas.rotate(-flipRotation);
// 保存状态
camera.save();
// 以X轴为轴心旋转*°
camera.rotateX(bottomFlip);
// 应用到画布 根据当前的变换计算出相应的矩阵,然后应用到制定的画布上
camera.applyToCanvas(canvas);
// 回滚状态
camera.restore();
// 只要下半部分,旋转前切割 因为涉及到旋转,所以切割范围要变大
canvas.clipRect(-mLength, 0, mLength, mLength);
// 旋转20°
canvas.rotate(flipRotation);
// 将画布移回来
canvas.translate(-(mPadding + mLength / 2), -(mPadding + mLength / 2));
// 绘制图片
canvas.drawBitmap(
getAvatar(mLength),
mPadding, mPadding,
mPaint
);
canvas.restore();
}
// 自定义动画配置 topFlip,bottomFlip,FlipRotation
public float getTopFlip() {
return topFlip;
}
public void setTopFlip(float topFlip) {
this.topFlip = topFlip;
invalidate();
}
public float getBottomFlip() {
return bottomFlip;
}
public void setBottomFlip(float bottomFlip) {
this.bottomFlip = bottomFlip;
invalidate();
}
public float getFlipRotation() {
return flipRotation;
}
public void setFlipRotation(float flipRotation) {
this.flipRotation = flipRotation;
invalidate();
}
Bitmap getAvatar(int width) {
BitmapFactory.Options options = new BitmapFactory.Options();
// inJustDecodeBounds为true,不返回bitmap,只返回这个bitmap的尺寸
options.inJustDecodeBounds = true;
// 从资源中读取(比较浪费资源,所以上面设置为true,只获取图片宽高)
BitmapFactory.decodeResource(getResources(), R.drawable.ysdry, options);
// 再设置为false,最后要返回bitmap
options.inJustDecodeBounds = false;
// 根据缩放比例重新计算宽高
options.inDensity = options.outWidth;
options.inTargetDensity = width;
return BitmapFactory.decodeResource(getResources(), R.drawable.ysdry, options);
}
}
下面是开启动画
/*
属性动画 大概分为两种,ViewPropertyAnimator 和 ObjectAnimator
前者是系统提供好的,可做一些简单的平移,缩放,旋转,渐变
后者是自定义属性动画,比较灵活
*/
CameraAnimatorView mCameraAnimatorView = findViewById(R.id.mCameraAnimatorView);
// bottomFlip对应CameraAnimatorView里面的bottomFlip,45代表bottomFlip要变化的值
ObjectAnimator bottomAnimator=ObjectAnimator.ofFloat(mCameraAnimatorView,"bottomFlip",45);
// 设置时长
bottomAnimator.setDuration(1500);
ObjectAnimator flipRotationAnimator=ObjectAnimator.ofFloat(mCameraAnimatorView,"flipRotation",270);
flipRotationAnimator.setDuration(1500);
ObjectAnimator topFlipAnimator=ObjectAnimator.ofFloat(mCameraAnimatorView,"topFlip",-45);
flipRotationAnimator.setDuration(3300);
// 多个动画,分先后顺序填写到playSequentially方法中
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(bottomAnimator,flipRotationAnimator,topFlipAnimator);
// 延迟动画启动的时间
animatorSet.setStartDelay(2000);
animatorSet.start();