一個圓角圖檔的解決方案,解決設計需要不同圓角圖檔時,避免重複讓設計師出不一樣的切圖。
廢話不多說,先上效果圖:
屬性介紹
參數 | 屬性 | 介紹 |
---|---|---|
round_rect_circle | boolean | 是否顯示圓形 |
round_rect_corner | int | 圓角大小 |
round_rect_corner_top_to_left | int | 左上角圓弧 |
round_rect_corner_top_to_right | int | 右上角圓弧 |
round_rect_corner_bottom_to_left | int | 左下角圓弧 |
round_rect_corner_bottom_to_right | int | 右下角圓弧 |
round_rect_stroke_color | int | 描邊顔色 |
round_rect_stroke_width | int | 描邊大小 |
* round_rect_circle:預設false,如果是true,直接繪制圓形。
* round_rect_corner:四個角圓弧屬性,預設為0。
* round_rect_corner_top_to_left:左上角圓弧,如果設定了該屬性,round_rect_corner的左上角設定不起作用。
* round_rect_corner_top_to_right:右上角圓弧,如果設定了該屬性,round_rect_corner的右上角設定不起作用。
* round_rect_corner_bottom_to_left:左下角圓弧,如果設定了該屬性,round_rect_corner的左下角設定不起作用。
* round_rect_corner_bottom_to_right:右下角圓弧,如果設定了該屬性,round_rect_corner的右下角設定不起作用。
* round_rect_stroke_color:描邊的顔色,預設是白色。
* round_rect_stroke_width:圓弧的描邊,預設描邊的寬度是0。
實作思路
先上完整代碼:
首先先定義圓角圖形所需要的屬性:
<declare-styleable name="RoundRectLayout">
<attr name="round_rect_circle" format="boolean"/>
<attr name="round_rect_corner" format="dimension"/>
<attr name="round_rect_corner_top_to_left" format="dimension"/>
<attr name="round_rect_corner_top_to_right" format="dimension"/>
<attr name="round_rect_corner_bottom_to_left" format="dimension"/>
<attr name="round_rect_corner_bottom_to_right" format="dimension"/>
<attr name="round_rect_stroke_color" format="color"/>
<attr name="round_rect_stroke_width" format="dimension"/>
</declare-styleable>
然後在定義一個RoundRectLayout繼承RelativeLayout:完整代碼如下:
ublic class RoundRectLayout extends RelativeLayout {
private RectF mRectF;//繪制畫布的大小
public float[] mRadii = new float[];//繪制眼角矩形所需的8個角
private boolean mCircle;//是否是繪制圓形
private int mRectCorner; //圓角弧度
private int mStrokeColor;// 描邊顔色
private int mStrokeWidth;// 描邊寬度
public RoundRectLayout(Context context) {
this(context, null);
}
public RoundRectLayout(Context context, AttributeSet attrs) {
this(context, attrs, );
}
public RoundRectLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);
}
private void initView(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundRectLayout);
mCircle = array.getBoolean(R.styleable.RoundRectLayout_round_rect_circle, false);
mRectCorner = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner, );
int roundRectTopLeft = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner_top_to_left, -);
int roundRectTopRight = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner_top_to_right, -);
int roundRectBottomLeft = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner_bottom_to_left, -);
int roundRectBottomRight = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner_bottom_to_right, -);
mRadii[] = roundRectTopLeft == - ? mRectCorner : roundRectTopLeft;
mRadii[] = roundRectTopLeft == - ? mRectCorner : roundRectTopLeft;
mRadii[] = roundRectTopRight == - ? mRectCorner : roundRectTopRight;
mRadii[] = roundRectTopRight == - ? mRectCorner : roundRectTopRight;
mRadii[] = roundRectBottomLeft == - ? mRectCorner : roundRectBottomLeft;
mRadii[] = roundRectBottomLeft == - ? mRectCorner : roundRectBottomLeft;
mRadii[] = roundRectBottomRight == - ? mRectCorner : roundRectBottomRight;
mRadii[] = roundRectBottomRight == - ? mRectCorner : roundRectBottomRight;
mStrokeColor = array.getColor(R.styleable.RoundRectLayout_round_rect_stroke_color, Color.WHITE);
mStrokeWidth = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_stroke_width, );
array.recycle();
mRectF = new RectF();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRectF.left = getPaddingLeft();
mRectF.top = getPaddingTop();
mRectF.right = w - getPaddingRight();
mRectF.bottom = h - getPaddingBottom();
}
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.saveLayer(mRectF, null, Canvas.ALL_SAVE_FLAG);
super.dispatchDraw(canvas);
Path path = new Path();
path.reset();
//判斷是否繪制圓
if (mCircle) {
float width = (mRectF.right - mRectF.left) > (mRectF.bottom - mRectF.top) ? mRectF.bottom - mRectF.top : mRectF.right - mRectF.left;
path.addCircle((mRectF.right - mRectF.left) / , (mRectF.bottom - mRectF.top) / , (width - mStrokeWidth) / , Path.Direction.CW);
} else {
path.addRoundRect(mRectF, mRadii, Path.Direction.CW);
}
Paint paint = new Paint();
paint.setAntiAlias(true);
//繪制描邊
if (mStrokeWidth > ) {
paint.setColor(mStrokeColor);
paint.setStrokeWidth(mStrokeWidth);
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, paint);
}
//剪切圓角矩形
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint);
}
}
使用步驟
在xml檔案直接引用
<com.android.round.weight.RoundRectLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="30dp"
app:round_rect_circle="true">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/club_bg"/>
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:background="@mipmap/bottom_bg"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="10sp"/>
</com.android.round.weight.RoundRectLayout>
具體的實作思路:
首先要擷取自定義的屬性:
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundRectLayout);
mCircle = array.getBoolean(R.styleable.RoundRectLayout_round_rect_circle, false);
mRectCorner = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner, );
int roundRectTopLeft = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner_top_to_left, -);
int roundRectTopRight = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner_top_to_right, -);
int roundRectBottomLeft = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner_bottom_to_left, -);
int roundRectBottomRight = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_corner_bottom_to_right, -);
mRadii[] = roundRectTopLeft == - ? mRectCorner : roundRectTopLeft;
mRadii[] = roundRectTopLeft == - ? mRectCorner : roundRectTopLeft;
mRadii[] = roundRectTopRight == - ? mRectCorner : roundRectTopRight;
mRadii[] = roundRectTopRight == - ? mRectCorner : roundRectTopRight;
mRadii[] = roundRectBottomLeft == - ? mRectCorner : roundRectBottomLeft;
mRadii[] = roundRectBottomLeft == - ? mRectCorner : roundRectBottomLeft;
mRadii[] = roundRectBottomRight == - ? mRectCorner : roundRectBottomRight;
mRadii[] = roundRectBottomRight == - ? mRectCorner : roundRectBottomRight;
mStrokeColor = array.getColor(R.styleable.RoundRectLayout_round_rect_stroke_color, Color.WHITE);
mStrokeWidth = array.getDimensionPixelSize(R.styleable.RoundRectLayout_round_rect_stroke_width, );
array.recycle();
擷取到之後要及時關閉typeArray,這個不需要詳述,然後在onSizeChanged()方法中擷取目前view的大小屬性。onSizeChanged方法會在view有改動的時候調用,第一次建立view的時候也會調用一次。然後在dispatchDraw()方法中繪制圖形。
因為要使用setXfermode方法,是以首先要進行離屏緩沖,至于為什麼,請點選了解,行離屏緩沖很簡單,隻需要一行代碼:
然後判斷是繪制圓形還是圓角矩形,繪制畫布路徑
Path path = new Path();
path.reset();
//判斷是否繪制圓
if (mCircle) {
float width = (mRectF.right - mRectF.left) > (mRectF.bottom - mRectF.top) ? mRectF.bottom - mRectF.top : mRectF.right - mRectF.left;
path.addCircle((mRectF.right - mRectF.left) / , (mRectF.bottom - mRectF.top) / , (width - mStrokeWidth) / , Path.Direction.CW);
} else {
path.addRoundRect(mRectF, mRadii, Path.Direction.CW);
}
之後判斷是否需要描邊
//繪制描邊
if (mStrokeWidth > ) {
paint.setColor(mStrokeColor);
paint.setStrokeWidth(mStrokeWidth);
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, paint);
}
最後通過drawPath方法繪制需要的圖形
//剪切圓角矩形
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint);
代碼很簡單,如果有需要,可以直接引用git項目