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

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

一、实现原理

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

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

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

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

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

PNG是一种使用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<alterBitmap.getWidth() ; 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);
} }
} 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);
} }
}
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添加自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FuseImageView">
<attr name="imageSrc" format="reference" />
<attr name="speed" format="integer" />
</declare-styleable>
</resources>

这里是自定义的两个属性,一个是图片资源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的知识和如何去改变像素的透明度。这个只是个人暂时想到的一个方法,如果有什么更好的方式实现,希望能交流一下。

另外“阳光小强”的另一篇博文《是男人就下100层【第三层】——高仿交通银行手机客户端界面》参加了CSDN举办的博文大赛,如果您觉得这些博文对您有帮助,希望您投出您宝贵的一票,投票地址:http://vote.blog.csdn.net/Article/Details?articleid=30101091

Android自定义组件系列【8】——遮罩文字动画的更多相关文章

  1. Android自定义组件系列【7】——进阶实践(4)

    上一篇<Android自定义组件系列[6]--进阶实践(3)>中补充了关于Android中事件分发的过程知识,这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpan ...

  2. Android自定义组件系列【6】——进阶实践(3)

    上一篇<Android自定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计划 ...

  3. Android自定义组件系列【5】——进阶实践(2)

    上一篇<Android自定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这一 ...

  4. Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动

    在上一篇文章<Android自定义组件系列[3]--自定义ViewGroup实现侧滑>中实现了仿Facebook和人人网的侧滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布局示 ...

  5. Android自定义组件系列【3】——自定义ViewGroup实现侧滑

    有关自定义ViewGroup的文章已经很多了,我为什么写这篇文章,对于初学者或者对自定义组件比较生疏的朋友虽然可以拿来主义的用了,但是要一步一步的实现和了解其中的过程和原理才能真真脱离别人的代码,举一 ...

  6. Android自定义组件系列【12】——非UI线程绘图SurfaceView

    一.SurfaceView的介绍 在前面我们已经会自定义View,使用canvas绘图,但是View的绘图机制存在一些缺陷. 1.View缺乏双缓冲机制. 2.程序必须重绘整个View上显示的图片,比 ...

  7. Android自定义组件系列【14】——Android5.0按钮波纹效果实现

    今天任老师发表了一篇关于Android5.0中按钮按下的波纹效果实现<Android L中水波纹点击效果的实现>,出于好奇我下载了源代码看了一下效果,正好手边有一个Nexus手机,我结合实 ...

  8. Android自定义组件系列【9】——Canvas绘制折线图

    有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了很多插件,但是很多时候我们需要根据具体项目自定义这些图表,这一篇文章我们一起来看看如何在Android中使用Canvas ...

  9. Android自定义组件系列【2】——Scroller类

    在上一篇中介绍了View类的scrollTo和scrollBy两个方法,对这两个方法不太了解的朋友可以先看<自定义View及ViewGroup> scrollTo和scrollBy虽然实现 ...

随机推荐

  1. 5.Node.js 安装配置

    转自:http://www.runoob.com/nodejs/nodejs-tutorial.html Node.js安装包及源码下载地址为:https://nodejs.org/en/downlo ...

  2. Filebeat-1.3.1安装和设置(图文详解)(多节点的ELK集群安装在一个节点就好)(以Console Output为例)

    前期博客 Filebeat的下载(图文讲解) 前提 Elasticsearch-2.4.3的下载(图文详解) Elasticsearch-2.4.3的单节点安装(多种方式图文详解) Elasticse ...

  3. 不用浏览器,直接用代码发送文件给webservices所在服务器 并且可以周期行的发送

    package com.toic.test; import java.io.DataInputStream; import java.io.DataOutputStream; import java. ...

  4. Object.prototype.toString.call(value)

    使用Object.prototype上的原生toString()方法判断数据类型,使用方法如下: Object.prototype.toString.call(value) 1.判断基本类型: Obj ...

  5. BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊(LCT)

    Description 某天,Lostmonkey发明了一种超级弹力装置,为了在 他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装 ...

  6. DTU(用于将串口数据转换为IP数据或将IP数据转换为串口数据通过无线通信网络进行传送的无线终端设备)

    DTU (Data Transfer unit),是专门用于将串口数据转换为IP数据或将IP数据转换为串口数据通过无线通信网络进行传送的无线终端设备.DTU广泛应用于气象.水文水利.地质等行业.

  7. windows.location属性(常用知识点)

    location对象 含有当前URL的信息. 属性 href 整个URL字符串.protocol 含有URL第一部分的字符串,如http:host 包含有URL中主机名:端口号部分的字符串.如//ww ...

  8. Spring HandlerInterceptor的使用

    http://blog.csdn.net/joeyon1985/article/details/49903761

  9. [Mobx] Use MobX actions to change and guard state

    This lesson explains how actions can be used to control and modify the state of your application. Th ...

  10. Altium Designer中画pcb如何隐藏和显示地线

    如何隐藏: 如何显示: 按下N后