Android 自定义View实现动画效果切换主题颜色
发布日期:2021-06-29 11:46:18 浏览次数:3 分类:技术文章

本文共 5925 字,大约阅读时间需要 19 分钟。

先看效果图:

想要实现这种效果,首先要了解下Xfermode图像混合模式中的PorterDuff.Mode.CLEAR,它可以用来清除原图像的部分绘制内容,可以理解为它是一块橡皮,可以擦去图像上的任意一块地方。

其次,canvas中的也有着图层的概念。图层是什么,简单来说就是一层一层的图片叠加在同一个地方,比如有一幢摩天大楼,它有一层,两层,三层......十八层等等,我们如果从大楼正上方俯瞰大楼,因为它的下面几层都被最上层压住了,所以我们只能看到它的最上层。图层的概念也一样,我们正常情况只能看到最上层的图层,其它层都被覆盖住了。

所以,大家应该已经猜到了这个动画效果的实现原理,没错,我们一共需要两层,第一层保留着主题更改前的效果图,第二层就是主题更改后的效果图,然后我们用PorterDuff.Mode.CLEAR这个模式,通过drawCircle画圆的方式来擦去旧样式。旧样式被擦去以后,我们就能看见被压在下面的新样式,也就是主题被更改以后的样式。

大致原理如下图:(注意:旧样式在最上层,是后面新添加进来的图层)

下面来说说代码实现。

1、我们需要获取到更改前界面的样式,并用bitmap保存下来,同时得到我们需要用来擦除旧图层的圆的半径:

//将View当前的样式截图保存在bg中private void createbg(){    rootView=(ViewGroup)((Activity)getContext()).getWindow().getDecorView();    totalRadius=rootView.getMeasuredWidth()>rootView.getMeasuredHeight()?rootView.getMeasuredWidth():rootView.getMeasuredHeight();    rootView.setDrawingCacheEnabled(true);    bg=Bitmap.createBitmap(rootView.getDrawingCache(), 0, 0, rootView.getMeasuredWidth(), rootView.getMeasuredHeight());    rootView.setDrawingCacheEnabled(false);    attachToRootView();}

rootView:原界面Activity的View

totalRadius:总共需要绘制的圆的半径

bg:用来保存原界面样式的Bitmap

attachToRootView():将该View添加到rootView中的方法(即动态添加该布局控件)

 2、通过saveLayer方法添加新图层来保存旧样式,并以动画形式擦除旧样式(注意,添加后的操作都是在新图层上进行的)

@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    //是否开始绘制    if(!start)return;    //如果已绘制的半径超过需要绘制的半径,则绘制完毕    if(radius>totalRadius){        animateFinish();        return;    }    //在新图层上进行绘制    int layer = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);    canvas.drawBitmap(bg, 0, 0, null);    canvas.drawCircle(0, 0, radius+perRadius, paint);    radius+=perRadius;    canvas.restoreToCount(layer);    postInvalidateDelayed(5);}//绘制完毕,动画结束private void animateFinish(){    start=false;    bg.recycle();    radius=0;}

3、在Activity中完成点击按钮的监听和新样式的更新

private void initView(){    animatorThemeView=new MyAnimatorThemeView(this);    textView=findViewById(R.id.tv_name);    findViewById(R.id.btn_change).setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View v) {            if(!animatorThemeView.hasStart()){                clickNum++;                //开始绘制                animatorThemeView.start();                //更新textView的样式                textView.setTextColor(getResources().getColor(tvColors[clickNum%2]));                textView.setBackground(getDrawable(bgColors[clickNum%2]));            }        }    });}

下面是完整的自定义View代码:

import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.view.View;import android.view.ViewGroup;public class MyAnimatorThemeView extends View {    private Paint paint;    private Bitmap bg;    private ViewGroup rootView;    private boolean start=false;    private int radius=0,totalRadius=3000,perRadius=55;   //当前已绘制的圆的半径;需要绘制出的圆的半径;每次绘制的半径    public MyAnimatorThemeView(Context context){        super(context);        init();    }    private void init(){        paint=new Paint();        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //是否开始绘制        if(!start)return;        //如果已绘制的半径超过需要绘制的半径,则绘制完毕        if(radius>totalRadius){            animateFinish();            return;        }        //在新的图层上面绘制        int layer = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);        canvas.drawBitmap(bg, 0, 0, null);        canvas.drawCircle(0, 0, radius+perRadius, paint);        radius+=perRadius;        canvas.restoreToCount(layer);        postInvalidateDelayed(5);    }    //开启动画    public void start(){        if(!start){            createbg();            start=true;            invalidate();        }    }    //将View当前的样式截图保存在bg中    private void createbg(){        rootView=(ViewGroup)((Activity)getContext()).getWindow().getDecorView();        totalRadius=rootView.getMeasuredWidth()>rootView.getMeasuredHeight()?rootView.getMeasuredWidth():rootView.getMeasuredHeight();        rootView.setDrawingCacheEnabled(true);        bg=Bitmap.createBitmap(rootView.getDrawingCache(), 0, 0, rootView.getMeasuredWidth(), rootView.getMeasuredHeight());        rootView.setDrawingCacheEnabled(false);        attachToRootView();    }    //当前View添加到布局中    private void attachToRootView() {        if(this.getParent()==null){            this.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));            rootView.addView(this);        }    }    //绘制完毕,动画结束    private void animateFinish(){        start=false;        bg.recycle();        radius=0;    }    //向外提供检查是否开始的方法    public boolean hasStart(){        return start;    }}

Activity类的代码:

public class MyAnimatorThemeAct extends AppCompatActivity {    private MyAnimatorThemeView animatorThemeView;    private TextView textView;    private int clickNum=0;    private int[] tvColors={R.color.white,R.color.black};    private int[] bgColors={R.color.light_black,R.color.blue};    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.act_animator_theme);        initView();    }    private void initView(){        animatorThemeView=new MyAnimatorThemeView(this);        textView=findViewById(R.id.tv_name);        findViewById(R.id.btn_change).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if(!animatorThemeView.hasStart()){                    clickNum++;                    animatorThemeView.start();                    textView.setTextColor(getResources().getColor(tvColors[clickNum%2]));                    textView.setBackground(getDrawable(bgColors[clickNum%2]));                }            }        });    }}

xml布局文件如下:

 

转载地址:https://blog.csdn.net/zz51233273/article/details/107713825 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android 自定义View贝塞尔曲线实现书籍翻页的效果(包含原理解释)
下一篇:Android 自定义View实现打钩(签到)的动画

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年04月24日 21时02分11秒