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));} @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);setMeasuredDimension(2 * width, height);} @Overrideprotected 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);} @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);setMeasuredDimension(2 * width, height);} @Overrideprotected 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));} @Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh);height = h;width = w;} @Overridepublic 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));} @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);setMeasuredDimension(2 * width, 2 * height);} @Overrideprotected 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+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区
相关文章:
【推荐】 市场调研中数据分析之道
Android View部分消失效果实现相关推荐
- Android仿QQ消息拖拽黏连消失效果,气泡爆炸效果
公司需要这个效果,看了很多博客,根据自己项目的需要写出来的一个完整的过程. 拖拽控件代码 根据手势拖动的位置利用贝塞尔曲线算法画出控件 package cn.stike.bubble.stickbub ...
- android view禁用,Android 禁止ViewPager的自带滑动效果
由于最近在做墨水屏的相关工作,ViewPager自带的滑动效果在墨水屏上表现的很不好,残影太重了.所以禁止自带的滑动效果,用接口来接管相关逻辑实现自处理. import android.content ...
- android中仿qq最新版抽屉,Android 自定义View实现抽屉效果
Android 自定义View实现抽屉效果 说明 这个自定义View,没有处理好多点触摸问题 View跟着手指移动,没有采用传统的scrollBy方法,而是通过不停地重新布局子View的方式,来使得子 ...
- Android 自定义 圆环,Android自定义view实现圆环效果实例代码
先上效果图,如果大家感觉不错,请参考实现代码. 重要的是如何实现自定义的view效果 (1)创建类,继承view,重写onDraw和onMesure方法 public class CirclePerc ...
- 代码android点击效果,GitHub - likaiyuan559/TouchEffects: Android View点击特效TouchEffects,几行代码为所有控件添加点击效果...
Android 点击特效TouchEffects TouchEffects能够帮助你更快速方便的增加点击时候的效果,TouchEffects的目标是打造一个稳定.全面.且能更方便的自定义及个性化的一款 ...
- android波纹效果弹窗,Android自定义View实现波纹效果
Android自定义View实现波纹效果 时间:2017-05-27 来源:移动互联网学院 1.引言:随着Android智能手机的普及,Android应用得到了大力支持,而Android应用的 ...
- Android 雪花飘落动画效果 自定义View
在码农的世界里,优美的应用体验,来源于程序员对细节的处理以及自我要求的境界,年轻人也是忙忙碌碌的码农中一员,每天.每周,都会留下一些脚印,就是这些创作的内容,有一种执着,就是不知为什么,如果你迷茫,不 ...
- android 动态创建view,react-native动态创建Android View 无效果
问题描述 react-native动态创建Android View 无效果,我想在react-native里面直接点击函数进行创建,也就是通过module中的方法创建View 问题出现的环境背景及自己 ...
- android 循环弹幕,Android自定义View实现弹幕效果
原标题:Android自定义View实现弹幕效果 在很多视频直播中都有弹幕功能,而安卓上没有简单好用的弹幕控件,本文介绍一个自定义弹幕view的demo. 效果图: 思路: 自定义Textitem类表 ...
最新文章
- swift string,Int,Double相互转换
- linux gcc安装
- spring序列化_使用@JsonIdentityInfo的Spring自定义序列化器
- 【项目.源码】深度学习实现任意风格任意内容的极速风格迁移
- ElasticSearch学习(四):可视化管理之Kibana
- 共享单车为什么这么重?
- 数学问题(三):最小公倍数、最大公约数
- esp连接服务器的协议,【零知ESP8266教程】WIFI TCP协议通信 TCP服务器示例
- 资源白朴收藏:图片、 视频、音频、字体、PPT模板类、动态图、壁纸、图标
- 2021认证杯 第二阶段 思路加代码
- 斯特林公式 (Stirling公式)
- [转载]创建、部署和调试 Apache Geronimo 应用程序
- php获取qq头像地址,获取 QQ 头像地址,并且不暴露 QQ 号
- FZU1892接水管游戏-BFS加上简单的状态压缩和位运算处理
- linux 文件追加,如何在Linux中将文本内容追加到文件末尾?
- 2022百度之星程序设计大赛 - 复赛 1001 子序列
- js 判断字符串是中文、数字、大小写字母
- python包离线安装教程_离线安装python包(附示例:featuretools)
- 《Qt5+安装包制作(Qt Installer Framework)》
- #2. 小明的成绩单