通常的圖片圓角一般是對單獨的圖片進行切圓角操作,但是像下圖的效果就沒那麼合適了,雖然對單張圖片切圓角也能實現,但更爲繁瑣、不簡潔,因爲數據內容是動態的,要根據數據源分很多種情況判斷哪張圖片該切哪個角。
所以,我在想能不能就在外層容器的四個角切圓角而不用管內部圖片的圓角情況呢?答案顯然是能!主要思路就是自定義一個layout,在dispatchDraw的時候將數據圖片的canvas與圓角bitmap混合,設置Xfermode爲PorterDuff.Mode.DST_IN使交集部分展示即可達到圖示的效果
關鍵代碼(根據後臺數據生成裏面的每個item相關代碼沒有貼,根據業務場景改變即可):
public class HotCityView extends LinearLayout {
private LinearLayout ll_item_container1, ll_item_container2;
private int itemH, itemSpace;
private int padding;
private Paint clipPaint;
private RectF clipRect;
private Bitmap maskBitmap;
private int cornerSize;
public HotCityView(@NonNull Context context) {
this(context, null);
}
public HotCityView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public HotCityView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setOrientation(VERTICAL);
inflate(context, R.layout.m_layout_hot_city, this);
padding = (int) getResources().getDimension(R.dimen.list_lr_margin1);
setPadding(padding, 0, padding, 0);
itemH = PixelUtil.dp2px(109);
itemSpace = (int) getResources().getDimension(R.dimen.hot_item_space);
cornerSize = PixelUtil.dp2px(8);
ll_item_container1 = findViewById(R.id.m_hot_city_item_container1);
ll_item_container2 = findViewById(R.id.m_hot_city_item_container2);
clipPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
clipPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (clipRect == null || getMeasuredWidth() == 0) {
clipRect = new RectF(padding, 0, getMeasuredWidth() - padding, getMeasuredHeight());
maskBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_4444);
Canvas c = new Canvas(maskBitmap);
//在蒙版上畫需要覆蓋的圖形
c.drawRoundRect(clipRect, cornerSize, cornerSize, clipPaint);
}
//保存還沒有繪製之前的圖層
int layerId = canvas.saveLayer(clipRect, clipPaint, Canvas.ALL_SAVE_FLAG);
//繪製底部圖層
super.dispatchDraw(canvas);
//設置混合模式,實現view的四個圓角
clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(maskBitmap, 0, 0, clipPaint);
clipPaint.setXfermode(null);
//恢復之前的圖層,要不然背景是黑色的
canvas.restoreToCount(layerId);
}
//這種方式也可以實現裁剪效果,但是需要5.0以上
private void clipRoundView() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
//修改outline爲特定形狀
outline.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), cornerSize);
}
};
//重新設置形狀
setOutlineProvider(viewOutlineProvider);
//添加背景或者是ImageView的時候失效,添加如下設置
setClipToOutline(true);
}
}
}
附各種Xfermode的效果(這張圖太經典了,按照命名規則其實很容易理解,無需記住,到用的時候查閱即可):