本文来自网易云社区

作者:孙有军

老需求

我们经常会有需求就是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);
    }
}

这里可以看到主要是将图片反转,注意这里不能采用旋转,旋转不会产生镜像效果。其他的都与之前的代码没有区别。那最终我们来看看实现的效果:

Done

如果有人有更简单的方式,可以探讨探讨。

网易云免费体验馆,0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 一招搞定短信验证码服务不稳定
【推荐】 一行代码搞定Dubbo接口调用

Android View部分消失效果实现的更多相关文章

  1. android View实现变暗效果

    android项目中做一个默认图片变暗,有焦点时变亮的效果.相信大家都能各种办法,各种手段很容易的实现这个效果.这里记录下作者实现这个效果的过程及遇到的问题,仅供参考.见下图(注:因为是eclipse ...

  2. android设置view透明度的效果

    android设置view透明度的效果 推荐textView.setBackgroundColor(Color.TRANSPARENT);     第一种方法:在xml文件中设置背景颜色. andro ...

  3. Android 自定义PopupWindow动画效果

    public class RollActivity extends Activity { private View view; private Button btn; private PopupWin ...

  4. QuickSand图片点击后分裂成几份消失效果《IT蓝豹》

    QuickSand图片点击后分裂成几份消失效果 QuickSand图片点击后分裂成几份消失效果,适合做图片退出和剪切效果.同时也可以学习android动画. demo中都封装好几个功能类,主要动画实现 ...

  5. 浅谈Android View滑动和弹性滑动

    引言 View的滑动这一块在实际开发中是非常重要的,无论是优秀的用户体验还是自定义控件都是需要对这一块了解的,我们今天来谈一下View的滑动. View的滑动 View滑动功能主要可以使用3种方式来实 ...

  6. 虾扯蛋:Android View动画 Animation不完全解析

    本文结合一些周知的概念和源码片段,对View动画的工作原理进行挖掘和分析.以下不是对源码一丝不苟的分析过程,只是以搞清楚Animation的执行过程.如何被周期性调用为目标粗略分析下相关方法的执行细节 ...

  7. Android View 的事件体系

    android 系统虽然提供了很多基本的控件,如Button.TextView等,但是很多时候系统提供的view不能满足我们的需求,此时就需要我们根据自己的需求进行自定义控件.这些控件都是继承自Vie ...

  8. Android Scroll分析——滑动效果产生

    相对于在Android2.x版本上出现的长按.点击事件的效果,不得不说,滑动操作具有更好的用户体验.因此,从Android 4.X版本开始,出现了更多滑动操作的效果.越来越多第三方应用模仿这样的效果, ...

  9. Android实现滑动刻度尺效果,选择身高体重和生日

    刻度尺效果虽然看起来很美,我个人认为很不实用,即使再不实用,也有用的,鉴于群里成员对我的苦苦哀求,我就分享一个他用不到的,横屏滑动刻度尺,因为他需要竖屏的,哈哈…… 最近群里的开发人员咨询怎样实现刻度 ...

随机推荐

  1. Operating System-进程间互斥的问题-生产者&&消费者引入

    之前介绍的几种解决进程间互斥的方案,不管是Peterson方案还是TSL指令的方式,都有一个特点:当一个进程被Block到临界区外面时,被Block的进程会一直处于忙等待的状态,这个不但浪费了CPU资 ...

  2. bzoj 2242 [SDOI2011]计算器——BSGS模板

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2242 第一道BSGS! 咳咳,我到底改了些什么?…… 感觉和自己的第一版写的差不多……可能是 ...

  3. 1.Linux下Git入门学习

    1.在Linux下安装git软件,使用以下命令: yum install git 2.设置用户名和邮箱(必须): git config --global user.name "Your Na ...

  4. Day2-Python基础2---字典操作

    一.字典操作 字典一种key - value 的数据类型,使用就像我们上学用的字典,通过笔划.字母来查对应页的详细内容. 语法: 1.基本语法 >>> info = { 'stu11 ...

  5. who命令参数及用法详解(linux查看在线用户命令)

    功能说明:显示目前登入系统的用户信息.  语 法:who [-Himqsw][--help][--version][am i][记录文件]  补充说明:执行这项指令可得知目前有那些用户登入系统,单独执 ...

  6. 四川第七届 C Censor (字符串哈希)

    Censor frog is now a editor to censor so-called sensitive words (敏感词). She has a long text pp. Her j ...

  7. Oracle user,role,profile常规操作--用户,权限,角色,配置文件

    Oracle user,role,profile常规操作--用户,权限,角色,配置文件 1 权限查询 1查看所有用户 SQL> select username,account_status,lo ...

  8. 如何使用安装光盘为本机创建yum repository

    在CentOS 6上可以使用系统安装光盘为本机创建yum repository,创建过程如下. 创建光盘mount点 [root@centos62 ~]# mkdir /media/CentOS mo ...

  9. 阿里云服务器ubuntu安装redis2.8.13

    阿里云服务器ubuntu安装redis2.8.13 2014-09-04 16:14 |  coding云 |  2198次阅读 | 暂无评论   一.下载redis 可以先下载到本地,然后ftp到服 ...

  10. myeclipse实用快捷键

    笔者这里总结的是个人在使用myeclipse时常用的快捷操作,总结如下: 1.Ctrl +  /             :为选中的一段代码加上或去掉注释符 Ctrl + Shift + /   :( ...