最近在做需求的時候,設(shè)計小姐姐提了個效果巧骚,說需要TextView展示文字的時候要鏤空效果,也就是文字和背景相交的地方是透明的效果就像下邊這張圖
雖然我剛開始沒有什么思路,但是我可以google一下啊搔扁,于是我找到了一個實現(xiàn)的方式,效果也和預(yù)期的一樣蟋字,我參考的就是這篇文章介紹的android如何實現(xiàn)鏤空文字稿蹲,于是Android中TextView文字鏤空效果的實現(xiàn)就完成了,到此結(jié)束 謝謝大家的閱讀~
開個玩笑鹊奖,這片文章的思路其實就是使用了PorterDuff.Mode的方式來混合圖片苛聘,如果對PorterDuff.Mode不熟悉的可以看下這篇文章各個擊破搞明白PorterDuff.Mode
我看了幾篇關(guān)于鏤空文字的文章,基本的思路都是如下:
- 自定義了HolloTextView繼承自View
- 重寫onDraw方法
- 繪制背景
- 使用PorterDuff.Mode.DST_OUT的畫筆調(diào)用canvas.drawText方法繪制文字
基本通過上面的方法就可做到了文字的鏤空效果忠聚,但是我覺得有兩點不太妥當?shù)牡胤剑?/p>
- HolloTextView繼承的View设哗,如果原來xml使用的是TextView這樣替換的話如果代碼里的引用的對象沒有替換,容易造成崩潰
- 因為是通過canvas的drawText方法繪制的文字两蟀,TextView原有的各種屬性全部都失效了网梢,textSize、textStyle赂毯、padding等等战虏,如果想實現(xiàn)這些屬性,需要自定義大量的屬性不是很方便
為了解決這兩個不方便的地方党涕,我想了一下解決的方法烦感,也是主要分為下面這幾步:
- HollowTextView繼承自TextView
- 自定義兩個Bitmap,一個用于繪制文字膛堤,一個用于繪制背景
- 定義兩個Canvas手趣,分別在new的時候傳入上面兩個bitmap
- 重寫onDraw方法繪制文字和背景
這里主要著重說一下這個onDraw方法是如何重寫的,既然我們想解決上面的第二個問題骑祟,那就需要我們能夠支持TextView的一些基本的屬性回懦,我們知道TextView的一些基本屬性最后都是在onDraw方法繪制出來的,那么我們能不能利用HollowTextView的super.onDraw(Canvas)方法把那些屬性保存下來呢次企?答案是可以的怯晕,這里我用了一個取巧的辦法,在onDraw里面調(diào)用super.onDraw(Canvas)時缸棵,傳入的Canvas是之前定義的傳入了TextBitmap的TextCanvas舟茶,這樣所有有關(guān)TextView的基本屬性就都繪制到了TextBitmap這個Bitmap上,然后我們在onDraw方法里可以先繪制背景,然后在使用PorterDuff.Mode.DST_OUT模式的Paint繪制TextBitmap吧凉,這樣就很輕松的實現(xiàn)了文字的鏤空效果
關(guān)于PorterDuff.Mode.DST_OUT的效果隧出,在我提到的那篇介紹的文章里有說明,我可以在這里引用一下
- DST_OUT
[Da * (1 - Sa), Dc * (1 - Sa)]阀捅,可以類比SRC_OUT , 在不相交的地方繪制目標圖像胀瞪,相交處根據(jù)源圖像alpha進行過濾,完全不透明處則完全過濾饲鄙,完全透明則不過濾
說這么多不如看一下代碼來的痛快凄诞,下面給出我實現(xiàn)的代碼:
public class HollowTextView extends AppCompatTextView{
private Paint mTextPaint, mBackgroundPaint;
private Bitmap mBackgroundBitmap,mTextBitmap;
private Canvas mBackgroundCanvas,mTextCanvas;
private RectF mBackgroundRect;
private int mBackgroundColor;
private int mCornerRadius;
public HollowTextView(Context context) {
this(context,null);
}
public HollowTextView(Context context, AttributeSet attrs) {
this(context, attrs,-1);
}
public HollowTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(attrs,defStyleAttr);
initPaint();
}
private void initAttrs(AttributeSet attrs,int defStyleAttr){
if(attrs == null){
return;
}
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.HollowTextView, defStyleAttr, 0);
mBackgroundColor = a.getColor(R.styleable.HollowTextView_background_color,Color.TRANSPARENT);
mCornerRadius = a.getDimensionPixelOffset(R.styleable.HollowTextView_corner_radius,0);
a.recycle();
}
/***
* 初始化畫筆屬性
*/
private void initPaint() {
//畫文字的paint
mTextPaint = new Paint();
//這是鏤空的關(guān)鍵
mTextPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mTextPaint.setAntiAlias(true);
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(mBackgroundColor);
mBackgroundPaint.setAntiAlias(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBackgroundBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
mBackgroundCanvas = new Canvas(mBackgroundBitmap);
mTextBitmap = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_4444);
mTextCanvas = new Canvas(mTextBitmap);
mBackgroundRect = new RectF(0,0,getWidth(),getHeight());
}
@Override
protected void onDraw(Canvas canvas) {
//這里給super傳入的是mTextCanvas,把一些基本屬性都支持進去
super.onDraw(mTextCanvas);
drawBackground(mBackgroundCanvas);
int sc;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ){
sc = canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null);
}else {
sc = canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
}
canvas.drawBitmap(mBackgroundBitmap,0,0,null);
canvas.drawBitmap(mTextBitmap, 0, 0, mTextPaint);
canvas.restoreToCount(sc);
}
private void drawBackground(Canvas canvas){
if(mCornerRadius > 0){
canvas.drawRoundRect(mBackgroundRect,mCornerRadius,mCornerRadius, mBackgroundPaint);
}else {
canvas.drawColor(mBackgroundColor);
}
}
}
這樣就實現(xiàn)了鏤空文字的效果忍级,什么粗體字啊帆谍、換字體啊、margin轴咱、padding的都不用我們再去考慮了汛蝙,但是我這里還有一個問題沒有想到好的解決辦法,就是TextView的backgroud屬性沒有辦法通過xml來支持朴肺,因為混合模式需要除了文字部分都要透明窖剑,而backgroud屬性會破壞這個規(guī)則,所以我這里自己定義了background_color屬性宇挫,希望有興趣的小伙伴可以思考一下這個問題然后交流一下~