本文来自网易云社区html
做者:孙有军android
咱们常常会有需求就是View消失的效果,这里咱们说的消失每每是所有消失,咱们可能采用一个alpha动画,在指定的时间内消失掉View,出现则实现相反的动画。咱们通常都采用以下的实现:canvas
采用tween动画实现:app
private void alphaTween() { AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f); alpha.setDuration(300); imageView.startAnimation(alpha); }
或者采用属性动画实现:ide
private void alphaOB() { ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 1.0f, 0.0f).setDuration(300); animator.start(); }
也可能采用xml来实现。函数
可是这里咱们须要的不是上面的效果,你是在逗我??不是上面的效果,你说这么多。咱们需求以下,这里咱们用两个图来展现:动画
左边是原始图片,右边是处理后的图片。能够看到从下到上愈来愈淡,顶部就已经像消失了同样。说道这里不少人确定会联想到图片的滤镜效果。可是这里实现的方式简单的多。this
这里咱们写一个demo来实现这个效果。既然是在对View进行处理,那这里咱们就先对一张图片进行处理。以后在扩展。rest
既然咱们须要将图片变淡消失,确定是须要合成了什么效果。那Android里面通常合成咱们都采用什么方式呐?code
Android里面咱们能够采用Xfermode来实现图片的合成,好比咱们能够实现各类各样的头像,例如圆形头像。那这里咱们须要采用哪一种mode?
这里先draw是dest,后draw是src,咱们须要的是将dest露出,同时将src产生的效果合成到dest上,那这里咱们须要用的是DST_IN效果。
上面图示表示一方是全透明的,其实还须要结合Mode定义来彻底理解该效果,Mode的计算方式以下:
public enum Mode { /** [0, 0] */ CLEAR (0), /** [Sa, Sc] */ SRC (1), /** [Da, Dc] */ DST (2), /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */ SRC_OVER (3), /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */ DST_OVER (4), /** [Sa * Da, Sc * Da] */ SRC_IN (5), /** [Sa * Da, Sa * Dc] */ DST_IN (6), /** [Sa * (1 - Da), Sc * (1 - Da)] */ SRC_OUT (7), /** [Da * (1 - Sa), Dc * (1 - Sa)] */ DST_OUT (8), /** [Da, Sc * Da + (1 - Sa) * Dc] */ SRC_ATOP (9), /** [Sa, Sa * Dc + Sc * (1 - Da)] */ DST_ATOP (10), /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */ XOR (11), /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */ DARKEN (16), /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */ LIGHTEN (17), /** [Sa * Da, Sc * Dc] */ MULTIPLY (13), /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */ SCREEN (14), /** Saturate(S + D) */ ADD (12), OVERLAY (15); Mode(int nativeInt) { this.nativeInt = nativeInt; } /** * @hide */ public final int nativeInt; }
从效果图上咱们能够看到,图片消失的效果,不是整块消失,而是部分效果,就是效果是有一个渐变的过程。那这里咱们须要合成的效果应该是一个渐变效果的图片。好比从全透明到全不透明。
咱们已经分析了所有须要的效果,那就最终写代码来看看最后的效果。
public class FadingPic extends View { private Paint paint; private Bitmap bitmap; private int height; private int width; public FadingPic(Context context) { super(context); init(context, null); } public FadingPic(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FadingPic(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.beautify); width = bitmap.getWidth(); height = bitmap.getHeight(); paint = new Paint(); paint.setAntiAlias(true); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); paint.setShader(new LinearGradient(0, 0, 0, height, 0x00000000, 0xff000000, Shader.TileMode.CLAMP)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(2 * width, height); } @Override protected void onDraw(Canvas c) { super.onDraw(c); c.drawBitmap(bitmap, 0, 0, null);// 原始图片 c.saveLayer(width, 0, width * 2, height, null, Canvas.ALL_SAVE_FLAG); c.drawBitmap(bitmap, width, 0, null); c.drawRect(width, 0, width * 2, height, paint); c.restore(); } }
咱们直接操做了本地的一张图片。先draw出原图,在draw出合成后效果图。
1,定义了Xfermode为PorterDuff.Mode.DST_IN 2,给paint设置一个线性渐变的shader,透明图从全透明到全不透明,平铺模式为CLAMP 3,这里必定要在新的Layer中进行操做,不然只是叠加效果
上面咱们采用在新的Layer进行操做,若是不在新的Layer只是叠加效果,那不采用Layer是否有方式能够实现?固然是能够的。这里咱们改变一下当前的代码再新建立一个bitmap:
package com.demo.opengl.widget;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.Shader;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;import com.demo.opengl.R;/** * Created by hzsunyj on 2017/8/9. */public class FadingPic extends View { private Paint paint; private Bitmap bitmap; private Bitmap wapperBitmap; private int height; private int width; private Canvas canvas; public FadingPic(Context context) { super(context); init(context, null); } public FadingPic(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FadingPic(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.beautify); width = bitmap.getWidth(); height = bitmap.getHeight(); paint = new Paint(); paint.setAntiAlias(true); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); paint.setShader(new LinearGradient(0, 0, 0, height, 0x00000000, 0xff000000, Shader.TileMode.CLAMP)); // new wapperBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); canvas = new Canvas(wapperBitmap); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(2 * width, height); } @Override protected void onDraw(Canvas c) { super.onDraw(c); c.drawBitmap(bitmap, 0, 0, null);// 原始图片 // c.saveLayer(width, 0, width * 2, height, null, Canvas.ALL_SAVE_FLAG); // c.drawBitmap(bitmap, width, 0, null); // c.drawRect(width, 0, width * 2, height, paint); // c.restore(); canvas.drawBitmap(bitmap, 0, 0, null); canvas.drawRect(0, 0, width, height, paint); c.drawBitmap(wapperBitmap, width, 0, null); } }
这里大部分代码是相同的,只是现将效果合成到一个新的bitmap,最后再将bitmap draw到屏幕上。
到这里也许你会问,这个需求没什么用啊!哪有这样需求。那这里咱们就举个栗子。好比咱们有一个列表页,当最上的条目滚动消失的时候,不是硬生生的消失,而是比较天然的消失。
好比上面,咱们向上滚动顶部就有一个明显的被切掉的效果。不太友好。那咱们能够处理成以下效果:
顶部一个渐变消失的效果。不会显的太突兀。
以前咱们实现了图片的部分消失效果,那这里咱们怎么处理,好比这个RecyclerView,是针对每一行来进行处理?这样是否是很麻烦,又这种bind状态,还有各类重用,感受问题比较多。前面的图片的方式根本不能重用啊!!
那这里咱们怎么实现呐?咱们是否能够不对item实现效果,而是针对整个RecyclerView来实现?
public class FadingRecyclerView extends RecyclerView { private Paint paint; private int height; private int width; public FadingRecyclerView(Context context) { super(context); init(context, null); } public FadingRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FadingRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } private void init(Context context, AttributeSet attrs) { paint = new Paint(); paint.setAntiAlias(true); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); paint.setShader(new LinearGradient(0, 0, 0, 160, 0x00000000, 0xff000000, Shader.TileMode.CLAMP)); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); height = h; width = w; } @Override public void draw(Canvas c) { c.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG); super.draw(c); c.drawRect(0, 0, width, 160, paint); c.restore(); } }
咱们重写了RecyclerView,在draw函数中合成了效果,这里RecyclerView是一个ViewGroup,须要复写draw函数,而不是onDraw。
前面咱们说过,若是不在新的Layer里面合成只会产生叠加效果,那这个叠加效果有什么用呐? 咱们能够来实现倒影效果,他也是阶梯渐变的,那这里咱们就来实现一下倒影效果。主要的区别为是否在新的layer里面实现。
public class FadingPic extends View { private Paint paint; private Paint paint1; private Bitmap bitmap; private Bitmap rotateBitmap; private int height; private int width; public FadingPic(Context context) { super(context); init(context, null); } public FadingPic(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FadingPic(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.beautify); width = bitmap.getWidth(); height = bitmap.getHeight(); paint = new Paint(); paint.setAntiAlias(true); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); paint.setShader(new LinearGradient(0, 0, 0, height, 0x00000000, 0xff000000, Shader.TileMode.CLAMP)); Matrix matrix = new Matrix(); matrix.setScale(1, -1); //matrix.setRotate(180); 不能造成镜像效果 rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); paint1 = new Paint(); paint1.setAntiAlias(true); paint1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); paint1.setShader(new LinearGradient(0, height, 0, 2 * height, 0xff000000, 0x00000000, Shader.TileMode.CLAMP)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(2 * width, 2 * height); } @Override protected void onDraw(Canvas c) { super.onDraw(c); c.drawBitmap(bitmap, 0, 0, null);// 原始图片 c.saveLayer(width, 0, width * 2, height, null, Canvas.ALL_SAVE_FLAG); c.drawBitmap(bitmap, width, 0, null); c.drawRect(width, 0, width * 2, height, paint); c.restore(); // 倒影 c.drawBitmap(rotateBitmap, 0, height, null); c.drawRect(0, height, width, 2 * height, paint1); } }
这里能够看到主要是将图片反转,注意这里不能采用旋转,旋转不会产生镜像效果。其余的都与以前的代码没有区别。那最终咱们来看看实现的效果:
若是有人有更简单的方式,能够探讨探讨。
网易云免费体验馆,0成本体验20+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区
相关文章:
【推荐】 聊聊WS-Federation
【推荐】 深刻浅出“跨视图粒度计算”--二、INCLUDE表达式
【推荐】 3分钟掌握一个有数小技能:制做动态标题