想要知道关于更多自定义View的实例,请参考:android自定义View索引
先上个效果图,以免大家跑错地了。
用高清的每次只能录三秒,不管了,大家能懂啥意思就行。
大家看这效果就感觉比较复杂了,事实上比一般的复杂一点。
看到这个样子的动画,首先我们需要理智分析,不要别表面现象所迷糊了,其实就是两个东西:
一:属性
二:动画
详细说呢就是,一是物体颜色的变化和形状的变化,二是动画的跳动。
属性
那么怎么动态修改一个物体的形状和颜色,从上面的图我们可以看出来,我们使用了三个形状,方形,三角形和圆形,给出三个绘制函数:
方形 :canvas.drawPath(rectPath,paint);
三角形:canvas.drawPath(trianglePath,paint);
圆形 :canvas.drawCircle(controlWidth /2,controlWidth /2,controlWidth /2,paint);
这里的方形和三角形,我们采用的是用Path的方式进行绘制,不知道啥是Path的百度一下吧,为了缩减篇幅就不说了。
好了,那现在我们能够随心所欲的绘制这三个图形,但是我们什么时候该绘制什么图形呢?我们可以写一个枚举类,用来储存我们的形状,然后进行切换:
变形的集中图形列表,分别是方形,三角形,圆形
public enum ShapeKind {
*RECT,TRIANGLE,CIRCLE*
}
枚举类,写好了,包含三个图形,现在来实现了动态切换图形的形状了,我们可以用一个变量去递增,例如,我给个变量 i 初始值为 1 , 但是每次重绘的时候添加了,如果到了100,那么就开始切换成另外一个图形,并且将 i 重置为 100, 然后又去循环,这样就能不断动态的切换形状了,至于颜色不就更简单了,每个图形给不同的画笔,不就有三种颜色了 。
但是有时我们会发现我们的重绘速度太快了,那么我们可以用定时器减慢速度:
handler.postDelayed(new
Runnable() {
@Override
public void run () {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
},20);
}
这样的话就会每个0.02秒进行重绘一次了,速度就在我们的掌控之中了。
整合上面的知识,我们就能绘制出一个不断变换形状和颜色的控件了,我们起名为: ** TransformationLoading**, 然后被下面动画的类进行使用即可。
动画
下面看看我们的物体的运动变化,观察到了三种动作,一个上升,一个下降,还有一个旋转,并且都是同时进行的,那么这样我就必须要用到一个平时不常用到的函数了:
AnimatorSet
这个函数的作用就是用来把单独的动画集合起来的工具,简单给个例子:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(transformationLoading,"translationY",0,jumpDistance);
ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow,"scaleX",0.2f,1f);
downAnimator =new AnimatorSet();
downAnimator.playTogether(objectAnimator, scaleIndication);
downAnimator.setDuration(time);
上面的前两行分别是位移和旋转的动画,第四行则是把两个动画集合在了一起,然后第五行是设置动画时间,基本用法就是这样。
但是我们仔细观察上面的图形,我们发现跳动的物体下面还有一个阴影和加载中三个字,不用慌,看到这里我们想到我们现在这个控件是一个容器,那我们就继承 LinearLayout 吧,起名为 ** JumpLoading ** 。
这样的话就能包含三个控件了,一个TransformationLoading,也就是我们上面属性部分创造的控件,一个阴影,其实就是一个ImageView,外加一个TextView。
那动画我们知道怎么集合再一起了,那动画上面时候开始执行和结束呢?下面给出两个函数:
//界面加载结束
@Override
protected void onFinishInflate() {*
super.onFinishInflate();*
startAnimate(0); //开始动画*
}
//界面被销毁
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopAnimate(); //停止动画
}
两个函数属于linearLayout的生命周期里面的函数,分别是刚被绘制时和被销毁时回调的函数,再这里我们可以开始和结束动画。
说到这里所有的运转流程大家应该也就比较清楚了,具体的操作大家还是要去代码里面感受一下,具体的使用大家直接使用动画控件就行,动画控件的代码是在属性代码的基础上使用的。
先给属性的代码
不断变换图形的加载动画,仿58,参考链接:https://github.com/zzz40500/android-shapeLoadingView
总的来说,颜色变换的速度收两个值控制,一个是:colorChangeRate,一个是time,一个颜色变换率,一个是切换颜色的速度
public class TransformationLoadingextends View {
public static ShapeKindshapeKind =RECT;
private float currentColorChange = 0f;//当前颜色切换总值
private ArgbEvaluatormAnimPercent =new
ArgbEvaluator();//用来转化两种颜色的系统工具,详情自查
private Paintpaint;
private float controlWidth;//控件宽度
private Handlerhandler;
}
//下面的参数可以根据实际需要进行更改
private int mRectColor;//方形的颜色
private int mTriangleColor;//三角形的颜色
private int mCircle;//圆形的颜色
private float colorChangeRate = 0.02f;//集中图形切换时颜色的变化速度,推荐0.01-0.5,数值越小颜色变化越不突兀
private long time = 10;//动画的执行速度,推荐10 - 100,数值越大,动画越慢
private boolean changeKind = false;//是否开启主动切换形状的控制器
private boolean FlashingOrNot = false;//是否开启颜色闪动
private boolean animateOrNot = true;//是否执行不断刷新
public TransformationLoading(Context context) {
super(context);
initData();
}
public TransformationLoading(Context context, AttributeSet attrs) {
super(context, attrs);
initData();
}
//初始化几个图形的颜色
private void initData() {
mRectColor = Color.argb(255, 114, 213, 114);
mTriangleColor = Color.argb(255, 115, 153, 254);
mCircle = Color.argb(255, 232, 78, 64);
paint = new Paint();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
invalidate();//重绘界面
break;
}
}
};
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
controlWidth = getMeasuredWidth();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (shapeKind) {
case RECT://绘制一个正方形
currentColorChange += colorChangeRate;
if (FlashingOrNot) {
int rectColor = (int) mAnimPercent.evaluate(currentColorChange, mRectColor, mTriangleColor);
paint.setColor(rectColor);
} else {
paint.setColor(mRectColor);
}
if (currentColorChange > 1) {
if (changeKind) {
shapeKind = TRIANGLE;
}
currentColorChange = 0f;
}
Path rectPath = new Path();
rectPath.moveTo(0, 0);
rectPath.lineTo(controlWidth, 0);
rectPath.lineTo(controlWidth, controlWidth);
rectPath.lineTo(0, controlWidth);
rectPath.close();
canvas.drawPath(rectPath, paint);
if (animateOrNot) {
toRefresh();
}
break;
case TRIANGLE://绘制一个三角形
currentColorChange += colorChangeRate;
if (FlashingOrNot) {
int triangleColor = (int) mAnimPercent.evaluate(currentColorChange, mTriangleColor, mCircle);
paint.setColor(triangleColor);
} else {
paint.setColor(mTriangleColor);
}
if (currentColorChange > 1) {
if (changeKind) {
shapeKind = CIRCLE;
}
currentColorChange = 0f;
}
Path trianglePath = new Path();
trianglePath.moveTo(0.5f * controlWidth, 0);
trianglePath.lineTo(controlWidth, controlWidth);
trianglePath.lineTo(0, controlWidth);
trianglePath.close();
canvas.drawPath(trianglePath, paint);
if (animateOrNot) {
toRefresh();
}
break;
case CIRCLE://绘制一个圆形
currentColorChange += colorChangeRate;
if (FlashingOrNot) {
int circleColor = (int) mAnimPercent.evaluate(currentColorChange, mCircle, mRectColor);
paint.setColor(circleColor);
} else {
paint.setColor(mCircle);
}
if (currentColorChange > 1) {
if (changeKind) {
shapeKind = RECT;
}
currentColorChange = 0f;
}
canvas.drawCircle(controlWidth / 2, controlWidth / 2, controlWidth / 2, paint);
if (animateOrNot) {
toRefresh();
}
break;
}
}
//用来供主动切换形状的方法
public void changeShape() {
currentColorChange = 0f;//重置颜色渐变数值
changeKind = false;//关闭自动切换图形的功能
switch (shapeKind) {
case RECT:
shapeKind = TRIANGLE;
break;
case TRIANGLE:
shapeKind = CIRCLE;
break;
case CIRCLE:
shapeKind = RECT;
break;
}
}
//重新刷新界面
private void toRefresh() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
}, time);
}
//变形的集中图形列表,分别是方形,三角形,圆形
public enum ShapeKind {
RECT, TRIANGLE, CIRCLE
}
//停止动画
public void stopAnimate() {
animateOrNot = false;
}
//开始动画
public void startAnimate() {
if (!animateOrNot) {
animateOrNot = true;
toRefresh();
}
}
再给动画的代码:
- 仿58加载动画跳动的小球,和TransformationLoading控件结合起来使用,并且要关闭TransformationLoading的主动切换形状的控制器,也就是:changeKind
public class JumpLoadingextends LinearLayout {
private AnimatorSetupAnimator;//上升的动画集合
private AnimatorSetdownAnimator;//下降的动画集合
private boolean move = false;//当前有没有执行动画
private Runnablerunnable;
private TransformationLoadingtransformationLoading;//跳动的小球
private ImageViewjumpShadow;//下面的阴影
//下面的参数可以根据实际需要进行更改
private int jumpDistance = 200;//跳动的距离
private int time = 500;//跳动一个周期的时间,0.5秒
private int angle = 180;//跳动过程中的旋转角度
public JumpLoading(Context context) {
super(context);
initData(context);
}
public JumpLoading(Context context, AttributeSet attrs) {
super(context, attrs);
initData(context);
}
private void initData(Context context) {
LayoutInflater.from(context).inflate(R.layout.jump_layout, this, true);
transformationLoading = (TransformationLoading) findViewById(R.id.transformationLoading);
jumpShadow = (ImageView) findViewById(R.id.jumpShadow);
runnable = new Runnable() {
@Override
public void run() {
ViewHelper.setRotation(transformationLoading, 180f);
ViewHelper.setTranslationY(transformationLoading, 0f);
ViewHelper.setScaleX(jumpShadow, 0.2f);
move = true;
freeFall();//先执行下降动画
}
};
}
//下降动画
private void freeFall() {
if (downAnimator == null) {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(transformationLoading, "translationY", 0, jumpDistance);
ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow, "scaleX", 0.2f, 1f);
downAnimator = new AnimatorSet();
downAnimator.playTogether(objectAnimator, scaleIndication);
downAnimator.setDuration(time);
downAnimator.setInterpolator(new AccelerateInterpolator(1.2f));
downAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (move) {
transformationLoading.changeShape();
rise();
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
downAnimator.start();
}
//上升动画
private void rise() {
if (upAnimator == null) {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(transformationLoading, "translationY", jumpDistance, 0);
ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow, "scaleX", 1f, 0.2f);
ObjectAnimator objectAnimator1 = null;
objectAnimator1 = ObjectAnimator.ofFloat(transformationLoading, "rotation", 0, angle);
upAnimator = new AnimatorSet();
upAnimator.playTogether(objectAnimator, objectAnimator1, scaleIndication);
upAnimator.setDuration(time);
upAnimator.setInterpolator(new DecelerateInterpolator(1.2f));
upAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (move) {
freeFall();
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
upAnimator.start();
}
//开始执行动画,从runnable开始,然后rise和freeFall互调
public void startAnimate(int delay) {
if (!move) {
if (downAnimator != null && downAnimator.isRunning()) {
return;
}
this.removeCallbacks(runnable);
if (delay > 0) {
this.postDelayed(runnable, delay);
} else {
this.post(runnable);
}
}
}
//停止动画,停止所有动画和监听
public void stopAnimate() {
move = false;
if (upAnimator != null) {
if (upAnimator.isRunning()) {
upAnimator.cancel();
}
upAnimator.removeAllListeners();
for (Animator animator : upAnimator.getChildAnimations()) {
animator.removeAllListeners();
}
upAnimator = null;
}
if (downAnimator != null) {
if (downAnimator.isRunning()) {
downAnimator.cancel();
}
downAnimator.removeAllListeners();
for (Animator animator : downAnimator.getChildAnimations()) {
animator.removeAllListeners();
}
downAnimator = null;
}
this.removeCallbacks(runnable);
}
//界面被加载结束
@Override
protected void onFinishInflate() {
super.onFinishInflate();
startAnimate(0);
}
//界面被销毁
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopAnimate();
}
}
喜欢我的文章的,请点击关注我哦。万学冬的简书