您的位置:首页 > 移动开发 > Android开发

Android开发 之 LinearGradient与闪动文字效果

2017-09-08 15:16 369 查看

LinearGradient与闪动文字效果

转自:http://blog.csdn.net/harvic880925/article/details/52350154

这篇就给大家讲setShader的另一个参数,LinearGradient,使用过shape标签的同学,对这个方法估计都不莫生,就是线性渐变。跟PhotoShop中的线性渐变的原理和作用是一样的。这篇文章的最终会实现一个闪动文字效果控件: 




一、引言

有关渐变,以前有讲过一篇《详解shape标签》,其中讲述了
<gradient/>
标签的用法:
<gradient
android:type=["linear" | "radial" | "sweep"]    //共有3中渐变类型,线性渐变(默认)/放射渐变/扫描式渐变
android:angle="integer"     //渐变角度,必须为45的倍数,0为从左到右,90为从上到下
android:centerX="float"     //渐变中心X的相当位置,范围为0~1
android:centerY="float"     //渐变中心Y的相当位置,范围为0~1
android:startColor="color"   //渐变开始点的颜色
android:centerColor="color"  //渐变中间点的颜色,在开始与结束点之间
android:endColor="color"    //渐变结束点的颜色
android:gradientRadius="float"  //渐变的半径,只有当渐变类型为radial时才能使用
android:useLevel=["true" | "false"] />  //使用LevelListDrawable时就要设置为true。设为false时才有渐变效果
1
2
3
4
5
6
7
8
9
10

其中的渐变类型有”linear” | “radial” | “sweep”,在代码中对应的类分别是LinearGradient、RaialGradient、SweepGradient;有关
<gradient/>
各个渐变效果的用法,不知道的同学强烈建议你先看看这篇文章。 

这篇我们要讲就是线性渐变的LinearGradient;


二、LinearGradient基本使用


1、构造函数

我们先来看下LinearGradient的构造函数: 
第一个构造函数:
public LinearGradient(float x0, float y0, float x1, float y1,int color0, int color1, TileMode tile)
1

用过PhotoShop的线性激变工具的同学,应该都知道,线性渐变其实是在指定的两个点之间填充渐变颜色。 

- 参数中的(x0,y0)就是起始渐变点坐标,参数中(x1,y1)就是结束渐变点坐标; 

- color0就是起始颜色,color1就是终止颜色;颜色值必须使用0xAARRGGBB形式的16进制表示!表示透明度的AA一定不能少。 

- TileMode tile:与BitmapShader一样,用于指定控件区域大于指定的渐变区域时,空白区域的颜色填充方法。

很显然!这个方法,只能指定两种颜色之间的渐变。如果需要多种颜色之间的渐变,就需要使用下面的这个构造函数了。

第二个构造函数:
public LinearGradient(float x0, float y0, float x1, float y1,int colors[], float positions[], TileMode tile)
1

同样,(x0,y0)就是起始渐变点坐标,参数中(x1,y1)就是结束渐变点坐标 

colors[]用于指定渐变的颜色值数组,同样,颜色值必须使用0xAARRGGBB形式的16进制表示!表示透明度的AA一定不能少。 

positions[]与渐变的颜色相对应,取值是0-1的float类型,表示在每一个颜色在整条渐变线中的百分比位置


2、两色渐变使用示例

我们先来看看两色渐变的构造函数是如何来使用的:
public class LinearGradientView extends View {
private Paint mPaint;
public LinearGradientView(Context context) {
super(context);
init();
}

public LinearGradientView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public LinearGradientView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

private void init(){
setLayerType(LAYER_TYPE_SOFTWARE,null);
mPaint = new Paint();
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setShader(new LinearGradient(0,getHeight()/2,getWidth(),getHeight()/2,0xffff0000,0xff00ff00, Shader.TileMode.CLAMP));
canvas.drawRect(0,0,getWidth(),getHeight(),mPaint);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

很简单,只需要在绘图的时候构造LinearGradient实例,通过Paint.setShader设置进去即可。 

大家注意一下,我这里设置的渐变范围是从控件的左边中点到右边中点:
mPaint.setShader(new LinearGradient(0,getHeight()/2,getWidth(),getHeight()/2,0xffff0000,0xff00ff00, Shader.TileMode.CLAMP));
1

最后通过canvas.drawRect把整个控件区域画出来:
canvas.drawRect(0,0,getWidth(),getHeight(),mPaint);
1

这里大家注意一下,上面我们也已经提到了,颜色值必须使用0xAARRGGBB的完整16进制的颜色样式表示,我们这里的颜色值就是0xffff0000和0xff00ff00;大家自己可以进行尝试,如果把红色的透明度值去掉,改写成0xff00000,是不会有任何显示的。 

效果图如下: 



3、多色渐变使用示例

下面我们来看第二个构造函数,多色渐变的使用方法:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int[] colors = {0xffff0000,0xff00ff00,0xff0000ff,0xffffff00,0xff00ffff};
float[]  pos = {0f,0.2f,0.4f,0.6f,1.0f};
LinearGradient multiGradient = new LinearGradient(0,getHeight()/2,getWidth(),getHeight()/2,colors,pos, Shader.TileMode.CLAMP);
mPaint.setShader(multiGradient);
canvas.drawRect(0,0,getWidth(),getHeight(),mPaint);
}
1
2
3
4
5
6
7
8

从这里可以看出,渐变的开始点同样是控件左边中点,渐变的结束点也同样是控件右边中点;这里我们指定了五种渐变颜色,而且指定了每个颜色的位置,前四种颜色是按20%均匀分布的,最后两种颜色相距40%;最后通过canvas.drawRect把整个控件区域画出来

效果图如下: 



注意: 

colors和pos的个数一定要相等,也就是说必须指定每一个颜色值的位置!如果多或少都会直接报错:(Signal 11是SO内部错误) 




2、渐变起始坐标与填充的关系——矩形填充

我们上面的示例中都是从控件左边中间到控件右边中点;如果我们改成从左上角到右上角的填充方式,结果会怎样呢?
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int[] colors = {0xffff0000,0xff00ff00,0xff0000ff,0xffffff00,0xff00ffff};
float[]  pos = {0f,0.2f,0.4f,0.6f,1.0f};
LinearGradient multiGradient = new LinearGradient(0,0,getWidth(),getHeight(),colors,pos, Shader.TileMode.CLAMP);
mPaint.setShader(multiGradient);
canvas.drawRect(0,0,getWidth(),getHeight(),mPaint);
}
1
2
3
4
5
6
7
8

渐变线是从控件的左上角到控件的右下角位置:
LinearGradient multiGradient = new LinearGradient(0,0,getWidth(),getHeight(),colors,pos, Shader.TileMode.CLAMP);
1

效果图如下: 


 

形成原理如下: 


 

就是说,首先是两个渐变点之间连线,然后以连线为对角线形成一个矩形,各种颜色都是以这条对角线为矩形的填充的。


3、TileMode重复方式

在讲到BitmapShader的时候,我们已经详细讲过TileMode的意义:当控件区域大小渐变区域时,用于填充空白位置的。 

下面我们就逐个看一下TileMode不同时,对于线性渐变的有什么作用。 
(1)、X、Y轴共用填充参数 

首先,我们再回来看一下LinearGradient的构造函数:
public LinearGradient(float x0, float y0, float x1, float y1,int colors[], float positions[], TileMode tile)
public LinearGradient(float x0, float y0, float x1, float y1,int color0, int color1, TileMode tile)
1
2

从构造函数中可以看出,LiearGradient只有一个TileMode参数,这说明X轴与Y轴共用这一个TileMode填充参数,而不能像BitmapShader那样分别指定X轴与Y轴的填充参数。 
(2)、TileMode.CLAMP
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
////        多色渐变
int[] colors = {0xffff0000,0xff00ff00,0xff0000ff,0xffffff00,0xff00ffff};
float[]  pos = {0f,0.2f,0.4f,0.6f,1.0f};
LinearGradient multiGradient = new LinearGradient(0,0,getWidth()/2,getHeight()/2,colors,pos, Shader.TileMode.CLAMP);
mPaint.setShader(multiGradient);
canvas.drawRect(0,0,getWidth(),getHeight(),mPaint);
}
1
2
3
4
5
6
7
8
9

这里做了一个多色渐变,渐变点是从(0,0)到屏幕的中间点(width/2,height.2); 

效果图如下: 


 

从效果图中可以看到,效果很好理解,就是以(0,0)到(width/2,height.2)为矩形对角线,来填充各种颜色渐变,对于之外的区域,用边缘色彩来填充。 
(3)、TileMode.REPEAT 

同样是上面的代码,渐变点是从(0,0)到屏幕的中间点(width/2,height.2),当空白区域填充模式改为TileMode.REPEAT时,效果图如下: 


 

大家初次看到这个效果,可能一脸懵逼 —_—!!!, 其实也不难理解,我们需要先找到哪块是我们的渐变,哪块是空白像素的填充: 



在这个图中,蓝色块是我们原始的渐变图形,从从(0,0)到屏幕的中间点(width/2,height.2),另外的没有遮起来的部分是空白位置填充的。 

在填充时,结束点做为填充点的起点,即填充的线性渐变的位置为从(width/2,height/2)到(width,height),即从中间点到右下角点位置的填充。 
(4)、TileMode.MIRROR 

同样,如果我们把填充模式改为镜像模式,效果图如下: 


 

很容易理解,就不再讲了。


4、填充方式:从控件左上角开始填充

与BitmapShader一样,同样是从控件左上角开始填充整个控件,利用canvas.drawXXX系列函数只是用来指定显示哪一块 

比如:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//多色渐变
int[] colors = {0xffff0000,0xff00ff00,0xff0000ff,0xffffff00,0xff00ffff};
float[]  pos = {0f,0.2f,0.4f,0.6f,1.0f};
LinearGradient multiGradient = new LinearGradient(0,0,getWidth()/2,getHeight()/2,colors,pos, Shader.TileMode.MIRROR);
mPaint.setShader(multiGradient);
canvas.drawRect(100,100,200,200,mPaint);
}
1
2
3
4
5
6
7
8
9

同样是使用镜像模式,但我们不再全屏绘制,而只是绘出其中一小部分: 


 

我们再来看看全屏绘制的镜像模式的效果图: 


 

很明显,这里所绘制的一小块,跟从全屏绘制的效果图上摘下来的一块一样。

这就说明了: 

无论哪种Shader,都是从控件的左上角开始填充的,利用canvas.drawXXX系列函数只是用来指定显示哪一块

我们说了如果利用drawXXX系列函数只是用来指定显示哪一块,那如果我们利用DrawText来显示,那是不是就会显示出彩色文字了?
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//多色渐变
int[] colors = {0xffff0000,0xff00ff00,0xff0000ff,0xffffff00,0xff00ffff};
float[]  pos = {0f,0.2f,0.4f,0.6f,1.0f};
LinearGradient multiGradient = new LinearGradient(0,0,getWidth()/2,getHeight()/2,colors,pos, Shader.TileMode.MIRROR);
mPaint.setShader(multiGradient);
mPaint.setTextSize(50);
canvas.drawText("欢迎关注启舰的blog",0,200,mPaint);
}
1
2
3
4
5
6
7
8
9
10

效果图如下: 


 

有没有感觉很酷炫……看似牛逼的效果其实就是这么简单…… 

如果我们把渐变效果移动起来,就直接实现了我们开篇时说的文字渐变的动画效果了。 

下面我们加入动画,让颜色动起来吧


三、闪光字符串实现

这部分我们要实现的效果图如下: 


 

闪光效果有木有……看起来很碉堡吧,我们就来具体看下原理吧,这个控件只给大家讲基本原理,就不再封装成控件了,博主近期太忙了,大家自己封装下吧…… 

我们先来看下原理图:


1、原理


(1)、初始状态

首先,我们要有一个渐变的LinearGradient,颜色是从文字颜色的黑色到中间的绿色,然后再到黑色,填充模式为 Shader.TileMode.CLAMP,初始的位置在文字的左侧; 

对应图像为: 


 

我这里为了表述文字效果,特地做了几个处理; 

1.首先我把渐变图像用红边框了起来。由于填充模式是Shader.TileMode.CLAMP,所以右侧文字的位置会被填充为边缘颜色黑色 

2.为了表述当前文字的位置,我特地把文字写成了红色。而文字真正的颜色应该是其底部LinearGradient的填充色才对的,大家这点注意。 

对应代码为:
mLinearGradient = new LinearGradient(- getMeasuredWidth(),0,0,0,new int[]{
getCurrentTextColor(),0xff00ff00,getCurrentTextColor()},
new float[]{
0,
0.5f,
1
},
Shader.TileMode.CLAMP
);
}
1
2
3
4
5
6
7
8
9
10


(2)、运动中

下图显示的是当渐变的LinearGradient移动到文字部分的时的状态 


 

由于使用的是Shader.TileMode.CLAMP填充模式,所以两次空白区域都会被填充为LinearGradient的边缘颜色,即文字的黑色。 

上面我们讲了,文字会显示其下方LinearGradient的填充颜色,所以现在文字的文字就会有一部分变绿了。


(3)、终止状态

在终止状态时,LinearGradient移动到文字的右侧 


 

同样是由于Shader.TileMode.CLAMP填充模式,文字会被填充为文字原本的颜色。

从上面的原理中,我们需要理出来几个点: 

第一:创建的LinearGradient渐变的构造方法,前面已经列出来代码了,初始位置是在文字左侧的,而且大小与文字所占位置相同,填充模式使用边缘填充 

第二:从起始位置和终止位置可以看出,LinearGradient渐变的运动长度是两个文字的长度。


二、代码实现

其实看了原理之后,实现起来就没有什么难度了,我们还是列出完整代码,然后针对性的讲一点就可以了,如果大家还没看懂,可以把示例源码下下来,自己再研究研究
public class ShimmerTextView extends TextView {
private Paint mPaint;
private int mDx;
private LinearGradient mLinearGradient;
public ShimmerTextView(Context context) {
super(context);
init();
}

public ShimmerTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public ShimmerTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

private void init(){
mPaint =getPaint();
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);

ValueAnimator animator = ValueAnimator.ofInt(0,2*getMeasuredWidth());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mDx = (Integer) animation.getAnimatedValue();
postInvalidate();
}
});
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setDuration(2000);
animator.start();

mLinearGradient = new LinearGradient(- getMeasuredWidth(),0,0,0,new int[]{
getCurrentTextColor(),0xff00ff00,getCurrentTextColor()
},
new float[]{
0,
0.5f,
1
},
Shader.TileMode.CLAMP
);
}

@Override
protected void onDraw(Canvas canvas) {

Matrix matrix = new Matrix();
matrix.setTranslate(mDx,0);
mLinearGradient.setLocalMatrix(matrix);
mPaint.setShader(mLinearGradient);

super.onDraw(canvas);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

1、派生自TextView 

首先需要注意的是,控件派生自TextView,所以可以使用TextView的自带方法getCurrentTextColor()来获取文字颜色。 
2、如何移动LinearGradient 

然后,上面我们讲了如何给文字加上渐变效果,其实让它动起来办法很简单,还记得我们说过Shader有一个setLocalMatrix(Matrix localM) 方法可以设置位置矩阵么,我们只需要给LinearGradient设置上逐渐平移的矩阵就可以了。 

比如:
@Override
protected void onDraw(Canvas canvas) {

Matrix matrix = new Matrix();
matrix.setTranslate(mDx,0);
mLinearGradient.setLocalMatrix(matrix);
mPaint.setShader(mLinearGradient);

super.onDraw(canvas);
}
1
2
3
4
5
6
7
8
9
10

其中向右偏移的距离mDx,是由ValueAnimator生成的; 
3、ValueAnimator的创建 

前面我们讲了LinearGradient移动距离是从0到两倍的text距离,我们通过getMeasuredWidth()可以得到TextView的宽度,乘以2就可以了,创建代码如下:
ValueAnimator animator = ValueAnimator.ofInt(0,2*getMeasuredWidth());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mDx = (Integer) animation.getAnimatedValue();
postInvalidate();
}
});
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setDuration(2000);
animator.start();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: