Android View部分消失效果实现
Posted freeCodeSunny
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android View部分消失效果实现相关的知识,希望对你有一定的参考价值。
老需求
我们经常会有需求就是View消失的效果,这里我们说的消失往往是全部消失,我们可能采用一个alpha动画,在指定的时间内消失掉View,出现则实现相反的动画。我们一般都采用如下的实现:
采用tween动画实现:
private void alphaTween()
AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f);
alpha.setDuration(300);
imageView.startAnimation(alpha);
或者采用属性动画实现:
private void alphaOB()
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 1.0f, 0.0f).setDuration(300);
animator.start();
也可能采用xml来实现。
新需求
但是这里我们需要的不是上面的效果,你是在逗我??不是上面的效果,你说这么多。我们需求如下,这里我们用两个图来展示:
效果图:
左边是原始图片,右边是处理后的图片。可以看到从下到上越来越淡,顶部就已经像消失了一样。说道这里很多人肯定会联想到图片的滤镜效果。但是这里实现的方式简单的多。
实现
这里我们写一个demo来实现这个效果。既然是在对View进行处理,那这里我们就先对一张图片进行处理。之后在扩展。
既然我们需要将图片变淡消失,肯定是需要合成了什么效果。那android里面一般合成我们都采用什么方式呐?
Xfermode
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中进行操作,否则只是叠加效果
实现2
上面我们采用在新的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);
这里可以看到主要是将图片反转,注意这里不能采用旋转,旋转不会产生镜像效果。其他的都与之前的代码没有区别。那最终我们来看看实现的效果:
左上为原图,右上为消失效果,右下为镜像效果
Code
代码Git地址如下:
Done
如果有人有更简单的方式,可以探讨探讨。
以上是关于Android View部分消失效果实现的主要内容,如果未能解决你的问题,请参考以下文章