之前寫了Android 自定義圓角+陰影布局,雖然實作了陰影效果,但陰影需要考慮在布局内,無法實作像CardView那樣陰影可以在布局外繪制。這篇為了解決陰影不能繪制在布局外的問題
實作布局外繪制陰影的思路
- 繪制view外部的陰影(其實就是擴大canvas繪制區域,讓其超出view)
- 讓view外部的陰影顯示(父視圖通過設定android:clipChildren="false",讓view可以超出自身範圍進行繪制)
思路實作遇到的關鍵問題:如何繪制view外部陰影,又不影響view區域内的圖像?
解決:
- 通過PorterDuffXfermode設定圖像混合模式,裁剪掉陰影繪制區域在view區域内的圖像 (實作後發現存在過度繪制問題)
- 使用canvas的clipPath(path,Region.Op),裁剪掉陰影繪制區域在view區域内的圖像(雖然存在鋸齒,但沒有過度繪制問題)
- 将陰影區域繪制放于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;
}
效果圖:
源碼:https://github.com/wudengwei/TextStrongView