天天看點

自定義view 圓角相對布局

跟以前一樣還是先上效果圖圖檔如下

自定義view 圓角相對布局

相信不少童鞋都在為定義圓角圖檔而苦惱,這裡我們提供了一個圓角相對布局比起圓角圖檔複用性更好,能嵌套任何想變成圓角的内容,處理也是相當簡單的下面來看代碼處理。

/**
 * 作用:圓角相對布局
 * 作者:KangJH
 */
public class RCRelativeLayout extends RelativeLayout {
    public float[] radii = {, , , , , , , };  // top-left, top-right, bottom-right, bottom-left
    public Path mClipPath;                            // 剪裁區域路徑
    public Paint mPaint;                              // 畫筆
    public int mEdgeFix = ;                        // 邊緣修複
    public RectF mLayer;                             // 畫布圖層大小

    public RCRelativeLayout(Context context) {
        this(context, null);
    }

    public RCRelativeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, );
    }

    public RCRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mLayer = new RectF();
        mClipPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.WHITE);
        mPaint.setAntiAlias(true);
    }

    @Override//當調用invalidate的時候會首先調用該處理
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mLayer.set(, , w, h);
        refreshRegion();
    }

    @Override//系統方法替代viewgroup的draw
    protected void dispatchDraw(Canvas canvas) {
        canvas.saveLayer(mLayer, null, Canvas.ALL_SAVE_FLAG);
        super.dispatchDraw(canvas);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        mPaint.setColor(Color.WHITE);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawPath(mClipPath, mPaint);
        canvas.restore();
    }

    @Override//在設定背景的情況下會調用draw方法 或者設定willnotdraw為false會調用是以要添加切割背景的處理
    public void draw(Canvas canvas) {
        refreshRegion();
        canvas.save();
        canvas.clipPath(mClipPath);
        super.draw(canvas);
        canvas.restore();
    }

    public void refreshRegion() {
        int w = (int) mLayer.width();
        int h = (int) mLayer.height();
        mClipPath.reset();
        mClipPath.addRoundRect(new RectF(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom()), radii, Path.Direction.CW);
        mClipPath.moveTo(-mEdgeFix, -mEdgeFix);  // 通過空操作讓Path區域占滿畫布
        mClipPath.moveTo(w + mEdgeFix, h + mEdgeFix);
    }

    public void setRadius(int radius) {
        for (int i = ; i < radii.length; i++) {
            radii[i] = radius;
        }
        invalidate();
    }
}
           

對外調用的處理就一個setRadius當然大家如果想添加其他處理可以向外添加方法這裡我隻講最簡單最核心的處理。

構造方法裡面的初始化就不做過多介紹了,大家應該都明白。

當調用invalidate方法是我們首先調用的是onSizeChanged方法,refreshRegion處理是通過path劃出圓角矩形的輪廓,mClipPath.moveTo的處理很關鍵沒有這兩行zailistview快速滑動過程中可能會導緻有些圓角首先展示直角然後逐漸變為圓角。

如果沒有設定背景之後會調用dispatchDraw處理将path畫到畫布上,如果有背景會調用draw這裡的處理是将背景切割保證背景也是圓角的。

下面就是外部的調用,也是十分簡單代碼如下

public class MainActivity extends AppCompatActivity {
    private ListView lv_main;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lv_main = findViewById(R.id.lv_main);
        List<String> aas = new ArrayList<>();
        for (int i = ; i < ; i++) {
            aas.add("sss");
        }
        lv_main.setAdapter(new MyAdapter(aas));

    }

    class MyAdapter extends BaseAdapter {
        private List<String> datas;

        public MyAdapter(List<String> datas) {
            this.datas = datas;
        }

        @Override
        public int getCount() {
            return datas.size();
        }

        @Override
        public Object getItem(int position) {
            return datas.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            @SuppressLint("ViewHolder") View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_rount_rect, null);
            RCRelativeLayout rc_rl = view.findViewById(R.id.rc_rl);
            rc_rl.setRadius();
            return view;
        }
    }
}
           

下面是item的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.example.pc_304.testrrelativelayout.RCRelativeLayout
        android:id="@+id/rc_rl"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:background="#ffffff">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@drawable/download"
            android:contentDescription="@string/app_name" />
    </com.example.pc_304.testrrelativelayout.RCRelativeLayout>

</RelativeLayout>
           

主界面布局如下

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.example.pc_304.testrrelativelayout.MainActivity">

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>
           

怎麼樣是不是很簡單。

繼續閱讀