Android——自定义View(三)
2019-09-29 本文已影响0人
如愿以偿丶
1.概述
最近看到 红橙Darren 写的贝塞尔曲线 - 花束直播点赞效果后实现,来记录一下自己实现的步骤。
2.效果
在这里插入图片描述3.实现思路:
1.点击添加按钮的时候有一张图片
2.延S型曲线从底部向上运动
4.开始
4.1.自定义RelativeLayout,初始化一些基本的参数
public class LoveLayout extends RelativeLayout {
//随机数
private Random mRandom;
//设置资源
private int[] mImageRes;
//屏幕宽高
private int mWidth,mHeight;
//图片的宽高
private int mResWidth,mResheight;
//插值器集合
private Interpolator[] mInterpolator;
public LoveLayout(Context context) {
this(context,null);
}
public LoveLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//1.初始化随机数
mRandom = new Random();
//2.初始化资源图片
mImageRes = new int[]{R.drawable.green,R.drawable.red,R.drawable.note};
//3.获取图片的宽高
Drawable drawable = getContext().getResources().getDrawable(R.drawable.green);
mResheight = drawable.getIntrinsicHeight();
mResWidth = drawable.getIntrinsicWidth();
//4.插值器集合
// mInterpolator = new Interpolator[]{new AccelerateDecelerateInterpolator()
// ,new AccelerateInterpolator()
// ,new DecelerateInterpolator()
// ,new LinearInterpolator()};
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
}
}
4.2.点击添加按钮,不断的往RelativeLayout中添加随机的ImageView,然后开始执行缩放和透明度的动画,采用属性动画ObjectAnimator.ofFloat();
public void addView() {
//创建ImageView,设置图片资源
final ImageView ivLove = new ImageView(getContext());
//设置图片资源随机
ivLove.setImageResource(mImageRes[mRandom.nextInt(mImageRes.length)]);
//设置图片添加位置
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(ALIGN_PARENT_BOTTOM);
params.addRule(CENTER_HORIZONTAL);
ivLove.setLayoutParams(params);
//添加到父容器
addView(ivLove);
//添加属性动画 缩放和透明度效果
AnimatorSet set = getAnimator(ivLove);
//执行动画
set.start();
}
private AnimatorSet getAnimator(ImageView ivLove) {
//1.设置动画,透明度,缩放
AnimatorSet innerAnimator = new AnimatorSet();
ValueAnimator alphaAnimator = ObjectAnimator.ofFloat(ivLove,"alpha",0.5f,1f);
ValueAnimator scaleXAnimator = ObjectAnimator.ofFloat(ivLove,"scaleX",0.5f,1f);
ValueAnimator scaleYAnimator = ObjectAnimator.ofFloat(ivLove,"scaleY",0.5f,1f);
innerAnimator.setDuration(350);
//一起执行
innerAnimator.playTogether(alphaAnimator,scaleXAnimator,scaleYAnimator);
return innerAnimator ;
}
}
4.3.将图片延S型曲线进行运动(使用贝塞尔曲线 + 属性动画 + 自定义TypeEvaluator)
先看一下公式
/**
* 贝塞尔路径动画 自定义TypeEvaluator
* @return
*/
private Animator getBezierAnimator(final ImageView ivLove) {
//1.四个点 p0,p1,p2,p3
PointF point0 = new PointF(mWidth / 2 - mResWidth / 2,mHeight - mResheight) ;
//2.P2点的y坐标要大于p1点的y坐标
PointF point1 = getPoint(1);
PointF point2 = getPoint(2);
//终点为屏幕的一半
Log.e("getBezierAnimator",mHeight+"");
PointF point3 = new PointF(mRandom.nextInt(mWidth - mResWidth),0);
//构造方法中传入中间的点
LoveTypeEvaluator typeEvaluator = new LoveTypeEvaluator(point1,point2);
ValueAnimator bezierAnimator = ObjectAnimator.ofObject(typeEvaluator,point0,point3);
// bezierAnimator.setInterpolator(mInterpolator[mRandom.nextInt(mInterpolator.length -1)]);
bezierAnimator.setDuration(6000);
bezierAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获取当前的值
PointF pointF = (PointF) animation.getAnimatedValue();
//设置位置
ivLove.setX(pointF.x);
ivLove.setY(pointF.y);
//获取的是自定义TypeEvaluator,evaluate方法中的t值,该值是0-1的
float alpha = animation.getAnimatedFraction(); //他是0-1的数
ivLove.setAlpha(1- alpha + 0.2f);
}
});
return bezierAnimator;
}
/**
* 1代表p1 2代表p2
* p1的时候y最大值位 mHeight / 2
* p2的时候y最大值 mHeight / 2 + ((2-1) *mHeight / 2) = mHeight
* @param index
* @return
*/
private PointF getPoint(int index) {
return new PointF(mRandom.nextInt(mWidth - mResWidth),mRandom.nextInt(mHeight / 2 +(index-1)*mHeight/2));
}
//自定义TypeEvaluator
public class LoveTypeEvaluator implements TypeEvaluator<PointF> {
//中间点p1,p2,实现TypeEvaluator实现的evaluate方法只有起始点和终点,所以中间点需要我们通过构造方法传入
private PointF point1;
private PointF point2;
public LoveTypeEvaluator(PointF point1, PointF point2) {
this.point1 = point1;
this.point2 = point2;
}
/**
*
* @param t [0 ,1]
* @param point0 起始点
* @param point3 终点
* @return
*/
@Override
public PointF evaluate(float t, PointF point0, PointF point3) {
PointF pointF = new PointF();
//直接套上方的公式,三阶贝塞尔曲线
pointF.x = point0.x * (1-t) * (1-t) * (1-t)
+ 3 * point1.x * t * (1-t) * (1-t)
+ 3 * point2.x * t * t * (1 - t)
+ point3.x * t * t * t;
pointF.y = point0.y * (1-t) * (1-t) * (1-t)
+ 3 * point1.y * t * (1-t) * (1-t)
+ 3 * point2.y * t * t * (1 - t)
+ point3.y * t * t * t;
return pointF;
}
}
5.最终总结
5.1.自定义的RelativeLayout
/**
* Created by ych on 2019/8/17.
* Description: 花束直播点赞
* 分析:1.点击的时候会有一张图片有放大和透明度的变化,图片向上延S路运动,图片是随机的图片
* 2.移动的过程也有透明度变化
* 思路:添加图片addView
*/
public class LoveLayout extends RelativeLayout {
//随机数
private Random mRandom;
//设置资源
private int[] mImageRes;
//屏幕宽高
private int mWidth,mHeight;
//图片的宽高
private int mResWidth,mResheight;
//插值器集合
private Interpolator[] mInterpolator;
public LoveLayout(Context context) {
this(context,null);
}
public LoveLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//1.初始化随机数
mRandom = new Random();
//2.初始化资源图片
mImageRes = new int[]{R.drawable.green,R.drawable.red,R.drawable.note};
//3.获取图片的宽高
Drawable drawable = getContext().getResources().getDrawable(R.drawable.green);
mResheight = drawable.getIntrinsicHeight();
mResWidth = drawable.getIntrinsicWidth();
//4.插值器集合
// mInterpolator = new Interpolator[]{new AccelerateDecelerateInterpolator()
// ,new AccelerateInterpolator()
// ,new DecelerateInterpolator()
// ,new LinearInterpolator()};
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
}
public void addView() {
//创建ImageView,设置图片资源
final ImageView ivLove = new ImageView(getContext());
//设置图片资源随机
ivLove.setImageResource(mImageRes[mRandom.nextInt(mImageRes.length)]);
//设置图片添加位置
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(ALIGN_PARENT_BOTTOM);
params.addRule(CENTER_HORIZONTAL);
ivLove.setLayoutParams(params);
//添加到父容器
addView(ivLove);
//添加属性动画 放大和透明度效果
AnimatorSet set = getAnimator(ivLove);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//将view移除
removeView(ivLove);
}
});
set.start();
}
private AnimatorSet getAnimator(ImageView ivLove) {
AnimatorSet allAnimatorSet = new AnimatorSet();
//1.设置动画,透明度,缩放
AnimatorSet innerAnimator = new AnimatorSet();
ValueAnimator alphaAnimator = ObjectAnimator.ofFloat(ivLove,"alpha",0.5f,1f);
ValueAnimator scaleXAnimator = ObjectAnimator.ofFloat(ivLove,"scaleX",0.5f,1f);
ValueAnimator scaleYAnimator = ObjectAnimator.ofFloat(ivLove,"scaleY",0.5f,1f);
innerAnimator.setDuration(350);
//一起执行
innerAnimator.playTogether(alphaAnimator,scaleXAnimator,scaleYAnimator);
//2.设置运动路径动画 playSequentially按顺序一次执行
allAnimatorSet.playSequentially(innerAnimator,getBezierAnimator(ivLove));
return allAnimatorSet;
}
/**
* 贝塞尔路径动画 自定义TypeEvaluator
* @return
*/
private Animator getBezierAnimator(final ImageView ivLove) {
//1.四个点 p0,p1,p2,p3
PointF point0 = new PointF(mWidth / 2 - mResWidth / 2,mHeight - mResheight) ;
//2.P2点的y坐标要大于p1点的y坐标
PointF point1 = getPoint(1);
PointF point2 = getPoint(2);
//终点为屏幕的一半
Log.e("getBezierAnimator",mHeight+"");
PointF point3 = new PointF(mRandom.nextInt(mWidth - mResWidth),0);
//构造方法中传入中间的点
LoveTypeEvaluator typeEvaluator = new LoveTypeEvaluator(point1,point2);
ValueAnimator bezierAnimator = ObjectAnimator.ofObject(typeEvaluator,point0,point3);
// bezierAnimator.setInterpolator(mInterpolator[mRandom.nextInt(mInterpolator.length -1)]);
bezierAnimator.setDuration(6000);
bezierAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获取值
PointF pointF = (PointF) animation.getAnimatedValue();
//设置最终之
ivLove.setX(pointF.x);
ivLove.setY(pointF.y);
//逐渐消失
float alpha = animation.getAnimatedFraction(); //他是0-1的数
ivLove.setAlpha(1- alpha + 0.2f);
}
});
return bezierAnimator;
}
/**
* 1代表p1 2代表p2
* p1的时候y最大值位 mHeight / 2
* p2的时候y最大值 mHeight / 2 + ((2-1) *mHeight / 2) = mHeight
* @param index
* @return
*/
private PointF getPoint(int index) {
return new PointF(mRandom.nextInt(mWidth - mResWidth),mRandom.nextInt(mHeight / 2 +(index-1)*mHeight/2));
}
}
5.2.自定义的TypeEvaluator
/**
* Created by ych on 2019/8/17.
* Description:
*/
public class LoveTypeEvaluator implements TypeEvaluator<PointF> {
//中间点
private PointF point1;
private PointF point2;
public LoveTypeEvaluator(PointF point1, PointF point2) {
this.point1 = point1;
this.point2 = point2;
}
/**
*
* @param t [0 ,1]
* @param point0 起始点
* @param point3 终点
* @return
*/
@Override
public PointF evaluate(float t, PointF point0, PointF point3) {
PointF pointF = new PointF();
//直接套公式,三阶贝塞尔曲线
pointF.x = point0.x * (1-t) * (1-t) * (1-t)
+ 3 * point1.x * t * (1-t) * (1-t)
+ 3 * point2.x * t * t * (1 - t)
+ point3.x * t * t * t;
pointF.y = point0.y * (1-t) * (1-t) * (1-t)
+ 3 * point1.y * t * (1-t) * (1-t)
+ 3 * point2.y * t * t * (1 - t)
+ point3.y * t * t * t;
return pointF;
}
}