Android視圖控件架構(gòu)分析之View、ViewGroup
在Android中,視圖控件大致被分為兩類(lèi),即ViewGroup和View,ViewGroup控件作為父控件,包含并管理著子View,通過(guò)ViewGroup和View便形成了控件樹(shù),各個(gè)ViewGoup對(duì)象和View對(duì)象就是控件樹(shù)中的節(jié)點(diǎn)。在控件樹(shù)中,以樹(shù)的深度來(lái)遍歷查找對(duì)應(yīng)的控件元素,同時(shí),上層控件負(fù)責(zé)子控件的測(cè)量與繪制,并傳遞交互事件。
Android控件樹(shù):
AndroidUI界面架構(gòu)圖:
一.測(cè)量View的工具類(lèi):MeasureSpec
1.MeasureSpec包含了測(cè)量的模式和測(cè)量的大小,通過(guò)MeasureSpec.getMode()獲取測(cè)量模式,通過(guò)MeasureSpec.getSize()獲取測(cè)量大小;
2.MeasureSpec是一個(gè)32位的int值,高2位為測(cè)量的模式,低30位為測(cè)量的大小,使用位運(yùn)算的目的在于提高優(yōu)化效率。
二.測(cè)量的模式
1.EXACTLY,精確值模式:將layout_width或layout_height屬性指定為具體數(shù)值或者match_parent。
2.AT_MOST,最大值模式:將layout_width或layout_height指定為wrap_content。
3.UNSPECIFIED: View想多大就多大
三.View類(lèi)默認(rèn)的onMeasure()方法只支持EXACTLY模式,如果要支持其它模式,就必須重寫(xiě)onMeasure(),重寫(xiě)onMeasure()的模板代碼:
package com.example.demoapp.views; import android.content.Context; import android.view.View; public class MeasuredView extends View { public MeasuredView(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 調(diào)用父類(lèi)的onMeasure() super.onMeasure(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); // 或者直接調(diào)用父類(lèi)的setMeasuredDimension(),因?yàn)楦割?lèi)的onMeasure()最終調(diào)用了setMeasuredDimension() // setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } /** * 測(cè)量View的width * @param measureSpec MeasureSpec對(duì)象 * @return View的width */ private int measureWidth(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = 200; if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } /** * 測(cè)量View的height * @param measureSpec MeasureSpec對(duì)象 * @return View的height */ private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = 200; if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } }
四.View的繪制
1.2D繪圖必備利器——Canvas
1)獲取Canvas對(duì)象的方式:
a.由方法中的參數(shù)傳入,例如,View的onDraw()中有一個(gè)參數(shù)就是Canvas對(duì)象
b.通過(guò)構(gòu)造方法構(gòu)造,即:Canvas canvas = new Canvas(bitmap),在Canvas的構(gòu)造方法傳入一個(gè)Bitmap對(duì)象,即可獲取一個(gè)Canvas對(duì)象。通過(guò)傳入Bitmap對(duì)象構(gòu)造Canvas對(duì)象的過(guò)程稱為“畫(huà)布的裝載”,傳入的Bitmap對(duì)象承載了多有繪制在Canvas上的像素信息,調(diào)用Canvas.drawXXX方法(如:Canvas.drawBitmap(bitmap, 0, 0, null))都將發(fā)生在該Bitmap對(duì)象上。
2)利用Canvas繪圖
a.通過(guò)Canvas.drwaXXX進(jìn)行繪制操作將直接作用于Bitmap對(duì)象,當(dāng)再次刷新View的時(shí)候,我們將會(huì)被繪制的Bitmap對(duì)象發(fā)生了改變;
b.利用Canvas和Paint進(jìn)行繪圖;
c.不管多么復(fù)雜、精美的空間,都可以被拆分為一個(gè)個(gè)小的圖形單元,我們只要找到這些圖形單元,就可以將控件繪制出來(lái)。
五.ViewGroup的測(cè)量
1.ViewGroup的作用:管理子View,如子View的大小、位置;
2.ViewGroup通過(guò)遍歷子View,調(diào)用子View的Measure()來(lái)獲得每一個(gè)子View的測(cè)量結(jié)果;
3.ViewGroup測(cè)量完子View,調(diào)用子View的Layout()將子View放到合適的位置;
4.在自定義ViewGroup的時(shí)候,通常會(huì)重寫(xiě)onLayout()控制子View的顯示;
5.如果需要支持wrap_content屬性,必須重寫(xiě)onMeasure()。
六、ViewGroup的繪制
通常情況下,ViewGoup不需要繪制,但是ViewGroup會(huì)使用dispatchDraw()來(lái)繪制其子View。
七.自定義View
1.自定義View的時(shí)候,通常需要重寫(xiě)onDraw()來(lái)繪制View要顯示的內(nèi)容,如果還需要支持wrap_content屬性,必須重寫(xiě)onMeasure();
2.通過(guò)自定義attrs屬性,可以設(shè)置新的View屬性;
3.View中一些重要的回調(diào)方法:
1)onFinishInflate():從XML中加載組建后回調(diào);
2)onSizeChanged():組件大小改變時(shí)回調(diào);
3)onMeasure():進(jìn)行測(cè)量;
4)onLayout():設(shè)置顯示的位置;
5)onTouchEvent():觸摸事件。
4.實(shí)現(xiàn)自定義View的三種常用方法:
1)通過(guò)重寫(xiě)onDraw()對(duì)原生控件進(jìn)行擴(kuò)展;
2)通過(guò)組合實(shí)現(xiàn)新的控件,通常集成一個(gè)合適的額ViewGoup,再通過(guò)addView()給它添加指定功能的控件,從而組合成新的復(fù)合控件。
3)重寫(xiě)View實(shí)現(xiàn)全新的控件,通過(guò)重寫(xiě)onDraw(),onMeasure()實(shí)現(xiàn)繪制邏輯,重寫(xiě)onTouchEvent()實(shí)現(xiàn)交互邏輯。
5.自定義屬性
1)自定義屬性的方法:在res資源目錄的values目錄下創(chuàng)建一個(gè)attrs.xml的屬性定義文件,文件模板:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="customAttr"> <attr name="title" format="string" /> <attr name="fontSize" format="dimension" /> <attr name="fontColor" format="color" /> <attr name="background" format="reference|color" /> <attr name="fontStyle" format="enum" /> <attr name="shadeSupport" format="boolean" /> </declare-styleable> </resources>
2)通過(guò)TypedArray獲取自定義屬性集,通過(guò)TypedArray.getString()、TypedArray.getColor()等方法獲取屬性值,模板代碼:
package com.jy.myrecyclerview.test; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import com.jy.myrecyclerview.R; /** * Created by 123 on 2016/5/6. */ public class TestCustomAttrs extends View { private Context mContext; private AttributeSet mAttrs; private String mTitle; private float mFontSize; private int mFontColor; private int mBackground; private int mFontStyle; private boolean mShadeSupport; public TestCustomAttrs(Context context) { super(context); this.mContext = context; } public TestCustomAttrs(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; this.mAttrs = attrs; } public TestCustomAttrs(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; this.mAttrs = attrs; } private void getCustomAttrs() { TypedArray ta = mContext.obtainStyledAttributes(mAttrs, R.styleable.customAttr); mTitle = ta.getString(R.styleable.customAttr_title); mFontSize = ta.getDimension(R.styleable.customAttr_fontSize, 10); mFontColor = ta.getColor(R.styleable.customAttr_fontColor, 0); mBackground = ta.getColor(R.styleable.customAttr_background, 0); mFontStyle = ta.getInt(R.styleable.customAttr_fontStyle, 0); mShadeSupport = ta.getBoolean(R.styleable.customAttr_shadeSupport, false); ta.recycle(); } }
6.定義回調(diào)接口,實(shí)現(xiàn)自定義控件的靈活控制;
7.引用UI模板
1)自定義控件需要使用命名空間進(jìn)行引入:xmlns:custom="http://schemas.android.com/apk/res-auto",即將自定義控件的命名空間取名為custom
2)在XML文件中使用自定義屬性的時(shí)候,就可以通過(guò)這個(gè)命名空間來(lái)引用,代碼模板如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" > <com.jy.myrecyclerview.test.TestCustomAttrs android:id="@+id/id_recyclerview" android:divider="#ffff0000" android:dividerHeight="10dp" android:layout_width="match_parent" android:layout_height="match_parent" custom:title="title" custom:fontSize="12sp" custom:fontColor="@color/colorPrimary" custom:background="@color/colorPrimary" custom:shadeSupport="false" /> </RelativeLayout>
九.自定義ViewGroup
1.需要重寫(xiě)的方法:
1)onMeasure():對(duì)子View進(jìn)行測(cè)量;
2)onLayout():設(shè)置子View的位置;
3)onTouchEvent():設(shè)置觸摸交互事件。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
- Android 架構(gòu)之?dāng)?shù)據(jù)庫(kù)框架搭建
- Android Jetpack架構(gòu)組件Lifecycle詳解
- Android Jetpack架構(gòu)組件 ViewModel詳解
- Android登錄代碼MVP架構(gòu)詳解
- Android應(yīng)用架構(gòu)思想分析
- Android 生命周期架構(gòu)組件使用方法
- Android架構(gòu)組件Room的使用詳解
- 詳解Android中的MVP架構(gòu)分解和實(shí)現(xiàn)
- 淺析Android系統(tǒng)的架構(gòu)以及程序項(xiàng)目的目錄結(jié)構(gòu)
- Android App的運(yùn)行環(huán)境及Android系統(tǒng)架構(gòu)概覽
- Android操作系統(tǒng)的架構(gòu)設(shè)計(jì)分析
- Android 架構(gòu)之?dāng)?shù)據(jù)庫(kù)框架升級(jí)
相關(guān)文章
Android 實(shí)現(xiàn)秒轉(zhuǎn)換成時(shí)分秒的方法
這篇文章主要介紹了Android 實(shí)現(xiàn)秒轉(zhuǎn)換成時(shí)分秒的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05Android 使用volley過(guò)程中遇到的問(wèn)題解決辦法
這篇文章主要介紹了Android 使用volley過(guò)程中遇到的問(wèn)題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-06-06Android EditText限制輸入字符類(lèi)型的方法總結(jié)
這篇文章主要介紹了Android EditText限制輸入字符類(lèi)型的方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-03-03Android中使用RecylerView實(shí)現(xiàn)聊天框效果
這篇文章主要介紹了Android中使用RecylerView實(shí)現(xiàn)聊天框效果,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08android實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳功能
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11