Android中TextView文字鏤空效果的實現(xiàn)

最近在做需求的時候,設(shè)計小姐姐提了個效果,說需要TextView展示文字的時候要鏤空效果,也就是文字和背景相交的地方是透明的效果就像下邊這張圖
雖然我剛開始沒有什么思路,但是我可以google一下啊,于是我找到了一個實現(xiàn)的方式,效果也和預(yù)期的一樣,我參考的就是這篇文章介紹的android如何實現(xiàn)鏤空文字,于是Android中TextView文字鏤空效果的實現(xiàn)就完成了,到此結(jié)束 謝謝大家的閱讀~

鏤空.jpg

開個玩笑,這片文章的思路其實就是使用了PorterDuff.Mode的方式來混合圖片,如果對PorterDuff.Mode不熟悉的可以看下這篇文章各個擊破搞明白PorterDuff.Mode
我看了幾篇關(guān)于鏤空文字的文章,基本的思路都是如下:

  • 自定義了HolloTextView繼承自View
  • 重寫onDraw方法
  • 繪制背景
  • 使用PorterDuff.Mode.DST_OUT的畫筆調(diào)用canvas.drawText方法繪制文字

基本通過上面的方法就可做到了文字的鏤空效果,但是我覺得有兩點不太妥當(dāng)?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 , 在不相交的地方繪制目標(biāo)圖像,相交處根據(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屬性,希望有興趣的小伙伴可以思考一下這個問題然后交流一下~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容