安卓自定義View進階-分類與流程

自定義View繪制流程函數調用鏈(簡化版)

自定義View繪制流程函數調用鏈

一.自定義View分類

我將自定義View分為了兩類(sloop個人分類法,非官方):

1.自定義ViewGroup

自定義ViewGroup一般是利用現有的組件根據特定的布局方式來組成新的組件,大多繼承自ViewGroup或各種Layout,包含有子View。

例如:應用底部導航條中的條目,一般都是上面圖標(ImageView),下面文字(TextView),那么這兩個就可以用自定義ViewGroup組合成為一個Veiw,提供兩個屬性分別用來設置文字和圖片,使用起來會更加方便。

2.自定義View

在沒有現成的View,需要自己實現的時候,就使用自定義View,一般繼承自View,SurfaceView或其他的View,不包含子View。

例如:制作一個支持自動加載網絡圖片的ImageView,制作圖表等。

PS: 自定義View在大多數情況下都有替代方案,利用圖片或者組合動畫來實現,但是使用后者可能會面臨內存耗費過大,制作麻煩更諸多問題。


二.幾個重要的函數

1.構造函數

構造函數是View的入口,可以用于初始化一些的內容,和獲取自定義屬性

View的構造函數有四種重載分別如下:

public void SloopView(Context context) {}
public void SloopView(Context context, AttributeSet attrs) {}
public void SloopView(Context context, AttributeSet attrs, int defStyleAttr) {}
public void SloopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {}

可以看出,關于View構造函數的參數有多有少,先排除幾個不常用的,留下常用的再研究。

有四個參數的構造函數在API21的時候才添加上,暫不考慮。

有三個參數的構造函數中第三個參數是默認的Style,這里的默認的Style是指它在當前Application或Activity所用的Theme中的默認Style,且只有在明確調用的時候才會生效,以系統中的ImageButton為例說明:

public ImageButton(Context context, AttributeSet attrs) {//調用了三個參數的構造函數,明確指定第三個參數this(context, attrs, com.android.internal.R.attr.imageButtonStyle);
}public ImageButton(Context context, AttributeSet attrs, int defStyleAttr) {//此處調了四個參數的構造函數,無視即可this(context, attrs, defStyleAttr, 0); 
}

注意:即使你在View中使用了Style這個屬性也不會調用三個參數的構造函數,所調用的依舊是兩個參數的構造函數。

由于三個參數的構造函數第三個參數一般不用,暫不考慮,第三個參數的具體用法會在以后用到的時候詳細介紹。

排除了兩個之后,只剩下一個參數和兩個參數的構造函數,他們的詳情如下:

//一般在直接New一個View的時候調用。
public void SloopView(Context context) {}//一般在layout文件中使用的時候會調用,關于它的所有屬性(包括自定義屬性)都會包含在attrs中傳遞進來。
public void SloopView(Context context, AttributeSet attrs) {}

以下方法調用的是一個參數的構造函數:

//在Avtivity中
SloopView view  new SloopView(this);

以下方法調用的是兩個參數的構造函數:

//在layout文件中 - 格式為: 包名.View名
<com.sloop.study.SloopViewandroid:layout_width"wrap_content"android:layout_height"wrap_content"/>

關于構造函數先講這么多,關于如何自定義屬性和使用attrs中的內容,在后面會詳細講解,目前只需要知道這兩個構造函數在何時調用即可。

2.測量View大小(onMeasure)

Q: 為什么要測量View大小?

A: View的大小不僅由自身所決定,同時也會受到父控件的影響,為了我們的控件能更好的適應各種情況,一般會自己進行測量。

測量View大小使用的是onMeasure函數,我們可以從onMeasure的兩個參數中取出寬高的相關數據:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthsize  MeasureSpec.getSize(widthMeasureSpec);      //取出寬度的確切數值int widthmode  MeasureSpec.getMode(widthMeasureSpec);      //取出寬度的測量模式int heightsize  MeasureSpec.getSize(heightMeasureSpec);    //取出高度的確切數值int heightmode  MeasureSpec.getMode(heightMeasureSpec);    //取出高度的測量模式
}

從上面可以看出 onMeasure 函數中有 widthMeasureSpec 和 heightMeasureSpec 這兩個 int 類型的參數, 毫無疑問他們是和寬高相關的, 但它們其實不是寬和高, 而是由寬、高和各自方向上對應的測量模式來合成的一個值:

測量模式一共有三種, 被定義在 Android 中的 View 類的一個內部類View.MeasureSpec中:

模式二進制數值描述
UNSPECIFIED00默認值,父控件沒有給子view任何限制,子View可以設置為任意大小。
EXACTLY01表示父控件已經確切的指定了子View的大小。
AT_MOST10表示子View具體大小沒有尺寸限制,但是存在上限,上限一般為父View大小。

在int類型的32位二進制位中,31-30這兩位表示測量模式,29~0這三十位表示寬和高的實際值,實際上如下:

以數值1080(二進制為: 1111011000)為例(其中模式和實際數值是連在一起的,為了展示我將他們分開了):

模式名稱模式數值實際數值
UNSPECIFIED00000000000000000000001111011000
EXACTLY01000000000000000000001111011000
AT_MOST10000000000000000000001111011000

PS: 實際上關于上面的東西了解即可,在實際運用之中只需要記住有三種模式,用 MeasureSpec 的 getSize是獲取數值, getMode是獲取模式即可。

注意:

如果對View的寬高進行修改了,不要調用 super.onMeasure( widthMeasureSpec, heightMeasureSpec); 要調用 setMeasuredDimension( widthsize, heightsize); 這個函數。

3.確定View大小(onSizeChanged)

這個函數在視圖大小發生改變時調用。

Q: 在測量完View并使用setMeasuredDimension函數之后View的大小基本上已經確定了,那么為什么還要再次確定View的大小呢?

A: 這是因為View的大小不僅由View本身控制,而且受父控件的影響,所以我們在確定View大小的時候最好使用系統提供的onSizeChanged回調函數。

onSizeChanged如下:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);
}

可以看出,它又四個參數,分別為 寬度,高度,上一次寬度,上一次高度。

這個函數比較簡單,我們只需關注 寬度(w), 高度(h) 即可,這兩個參數就是View最終的大小。

4.確定子View布局位置(onLayout)

確定布局的函數是onLayout,它用于確定子View的位置,在自定義ViewGroup中會用到,他調用的是子View的layout函數。

在自定義ViewGroup中,onLayout一般是循環取出子View,然后經過計算得出各個子View位置的坐標值,然后用以下函數設置子View位置。

child.layout(l, t, r, b);

四個參數分別為:

名稱說明對應的函數
lView左側距父View左側的距離getLeft();
tView頂部距父View頂部的距離getTop();
rView右側距父View左側的距離getRight();
bView底部距父View頂部的距離getBottom();

具體可以參考 坐標系 這篇文章。

View坐標系

PS:關于onLayout這個函數在講解自定義ViewGroup的時候會詳細講解。

5.繪制內容(onDraw)

onDraw是實際繪制的部分,也就是我們真正關心的部分,使用的是Canvas繪圖。

@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);
}

關于Canvas繪圖是本章節的重點,會分幾篇文章進行詳細講解,敬請期待OwO。

6.對外提供操作方法和監聽回調

自定義完View之后,一般會對外暴露一些接口,用于控制View的狀態等,或者監聽View的變化.

本內容會在后續文章中以實例的方式進講解。


三.重點知識梳理

自定義View分類

PS :實際上ViewGroup是View的一個子類。

類別繼承自特點
ViewView SurfaceView 等不含子View
ViewGroupViewGroup xxLayout等包含子View

自定義View流程:

步驟關鍵字作用
1構造函數View初始化
2onMeasure測量View大小
3onSizeChanged確定View大小
4onLayout確定子View布局(自定義View包含子View時有用)
5onDraw實際繪制內容
6提供接口控制View或監聽View某些狀態。

About

本系列相關文章

作者微博: GcsSloop

參考資料:

View
ViewGroup
View.MeasureSpec
onMeasure,MeasureSpec源碼 流程 思路詳解
Android中自定義樣式與View的構造函數中的第三個參數defStyle的意義
android view構造函數研究
Android View構造方法第三參數使用方法詳解
Android 自定義View onMeasure方法的實現
Android API指南(二)自定義控件02之 onMeasure
Android中View的繪制過程 onMeasure方法簡述
Android自定義View基礎-坐標系

轉載于:https://www.cnblogs.com/youseiraws/p/8633075.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452245.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452245.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452245.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【筆試記錄】2021/3/13美團

2021/3/13美團筆試 1.旋轉二維數組 m, n map(int, input().split()) a [] for _ in range(m):a.append(list(map(int, input().split()))) for j in range(n):for k in range(m):print(a[k][j], end )print(end\n)輸入輸出 2.找出輸入字符串中的所有數字 s input() n …

Ubuntu品牌機批量涌入世界市場,中國不會例外

根據種種信息來源&#xff0c;我們可以預見&#xff1a; Ubuntu 品牌機不久即將涌入世界市場&#xff0c;中國也不會例外。對此現象&#xff0c;我們必須有一定的思想準備。 2004年4月&#xff0c;在澳大利亞一次學術會議上萌發了Ubuntu發行版的念頭&#xff0c;至今已經有8個年…

學成在線--9.頁面靜態化

文章目錄一.頁面靜態化流程二.數據模型1.輪播圖DataUrl接口1&#xff09;需求分析2&#xff09;接口定義3&#xff09;Dao4&#xff09;Service5&#xff09;Controller6&#xff09;測試2.遠程請求接口1&#xff09;添加依賴2&#xff09;配置RestTemplate3&#xff09;測試Re…

數據庫的完整性和安全性

數據的完整性防止數據庫中存在不符合語義的數據&#xff0c;也就是防止數據庫中存在不正確的數據防范對象&#xff1a;不合語義的、不正確的數據數據的安全性保護數據庫 防止惡意的破壞和非法的存取防范對象&#xff1a;非法用戶和非法操作

數據庫MySQL/mariadb知識點——日志記錄(2)二進制日志

二進制日志 記錄已提交事務導致數據改變或潛在導致數據改變的SQL語句&#xff0c;通過“重放”日志文件中的事件來生成數據副本&#xff0c;不依賴存儲引擎類型。 開啟二進制日志&#xff0c;默認是關閉的&#xff0c;二進制日志和數據分開存放 開啟記錄二進制文件的功能&#…

【面試記錄】Python常見面試200題及答案總結

Python常見面試200題及答案總結 /待完善/ 1. 列出5個常用python標準庫&#xff1f; os&#xff1a;提供了不少與操作系統相關聯的函數&#xff0c;提供了一種可移植的使用操作系統功能的方法。使用os模塊中提供的接口&#xff0c;可實現跨平臺訪問。但是&#xff0c;并不是所…

Linux負載均衡軟件LVS之一(概念篇)

2019獨角獸企業重金招聘Python工程師標準>>> 一、 LVS簡介 LVS是Linux Virtual Server的簡稱&#xff0c;也就是Linux虛擬服務器, 是一個由章文嵩博士發起的自由軟件項目&#xff0c;它的官方站點是www.linuxvirtualserver.org。現在LVS已經是 Linux標準內核的一部分…

C語言目錄操作 (Linux/Unix)

獲得工作目錄&#xff1a;#include <unistd.h> char *getcwd(char *buf,size_t size);char *getwd(char *buf);/*this is for FreeBSD*/ 改變當前目錄&#xff1a;#include <unistd.h> int chdir(const char *path); 保存當前目錄&#xff1a;#include <unist…

學成在線--10.頁面預覽

文章目錄一.需求分析二. 搭建環境1.在cms服務中集成freemarker&#xff1a;2.在application.yml配置freemarker三.Service四.Controller五.頁面預覽測試-- 配置Nginx代理六.頁面預覽測試-- 添加“頁面預覽”鏈接一.需求分析 頁面在發布前增加頁面預覽的步驟&#xff0c;方便用…

參照完整性違約處理

參照完整性違約處理 &#xff08;1&#xff09; 拒絕&#xff08;NO ACTION&#xff09;執行 不允許該操作執行。該策略一般設置為默認策略 &#xff08;2&#xff09; 級聯&#xff08;CASCADE&#xff09;操作 當刪除或修改被參照表&#xff08;Student&#xff09;的一個元…

BSON和JSON的區別

BSON目前主要用于MongoDB中&#xff0c;是mongoDB的數據存儲格式&#xff0c;BSON基于JSON格式&#xff0c;選擇JSON進行改造的原因主要是JSON的通用性及JSON的schemaless的特性。 BSON主要實現下面三點&#xff1a; 1、更快的遍歷速度 對json格式來說&#xff0c;太大的json結…

【代碼刷題】排序算法總結(python實現)

排序算法總結&#xff08;Python實現&#xff09;算法介紹算法分類相關概念1. 冒泡排序&#xff08;Bubble Sort&#xff09;1.1 思想1.2 python實現1.3 復雜度1.4 穩定性2. 快速排序&#xff08;Quick Sort&#xff09;2.1 思想&#xff08;偽代碼&#xff09;2.2 python實現2…

C語言遍歷目錄

C語言遍歷目錄&#xff0c;可以循環的遍歷子目錄#include <stdio.h>#include <string.h>#include <stdlib.h>#include <dirent.h>#include <sys/stat.h>#include <unistd.h>#include <sys/types.h>void listDir(char *path){struct …

學成在線--11.RabbitMQ快速入門

文章目錄一.RabbitMQ簡介二.相關知識1.AMQP2.JMS是什么 &#xff1f;三.RabbitMQ的工作原理四.Hello World1.創建Maven工程2.生產者3.消費者五.總結一.RabbitMQ簡介 MQ全稱為Message Queue&#xff0c;即消息隊列&#xff0c; RabbitMQ是由erlang語言開發&#xff0c;基于AMQP…

數據庫斷言

SQL中&#xff0c;可以使用 CREATE ASSERTION語句&#xff0c;通過聲明性斷言來指定更具一般性的約束。 可以定義涉及多個表的或聚集操作的比較復雜的完整性約束。 斷言創建以后&#xff0c;任何對斷言中所涉及的關系的操作都會觸發關系數據庫管理系統對斷言的檢查&#xff0c;…

mysql帳號不允許從遠程登陸

默認情況下&#xff0c;mysql帳號不允許從遠程登陸&#xff0c;只能在localhost登錄。本文提供了二種方法設置mysql可以通過遠程主機進行連接。 一、改表法 在localhost登入mysql后&#xff0c;更改 “mysql” 數據庫里的 “user” 表里的 “host” 項&#xff0c;將”localhos…

maven工程建立和SSM(springMVC+spring+mybatis)整合

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1.環境&#xff1a; maven 版本&#xff1a;3.5.1 ecelipse mars.2 JDK : jdk1.8.0_45 tomcat : apache-tomcat-8.0.0-RC1 2. 建…

Java——網絡編程(實現基于命令行的多人聊天室)

2019獨角獸企業重金招聘Python工程師標準>>> 目錄&#xff1a; 1.ISO和TCP/IP分層模型 2.IP協議 3.TCP/UDP協議 4.基于TCP的網絡編程 5.基于UDP的網絡編程 6.基于TCP的多線程的聊天室的實現 1.ISO和TCP/IP分層模型&#xff1a; OSI分層模型&#xff08;Open System …

學成在線--12.Spring整合RibbitMQ

文章目錄一.搭建SpringBoot環境二.配置1.配置application.yml2.定義RabbitConfig類三.生產端四.消費端一.搭建SpringBoot環境 我們選擇基于Spring-Rabbit去操作RabbitMQ 使用spring-boot-starter-amqp會自動添加spring-rabbit依賴&#xff0c;如下&#xff1a; <dependenc…

一網打盡中文編碼轉換---6種編碼30個方向的轉換

一網打盡中文編碼轉換——6種編碼30個方向的轉換 1.問題提出 在學編程序時&#xff0c;曾經有人問過“你可以編一個記事本程序嗎?”當時很不屑一顧&#xff0c;但是隨著學習MFC的深入&#xff0c;了解到記事本程序也并非易事&#xff0c;難點就是四種編碼之間的轉換。 對于編…