轉載愛哥自定義View系列--文字詳解

FontMetrics

FontMetrics意爲字體測量,這麼一說你們是否是瞬間感覺到了這玩意的重要性?那這東西有什麼用呢?咱們經過源碼追蹤進去能夠看到FontMetrics實際上是Paint的一個內部類,而它裏面呢就定義了top,ascent,descent,bottom,leading五個成員變量其餘什麼也沒有:java

這五個成員變量除了top和bottom咱們較熟悉外其他三個都很陌生是作什麼用的呢?首先我給你們看張圖:android

這張圖很簡單可是也很扼要的說明了top,ascent,descent,bottom,leading這五個參數。首先咱們要知道Baseline基線,在Android中,文字的繪製都是從Baseline處開始的,Baseline往上至字符最高處的距離咱們稱之爲ascent(上坡度),Baseline往下至字符最底處的距離咱們稱之爲descent(下坡度),而leading(行間距)則表示上一行字符的descent到該行字符的ascent之間的距離,top和bottom文檔描述地很模糊,其實這裏咱們能夠借鑑一下TextView對文本的繪製,TextView在繪製文本的時候總會在文本的最外層留出一些內邊距,爲何要這樣作?由於TextView在繪製文本的時候考慮到了相似讀音符號,可能你們好久沒寫過拼音了已經忘了什麼叫讀音符號了吧……下圖中的A上面的符號就是一個拉丁文的相似讀音符號的東西:canvas

然而根據世界範圍內已入案的使用語言中可以標註在字符上方或者下方的除了相似的符號確定是數不勝數的……哥不是語言專家我母雞啊……而top的意思其實就是除了Baseline到字符頂端的距離外還應該包含這些符號的高度,bottom的意思也是同樣,通常狀況下咱們極少使用到相似的符號因此每每會忽略掉這些符號的存在,可是Android依然會在繪製文本的時候在文本外層留出必定的邊距,這就是爲何top和bottom總會比ascent和descent大一點的緣由。而在TextView中咱們能夠經過xml設置其屬性android:includeFontPadding="false"去掉必定的邊距值可是不能徹底去掉。下面咱們在Canvas上繪製一段文本並嘗試打印文本的top,ascent,descent,bottom和leading:數組

public class FontView extends View {
    private static final String TEXT = "ap愛哥ξτβбпшㄎㄊěǔぬも┰┠№@↓";
    private Paint mPaint;// 畫筆
    private FontMetrics mFontMetrics;// 文本測量對象

    public FontView(Context context) {
        this(context, null);
    }

    public FontView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 初始化畫筆
        initPaint();
    }

    /**
     * 初始化畫筆
     */
    private void initPaint() {
        // 實例化畫筆
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setTextSize(50);
        mPaint.setColor(Color.BLACK);

        mFontMetrics = mPaint.getFontMetrics();

        Log.d("Aige", "ascent:" + mFontMetrics.ascent);
        Log.d("Aige", "top:" + mFontMetrics.top);
        Log.d("Aige", "leading:" + mFontMetrics.leading);
        Log.d("Aige", "descent:" + mFontMetrics.descent);
        Log.d("Aige", "bottom:" + mFontMetrics.bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText(TEXT, 0, Math.abs(mFontMetrics.top), mPaint);
    }
}

logcat輸出以下:
緩存

 

注:Baseline上方的值爲負,下方的值爲正less

如圖咱們獲得了top,ascent,descent,bottom和leading的值,由於只有一行文本因此leading恆爲0,那麼此時的顯示效果是如何的呢?上面咱們說到Android中文本的繪製是從Baseline開始的,在屏幕上的體現即是Y軸座標,因此在dom

canvas.drawText(TEXT, 0, Math.abs(mFontMetrics.top), mPaint);

中咱們將文本繪製的起點Y座標向下移動Math.abs(mFontMetrics.top)個單位(注:mFontMetrics.top是負數),至關於把文本的Baseline向下移動Math.abs(mFontMetrics.top)個單位,此時文本的頂部恰好會和屏幕頂部重合:ide

 

從代碼中咱們能夠看到一個很特別的現象,在咱們繪製文本以前咱們即可以獲取文本的FontMetrics屬性值,也就是說咱們FontMetrics的這些值跟咱們要繪製什麼文本是無關的,而僅與繪製文本Paint的size和typeface有關咱們來分別更改這兩個值看看:佈局

mPaint.setTextSize(70);

 

如圖所示全部值都改變了,咱們再爲Paint設置一個typeface:學習

mPaint.setTypeface(Typeface.SERIF);


一樣全部的值也改變了,那麼咱們知道這樣的一個東西有什麼用呢?如上所說文本的繪製是從Baseline開始,而且Baseline並不是文本的分割線,當咱們想讓文本繪製的時候居中屏幕或其餘的東西時就須要計算Baseline的Y軸座標,,好比咱們讓咱們的文本居中畫布:

public class FontView extends View {
    private static final String TEXT = "ap愛哥ξτβбпшㄎㄊ";
    private Paint textPaint, linePaint;// 文本的畫筆和中心線的畫筆

    private int baseX, baseY;// Baseline繪製的XY座標

    public FontView(Context context) {
        this(context, null);
    }

    public FontView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 初始化畫筆
        initPaint();
    }

    /**
     * 初始化畫筆
     */
    private void initPaint() {
        // 實例化畫筆
        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(70);
        textPaint.setColor(Color.BLACK);

        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setStrokeWidth(1);
        linePaint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 計算Baseline繪製的起點X軸座標
        baseX = (int) (canvas.getWidth() / 2 - textPaint.measureText(TEXT) / 2);

        // 計算Baseline繪製的Y座標
        baseY = (int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2));

        canvas.drawText(TEXT, baseX, baseY, textPaint);

        // 爲了便於理解咱們在畫布中心處繪製一條中線
        canvas.drawLine(0, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, linePaint);
    }
}

效果如圖:

Baseline繪製的起點x座標爲畫布寬度的一半(中點x座標)減去文本寬度的一半(這裏咱們的畫布大小與屏幕大小同樣),這個很好理解,而y座標爲畫布高度的一半(中點y座標)減去ascent和descent絕對值之差的一半,這一點不少朋友可能不是很好理解,其實很簡單,若是直接以畫布的中心爲Baseline:

 

也就是說Baseline和屏幕中線重合,而這樣子繪製出來的文本一定不在屏幕中心,由於ascent的距離大於descent的距離(大多數狀況下咱們沒有考慮top和bottom),那麼咱們就須要將Baseline往下移使繪製出來的文本能在中心

那麼該下移多少呢?這是一個問題,不少童鞋的第一反應是下移ascent的一半高度,可是你要考慮到已經在中線下方的descent的高度,因此咱們應該先在ascent的高度中減去descent的高度再除以二再讓屏幕的中點Y座標(也就是高度的一半)加上這個偏移值

baseY = (int) ((canvas.getHeight() / 2) + ((Math.abs(textPaint.ascent()-Math.abs(textPaint.descent()))) / 2));

計算偏移距離公式 ascent-(ascent+descent)/2 = (asent-descent)/2

畫個圖就清楚了,文字中間位置爲(ascent+descent)/2

這個公式跟咱們上面代碼中的是同樣的,不信你們能夠本身算算這裏就很少說了。這裏咱們的需求是讓文本繪製在某個區域的中心,實際狀況中有不少不一樣的需求不如靠近某個區域離某個區域須要多少距離等等,熟練地去學會計算文本測繪中的各個值就顯得頗有必要了!

 

Paint有一個惟一的子類TextPaint就是專門爲文本繪製量身定作的「筆」,而這支筆就如API所描述的那樣可以在繪製時爲文本添加一些額外的信息,這些信息包括:baselineShift,bgColor,density,drawableState,linkColor,這些屬性都很簡單你們顧名思義或者本身去嘗試下便可這裏就很少說了,那麼這支筆有何用呢?最經常使用的用法是在繪製文本時可以實現換行繪製!在正常狀況下Android繪製文本是不能識別換行符之類的標識符的,這時候若是咱們想實現換行繪製就得另闢途徑使用StaticLayout結合TextPaint實現換行,StaticLayout是android.text.Layout的一個子類,很明顯它也是爲文本處理量身定作的,其內部實現了文本繪製換行的處理,該類不是本系列重點咱們再也不多說直接Look一下它是如何實現換行的:

public class StaticLayoutView extends View {
    private static final String TEXT = "This is used by widgets to control text layout. You should not need to use this class directly unless you are implementing your own widget or custom display object, or would be tempted to call Canvas.drawText() directly.";
    private TextPaint mTextPaint;// 文本的畫筆
    private StaticLayout mStaticLayout;// 文本佈局

    public StaticLayoutView(Context context) {
        this(context, null);
    }

    public StaticLayoutView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 初始化畫筆
        initPaint();
    }

    /**
     * 初始化畫筆
     */
    private void initPaint() {
        // 實例化畫筆
        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextSize(50);
        mTextPaint.setColor(Color.BLACK);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mStaticLayout = new StaticLayout(TEXT, mTextPaint, canvas.getWidth(), Alignment.ALIGN_NORMAL, 1.0F, 0.0F, false);
        mStaticLayout.draw(canvas);
        canvas.restore();
    }
}

好了,對Paint繪製文本的一個簡單瞭解就先到這,咱們來看看Paint中到底提供了哪些實用的方法來繪製文本

ascent()

顧名思義就是返回上坡度的值,咱們已經用過了,注意是負數

descent()

同上,很少說了

breakText (CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth)

這個方法讓咱們設置一個最大寬度在不超過這個寬度的範圍內返回實際測量值不然中止測量,參數不少可是都很好理解,text表示咱們的字符串,start表示從第幾個字符串開始測量,end表示從測量到第幾個字符串爲止,measureForwards表示向前仍是向後測量,maxWidth表示一個給定的最大寬度在這個寬度內能測量出幾個字符,measuredWidth爲一個可選項,能夠爲空,不爲空時返回真實的測量值。一樣的方法還有breakText (String text, boolean measureForwards, float maxWidth, float[] measuredWidth)和breakText (char[] text, int index, int count, float maxWidth, float[] measuredWidth)。這些方法在一些結合文本處理的應用裏比較經常使用,好比文本閱讀器的翻頁效果,咱們須要在翻頁的時候動態折斷或生成一行字符串,這就派上用場了~~~

getFontMetrics (Paint.FontMetrics metrics)

這個和咱們以前用到的getFontMetrics()相比多了個參數,getFontMetrics()返回的是FontMetrics對象而getFontMetrics(Paint.FontMetrics metrics)返回的是文本的行間距,若是metrics的值不爲空則返回FontMetrics對象的值。

getFontMetricsInt()

該方法返回了一個FontMetricsInt對象,FontMetricsInt和FontMetrics是同樣的,只不過FontMetricsInt返回的是int而FontMetrics返回的是float

getFontMetricsInt(Paint.FontMetricsInt fmi)

不扯了

getFontMetricsInt()

該方法返回了一個FontMetricsInt對象,FontMetricsInt和FontMetrics是同樣的,只不過FontMetricsInt返回的是int而FontMetrics返回的是float

getFontMetricsInt(Paint.FontMetricsInt fmi)

不扯了

getFontSpacing()

返回字符行間距

setUnderlineText(boolean underlineText)

設置下劃線

setTypeface(Typeface typeface)

設置字體類型,上面咱們也使用過,Android中字體有四種樣式:BOLD(加粗),BOLD_ITALIC(加粗並傾斜),ITALIC(傾斜),NORMAL(正常);而其爲咱們提供的字體有五種:DEFAULT,DEFAULT_BOLD,MONOSPACE,SANS_SERIF和SERIF,這些什麼類型啊、字體啊之類的都很簡單你們本身去試試就知道就很少說了。可是系統給咱們的字體有限咱們可不可使用本身的字體呢?答案是確定的!Typeface這個類中給咱們提供了多個方法去個性化咱們的字體

defaultFromStyle(int style)

最簡單的,簡而言之就是把上面所說的四種Style封裝成Typeface

create(String familyName, int style)和create(Typeface family, int style)

二者大概意思都同樣,好比

textPaint.setTypeface(Typeface.create("SERIF", Typeface.NORMAL));
textPaint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.NORMAL));

二者效果是同樣的

createFromAsset(AssetManager mgr, String path)、createFromFile(String path)和createFromFile(File path)
// 獲取字體並設置畫筆字體
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "kt.ttf");
textPaint.setTypeface(typeface);

這三者也是同樣的,它們都容許咱們使用本身的字體好比咱們從asset目錄讀取一個字體文件:

 

// 獲取字體並設置畫筆字體
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "kt.ttf");
textPaint.setTypeface(typeface);

咱們將會獲得以下效果:

 


這裏我用了一個卡通的字體,而另外兩個方法也相似的我就不講了。

說到文本你們第一時間想到的應該是TextView,其實在TextView裏咱們依然能夠找到上面不少方法的影子,好比咱們能夠從TextView中獲取到TextPaint:

TextPaint paint = mTextView.getPaint();

固然也能夠設置TextView的字體等等:

Typeface typeface = Typeface.createFromAsset(getAssets(), "kt.ttf");
mTextView.setTypeface(typeface);

更多的雷同點仍是留給你們去發掘,下面繼續來看

setTextSkewX(float skewX) 

這個方法能夠設置文本在水平方向上的傾斜,效果相似下圖:

// 設置畫筆文本傾斜
textPaint.setTextSkewX(-0.25F);


這個傾斜值沒有具體的範圍,可是官方推崇的值爲-0.25能夠獲得比較好的傾斜文本效果,值爲負右傾值爲正左傾,默認值爲0

setTextSize (float textSize)

不說了可是要注意該值必需大於零

setTextScaleX (float scaleX)

將文本沿X軸水平縮放,默認值爲1,當值大於1會沿X軸水平放大文本,當值小於1會沿X軸水平縮放文本

你們注意哦!setTextScaleX不只放大了文本寬度同時還拉伸了字符!這是亮點~~ 

setTextLocale (Locale locale)

設置地理位置,這個不講,咱們會在屏幕適配系列詳解什麼是Locale,這裏若是你要使用,直接傳入Locale.getDefault()便可

setTextAlign (Paint.Align align)

設置文本的對其方式,可供選的方式有三種:CENTER,LEFT和RIGHT,其實從這三者的名字上看咱們就知道其意思,可是問題是這玩意怎麼用的?好像沒什麼用啊……咱們的文本大小是經過size和typeface肯定的(其實還有其餘的因素但這裏影響不大忽略~~),一旦baseline肯定,對不對齊好像不相干吧……可是,你要知道一點,文本的繪製是從baseline開始沒錯,可是是從哪邊開始繪製的呢?左端仍是右端呢?而這個Align就是爲咱們定義在baseline繪製文本究竟該從何處開始,上面咱們在進行對文本的水平居中時是用Canvas寬度的一半減去文本寬度的一半:

實際上咱們大可沒必要這樣計算,咱們只需設置Paint的文本對齊方式爲CENTER,drawText的時候起點x = canvas.getWidth() / 2便可:

textPaint.setTextAlign(Align.CENTER);
canvas.drawText(TEXT, canvas.getWidth() / 2, baseY, textPaint);

當咱們將文本對齊方式設置爲CENTER後就至關於告訴Android咱們這個文本繪製的時候從文本的中點開始向兩端繪製,若是設置爲LEFT則從文本的左端開始往右繪製,若是爲RIGHT則從文本的右端開始往左繪製:

 

setSubpixelText (boolean subpixelText)

設置是否打開文本的亞像素顯示,什麼叫亞像素顯示呢?你能夠理解爲對文本顯示的一種優化技術,若是你們用的是Win7+系統能夠在控制面板中找到一個叫ClearType的設置,該設置可讓你的文本更好地顯示在屏幕上就是基於亞像素顯示技術。具體咱們在設計色彩系列將會細說,這裏就不扯了

setStrikeThruText (boolean strikeThruText)

文本刪除線,不扯

setLinearText (boolean linearText)

設置是否打開線性文本標識,這玩意對大多數人來講都很奇怪不知道這玩意什麼意思。想要明白這東西你要先知道文本在Android中是如何進行存儲和計算的。在Android中文本的繪製須要使用一個bitmap做爲單個字符的緩存,既然是緩存一定要使用必定的空間,咱們能夠經過setLinearText (true)告訴Android咱們不須要這樣的文本緩存。

setFakeBoldText (boolean fakeBoldText)

設置文本仿粗體

measureText (String text),measureText (CharSequence text, int start, int end),measureText (String text, int start, int end),measureText (char[] text, int index, int count)

測量文本寬度,上面咱們已經使用過了,這四個方法都是同樣的只是參數稍有不一樣這裏就不撤了!Paint對文本的繪製方法就上面那些,API 21中還新增了兩個方法這裏就先不講了,你們能夠看到雖說這些方法不少不少可是效果都是顯而易見的,不少方法你們一試就知道因此哥也沒有作太多的測試之類什麼什麼的,這樣講東西是很累的,關於文本也沒有什麼有趣的Demo能夠玩~~~~~so~~~~~Fuck……

下面咱們來看一個比較深奧的東西

setDither(boolean dither)

這玩意用來設置咱們在繪製圖像時的抗抖動,也稱爲遞色,那什麼叫抗抖動呢?在Android中我確實很差拿出一個明顯的例子,我就在PS裏模擬說明一下

你們看到的這張七彩漸變圖是一張RGB565模式下圖片,即使圖片不是很大咱們依然能夠很清晰地看到在兩種顏色交接的地方有一些色塊之類的東西感受很不柔和,由於在RGB模式下只能顯示2^16=65535種色彩,所以不少豐富的色彩變化沒法呈現,而Android呢爲咱們提供了抗抖動這麼一個方法,它會將相鄰像素之間顏色值進行一種「中和」以呈現一個更細膩的過渡色:

放大來看,其在不少相鄰像素之間插入了一個「中間值」:

setMaskFilter(MaskFilter maskfilter)

MaskFilter類中沒有任何實現方法,而它有兩個子類BlurMaskFilter和EmbossMaskFilter,前者爲模糊遮罩濾鏡(比起稱之爲過濾器哥更喜歡稱之爲濾鏡)然後者爲浮雕遮罩濾鏡,咱們先來看第一個

BlurMaskFilter

Android中的不少自帶控件都有相似軟陰影的效果,好比說Button

它周圍就有一圈很淡的陰影效果,這種效果看起來讓控件更真實,那麼是怎麼作的呢?其實很簡單,使用BlurMaskFilter就能夠獲得相似的效果,須要關閉硬件加速

EmbossMaskFilter

浮雕效果

setRasterizer (Rasterizer rasterizer)

設置光柵,光柵這東西涉及太多太多物理知識,不講了一講又是一大堆,並且該方法一樣不支持HW在API 21中遺棄了~~~咱們仍是來看看對咱們來講更好玩有趣的方法

setPathEffect(PathEffect effect)

PathEffect見文知意很明顯就是路徑效果的意思~~那這玩意確定跟路徑Path有關咯?那是必須的撒!PathEffect跟上面的不少類同樣沒有具體的實現,可是其有六個子類:

這六個子類分別能夠實現不一樣的路徑效果:

上圖從上往下分別是沒有PathEffect、CornerPathEffect、DiscretePathEffect、DashPathEffect、PathDashPathEffect、ComposePathEffect、SumPathEffect的效果,代碼的實現也很是簡單:

public class PathEffectView extends View {
    private float mPhase;// 偏移值
    private Paint mPaint;// 畫筆對象
    private Path mPath;// 路徑對象
    private PathEffect[] mEffects;// 路徑效果數組

    public PathEffectView(Context context, AttributeSet attrs) {
        super(context, attrs);

        /*
         * 實例化畫筆並設置屬性
         */
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.DKGRAY);

        // 實例化路徑
        mPath = new Path();

        // 定義路徑的起點
        mPath.moveTo(0, 0);

        // 定義路徑的各個點
        for (int i = 0; i <= 30; i++) {
            mPath.lineTo(i * 35, (float) (Math.random() * 100));
        }

        // 建立路徑效果數組
        mEffects = new PathEffect[7];
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        /*
         * 實例化各種特效
         */
        mEffects[0] = null;
        mEffects[1] = new CornerPathEffect(10);
        mEffects[2] = new DiscretePathEffect(3.0F, 5.0F);
        mEffects[3] = new DashPathEffect(new float[] { 20, 10, 5, 10 }, mPhase);
        Path path = new Path();
        path.addRect(0, 0, 8, 8, Path.Direction.CCW);
        mEffects[4] = new PathDashPathEffect(path, 12, mPhase, PathDashPathEffect.Style.ROTATE);
        mEffects[5] = new ComposePathEffect(mEffects[2], mEffects[4]);
        mEffects[6] = new SumPathEffect(mEffects[4], mEffects[3]);

        /*
         * 繪製路徑
         */
        for (int i = 0; i < mEffects.length; i++) {
            mPaint.setPathEffect(mEffects[i]);
            canvas.drawPath(mPath, mPaint);

            // 每繪製一條將畫布向下平移250個像素
            canvas.translate(0, 250);
        }

        // 刷新偏移值並重繪視圖實現動畫效果
        mPhase += 1;
        invalidate();
    }
}

當咱們不設置路徑效果的時候路徑的默認效果就如上圖第一條線那樣直的轉折生硬;而CornerPathEffect則能夠將路徑的轉角變得圓滑如圖第二條線的效果,這六種路徑效果類都有且只有一個含參的構造方法,CornerPathEffect的構造方法只接受一個參數radius,意思就是轉角處的圓滑程度,咱們嘗試更改一下上面的代碼:

mEffects[1] = new CornerPathEffect(50);

 

Look Pic是否是更平滑了呢?CornerPathEffect相對於其餘的路徑效果來講最簡單了;DiscretePathEffect離散路徑效果相對來講則稍微複雜點,其會在路徑上繪製不少「雜點」的突出來模擬一種相似生鏽鐵絲的效果如上圖第三條線,其構造方法有兩個參數,第一個呢指定這些突出的「雜點」的密度,值越小雜點越密集,第二個參數呢則是「雜點」突出的大小,值越大突出的距離越大反之反之,你們能夠去本身去試下我就不演示了;DashPathEffect的效果相對與上面兩種路徑效果來講要略顯複雜,其雖然說也是包含了兩個參數,可是第一個參數是一個浮點型的數組,那這個數組有什麼意義呢?實際上是這樣的,咱們在定義該參數的時候只要浮點型數組中元素個數大於等於2便可,也就是說上面咱們的代碼能夠寫成這樣的:

mEffects[3] = new DashPathEffect(new float[] {20, 10}, mPhase);

 

從圖中咱們能夠看到咱們以前的那種線條變成了一長一短的間隔線條,而float[] {20, 10}的偶數參數20(注意數組下標是從0開始哦)定義了咱們第一條實線的長度,而奇數參數10則表示第一條虛線的長度,若是此時數組後面再也不有數據則重複第一個數以此往復循環,好比咱們20,10後沒數了,那麼整條線就成了[20,10,20,10,20,10…………………………]這麼一個狀態,固然若是你無聊,也能夠:

mEffects[3] = new DashPathEffect(new float[] {20, 10, 50, 5, 100, 30, 10, 5}, mPhase);

 

而DashPathEffect的第二個參數我稱之爲偏移值,動態改變其值會讓路徑產生動畫的效果,上面代碼已給出你們能夠本身去試試;PathDashPathEffect和DashPathEffect是相似的,不一樣的是PathDashPathEffect可讓咱們本身定義路徑虛線的樣式,好比咱們將其換成一個個小圓組成的虛線:

Path path = new Path();
path.addCircle(0, 0, 3, Direction.CCW);
mEffects[4] = new PathDashPathEffect(path, 12, mPhase, PathDashPathEffect.Style.ROTATE);

 

ComposePathEffect和SumPathEffect均可以用來組合兩種路徑效果,惟一不一樣的是組合的方式,ComposePathEffect(PathEffect outerpe, PathEffect innerpe)會先將路徑變成innerpe的效果,再去複合outerpe的路徑效果,即:outerpe(innerpe(Path));而SumPathEffect(PathEffect first, PathEffect second)則會把兩種路徑效果加起來再做用於路徑,具體區別你們去試試吧…………哥累了睡會~~~囧……

記得在1/12中咱們繪製了了一個圓環並讓其實現動畫的效果,當時咱們使用了線程來使其產生動畫,可是咱們是否是也能夠像上面的例子同樣直接在onDraw中invalidate()來產生動畫呢?這個問題留給你們。

在1/12中咱們還說過儘可能不要在onDraw中使用new關鍵字來生成對象,可是上例的代碼中咱們卻在頻繁地使用,可是六個PathEffect的子類中除了構造方法什麼都沒有,咱們該如何避免頻繁地去new對象呢?這個問題也留給你們思考。

Path應用的普遍性註定了PathEffect應用的普遍,所謂一人得道雞犬升天就是這麼個道理,只要是Path能存在的地方均可以考慮使用,下面咱們來模擬一個相似心電圖的路徑小動畫:

這種效果呢也是很是很是地簡單,說白了就是無數條短小精悍的小「Path」鏈接成一條完整的心電路徑

咱們在onSizeChanged(int w, int h, int oldw, int oldh)方法中獲取屏幕的寬高,該方法的具體用法咱們會在7/12學習View的測繪時具體說明,這裏就先不說了

上面在設置Paint屬性的時候咱們使用到了一個

setStrokeCap(Paint.Cap cap)

方法,該方法用來設置咱們畫筆的筆觸風格,上面的例子中我使用的是ROUND,表示是圓角的筆觸,那麼什麼叫筆觸呢,其實很簡單,就像咱們現實世界中的筆,若是你用圓珠筆在紙上戳一點,那麼這個點必定是個圓,即使很小,它表明了筆的筆觸形狀,若是咱們把一支鉛筆筆尖削成方形的,那麼畫出來的線條會是一條彎曲的「矩形」,這就是筆觸的意思。除了ROUND,Paint.Cap還提供了另外兩種類型:SQUARE和BUTT,具體你們本身去try~~

setStrokeJoin(Paint.Join join)

這個方法用於設置結合處的形態,就像上面的代碼中咱們雖然說是花了一條心電線,可是這條線實際上是由無數條小線拼接成的,拼接處的形狀就由該方法指定。

上面的例子中咱們還使用到了一個方法

setShadowLayer(float radius, float dx, float dy, int shadowColor)

該方法爲咱們繪製的圖形添加一個陰影層效果:

radius表示陰影的擴散半徑,而dx和dy表示陰影平面上的偏移值,shadowColor就不說了陰影顏色,最後提醒一點setShadowLayer一樣不支持硬件加速哦!

相關文章
相關標籤/搜索