學習一門技術或者看一篇文章最好的方式就是帶着問題去學習,這樣才能在過程當中有茅塞頓開、燈火闌珊的感受,記憶也會更深入。java
何時會用到自定義 View?在咱們的平常開發中,可能會遇到一些界面、控件沒法用 Android 系統內置的 View 來完成的,這時候就須要咱們使用自定義 View 來進行繪製了。android
自定義 View 這東西不少人會比較畏懼,若是你認爲他比較難,關鍵仍是缺乏實踐寫得少;若是你認爲很簡單,那多是你沒有遇到過那些奇葩的效果,須要高等數學和各類算法。git
如今咱們就一塊兒敲開自定義 View 的大門,揭開它的神祕面紗,也許你就會發現,其實它並不可怕。github
本文主要作入門級別講解,當然不會太複雜,自定義 View 的時候,主要重寫兩個方法算法
onMeasure():用於測量,你的控件佔多大的地方由這個方法指定;canvas
onMeasure( )方法中有兩個參數,widthMeasureSpec 和 heightMeasureSpec,能夠經過以下代碼獲取模式和大小bash
//獲取高度模式
int height_mode = MeasureSpec.getMode(heightMeasureSpec);
//獲取寬度模式
int with_mode = MeasureSpec.getMode(widthMeasureSpec);
//獲取高度尺寸
int height_size = MeasureSpec.getSize(heightMeasureSpec);
//獲取寬度尺寸
int width_size = MeasureSpec.getSize(widthMeasureSpec);
複製代碼
測量模式的話,有下面三種app
UNSPECIFIED:任意大小,想要多大就多大,儘量大,通常咱們不會遇到,如 ListView,RecyclerView,ScrollView 測量子 View 的時候給的就是 UNSPECIFIED ,通常開發中不須要關注它;ide
EXACTLY:一個肯定的值,好比在佈局中你是這樣寫的 layout_width="100dp","match_parent","fill_parent";函數
AT_MOST:包裹內容,好比在佈局中你是這樣寫的 layout_width="wrap_content"。
onDarw():用於繪製,你的控件呈現給用戶長什麼樣子由這個方法決定;
onDarw( ) 方法中有個參數 Canvas,Canvas 就是咱們要在上面繪製的畫布,咱們可使用咱們的畫筆在上面進行繪製,最後呈現給用戶。
接下來咱們就動手實現一個簡單的自定義 View,重在講解實現自定義 View 的過程。
public class ViewProperty extends View {
/**
*在java代碼建立視圖的時候被調用,
*若是是從xml填充的視圖,就不會調用這個
*/
public ViewProperty(Context context) {
super(context);
}
/**
* 這個是在xml建立可是沒有指定style的時候被調用
*/
public ViewProperty(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
複製代碼
<!-- 這裏自定義了兩個屬性,一個是默認大小,一個是背景顏色-->
<declare-styleable name="ViewProperty">
<attr name="default_size" format="dimension"/>
<attr name="backgroundColor" format="color"/>
</declare-styleable>
複製代碼
public ViewProperty(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//經過 TypedArray 獲取自定義屬性,使用完後記得及時回收
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ViewProperty);
mSize = array.getDimensionPixelSize(R.styleable.ViewProperty_default_size, 10);
mColor = array.getColor(R.styleable.ViewProperty_backgroundColor, Color.RED);
array.recycle();
//初始化畫筆
mPaint = new Paint();
mPaint.setColor(mColor);
}
複製代碼
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = 100;
int width = 100;
//獲取寬高的測量模式及具體尺寸
int height_mode = MeasureSpec.getMode(heightMeasureSpec);
int with_mode = MeasureSpec.getMode(widthMeasureSpec);
int height_size = MeasureSpec.getSize(heightMeasureSpec);
int width_size = MeasureSpec.getSize(widthMeasureSpec);
//根據具體測量模式來給定不一樣的尺寸
if (height_mode == MeasureSpec.AT_MOST) {
height = Dp2Px(getContext(),100);
} else if (height_mode == MeasureSpec.EXACTLY) {
height = height_size;
}
if (with_mode == MeasureSpec.AT_MOST) {
width = Dp2Px(getContext(),100);
} else if (with_mode == MeasureSpec.EXACTLY) {
width = width_size;
}
//調用setMeasuredDimension()方法,傳入測量後的尺寸值
setMeasuredDimension(width, height);
}
複製代碼
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//這裏繪製一個矩形
//傳入左、上、右、下座標及畫筆
canvas.drawRect(0,0,getWidth(),getHeight(),mPaint);
}
/**
*下面是兩個工具類,用於 dp 和 px 的轉換,
* 因爲代碼中使用的是 px,佈局中尺寸通常使用 dp,
* 因此須要作個轉換
*/
public static int Px2Dp(Context context, int px) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, px, context.getResources().getDisplayMetrics());
}
public static int Dp2Px(Context context, int dpi) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpi, context.getResources().getDisplayMetrics());
}
複製代碼
先 Rebuild 的一下項目,讓編譯器識別自定義 View,而後在佈局中引用
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keven.jianshu.part3.ShowActivity">
<com.keven.jianshu.part3.ViewProperty
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundColor="@color/colorAccent"/>
</android.support.constraint.ConstraintLayout>
複製代碼
顯示效果
所有代碼見 GitHub 地址
github.com/keven0632/J…
文章已經讀到末尾了,不知道最初的幾個問題你都會了嗎?若是不會的話?能夠再針對不會的問題進行精讀哦!答案都在文中,相信你確定能夠解決的!