Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義控件三部曲之繪圖篇(十九)——LinearGradient與閃動文字效果

自定義控件三部曲之繪圖篇(十九)——LinearGradient與閃動文字效果

編輯:關於Android編程

博主這段時間工作實在是太忙了,全天無尿點……博客一直沒更新,實在對不住大家……

這篇就給大家講setShader的另一個參數,LinearGradient,使用過shape標簽的同學,對這個方法估計都不莫生,就是線性漸變。跟PhotoShop中的線性漸變的原理和作用是一樣的。這篇文章的最終會實現一個閃動文字效果控件:
這裡寫圖片描述

一、引言

有關漸變,以前有講過一篇《詳解shape標簽》,其中講述了標簽的用法:

  //使用LevelListDrawable時就要設置為true。設為false時才有漸變效果    

其中的漸變類型有”linear” | “radial” | “sweep”,在代碼中對應的類分別是LinearGradient、RaialGradient、SweepGradient;有關各個漸變效果的用法,不知道的同學強烈建議你先看看這篇文章。
這篇我們要講就是線性漸變的LinearGradient;

二、LinearGradient基本使用

1、構造函數

我們先來看下LinearGradient的構造函數:
第一個構造函數:

public LinearGradient(float x0, float y0, float x1, float y1,int color0, int color1, TileMode tile)

用過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)

同樣,(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);
    }
}

很簡單,只需要在繪圖的時候構造LinearGradient實例,通過Paint.setShader設置進去即可。
大家注意一下,我這裡設置的漸變范圍是從控件的左邊中點到右邊中點:

mPaint.setShader(new LinearGradient(0,getHeight()/2,getWidth(),getHeight()/2,0xffff0000,0xff00ff00, Shader.TileMode.CLAMP));

最後通過canvas.drawRect把整個控件區域畫出來:

canvas.drawRect(0,0,getWidth(),getHeight(),mPaint);

這裡大家注意一下,上面我們也已經提到了,顏色值必須使用0xAARRGGBB的完整16進制的顏色樣式表示,我們這裡的顏色值就是0xffff0000和0xff00ff00;大家自己可以進行嘗試,如果把紅色的透明度值去掉,改寫成0xff00000,是不會有任何顯示的。
效果圖如下:
這裡寫圖片描述
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoNCBpZD0="3多色漸變使用示例">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);
}

從這裡可以看出,漸變的開始點同樣是控件左邊中點,漸變的結束點也同樣是控件右邊中點;這裡我們指定了五種漸變顏色,而且指定了每個顏色的位置,前四種顏色是按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);
}        

漸變線是從控件的左上角到控件的右下角位置:

LinearGradient multiGradient = new LinearGradient(0,0,getWidth(),getHeight(),colors,pos, Shader.TileMode.CLAMP);

效果圖如下:
這裡寫圖片描述
形成原理如下:
這裡寫圖片描述
就是說,首先是兩個漸變點之間連線,然後以連線為對角線形成一個矩形,各種顏色都是以這條對角線為矩形的填充的。

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)

從構造函數中可以看出,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);
}

這裡做了一個多色漸變,漸變點是從(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);
 }

同樣是使用鏡像模式,但我們不再全屏繪制,而只是繪出其中一小部分:
這裡寫圖片描述
我們再來看看全屏繪制的鏡像模式的效果圖:
這裡寫圖片描述
很明顯,這裡所繪制的一小塊,跟從全屏繪制的效果圖上摘下來的一塊一樣。

這就說明了:
無論哪種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、原理

(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
    );
}

(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、派生自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);
}

其中向右偏移的距離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();

有關ValueAnimator的使用方法,如果有不理解的同學,請先去看《Android自定義三部曲之動畫篇》;

好了,本篇到這裡就結束了,下篇我們再來看最後一個漸變RadialGradient的使用方法與效果吧。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved