天天看點

Android 自定義TextView實作陰影效果(不占view空間)

之前寫了Android 自定義圓角+陰影布局,雖然實作了陰影效果,但陰影需要考慮在布局内,無法實作像CardView那樣陰影可以在布局外繪制。這篇為了解決陰影不能繪制在布局外的問題

實作布局外繪制陰影的思路

  1. 繪制view外部的陰影(其實就是擴大canvas繪制區域,讓其超出view)
  2. 讓view外部的陰影顯示(父視圖通過設定android:clipChildren="false",讓view可以超出自身範圍進行繪制)

 思路實作遇到的關鍵問題:如何繪制view外部陰影,又不影響view區域内的圖像?

解決:

  1. 通過PorterDuffXfermode設定圖像混合模式,裁剪掉陰影繪制區域在view區域内的圖像 (實作後發現存在過度繪制問題)
  2. 使用canvas的clipPath(path,Region.Op),裁剪掉陰影繪制區域在view區域内的圖像(雖然存在鋸齒,但沒有過度繪制問題)
  3. 将陰影區域繪制放于super.draw(canvas)之前,這樣view将繪制在陰影區域之上,這樣不用剪切陰影畫筆填充區域了,同時可以設定陰影畫筆顔色作為背景色,也沒有鋸齒問題

實作核心代碼(第三種方案結合使用canvas的clipPath(path,Region.Op)):

@Override
    public void draw(Canvas canvas) {
        ...
        //畫陰影
        if (shadowElevation > 0) {
            Bitmap srcBmp = createShadowBitmap((int) (getWidth()+shadowElevation*2), (int) (getHeight()+shadowElevation*2));
            canvas.drawBitmap(srcBmp, shadow_x-shadowElevation, shadow_y-shadowElevation, clipShadowPaint);
        }   
    } 

    //添加陰影bitmap
    private Bitmap createShadowBitmap(int shadowWidth, int shadowHeight) {
        Bitmap output = Bitmap.createBitmap(shadowWidth, shadowHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        shadowRect.set(shadowElevation-shadow_x,shadowElevation-shadow_y,shadowWidth-shadowElevation-shadow_x,shadowHeight-shadowElevation-shadow_y);

        shadowPaint.setColor(shadowColor);
        if (!isInEditMode()) {
            shadowPaint.setShadowLayer(shadowElevation, shadow_x, shadow_y, shadowColor);
        }
        shadowPath.reset();
        shadowPath.addRoundRect(shadowRect, radiusShadowArray, Path.Direction.CW);
        if (clipShadowPath) {
            //保留shadowPath路徑以外區域
            canvas.clipPath(shadowPath, Region.Op.DIFFERENCE);
        }
        canvas.drawPath(shadowPath, shadowPaint);

        return output;
    }
           

效果圖:

Android 自定義TextView實作陰影效果(不占view空間)
Android 自定義TextView實作陰影效果(不占view空間)

 源碼:https://github.com/wudengwei/TextStrongView