遮罩文字的动画我们在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. 【习题 8-12 UVA - 1153】Keep the Customer Satisfied

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 结束时间比较早的,就早点开始做. 所以,将n件事情,按照结束时间升序排. 然后对于第i件事情. 尽量把它往左排. 即t+1..t+a ...

  2. Spring3 整合MyBatis3 配置多数据源 动态选择SqlSessionFactory(转)

    1. Spring整合MyBatis切换SqlSessionFactory有两种方法,第一. 继承SqlSessionDaoSupport,重写获取SqlSessionFactory的方法.第二.继承 ...

  3. [React Native] Animate the Scale of a React Native Button using Animated.spring

    In this lesson we will use Animated.spring and TouchableWithoutFeedback to animate the scale of a bu ...

  4. Android自己定义圆角ImageView 支持网络图片

    先看下效果图 我们再来看一张CSDN的圆角图片 从布局能够看出csdn app 的头像也是圆角的Image,但能够看到.有明显的毛刺感.不知道是csdn 程序猿的疏忽还是 我手机的问题,本人手机(小米 ...

  5. AngularJsDEMO

    接触AngularJs时间不长,纯粹是学着好玩而已,因此没有深挖原理,针对理论性的知识,园子里面有很多介绍,我就不多介绍了. 这里写了个简单的DEMO,部署起来就可以直接运行了,里面 大概用了最基础的 ...

  6. Codeforces Round #193 (Div. 2) 部分题解

    A:直接判断前三项是否相等 int main() { //FIN; //CHEAT; int n; cin>>n; getchar(); ]; gets(a); int len = str ...

  7. 解读OpenRTB(实时竞价)生态系统

    最近3年,广告实时竞价(RealTimeBidding)模式逐渐流行起来. 2012年大致了解过,最近一段时间,重新温习下. 半壁江山 生态系统总的来说分为2个部分,卖方和买方. 卖方:媒体,即拥有广 ...

  8. 阶段复习-.NET下托管资源与非托管资源的小记

    托管资源由由程序员负责分配,在系统的二级缓存中,GC自动回收释放:而非托管资源也是由程序员负责分配,资源的释放回收也是由程序员负责,使用Dispose或者析构函数对资源进行回收,常见的非托管资源是包装 ...

  9. [CortexM0--stm32f0308]discovery开发板

        问题描写叙述:stm32提供了很多IC入门级开发板,价格还是蛮廉价的. stm32f0308-discovery就是一款cortex-m0架构的入门级开发板. 例如以下对其进行下简介. IO便 ...

  10. RHEL6安装调试过程中遇到的问题集

    /*** **问题集 2014/6/9 20:55:52** **Autor: Weigong Xu ** ***/ 1. Linux中开启22port: 22port是ssh服务的.你仅仅要启动ss ...