Android中TextView文字鏤空效果的實現
最近在做需求的時候,設計小姐姐提了個效果,說需要TextView展示文字的時候要鏤空效果,也就是文字和背景相交的地方是透明的效果就像下邊這張圖
鏤空.jpg
雖然我剛開始沒有什麼思路,但是我可以google一下啊,於是我找到了一個實現的方式,效果也和預期的一樣,我參考的就是這篇文章介紹的 ofollow,noindex">android如何實現鏤空文字 ,於是Android中TextView文字鏤空效果的實現就完成了,到此結束 謝謝大家的閱讀~
開個玩笑,這片文章的思路其實就是使用了PorterDuff.Mode的方式來混合圖片,如果對PorterDuff.Mode不熟悉的可以看下這篇文章 各個擊破搞明白PorterDuff.Mode
我看了幾篇關於鏤空文字的文章,基本的思路都是如下:
- 自定義了HolloTextView繼承自View
- 重寫onDraw方法
- 繪製背景
- 使用PorterDuff.Mode.DST_OUT的畫筆呼叫canvas.drawText方法繪製文字
基本通過上面的方法就可做到了文字的鏤空效果,但是我有兩點不太妥當的地方:
- HolloTextView繼承的View,如果原來xml使用的是TextView這樣替換的話如果程式碼裡的引用的物件沒有替換,容易造成崩潰
- 因為是通過canvas的drawText方法繪製的文字,TextView原有的各種屬性全部都失效了,textSize、textStyle、padding等等,如果想實現這些屬性,需要自定義大量的屬性不是很方便
為了解決這兩個不方便的地方,我想了一下解決的方法,也是主要分為下面這幾步:
- HollowTextView繼承自TextView
- 自定義兩個Bitmap,一個用於繪製文字,一個用於繪製背景
- 定義兩個Canvas,分別在new的時候傳入上面兩個bitmap
- 重寫onDraw方法繪製文字和背景
這裡主要著重說一下這個onDraw方法是如何重寫的,既然我們想解決上面的第二個問題,那就需要我們能夠支援TextView的一些基本的屬性,我們知道TextView的一些基本屬性最後都是在onDraw方法繪製出來的,那麼我們能不能利用HollowTextView的super.onDraw(Canvas)方法把那些屬性儲存下來呢?答案是可以的,這裡我用了一個取巧的辦法,在onDraw裡面呼叫super.onDraw(Canvas)時,傳入的Canvas是之前定義的傳入了TextBitmap的TextCanvas,這樣所有有關TextView的基本屬性就都繪製到了TextBitmap這個Bitmap上,然後我們在onDraw方法裡可以先繪製背景,然後在使用PorterDuff.Mode.DST_OUT模式的Paint繪製TextBitmap,這樣就很輕鬆的實現了文字的鏤空效果
關於PorterDuff.Mode.DST_OUT的效果,在我提到的那篇介紹的文章裡有說明,我可以在這裡引用一下
-
DST_OUT
[Da * (1 - Sa), Dc * (1 - Sa)],可以類比SRC_OUT , 在不相交的地方繪製目標影象,相交處根據源影象alpha進行過濾,完全不透明處則完全過濾,完全透明則不過濾
說這麼多不如看一下程式碼來的痛快,下面給出我實現的程式碼:
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(); //這是鏤空的關鍵 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); } } }
這樣就實現了鏤空文字的效果,什麼粗體字啊、換字型啊、margin、padding的都不用我們再去考慮了,但是我這裡還有一個問題沒有想到好的解決辦法,就是TextView的backgroud屬性沒有辦法通過xml來支援,因為混合模式需要除了文字部分都要透明,而backgroud屬性會破壞這個規則,所以我這裡自己定義了background_color屬性,希望有興趣的小夥伴可以思考一下這個問題然後交流一下~