从属性动画看自定义View
本菜开源的一个自己写的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
2018年7月2号,我正式开始了自己的Android工作,为了能够让自己能够好好完成工作,并且能够快速得到技术提升。所以打算以公众号的方式去敦促自己学习,我会把自己日常的学习笔记发布到公众号上,如果可以,共同进步!~
个人公众号