您好,登錄后才能下訂單哦!
本篇文章講的是自定義View之邊緣凹凸的優(yōu)惠券效果,之前有見過很多優(yōu)惠券的效果都是使用了邊緣凹凸的樣式。和往常一樣,主要總結(jié)一下在自定義View的開發(fā)過程中需要注意的一些地方。
按照慣例,我們先來看看效果圖
一、寫代碼之前,我們先弄清楚view的啟動(dòng)過程:
之所以想要弄清楚這個(gè)問題是因?yàn)榇a里面用到了onSizeChanged()
方法,一開始我有點(diǎn)猶豫onSizeChanged
是在什么時(shí)候啟動(dòng)的呢,所以看看View的啟動(dòng)流程吧
package per.lijuan.coupondisplayviewdome; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; import android.widget.LinearLayout; /** * 自定義邊緣凹凸的優(yōu)惠券效果view * Created by lijuan on 2016/9/26. */ public class CouponDisplayView extends LinearLayout { public CouponDisplayView(Context context) { this(context, null); Log.d("mDebug", "CouponDisplayView:context"); } public CouponDisplayView(Context context, AttributeSet attrs) { this(context, attrs, 0); Log.d("mDebug", "CouponDisplayView:context,attrs"); } public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Log.d("mDebug", "CouponDisplayView:context,attrs,defStyleAttr"); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); Log.d("mDebug", "onSizeChanged:w=" + w + ",h=" + h + ",oldw=" + oldw + ",oldh=" + oldh); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.d("mDebug", "onDraw"); } }
輸出如下:
09-27 15:29:31.957 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: CouponDisplayView:context,attrs,defStyleAttr 09-27 15:29:31.957 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: CouponDisplayView:context,attrs 09-27 15:29:32.050 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: onSizeChanged:w=984,h=361,oldw=0,oldh=0 09-27 15:29:32.083 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: onDraw
在這里可以看到,onSizeChanged()方法的啟動(dòng)是在onDraw之前
二、view的幾個(gè)常用觸發(fā)方法
1. onFinishInflate():當(dāng)View中所有的子控件均被映射成xml后觸發(fā)
2. onMeasure(int widthMeasureSpec, int heightMeasureSpec):確定所有子元素的大小
3. onLayout(boolean changed, int l, int t, int r, int b):當(dāng)View分配所有的子元素的大小和位置時(shí)觸發(fā)
4. onSizeChanged(int w, int h, int oldw, int oldh):當(dāng)view的大小發(fā)生變化時(shí)觸發(fā)
5. onDraw(Canvas canvas):負(fù)責(zé)將View繪制在屏幕上
三、View 的幾個(gè)構(gòu)造函數(shù)
1、public CouponDisplayView(Context context)
—>Java代碼直接new一個(gè)CouponDisplayView實(shí)例的時(shí)候,會(huì)調(diào)用這個(gè)只有一個(gè)參數(shù)的構(gòu)造函數(shù);
2、public CouponDisplayView(Context context, AttributeSet attrs)
—>在默認(rèn)的XML布局文件中創(chuàng)建的時(shí)候調(diào)用這個(gè)有兩個(gè)參數(shù)的構(gòu)造函數(shù)。AttributeSet類型的參數(shù)負(fù)責(zé)把XML布局文件中所自定義的屬性通過AttributeSet帶入到View內(nèi);
3、public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr)
—>構(gòu)造函數(shù)中第三個(gè)參數(shù)是默認(rèn)的Style,這里的默認(rèn)的Style是指它在當(dāng)前Application或者Activity所用的Theme中的默認(rèn)Style,且只有在明確調(diào)用的時(shí)候才會(huì)調(diào)用
4、public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
—>該構(gòu)造函數(shù)是在API21的時(shí)候才添加上的
自定義View中,我們需要重寫了3個(gè)構(gòu)造方法,在上面的構(gòu)造方法中說過默認(rèn)的布局文件調(diào)用的是兩個(gè)參數(shù)的構(gòu)造方法,所以記得讓所有的構(gòu)造方法調(diào)用三個(gè)參數(shù)的構(gòu)造方法,然后在三個(gè)參數(shù)的構(gòu)造方法中獲得自定義屬性。
一開始一個(gè)參數(shù)的構(gòu)造方法和兩個(gè)參數(shù)的構(gòu)造方法是這樣的:
public CouponDisplayView(Context context) { super(context); } public CouponDisplayView(Context context, AttributeSet attrs) { super(context, attrs); }
我們需要注意的是super應(yīng)該改成this,然后讓一個(gè)參數(shù)的構(gòu)造方法引用兩個(gè)參數(shù)的構(gòu)造方法,兩個(gè)參數(shù)的構(gòu)造方法引用三個(gè)參數(shù)的構(gòu)造方法,代碼如下:
public CouponDisplayView(Context context) { this(context, null); } public CouponDisplayView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
四、分析具體的實(shí)現(xiàn)思路:
從上面的效果圖來看,這個(gè)自定義View和普通的Linearlayout,RelativeLayout一樣,只是上下兩邊多了類似于半圓鋸齒的形狀,我們需要在上下兩條線上畫一個(gè)個(gè)白色的小圓來實(shí)現(xiàn)這種效果。
假如我們上下線的半圓以及半圓與半圓之間的間距是固定的,那么不同尺寸的屏幕肯定會(huì)畫出不同數(shù)量的半圓,那么我們只需要根據(jù)控件的寬度來獲取能畫的半圓數(shù)。
我們觀察效果圖會(huì)發(fā)現(xiàn),圓的數(shù)量總是圓間距數(shù)量-1,也就是說,假設(shè)圓的數(shù)量是circleNum,那么圓間距就是circleNum+1,所以我們可以根據(jù)這個(gè)計(jì)算出circleNum:
circleNum = (int) ((w-gap)/(2*radius+gap));
這里gap就是圓間距,radius是圓半徑,w是view的寬。
五、下面我們就開始來看看代碼啦
1、自定義View的屬性,首先在res/values/ 下建立一個(gè)attr.xml , 在里面定義我們的需要用到的屬性以及聲明相對(duì)應(yīng)屬性的取值類型
<?xml version="1.0" encoding="utf-8"?> <resources> <!--圓間距--> <attr name="radius" format="dimension" /> <!--半徑--> <attr name="gap" format="dimension" /> <declare-styleable name="CouponDisplayView"> <attr name="radius" /> <attr name="gap" /> </declare-styleable> </resources>
我們定義了圓間距和半徑2個(gè)屬性,format是值該屬性的取值類型,format取值類型總共有10種,包括:string,color,demension,integer,enum,reference,float,boolean,fraction和flag。
2、然后在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" android:layout_margin="16dp"> <per.lijuan.coupondisplayviewdome.CouponDisplayView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FBB039" android:orientation="horizontal" android:padding="16dp" custom:gap="8dp" custom:radius="5dp"> <ImageView android:layout_width="90dp" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:orientation="vertical"> <TextView android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="電影新客代金劵" android:textSize="18dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="5dp" android:text="編號(hào):525451122312431" android:textSize="12dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="5dp" android:text="滿200元可用、限最新版本客戶端使用" android:textSize="12dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="5dp" android:text="截止日期:2016-11-07" android:textSize="12dp" /> </LinearLayout> </per.lijuan.coupondisplayviewdome.CouponDisplayView> </LinearLayout>
一定要引入xmlns:custom=”http://schemas.android.com/apk/res-auto”
,Android Studio中我們可以使用res-atuo命名空間,就不用添加自定義View全類名。
3、在View的構(gòu)造方法中,獲得我們的xml布局文件中定義的圓的半徑和圓間距
private Paint mPaint; /** * 半徑 */ private float radius=10; /** * 圓間距 */ private float gap=8; /** * 圓數(shù)量 */ private int circleNum; private float remain; public CouponDisplayView(Context context) { this(context, null); Log.d("mDebug", "CouponDisplayView context"); } public CouponDisplayView(Context context, AttributeSet attrs) { this(context, attrs, 0); Log.d("mDebug", "CouponDisplayView context, attrs"); } 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_radius: radius = a.getDimensionPixelSize(R.styleable.CouponDisplayView_radius, 10); break; case R.styleable.CouponDisplayView_gap: gap = a.getDimensionPixelSize(R.styleable.CouponDisplayView_radius, 8); break; } } a.recycle(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setDither(true); mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.FILL); }
4、重寫onSizeChanged()方法,根據(jù)上面的圓的半徑和圓間距來計(jì)算需要畫的圓數(shù)量circleNum
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); Log.d("mDebug", "onSizeChanged,w=" + w + ",h=" + h + ",oldw=" + oldw + ",oldh=" + oldh); if (remain == 0) { //計(jì)算不整除的剩余部分 remain = (int) (w - gap) % (2 * radius + gap); } circleNum = (int) ((w - gap) / (2 * radius + gap)); }
5、接下來只需要重寫onDraw()方法,簡(jiǎn)單的根據(jù)circleNum的數(shù)量將一個(gè)一個(gè)的圓繪制在屏幕上就可以了
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.d("mDebug", "onDraw"); for (int i = 0; i < circleNum; i++) { float x = gap + radius + remain / 2 + ((gap + radius * 2) * i); canvas.drawCircle(x, 0, radius, mPaint); canvas.drawCircle(x, getHeight(), radius, mPaint); } }
這里remain/2是因?yàn)楸苊庥幸恍┣闆r:當(dāng)計(jì)算出來的圓的數(shù)量不是整除時(shí),這樣就會(huì)出現(xiàn)右邊最后一個(gè)間距會(huì)比其它的間距都要寬,所以我們?cè)诶L制第一個(gè)的時(shí)候加上了余下的間距的一半,即使是不整除的情況,至少也能保證第一個(gè)和最后一個(gè)間距寬度一致。
總結(jié)
以上所述是小編給大家介紹的Android 自定義View之邊緣凹凸的優(yōu)惠券效果的開發(fā)過程,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)億速云網(wǎng)站的支持!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。