1. 程式人生 > >Android圓角Tag控件的另類實現

Android圓角Tag控件的另類實現

tao 這樣的 frame set tag 建議 開啟 height 基本

一般的圓角標簽控件都是用xml設置shape做實現。可是假設我們想要做一個更加強大通用的的圓角控件,不須要使用者去關心圓角,僅僅設置背景就能夠了。

應該怎麽實現呢?這個就須要把背景先設置成圖片,然後再把這個圖片處理成圓角的,最後再設置成背景。基本思路例如以下代碼:

Bitmap bitmap = ((BitmapDrawable)getBackground()).getBitmap();
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Matrix matrix = new Matrix();
bitmapShader.setLocalMatrix(matrix);
// 設置縮放
float scale = Math.max(getWidth() * 1.0f / bmp.getWidth(), getHeight() * 1.0f / bmp.getHeight());  
mBitmapPaint.setShader(bitmapShader);
Canvas canvas = new Canvas(bitmap);

if (mRadius > 0) {
    canvas.drawRoundRect(mDrawableRect, mRadius, mRadius, mBitmapPaint);
} else {
    canvas.drawRect(mDrawableRect, mBitmapPaint);
}
setBackground(new BitmapDrawable(bitmap));
上面的代碼把背景圖取出來後,用著色器畫圓角(假設背景色是color,須要做一次ColorDrawable轉Bitmap的轉換)。這樣就行強制實現一個圓角背景的TextView了。你也可以使用Xfermode來做,網上文章非常多。不做贅述了。

如今問題來了,假設我背景是ShapDrawable(xml:shape)或者設置的StateListDrawable(selector)呢?這就沒辦法了,ShapeDrawable還可以做自己定義,可是StateListDrawable開放的可以自己定義的接口非常少,全然沒辦法。

此外,假設我們須要設置drawableLeft或者drawableRight。也會掩蓋住背景。圓角也就沒了。

假設也對這些compound drawable也設置圓角。並且圓角的半徑還不能和主空間的半徑一樣。否則會因為寬高不同。畫出來的圓角也會不一樣。

這個方法太復雜。

放棄!

有沒有什麽簡單的辦法呢?我們能夠用Xfermode來對畫板的底圖做文章。

這裏就要說到Android(預計其它OS也是差點兒相同)控件的實現方式了。

我們在顯示屏看到的全部東西。事實上都是一塊內存,放在一塊叫做framebuffer的內存緩沖區裏面。這塊Buffer以像素點為單位,用一定的色彩規則,給我們排列出了各種看到的屏幕上的東西。Android通過一個叫Surface的系統來管理這一塊FrameBuffer,所謂的控件,事實上就是依照一定的規則,告訴Surface

系統怎麽畫我這一塊區域。這樣就行給我們具體的畫出我們想要看到的畫面了。關於細節,東西太多,以後再慢慢說。

繼續說到實現圓角控件的問題來。

Android裏面全部的控件,都給我們提供了一個叫onDraw()的方法,參數為Canvas。Canvas就是畫布的意思,我們在這塊布上面畫的東西。就是空間的長相了。

這種方法會在View.draw()裏面調用下來,告訴系統怎麽畫這個控件。比方TextView裏面的onDraw就基本是一些寫字。設置顏色,字體的操作,ImageView就是用Drawable來畫canvas的操作。

Android提供了各種各樣的方法,方便我們對這塊Canvas進行操作。我們想要實現圓角,是不是能夠對這塊Canvas做點手腳呢?我們能夠Xfermode來畫一個橢圓形 的Bitmap在原圖上面,然後僅僅保留他們相交的這一部分。去掉不相交的部分,圓角控件不就大功告成了?關於Xfermode,有一經典的圖深動形象的說明了使用方法:

技術分享

我們想要做的就例如以下圖:

技術分享

生成掩蓋圖的代碼例如以下:

private Bitmap generateMaskBitmap() {
    Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
    Canvas bitmapCanvas = new Canvas(bitmap);
    RectF r = new RectF(0, 0, getWidth(), getHeight());
    Rect rect = new Rect(0, 0, getWidth(), getHeight());
    Paint bitmapPaint = new Paint();
    bitmapPaint.setAlpha(0);
    bitmapPaint.setColor(Color.TRANSPARENT);
    bitmapCanvas.drawRect(rect, bitmapPaint);
    bitmapPaint.reset();
    bitmapPaint.setStyle(Paint.Style.FILL);
    bitmapPaint.setAntiAlias(true);
    bitmapPaint.setColor(Color.WHITE);
    bitmapCanvas.drawRoundRect(r, mRadius, mRadius, bitmapPaint);
    return bitmap;
}
onDraw代碼例如以下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (mRadius > 0) {
        Bitmap bitmap = generateMaskBitmap();
        Paint bitmapPaint = new Paint();
        bitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(bitmap, 0f, 0f, bitmapPaint);
        bitmap.recycle();
    }
}

可是這樣並不OK啊。用這個控件卻是這樣:

<com.example.widget.TagTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#ff0000"
    android:text="哎喲不錯"
    app:border_radius="5dp"
    />

技術分享

為什麽會這樣呢?圓角是有了,可是為嘛周圍是是黑色啊。感覺代碼是沒問題的呀,後來去網上搜了下。XferMode在開啟了硬件加速的情況下有一些局限性(http://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported),預計這個問題也是局限性之中的一個。於是在本widget關閉了硬件加速,又一次build後,發現還是沒有變化。我就有點暈了,為嘛不是其它顏色。偏偏是黑色啊,並且我的maskBitmap也是設置的白色呀。

解釋僅僅能為一個了:我的Background沒有設置alpha值,沒有不論什麽的透明度,導致刪除的顏色變為了無顏色狀態。我來設置一個帶有透明度的背景色試試:

<com.example.widget.TagTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#feff0000"
    android:text="哎喲不錯"
    app:border_radius="5dp"
    />


技術分享

能夠了耶。這樣,以後不管這個控件做了什麽,畫出來周圍肯定是圓角的。

總結

這個盡管實現了強制性的圓角,可是非常有局限性。假設View的Canvas本身沒有透明通道,被清除的圓角處就會變成黑色。

我有嘗試事先畫一個透明的Color在控件上,可是仍然無論用。詳細原因還不知道。還沒來得及深入的去看。假設有哥們知道。最好還是分享一下原因。 建議:對於這樣的需求的建議是建立一個Factory,用來生成一些定義好的Tag控件。這樣更加的符合Android規範。也可以非常好的管理各種Tag控件。
代碼鏈接:http://download.csdn.net/detail/yutao52shi/8972539

Android圓角Tag控件的另類實現