博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android自定义组件系列【8】——遮罩文字动画
阅读量:5734 次
发布时间:2019-06-18

本文共 9009 字,大约阅读时间需要 30 分钟。

遮罩文字的动画我们在Flash中非常常见,作为Android的应用开发者你是否也想将这种动画做到你的应用中去呢?这一篇文章我们来看看如何自定义一个ImageView来实现让一张文字图片实现文字的遮罩闪烁效果,下面先来看看效果吧。

(录屏幕延时导致效果看起来不是很好)

一、实现原理

  实现原理是重写View的onCreate方法,获取图片资源后对每个像素的透明度进行修改来实现,再启动一个线程来循环改变某个区域中的像素透明度。

RGBA基础知识:(下面几段介绍文字引用自维基百科)

  RGBA是代表Red()Green()Blue()和Alpha的。虽然它有的时候被描述为一个颜色空间,但是它其实仅仅是模型的附加了额外的信息。采用的颜色是RGB,可以属于任何一种RGB,但是和在1971至1972年间提出了这个不可或缺的alpha数值,使得和变得可能。提出者以alpha来命名是源于经典的方程αA + (1-α)B所用的就是这个。

  alpha通道一般用作不透明度参数。如果一个像素的alpha通道数值为0%,那它就是完全透明的(也就是看不见的),而数值为100%则意味着一个完全不透明的像素(传统的数字图像)。在0%和100%之间的值则使得像素可以透过背景显示出来,就像透过玻璃(半透明性),这种效果是简单的二元透明性(透明或不透明)做不到的。它使变得容易。alpha通道值可以用百分比、整数或者像RGB参数那样用0到1的实数表示。

  有时它也被写成ARGB(像RGBA一样,但是第一个数据是alpha),是的产品使用的术语。比如,0x80FFFF00是50%透明的黄色,因为所有的参数都在0到255的范围内表示。0x80是128,大约是255的一半。

是一种使用RGBA的图像格式。

二、具体实现

package com.example.helloworld;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graphics.Paint;import android.os.Handler;import android.os.Message;import android.os.SystemClock;import android.util.AttributeSet;import android.view.View;import android.widget.ImageView;/** * @author 阳光小强 * */public class SplashImageView extends ImageView{     private Bitmap alterBitmap;      private Canvas canvas;      private Paint paint;  	private Handler handler;	private static int START_POSITION = 20;	private final int speed;	private int nowPosition = START_POSITION;	private static int SHOW_WIDTH = 20;	private boolean isFirst = true;	private boolean isStop = false;    	private class MyHandler extends Handler {		private static final long SCALE = 10000;		private static final int MSG_PAINT = 1;		private final SplashImageView owner;		private final int speed;		private long angle;		private long lastTime;		public MyHandler(SplashImageView owner) {			this.owner = owner;			this.lastTime = SystemClock.elapsedRealtime();			this.speed = owner.speed;			sendEmptyMessage(MSG_PAINT);		}		@Override		public void handleMessage(Message msg) {			if (msg.what == MSG_PAINT) {				long now = SystemClock.elapsedRealtime();				long delta_time = now - lastTime;				System.out.println("delta_time = " + delta_time);				System.out.println("alterBitmap.Width = " + alterBitmap.getWidth());				if(nowPosition + speed >= alterBitmap.getWidth() - START_POSITION - SHOW_WIDTH){					if(isStop){						handler.removeCallbacksAndMessages(null);						handler = null;						isStop = false;						return;					}else{						nowPosition = START_POSITION;					}				}				nowPosition = nowPosition + speed;				if (delta_time > 0) {					if(!notifiDraw(nowPosition)){						return;					}				}				this.sendEmptyMessageDelayed(MSG_PAINT, 10);			}		}	}		private boolean notifiDraw(long position) {		System.out.println("nofityDrawToatal = " + position);		if(position < alterBitmap.getWidth() - START_POSITION - SHOW_WIDTH){			this.invalidate();			return true;		}		if (handler != null) {			handler.removeCallbacksAndMessages(null);			handler = null;		}		return false;	}		@Override	public void setVisibility(int visibility) {		super.setVisibility(visibility);		if(visibility == View.VISIBLE){			if(handler == null){	        	handler =  new MyHandler(this);	        }else{	        	handler.removeCallbacksAndMessages(null);	        	handler.sendEmptyMessage(MyHandler.MSG_PAINT);	        }		}else{			if(handler != null){				handler.removeCallbacksAndMessages(null);				handler = null;			}		}	}		public void stopSplashAnimation(){		if(handler != null){			isStop = true;		}	}		public SplashImageView(Context context, AttributeSet attrs) {		super(context, attrs);		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FuseImageView, 0, 0);		int resId = a.getResourceId(R.styleable.FuseImageView_imageSrc, 0);		int speed = a.getInt(R.styleable.FuseImageView_speed, 5);		this.speed = speed <= 0 ? 1 : speed;		Bitmap up = BitmapFactory.decodeResource(context.getResources(), resId);        alterBitmap = Bitmap.createBitmap(up.getWidth(), up.getHeight(), up.getConfig());                     canvas = new Canvas(alterBitmap);           paint = new Paint();          paint.setStrokeWidth(5);          paint.setColor(Color.BLACK);          canvas.drawBitmap(up, new Matrix(), paint);                    setImageBitmap(alterBitmap);                 if(getVisibility() == View.VISIBLE){        	if(handler == null){	        	handler =  new MyHandler(this);	        }        }	}		@Override	protected void onDraw(Canvas canvas) {				super.onDraw(canvas);				if(isFirst){			isFirst = false;			 for(int i=nowPosition; i
200){ color = Color.argb(80, r, g, b); }else{ color = Color.argb(a, r, g, b); } alterBitmap.setPixel(i, j, color); } } } for(int i=nowPosition; i
START_POSITION){ for(int i= nowPosition - SHOW_WIDTH; i
200){ color = Color.argb(80, r, g, b); }else{ color = Color.argb(a, r, g, b); } alterBitmap.setPixel(i, j, color); } } } setImageBitmap(alterBitmap); }}
三、实现详解

1、构造方法中进行初始化操作

public SplashImageView(Context context, AttributeSet attrs) {		super(context, attrs);		TypedArray a = context.obtainStyledAttributes(attrs,				R.styleable.FuseImageView, 0, 0);		int resId = a.getResourceId(R.styleable.FuseImageView_imageSrc, 0);		int speed = a.getInt(R.styleable.FuseImageView_speed, 5);		this.speed = speed <= 0 ? 1 : speed;		Bitmap up = BitmapFactory.decodeResource(context.getResources(), resId);		alterBitmap = Bitmap.createBitmap(up.getWidth(), up.getHeight(),				up.getConfig());		canvas = new Canvas(alterBitmap);		paint = new Paint();		paint.setStrokeWidth(5);		paint.setColor(Color.BLACK);		canvas.drawBitmap(up, new Matrix(), paint);		setImageBitmap(alterBitmap);		if (getVisibility() == View.VISIBLE) {			if (handler == null) {				handler = new MyHandler(this);			}		}	}
上面的TypedArray是自定义的属性,在res/values目录下新建一个attrs.xml添加自定义属性

这里是自定义的两个属性,一个是图片资源ID另一个是遮罩移动速度(其实上面也可以继承自View来实现自定义,我这里是有特殊需要才继承自ImageView的)

然后通过BitmapFactory获取图片资源,并通过createBitmap方法创建一个可写的Bitmap资源给画布(Canvas),将可写的Bitmap绘制到同样资源的背景上。

底下的判读View是否可看见,是用来判读View是否可见,如果可见才开启线程进行动画的,不然的话开启线程绘制会浪费资源的(因为它根本就看不见)。

2、如何改变透明度并且绘制(onDraw方法)

for (int i = nowPosition; i < nowPosition + SHOW_WIDTH; i++) {			for (int j = 0; j < alterBitmap.getHeight(); j++) {				int color = alterBitmap.getPixel(i, j);				int r = Color.red(color);				int g = Color.green(color);				int b = Color.blue(color);				int a = Color.alpha(color);				if (a == 80) {					color = Color.argb(255, r, g, b);				} else {					color = Color.argb(a, r, g, b);				}				alterBitmap.setPixel(i, j, color);			}		}		if (nowPosition > START_POSITION) {			for (int i = nowPosition - SHOW_WIDTH; i < nowPosition; i++) {				for (int j = 0; j < alterBitmap.getHeight(); j++) {					int color = alterBitmap.getPixel(i, j);					int r = Color.red(color);					int g = Color.green(color);					int b = Color.blue(color);					int a = Color.alpha(color);					if (a > 200) {						color = Color.argb(80, r, g, b);					} else {						color = Color.argb(a, r, g, b);					}					alterBitmap.setPixel(i, j, color);				}			}		}
主要是上面两个循环来实现绘制的,上面的循环是绘制一块区域来将文字的透明度调为最小(255),这一部分的文字就显示为高亮了,其余部分的文字透明度调值调为80,就会显示背景颜色,文字的暗度就会下降。

3、如何循环移动遮罩

private class MyHandler extends Handler {		private static final long SCALE = 10000;		private static final int MSG_PAINT = 1;		private final SplashImageView owner;		private final int speed;		private long angle;		private long lastTime;		public MyHandler(SplashImageView owner) {			this.owner = owner;			this.lastTime = SystemClock.elapsedRealtime();			this.speed = owner.speed;			sendEmptyMessage(MSG_PAINT);		}		@Override		public void handleMessage(Message msg) {			if (msg.what == MSG_PAINT) {				long now = SystemClock.elapsedRealtime();				long delta_time = now - lastTime;				System.out.println("delta_time = " + delta_time);				System.out.println("alterBitmap.Width = "						+ alterBitmap.getWidth());				if (nowPosition + speed >= alterBitmap.getWidth()						- START_POSITION - SHOW_WIDTH) {					if (isStop) {						handler.removeCallbacksAndMessages(null);						handler = null;						isStop = false;						return;					} else {						nowPosition = START_POSITION;					}				}				nowPosition = nowPosition + speed;				if (delta_time > 0) {					if (!notifiDraw(nowPosition)) {						return;					}				}				this.sendEmptyMessageDelayed(MSG_PAINT, 10);			}		}	}
循环移动遮罩是写在一个线程中的,每隔10毫秒就去移动speed(配置的速度)的距离,来实现遮罩的移动效果,再取图片的宽度来判断是否已经到了最右边。

总结:其实上面的实现原理并不难,要点是要知道RGBA的知识和如何去改变像素的透明度。这个只是个人暂时想到的一个方法,如果有什么更好的方式实现,希望能交流一下。

另外“阳光小强”的另一篇博文《》参加了CSDN举办的博文大赛,如果您觉得这些博文对您有帮助,希望您投出您宝贵的一票,投票地址:http://vote.blog.csdn.net/Article/Details?articleid=30101091

转载于:https://www.cnblogs.com/lanzhi/p/6468899.html

你可能感兴趣的文章
Redrain duilib中事件委托存在的问题
查看>>
43、我的C#学习笔记9
查看>>
网站建表实践及优化
查看>>
字符串的简单操作
查看>>
C#新功能--命名参数与可选参数
查看>>
strtok和strtok_r
查看>>
维辰超市:借助云商城成功转型新零售
查看>>
web.xml中<load-on-start>n</load-on-satrt>作用
查看>>
python之路---进程
查看>>
1061. Dating (20)
查看>>
leetcode 【 Best Time to Buy and Sell Stock II 】python 实现
查看>>
【算法】CRF
查看>>
windows 8 微软拼音输入法
查看>>
Windows UI风格的设计(7)
查看>>
SQL中使用WITH AS提高性能 使用公用表表达式(CTE)简化嵌套SQL
查看>>
oracle 强行杀掉一个用户连接
查看>>
Git提交本地库代码到远程服务器的操作
查看>>
灾难拯救——让软件项目重回轨道
查看>>
ssh链接git服务器,解决push pull要求输入密码问题
查看>>
Netty 源码解析(二):对 Netty 中一些重要接口和类的介绍
查看>>