android自定義佈局實現優惠券效果

最近須要實現一個凹凸效果的擬物化優惠券效果,我一看,原本想用.9圖片作背景實現的,雖然說圖片作背景實現省事兒方便,可是能用代碼實現最好不過了,最終我仍是選擇了用代碼來實現,因而有了下文。android

最終效果圖git

demo下載地址github

###1.完整代碼 先看完整的代碼,後面咱們再對代碼逐一的解釋canvas

public class CouponDisplayView extends RelativeLayout {

    private Paint mPaint;
    private Paint mPaint2;
// 圓間距
    private float gap = 0;
// 半徑
    private float radius = 20;
// 圓數量
    private int circleNum;
    private float remain;
    private int color;

    public CouponDisplayView(Context context) {
        super(context);
    }
    public CouponDisplayView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setDither(true);
        mPaint.setColor(color);
        mPaint.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (remain == 0) {
            remain = (int) (w - gap) % (2 * radius + gap);
        }
        circleNum = (int) ((w - gap) / (2 * radius + gap));

    }
    public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < circleNum; i++) {
            float x = gap + radius + remain / 2 + ((gap + radius * 2) * i);
            canvas.drawCircle(x, 0, radius, mPaint);
        }
        mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint2.setDither(true);
        mPaint2.setColor(getResources().getColor(R.color.divider_color_car));
        mPaint2.setStyle(Paint.Style.FILL);

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.DKGRAY);
        Path path = new Path();
        path.moveTo(0, getHeight() / 2 + 60);
        path.lineTo(getWidth(), getHeight() / 2 + 60);
        PathEffect effects = new DashPathEffect(new float[]{15, 15, 15, 15}, 2);
        paint.setPathEffect(effects);
        canvas.drawPath(path, paint);
        canvas.drawCircle(0, getHeight() / 2 + 60, radius, mPaint2);
        canvas.drawCircle(getWidth(), getHeight() / 2 + 60, radius, mPaint2);
    }
    public void setColor(int color) {
        this.color = color;
    }
}
複製代碼

###2.方法解釋 一、CouponDisplayView繼承自RelativeLayout,經過打印日誌測試已知View的執行順序以下:bash

CouponDisplayView(context,attrs,defStyleAttr)
CouponDisplayView(context,attrs)
onSizeChanged()
onDraw()
複製代碼

onSizeChanged(int w, int h, int oldw, int oldh) 當view的大小發生變化時觸發 onDraw(Canvas canvas) 負責將View繪製在屏幕上 public CouponDisplayView(Context context) Java代碼直接new一個CouponDisplayView實例的時候,會調用這個只有一個參數的構造函數 public CouponDisplayView(Context context, AttributeSet attrs) 在默認的XML佈局文件中建立的時候調用這個有兩個參數的構造函數。AttributeSet類型的參數負責把XML佈局文件中所自定義的屬性經過AttributeSet帶入到View內; public CouponDisplayView(Context context,AttributeSet attrs, int defStyleAttr) 構造函數中第三個參數是默認的Style,這裏的默認的Style是指它在當前Application或者Activity所用的Theme中的默認Style,且只有在明確調用的時候纔會調用ide

###3.代碼實現思路 從上面的效果圖來看,這個自定義View和普通的Linearlayout,RelativeLayout同樣,只是上下兩邊多了相似於半圓鋸齒的形狀,咱們須要在上下兩條線上畫一個個白色的小圓來實現這種效果。 假如咱們上下線的半圓以及半圓與半圓之間的間距是固定的,那麼不一樣尺寸的屏幕確定會畫出不一樣數量的半圓,那麼咱們只須要根據控件的寬度來獲取能畫的半圓數。 咱們觀察效果圖會發現,圓的數量老是圓間距數量-1,函數

也就是說,假設圓的數量是circleNum,那麼圓間距就是circleNum+1,因此咱們能夠根據這個計算出circleNum: 這裏gap就是圓間距,radius是圓半徑,w是view的寬佈局

circleNum = (int) ((w-gap)/(2*radius+gap));
複製代碼

1 、重寫onSizeChanged()方法,根據上面的圓的半徑和圓間距來計算須要畫的圓數量circleNum測試

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (remain == 0) {
            remain = (int) (w - gap) % (2 * radius + gap);
        }
        circleNum = (int) ((w - gap) / (2 * radius + gap));
    }
複製代碼

2.接下來只須要重寫onDraw()方法,簡單的根據circleNum的數量將一個一個的圓繪製在屏幕上ui

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
       for (int i = 0; i < circleNum; i++) {
            float x = gap + radius + remain / 2 + ((gap + radius * 2) * i);
            canvas.drawCircle(x, 0, radius, mPaint);
        }
}
複製代碼

3.畫中間的黑色虛線

Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.DKGRAY);
        Path path = new Path();
        path.moveTo(0, getHeight() / 2 + 60);
        path.lineTo(getWidth(), getHeight() / 2 + 60);
        PathEffect effects = new DashPathEffect(new float[]{15, 15, 15, 15}, 2);
        paint.setPathEffect(effects);
        canvas.drawPath(path, paint);
複製代碼

4.畫兩邊居中的半圓

mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint2.setDither(true);
        mPaint2.setColor(getResources().getColor(R.color.divider_color_car));
        mPaint2.setStyle(Paint.Style.FILL);
      canvas.drawCircle(0, getHeight() / 2 + 60, radius, mPaint2);
        canvas.drawCircle(getWidth(), getHeight() / 2 + 60, radius, mPaint2);
複製代碼

代碼分析完畢

###3.設置自定義樣式屬性

考慮到複用地方不是不少,因此上面的代碼沒有寫自定義樣式屬性,而是用了public void setColor(int color) {this.color = color;}有須要設置自定義屬性的我在這裏寫一下哈,嘻嘻

一、在res/values/ 下創建一個attr.xml , 在裏面定義咱們的須要用到的屬性以及聲明相對應屬性的取值類型

<?xml version="1.0" encoding="utf-8"?>
<resources>
    //半圓顏色
    <attr name="radiusColor" format="color" />
    <declare-styleable name="CouponDisplayView">
        <attr name="radiusColor" />
    </declare-styleable>

</resources>
複製代碼

上面定義的半圓顏色的屬性,format屬性的取值類型總共有10種,包括:stringcolordemensionintegerenumreferencefloatbooleanfractionflag

二、而後在XML佈局中聲明咱們的自定義View

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<--注意:必定要引入xmlns:custom="http://schemas.android.com/apk/res-auto"
custom名字能夠自定義-->
    <com.xxx.xxx.CouponDisplayView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FBB039"
        android:orientation="horizontal"
        android:padding="16dp"
        custom:radiusColor="@Color/red">
............
    </com.xxx.xxx.CouponDisplayView>
</LinearLayout>
複製代碼

三、在View的構造方法中,得到咱們的xml佈局文件中定義的顏色

public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.d("mDebug", "CouponDisplayView context,attrs,defStyleAttr");
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CouponDisplayView, defStyleAttr, 0);
        for (int i = 0; i < a.getIndexCount(); i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.CouponDisplayView_radiusColor:
                    radius = a.getDimensionPixelSize(R.styleable.CouponDisplayView_radiusColor, 10);
                    break;
            }
        }
        a.recycle();
}
複製代碼

OK,設置自定義樣式屬性到此就寫完了。

相關文章
相關標籤/搜索