本篇文章講的是自定義View之邊緣凹凸的優惠券效果,之前有見過很多優惠券的效果都是使用了邊緣凹凸的樣式。和往常一樣,主要總結一下在自定義View的開發過程中需要注意的一些地方。
按照慣例,我們先來看看效果圖

一、寫代碼之前,我們先弄清楚view的啟動過程:
之所以想要弄清楚這個問題是因為代碼里面用到了onSizeChanged()方法,一開始我有點猶豫onSizeChanged是在什么時候啟動的呢,所以看看View的啟動流程吧
package per.lijuan.coupondisplayviewdome;import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.util.Log;import android.widget.LinearLayout;/** * 自定義邊緣凹凸的優惠券效果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,defStyleAttr09-27 15:29:31.957 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: CouponDisplayView:context,attrs09-27 15:29:32.050 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: onSizeChanged:w=984,h=361,oldw=0,oldh=009-27 15:29:32.083 8210-8210/per.lijuan.coupondisplayviewdome D/mDebug: onDraw
在這里可以看到,onSizeChanged()方法的啟動是在onDraw之前
二、view的幾個常用觸發方法
1. onFinishInflate():當View中所有的子控件均被映射成xml后觸發
2. onMeasure(int widthMeasureSpec, int heightMeasureSpec):確定所有子元素的大小
3. onLayout(boolean changed, int l, int t, int r, int b):當View分配所有的子元素的大小和位置時觸發
4. onSizeChanged(int w, int h, int oldw, int oldh):當view的大小發生變化時觸發
5. onDraw(Canvas canvas):負責將View繪制在屏幕上
三、View 的幾個構造函數
1、public CouponDisplayView(Context context)
―>Java代碼直接new一個CouponDisplayView實例的時候,會調用這個只有一個參數的構造函數;
2、public CouponDisplayView(Context context, AttributeSet attrs)
―>在默認的XML布局文件中創建的時候調用這個有兩個參數的構造函數。AttributeSet類型的參數負責把XML布局文件中所自定義的屬性通過AttributeSet帶入到View內;
3、public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr)
―>構造函數中第三個參數是默認的Style,這里的默認的Style是指它在當前Application或者Activity所用的Theme中的默認Style,且只有在明確調用的時候才會調用
4、public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
―>該構造函數是在API21的時候才添加上的
自定義View中,我們需要重寫了3個構造方法,在上面的構造方法中說過默認的布局文件調用的是兩個參數的構造方法,所以記得讓所有的構造方法調用三個參數的構造方法,然后在三個參數的構造方法中獲得自定義屬性。
一開始一個參數的構造方法和兩個參數的構造方法是這樣的:
public CouponDisplayView(Context context) { super(context); } public CouponDisplayView(Context context, AttributeSet attrs) { super(context, attrs); }我們需要注意的是super應該改成this,然后讓一個參數的構造方法引用兩個參數的構造方法,兩個參數的構造方法引用三個參數的構造方法,代碼如下:
public CouponDisplayView(Context context) { this(context, null); } public CouponDisplayView(Context context, AttributeSet attrs) { this(context, attrs, 0); }四、分析具體的實現思路:
從上面的效果圖來看,這個自定義View和普通的Linearlayout,RelativeLayout一樣,只是上下兩邊多了類似于半圓鋸齒的形狀,我們需要在上下兩條線上畫一個個白色的小圓來實現這種效果。
假如我們上下線的半圓以及半圓與半圓之間的間距是固定的,那么不同尺寸的屏幕肯定會畫出不同數量的半圓,那么我們只需要根據控件的寬度來獲取能畫的半圓數。
我們觀察效果圖會發現,圓的數量總是圓間距數量-1,也就是說,假設圓的數量是circleNum,那么圓間距就是circleNum+1,所以我們可以根據這個計算出circleNum:
circleNum = (int) ((w-gap)/(2*radius+gap));
這里gap就是圓間距,radius是圓半徑,w是view的寬。
五、下面我們就開始來看看代碼啦
1、自定義View的屬性,首先在res/values/ 下建立一個attr.xml , 在里面定義我們的需要用到的屬性以及聲明相對應屬性的取值類型
<?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個屬性,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="電影新客代金 主站蜘蛛池模板: 临洮县| 昌江| 兰州市| 旬阳县| 遂川县| 孟津县| 安顺市| 泰宁县| 徐闻县| 黄浦区| 黄梅县| 噶尔县| 广西| 大宁县| 宁城县| 新沂市| 正镶白旗| 通化市| 东平县| 武清区| 黄浦区| 五家渠市| 太和县| 洪雅县| 饶河县| 时尚| 无锡市| 海城市| 柘荣县| 许昌县| 福泉市| 顺昌县| 彰武县| 宁德市| 嘉定区| 都昌县| 大姚县| 南溪县| 会泽县| 巴楚县| 定结县|