• 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);
        }
    }

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


    Done

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


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

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



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

  • 相关阅读:
    Java实现 LeetCode 735 行星碰撞(栈)
    Java实现 LeetCode 735 行星碰撞(栈)
    Java实现 LeetCode 887 鸡蛋掉落(动态规划,谷歌面试题,蓝桥杯真题)
    Java实现 LeetCode 887 鸡蛋掉落(动态规划,谷歌面试题,蓝桥杯真题)
    Java实现 LeetCode 887 鸡蛋掉落(动态规划,谷歌面试题,蓝桥杯真题)
    Java实现 蓝桥杯算法提高 求最大值
    Java实现 蓝桥杯算法提高 求最大值
    Java实现 蓝桥杯算法提高 求最大值
    Python eval() 函数
    Python repr() 函数
  • 原文地址:https://www.cnblogs.com/163yun/p/9711840.html
Copyright © 2020-2023  润新知