1. 程式人生 > >自定義View實踐篇(1)- 自定義單一View

自定義View實踐篇(1)- 自定義單一View

1. 簡介

2.自定義View的分類

自定義View可以分為兩大類,一種是自定義單一View,另一種是自定義ViewGroup。具體如下圖所示:

型別 實現 目的
自定義單一View 繼承系統已有View
如:TextView
擴充套件已有View的功能
繼承View 實現一些不規則效果
自定義ViewGroup 繼承系統已有ViewGroup
如:LinearLayout
擴充套件已有ViewGroup的功能
組合View功能
繼承ViewGroup 實現自定義佈局

本章主要介紹一下自定義單一View

,自定義ViewGroup下一章來說明。

3.自定義單一View

自定義單一View又分為兩類,一類是繼承系統已有View,另一類是直接繼承View類,我們分開來看下。

3.1 繼承系統已有View

這種方式可以去擴充套件系統已有View的功能,比如要顯示一個圓形的ImageView等等,都可以通過這種方式去實現。
我們這裡的例子就簡單點,給一個ImageView加一個水印:

public class WatermarkImageView extends ImageView {//繼承ImageView

    private Paint mPaint;

    public
WatermarkImageView(Context context) { super(context); init(); } public WatermarkImageView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public WatermarkImageView(Context context, AttributeSet attrs, int defStyleAttr) { super
(context, attrs, defStyleAttr); init(); } //畫筆初始化 private void init() { mPaint = new Paint();//建立畫筆 mPaint.setColor(Color.RED);// 設定畫筆顏色 mPaint.setTextSize(100);//建立字型大小 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //畫上水印 canvas.drawText("四月葡萄園部落格", 20, getHeight() - 20, mPaint); } }

在佈局中引用這個WatermarkImageView,引用自定義的View需要包名+View名

    <com.april.view.WatermarkImageView
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/tuseji"/>

執行程式看看效果:
自定義View-WatermarkImageView.png

簡單總結一下:繼承系統已有View去擴充套件功能還是比較簡單的,這種方法一般只需重寫onDraw()即可,同時也無需支援wrap_contentpadding

3.2 繼承View類

繼承View類可以用來實現一些不規則效果,但是需要注意的是,這種方式不僅需要重寫onDraw(),還需要自己去支援wrap_contentpadding,否則wrap_contentpadding將不起效。另外,為了方便使用這個自定義View,我們通常還會提供一些自定義的屬性。
這裡我們以畫一個圓角矩形為例:

public class RoundRectView extends View {//繼承View
    private Paint mPaint;
    private int mColor=Color.RED;

    public RoundRectView(Context context) {
        super(context);
        init();
    }

    public RoundRectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

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

    private void init() {
        mPaint = new Paint();//建立畫筆
        mPaint.setColor(mColor);//設定畫筆顏色
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //獲取View的寬高
        int width = getWidth();
        int height = getHeight();

        //畫圓角矩形
        RectF rectF = new RectF(0, 0, width, height);
        canvas.drawRoundRect(rectF, 50, 50, mPaint);
    }
}

在佈局中引用這個RoundRectView

    <com.april.view.RoundRectView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"/>

需要注意的是,我們這裡使用了wrap_contentpadding,我們先來看看效果:
自定義View-RoundRectView.png

可以看到,wrap_content沒有起到效果,這裡跟match_parent一樣了,至於原因,可以看下這篇文章的分析:自定義View原理篇(1)- measure過程
同樣,padding也沒有生效。
所以,我們需要自己去支援wrap_contentpadding

3.2.1 支援wrap_content

支援wrap_content需重寫onMeasure():

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // 獲取寬的測量模式和大小
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        // 獲取高的測量模式和大小
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        // 設定wrap_content的預設寬高值
        // 預設寬高的設定並無固定依據,根據需要靈活設定
        // 類似TextView,ImageView等針對wrap_content均在onMeasure()對設定預設寬高值有特殊處理,具體細節請自行檢視
        int mWidth = 400;
        int mHeight = 400;

        // 當測量模式是AT_MOST(即wrap_content)時設定預設值
        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, mHeight);

        // 寬或高其中一個模式為AT_MOST(即wrap_content)時,設定為預設值
        } else if (widthMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, heightSize);
        } else if (heightMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSize, mHeight);
        }
    }

再來看看效果:
自定義View-RoundRectView-支援wrap_content.png
wrap_content生效了。

3.2.2 支援padding

支援padding需要在onDraw()方法作相應的修改:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //獲取左右上下的padding值
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        //View的寬高需要減去padding
        int width = getWidth() - paddingLeft - paddingRight;
        int height = getHeight() - paddingTop - paddingBottom;

        //畫圓角矩形
        RectF rectF = new RectF(0 + paddingLeft, 0 + paddingTop, width + paddingRight, height + paddingBottom);
        canvas.drawRoundRect(rectF, 50, 50, mPaint);
    }

再來看看效果:
自定義View-RoundRectView-支援padding.png
padding也生效了。

3.2.3 自定義View屬性

Android系統的控制元件以android開頭的比如android:layout_width等等,這些都是系統自帶的屬性。
為了方便使用自定義View,通常我們都會加上一些自定義屬性,比如:為RoundRectView增加一個設定顏色的屬性。
自定義View屬性可以分為三個步驟:

  1. values目錄下建立自定義屬性的xml檔案
  2. 在自定義View的構造方法中解析自定義屬性的值
  3. 在佈局檔案中使用自定義屬性

下面對每個步驟來進行詳細的講解:

3.2.3.1 在values目錄下建立自定義屬性的xml檔案

values目錄下建立attrs_round_rect_view.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--自定義屬性集合:RoundRectView-->
    <!--在該集合下,可以設定不同的自定義屬性-->
    <declare-styleable name="RoundRectView">
        <!--在attr標籤下設定需要的自定義屬性-->
        <!--此處定義了一個設定圖形的顏色:round_rect_color屬性,格式是color,代表顏色-->
        <!--格式有很多種,如資源id(reference)等等-->
        <attr name="round_rect_color" format="color"/>

    </declare-styleable>
</resources>

3.2.3.2 在自定義View的構造方法中解析自定義屬性的值

我們修改一下RoundRectView的構造方法:

    public RoundRectView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 載入自定義屬性集合RoundRectView
        TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.RoundRectView);
        //解析RoundRectView屬性集合的round_rect_color屬性,如果沒設定預設值為Color.RED
        mColor=typedArray.getColor(R.styleable.RoundRectView_round_rect_color,Color.RED);
        //獲取資源後要及時釋放
        typedArray.recycle();

        init();
    }

3.2.3.3 在佈局檔案中使用自定義屬性

最後,在佈局中用上:

<?xml version="1.0" encoding="utf-8"?>
<!--必須新增schemas宣告才能使用自定義屬性-->
<!--新增的是xmlns:app="http://schemas.android.com/apk/res-auto"-->
<FrameLayout
    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"
    android:background="#fff">

    <com.april.view.RoundRectView
        app:round_rect_color="#00ffff"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"/>

</FrameLayout>

需要注意的是:使用自定義屬性需要新增schemas: xmlns:app=”http://schemas.android.com/apk/res-auto”,其中app是我們自定義的名字,也可以使用其他名字。使用時需要加上這個名字作為額字首,如:app:round_rect_color="#00ffff"
我們再來看看效果:
自定義View-RoundRectView-自定義屬性.png
至此,一個功能比較完善的自定義View就完成了。

3.2.4 完整程式碼

最後貼一下完整的程式碼:
RoundRectView.java :

public class RoundRectView extends View {//繼承View
    private Paint mPaint;
    private int mColor = Color.RED;

    public RoundRectView(Context context) {
        super(context);
        init();
    }

    public RoundRectView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 載入自定義屬性集合RoundRectView
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundRectView);
        //解析RoundRectView屬性集合的round_rect_color屬性,如果沒設定預設值為Color.RED
        mColor = typedArray.getColor(R.styleable.RoundRectView_round_rect_color, Color.RED);
        //獲取資源後要及時釋放
        typedArray.recycle();

        init();
    }

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

    private void init() {
        mPaint = new Paint();//建立畫筆
        mPaint.setColor(mColor);//設定畫筆顏色
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // 獲取寬的測量模式和大小
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        // 獲取高的測量模式和大小
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        // 設定wrap_content的預設寬高值
        // 預設寬高的設定並無固定依據,根據需要靈活設定
        // 類似TextView,ImageView等針對wrap_content均在onMeasure()對設定預設寬高值有特殊處理,具體細節請自行檢視
        int mWidth = 400;
        int mHeight = 400;

        // 當測量模式是AT_MOST(即wrap_content)時設定預設值
        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, mHeight);

            // 寬或高其中一個模式為AT_MOST(即wrap_content)時,設定為預設值
        } else if (widthMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mWidth, heightSize);
        } else if (heightMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSize, mHeight);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //獲取左右上下的padding值
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        //View的寬高需要減去padding
        int width = getWidth() - paddingLeft - paddingRight;
        int height = getHeight() - paddingTop - paddingBottom;

        //畫圓角矩形
        RectF rectF = new RectF(0 + paddingLeft, 0 + paddingTop, width + paddingRight, height + paddingBottom);
        canvas.drawRoundRect(rectF, 50, 50, mPaint);
    }
}

values/attrs_round_rect_view.xml :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--自定義屬性集合:RoundRectView-->
    <!--在該集合下,可以設定不同的自定義屬性-->
    <declare-styleable name="RoundRectView">
        <!--在attr標籤下設定需要的自定義屬性-->
        <!--此處定義了一個設定圖形的顏色:round_rect_color屬性,格式是color,代表顏色-->
        <!--格式有很多種,如資源id(reference)等等-->
        <attr name="round_rect_color" format="color"/>

    </declare-styleable>
</resources>

activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<!--必須新增schemas宣告才能使用自定義屬性-->
<!--新增的是xmlns:app="http://schemas.android.com/apk/res-auto"-->
<FrameLayout
    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"
    android:background="#fff">

    <com.april.view.RoundRectView
        app:round_rect_color="#00ffff"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"/>

</FrameLayout>

相關推薦

定義View實踐篇1- 定義單一View

1. 簡介 2.自定義View的分類 自定義View可以分為兩大類,一種是自定義單一View,另一種是自定義ViewGroup。具體如下圖所示: 型別 實現 目的 自定義單一View 繼承系統已有View 如:Tex

定義View實踐篇2- 定義ViewGroup

1. 簡介 上一章:自定義View實踐篇(1)- 自定義單一View 我們實現了自定義單一View,這章我們來看下自定義ViewGroup。 2. 自定義ViewGroup 自定義ViewGroup同樣分為兩類,一類是繼承系統已有的ViewGroup

微信開發學習總結——定義選單1——定義選單建立介面

一、自定義選單建立介面說明 自定義選單能夠幫助公眾號豐富介面,讓使用者更好更快地理解公眾號的功能。開啟自定義選單後,公眾號介面如圖所示: 請注意: ①自定義選單最多包括3個一級選單,每個一級選單最多包含5個二級選單。 ②一級選單最多4個漢字,二級選單最多7個漢字,多出來的部分將

使用VBA代碼實現簡單定義函數1

分享圖片 excel表格 style 實現 圖片 ima 我們 text inf 自定義函數VBA代碼1 有一份數據如下,要添加季度信息,我們用VBA實現自定義季度函數: 打開VBE編輯器,插入一個函數 代碼如下: 此時返回excel表格出現了我們自定義的函數如下:

定義連結串列1

通過學習自定義連結串列,瞭解連結串列的資料結構。 首先寫一個連結串列類  :LinkedList.java 最後,測試: Test.java、   知識點: 1.連結串列的內部維護了一個節點Node類。該類包括資料域和指標域(指標域指向下一個Node節點)

vue進階1 ---定義元件

vue自定義元件 1、區域性元件,區域性元件必須要手動掛載,不然無法生效 2、全域性元件,全域性元件不需要手動掛載,但是不常用,儘量不要在全域性上掛載變數或者元件(可能會影響瀏覽器效能) 3、配合

微信小程式-定義NavBar元件1

最近公司專案忙得不可開交,各種不可描述的事情,然後學習基本停機。這個週末沒出去浪,就在家把之前王夫人給我設計的小程式做了做,然後一步步分享做的過程中遇到的問題和積累,希望大家爬坑過程中能幫到一些吧 元件化 元件化本身是一個可以講的很大,也可以濃縮為

實驗報告:(1)合理定義一個三角形類Triangle,成員屬性包括3條邊,能否構成三角形的標誌;成員方法包括構造方法、修改3條邊、計算面積。 2寫一測試類,測試自定義三角形類Triangle是否正

(1)合理定義一個三角形類Triangle,成員屬性包括3條邊,能否構成三角形的標誌;成員方法包括構造方法、修改3條邊、計算面積。(2)寫一測試類,測試自定義三角形類Triangle是否正確。class Triangle_D{ private double f_edge;

iOS定義轉場動畫1——定義Push轉場動畫

版本:Xcode 7.0.1 語言:Objective-C 轉場動畫就是viewController之間切換的動畫。 主要有以下三種自定義方法: 列Push & Pop Modal Segue 第一種是UINavigationControl

WPF定義控件定義控件

自己 setvalue prop 一個 自己的 支持 property get element 在實際工作中,WPF提供的控件並不能完全滿足不同的設計需求。這時,需要我們設計自定義控件。 這裏LZ總結一些自己的思路,特性如下: Coupling UITemplate Be

WPF定義控制元件定義控制元件

原文: WPF自定義控制元件(四)の自定義控制元件 在實際工作中,WPF提供的控制元件並不能完全滿足不同的設計需求。這時,需要我們設計自定義控制元件。 這裡LZ總結一些自己的思路,特性如下: Coupling UITemplate Behaviour Function Package

Android 定義數字鍵盤定義輸入框

Android 自定義數字鍵盤(一) Android 自定義數字鍵盤(二)隨機數字 Demo地址:https://github.com/danfengfirst/KeyDemo 這篇部落格是在上面兩篇部落格的基礎上修改的一個相對比較完整的demo,

shiro學習筆記1--基礎定義

一:shiro基礎 (學習地址:https://www.w3cschool.cn/shiro/andc1if0.html) 1、核心概念 Authentication:認證 Authorization:授權 SessionManageMent:session管理 Cryptography:

Android開發雜記1---截圖某個View並儲存到系統圖庫

View view = new View(context) view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bitmap = Bitmap.createBitmap(mExa

Python 函式定義及呼叫1

1、函式的功能: (1)程式碼的一種組織形式; (2)一個函式一般完成一項特定的功能。 2、函式使用 (1)函式需要先定義 (2)使用函式,俗稱呼叫 3、函式定義的一般規則 (1)def 關鍵字,後跟一個空格; (2)函式名,自己定義,起名需要遵循

自己定義控件自己定義Dialog

ble top cancel type lis icon ner javascrip findview 本節要實現:自己定義一個Dialog 結果例如以下: 步 驟 1.配置register_dialog.xml: 以下是一個自己定義的dia

定義View1

點操作:moveTo和lineTo和rLinneTo的理解 1、lineTo 用於進行直線繪製。起點預設為座標原點(左上),如果有path的存在,則是繪製的最後點為基準,座標點對應的(0,0)到lineTo(x,y)的偏移量 比如 /** * 線操作 * lineTo的偏移量相對於原

Android進階之定義View1實現可換行的TextView

         今天來一起學習一下最簡單的自定義view,自己動手寫一個MyTextView,當然不會像系統的TextView那麼複雜,只是實現一下TextView的簡單功能,包括分行顯示及自定義屬性的處理,主要目的是介紹自定義view的實現的基本思路和需要掌握的一些基礎知

最易懂的定義View原理系列1

轉自這裡 前言 自定義View原理是Android開發者必須瞭解的基礎; 在瞭解自定義View之前,你需要有一定的知識儲備; 本文將全面解析關於自定義View中的所有知識基礎。 1. View的分類 檢視View主要分為兩類: 類

android-進階3-定義view(1)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"     xmlns:custom="http://sch