26
2017
09

从属性动画看自定义View(1)

转载请注意:http://blog.csdn.net/wjzj000/article/details/78080165

本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)

写在前面

好久都没有写博客了,因为上班之后才发现时间真的变得非常的珍贵。没了校园里的无忧,还真有些惆怅…

但是!只要能学习,那就是元气满满的一天呢。
这篇博客对于我来说,并不单单是记录一个用法,而是让我自己对动画和自定义View有了一个新的认识和理解,这里大概不是标准意义上对某个知识或者是效果的重演,更多的是我从动画和自定义View这俩者身上找到的联系,这让我对安卓的学习有了些更加广阔且随意的认知。
当然这里对我的帮助,也许未必对各位看官有帮助,但反过来也有可能会有帮助。这种缘分上的事,谁又能说的准呢。


开始

首先让我们看一个效果,这样点进来的看官就可以根据效果选择是继续往下看,还是右上角点叉…

这里写图片描述

分析:

  • 首先来说这里不是一个为了实现某个效果的View,它是俩个点一个上上边那个静态一张图片再加上左上角的一个鲁字;这一部分主要是用于记录PorterDuff和Shader以及其中涉及的到的硬件加速,离屏缓存等内容,相对只是点比较散。

  • 中部的动态效果,是这篇博客的主角,它涉及到了Camera,已经动画相结合的内容。

进入代码:

第一部分:

关于这种俩个图片通过叠加的以展示不同效果,本质上通过PorterDuff的不同模式在做,至于什么是PorterDuff这个随便百度谷歌就可以了,说白了就是一个标识,通过不同的PorterDuff实现不同的图片叠加的展示效果。

针对于PorterDuff又有多种的具体实现过程,这里提供俩条思路:

思路1:


Bitmap bitmapBg = BitmapFactory.decodeResource(context.getResources(), R.drawable.pintu);
Bitmap mWord = BitmapFactory.decodeResource(context.getResources(), R.drawable.btm_lu);

Paint mPaint = new Paint();
BitmapShader bgShader = new BitmapShader(bitmapBg, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
BitmapShader wordShader = new BitmapShader(mWord, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
ComposeShader composeShader = new ComposeShader(bgShader, wordShader, PorterDuff.Mode.SRC_OVER);
mPaint.setShader(composeShader);

canvas.drawCircle(0,0,500,mPaint);

这里使用的思路是通过俩个BitmapShader进行组合,然后Paint把Shader,set进去。最后通过canvas的特定api去绘制我们想要显示的图形。
但是这里有个致命的缺陷就是如果当前使用了硬件加速,那么同一类的ComposeShader就会失效,而这里我们想要实现的失效正是使用了俩个BitmapShader因此必须关闭赢家加速才可以看到效果。
关于关闭硬件加速,我们可以选择在应用级别,Activity级别,View级别等级别上关闭硬件加速。
比如View级别,直接咱们的自定义View类中调用setLayerType(LAYER_TYPE_SOFTWARE, null)
如果选择关闭硬件加速,一切动态效果要求比较高的动画机会一卡一卡的…

思路2:


>这里我们使用了Paint的setXfermode方法,依旧传递的是PorterDuff。不过与上边不同的是,这里我们直接通过drawBitmap去画我们想要显示的图像,这里setXfermode上边draw出来的Bitmap为PorterDuff这套逻辑里的SRC图像,下边draw出来的Bitmap为DST。

canvas.save();

//离屏缓冲,避免一系列绘制问题(比如绘制的过程中出现黑色的底色问题)
int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(mBg,0,0,mPaint);

mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));

canvas.drawBitmap(mWord,0,0,mPaint);
mPaint.setXfermode(null);

canvas.restoreToCount(saved);

关于第一部分就介绍到这里,其实没有啥多么眼花缭乱的技巧。主要就是通过这么个东西,来记录一下其中涉及到的内容。


第二部分:

这一部分同样也没有什么高深的用法,但是对于我来说意义非凡。因为我之前从未想过在自定义View之中结合动画去实现View的动态效果,之前都是通过其他手段来在外部不断的调用内部的invalidate()来起到重绘View的效果。今天才发现在内部使用动画,更为简单快捷。

绘制翻页效果:

实现这个效果比较简单,主要是借助了Camera,通过Camera可以轻松实现像3D翻转这种效果。但是在关闭硬件加速的请款下,比较的卡。

这里直接贴代码,没啥好说的内容。把Camera当成投影仪的镜头,Canvas就相当于白布,我们在白布上看到,实际只是具体事物的投影,比如这里的Bitmap。
关于Camera具体细节,看官如果想深入了解可以看一下扔物线大神的系列文章

        mCamera=new Camera();

        int bitmapWidth = mBg.getWidth();
        int bitmapHeight = mBg.getHeight();
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        int x = centerX - bitmapWidth / 2;
        int y = centerY - bitmapHeight / 2;

        // 第一遍绘制:上半部分
        canvas.save();
        canvas.clipRect(0, 0, getWidth(), centerY);
        canvas.drawBitmap(mBg, x, y, mPaint);
        canvas.restore();

        // 第二遍绘制:下半部分
        canvas.save();

        if (mRotation < 90) {
            canvas.clipRect(0, centerY, getWidth(), getHeight());
        } else {
            canvas.clipRect(0, 0, getWidth(), centerY);
        }
        mCamera.save();
        // 旋转 Camera 的三维空间
        mCamera.rotateX(mRotation);
        canvas.translate(centerX, centerY);
        // 把旋转投影到 Canvas
        mCamera.applyToCanvas(canvas);
        // 旋转之后把投影移动回来
        canvas.translate(-centerX, -centerY);
        mCamera.restore();

        canvas.drawBitmap(mBg, x, y, mPaint);
        canvas.restore();

这里我们控制图像的动态是通过控制mRotation值的变化。按我以前的思路就是用过一个公共的set方法去不断的传递一个新值。那么这里提供全新的思路,内部直接通过动画的方法:

//建立对应cameraRotation的getter/setter
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "cameraRotation", 0, 180);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setDuration(2000);
objectAnimator.start();


    public void setCameraRotation(float cameraRotation) {
        mRotation = cameraRotation;
        invalidate();
    }

    public float getCameraRotation() {
        return mRotation;
    }

这样的话,如果我们不需要外部使用者去关心View的效果展示的话,这种写法真的很简单,而且不需要外部进行过多的业务干涉。
这种实现方式对于我来说,更多是给我提供了一种全新看问题的角度。有些时候互相结合真的非常的方便。


尾声

来到一个屌屌的团队,真的感受到了站在巨人肩膀上的感受。视野真的变的很宽阔。看到了比我优秀的人还比我努力,还比我智商高,最可气的还TM比我好看…
人生太难了…

最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp

上一篇:为什么“15。。。”会导致微信ANR? 下一篇:浅谈Java中的equals和==