Android 自定義View 加 lifecycle 簡單使用

前言

?本文是自定義view中最簡單的使用方法,分別進行 ‘onMeasure’、‘onDraw’、‘自定義樣式’、‘lifecycle’的簡單使用,了解自定義view的使用。

通過lifecycle來控制 動畫的狀態

一、onMeasure做了什么?

在onMeasure中獲取view 的寬和高 是 ‘0’

?測量View的寬 / 高
  • 在某些情況下,需要多次測量(measure)才能確定View最終的寬/高;
  • 該情況下,measure過程后得到的寬 / 高可能不準確;
  • 此處建議:在layout過程中onLayout()去獲取最終的寬 / 高
?必須要了解 MeasureSpec 作用

測量規格(MeasureSpec)是由測量模式(mode)和測量大小(size)組成,共32位(int類型),其中:

  • 測量模式(mode):占測量規格(MeasureSpec)的高2位;
  • 測量大小(size):占測量規格(MeasureSpec)的低30位。

MeasureSpec類用一個變量封裝了測量模式(mode)和測量大小(size):通過使用二進制,將測量模式(mode)和測量大小(size)打包成一個int值,并提供了打包和解包的方法,這樣的做法是為了減少對象內存分配和提高存取效率。具體使用如下所示:

 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val widthModel = MeasureSpec.getMode(widthMeasureSpec)val widthSize = MeasureSpec.getSize(widthMeasureSpec)val heightModel = MeasureSpec.getMode(heightMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)
//        @TODO 在 onMeasure 中獲取view的 寬高 獲取到是 0Log.e(TAG, "onMeasure: ${widthSize}-${width}__${heightSize}__${height}")val defWidth = 400val defHeight = 400
//        @TODO MeasureSpec.AT_MOST:wrap_content ; MeasureSpec.EXACTLY:match_parent ;if (widthModel == MeasureSpec.AT_MOST && heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(defWidth, defHeight)} else if (widthModel == MeasureSpec.AT_MOST) {setMeasuredDimension(defWidth, heightSize)} else if (heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, defHeight)}}

2、onLayout 做了什么

計算位置,里面包含子view 的情況下才會用到這個函數

一般繼承自viewGroup或者重新寫layout布局

3、onDraw 做了什么

繪制View自身,設置padding 時要在onDraw中計算

1. 繪制view背景
2. 繪制view內容
3. 繪制子View
4. 繪制裝飾(漸變框,滑動條等等)

  override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)canvas?.let {val pL = paddingLeftval pR = paddingRightval pT = paddingTopval pB = paddingBottomvar mHeight = height - pT - pBvar mWidth = width - pL - pRval cy = pT.plus(pB).div(2) + mHeight.div(2).toFloat()val cx = pL.plus(pR).div(2) + mWidth.div(2).toFloat()val cc = Math.min(mHeight, mWidth).div(2).toFloat()it.drawCircle(cx,cy,cc,mPaint)}}

4、lifecycle控制動畫的狀態

自定義view 繼承 DefaultLifecycleObserver 類 然后實現  生命周期=中的方法override fun onStart(owner: LifecycleOwner) {super.onStart(owner)animSetColor.start()}override fun onDestroy(owner: LifecycleOwner) {super.onDestroy(owner)animSetColor.cancel()}override fun onPause(owner: LifecycleOwner) {super.onPause(owner)animSetColor.pause()}override fun onResume(owner: LifecycleOwner) {super.onResume(owner)animSetColor.resume()}在Act中 進行生命周期監聽的綁定lifecycle.addObserver(customView)

5、代碼示例

自定義View代碼

/*** @TODO 自定義view***/
class MyView(context: Context?, attrs: AttributeSet?) :View(context, attrs), DefaultLifecycleObserver {private val mPaint by lazy { Paint() }private val TAG = "MyView"private var i = 0//  @TODO 動畫實現改變顏色  然后 通過  lifecycle 控制動畫的狀態:開始、暫停、恢復、取消private val animSetColor by lazy {ValueAnimator.ofInt(0, 100).apply {addListener(object : AnimatorListener {override fun onAnimationStart(animation: Animator) {}override fun onAnimationEnd(animation: Animator) {}override fun onAnimationCancel(animation: Animator) {}override fun onAnimationRepeat(animation: Animator) {i++if (i % 2 == 0) {mPaint.color = android.graphics.Color.BLUE}mPaint.color = when (i % 5) {0 -> android.graphics.Color.BLUE1 -> android.graphics.Color.YELLOW2 -> android.graphics.Color.CYAN3 -> android.graphics.Color.MAGENTA4 -> android.graphics.Color.LTGRAYelse -> android.graphics.Color.TRANSPARENT}
//                    @TODO 每次設置顏色后 調用postInvalidate 重新繪制ViewpostInvalidate()}})
//            動畫無線循環執行repeatCount = ValueAnimator.INFINITE
//            間隔一秒執行一次duration = 1000}}init {mPaint.color = Color.Blue.hashCode()mPaint.style = Paint.Style.FILLmPaint.strokeWidth = 20fcontext?.obtainStyledAttributes(attrs, R.styleable.MyView)?.apply {mPaint.color = getColor(R.styleable.MyView_circlr_color, android.graphics.Color.GREEN)recycle()}}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val widthModel = MeasureSpec.getMode(widthMeasureSpec)val widthSize = MeasureSpec.getSize(widthMeasureSpec)val heightModel = MeasureSpec.getMode(heightMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)
//        @TODO 在 onMeasure 中獲取view的 寬高 獲取到是 0Log.e(TAG, "onMeasure: ${widthSize}-${width}__${heightSize}__${height}")val defWidth = 400val defHeight = 400
//        @TODO MeasureSpec.AT_MOST:wrap_content ; MeasureSpec.EXACTLY:match_parent ;if (widthModel == MeasureSpec.AT_MOST && heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(defWidth, defHeight)} else if (widthModel == MeasureSpec.AT_MOST) {setMeasuredDimension(defWidth, heightSize)} else if (heightModel == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, defHeight)}}
//掛在到Act上時
//    override fun onAttachedToWindow() {
//        super.onAttachedToWindow()
//        Log.e(TAG, "onAttachedToWindow: ")
//        anim.start()
//    }//在Act 銷毀時
//    override fun onDetachedFromWindow() {
//        super.onDetachedFromWindow()
//        Log.e(TAG, "onDetachedFromWindow: ")
//        anim.cancel()
//
//    }override fun onStart(owner: LifecycleOwner) {super.onStart(owner)animSetColor.start()}override fun onDestroy(owner: LifecycleOwner) {super.onDestroy(owner)animSetColor.cancel()}override fun onPause(owner: LifecycleOwner) {super.onPause(owner)animSetColor.pause()}override fun onResume(owner: LifecycleOwner) {super.onResume(owner)animSetColor.resume()}override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {super.onLayout(changed, left, top, right, bottom)Log.e(TAG, "onLayout: ")}/*** 作用:根據給定的 Canvas 自動渲染View包括其所有子 View)。* 繪制過程:*   1. 繪制view背景*   2. 繪制view內容*   3. 繪制子View*   4. 繪制裝飾(漸變框,滑動條等等)* 注:*    a. 在調用該方法之前必須要完成 layout 過程*    b. 所有的視圖最終都是調用 View 的 draw()繪制視圖( ViewGroup 沒有復寫此方法)*    c. 在自定義View時,不應該復寫該方法,而是復寫 onDraw(Canvas) 方法進行繪制*    d. 若自定義的視圖確實要復寫該方法,那么需先調用 super.draw(canvas)完成系統的繪制,然后再進行自定義的繪制*/override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)canvas?.let {val pL = paddingLeftval pR = paddingRightval pT = paddingTopval pB = paddingBottomvar mHeight = height - pT - pBvar mWidth = width - pL - pRval cy = pT.plus(pB).div(2) + mHeight.div(2).toFloat()val cx = pL.plus(pR).div(2) + mWidth.div(2).toFloat()val cc = Math.min(mHeight, mWidth).div(2).toFloat()it.drawCircle(cx,cy,cc,mPaint)}}
}
自定義View的xml樣式文件
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="MyView"><attr name="circlr_color" format="color"/></declare-styleable>
</resources>
layout布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"android:background="#11008811"tools:context=".CustomViewActivity"><com.andriod.police.view.MyViewandroid:id="@+id/customView"android:layout_width="wrap_content"android:layout_height="130dp"android:background="#11f08811"app:circlr_color="@color/cardview_light_background"android:padding="20dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>
Act
class CustomViewActivity : AppCompatActivity() {private val customView: MyView by lazy { findViewById(R.id.customView) }override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_custom_view)
//        @TODO 通過  lifecycle 控制動畫的狀態:開始、暫停、恢復、取消lifecycle.addObserver(customView)}
}

總結

?在自定義View中了解在 onMeasure中進行view 的測量,在onLayout中進行對view位置的控制,在onDraw中進行view的繪制。

通過 lifecycle控制view的生命周期,防止出現內存泄露問題如在相應的生命周期中操作動畫的執行狀態

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

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

相關文章

《挑戰你的控制力!開源小游戲“保持平衡”開發解析:用HTML+JS+CSS實現物理平衡挑戰》?

&#x1f4cc; 大家好&#xff0c;我是智界工具庫&#xff0c;致力于分享好用實用且智能的軟件以及在JAVA語言開發中遇到的問題&#xff0c;如果本篇文章對你有所幫助請幫我點個小贊小收藏吧&#xff0c;謝謝喲&#xff01;&#x1f618;&#x1f618;&#x1f618; 博主聲…

淺淺初識AI、AI大模型、AGI

前記&#xff1a;這里只是簡單了解&#xff0c;后面有時間會專門來擴展和深入。 當前&#xff0c;人工智能&#xff08;AI&#xff09;及其細分領域&#xff08;如AI算法工程師、自然語言處理NLP、通用人工智能AGI&#xff09;的就業前景呈現高速增長態勢&#xff0c;市場需求…

服務器時間同步

方法一 [rootbogon hwh-ansible]# cat time-sync.sh #!/bin/bash # NTP 服務器信息 NTP_SERVER"192.168.42.12" PASSWORD"123456" # 多個 IP 地址 HOSTS("192.168.42.8" "192.168.42.9" "192.168.42.10" "192.168.42…

Android Studio安裝與配置詳解

Android Studio安裝與配置詳解 前言 作為一名Android開發者&#xff0c;Android Studio是我們日常開發中最重要的工具。本文將詳細介紹Android Studio的安裝配置過程&#xff0c;幫助你搭建一個高效的開發環境。 一、Android Studio下載與安裝 1.1 下載Android Studio 訪問…

在PyCharm開發環境中,如何建立hello.py文件?

李升偉 整理 一、分析 首先&#xff0c;用戶可能是剛接觸PyCharm或者Python的新手&#xff0c;所以需要從打開軟件開始講起。不過用戶可能已經安裝好了PyCharm&#xff0c;但也許需要確認是否已經正確安裝。不過問題重點在創建文件&#xff0c;可能不需要深入安裝步驟。 接下…

es6常見知識點

官方文檔&#xff1a;[https://es6.ruanyifeng.com/](https://es6.ruanyifeng.com/) 一、Class 1、Class Class只是一個語法糖,其功能用es5也能實現,但是比es5更符合類的期待 定義: constructor代表構造方法,而this指向new 生成的實例 定義類方法時,可以不使用function 注…

國內外優秀AI外呼產品推薦

在數字化轉型浪潮中&#xff0c;AI外呼系統憑借其高效率、低成本、精準交互的特點&#xff0c;成為企業客戶觸達與服務的核心工具。本文基于行業實踐與技術測評&#xff0c;推薦國內外表現突出的AI外呼產品&#xff0c;重點解析國內標桿企業云蝠智能&#xff0c;并對比其他代表…

【無標題】FrmImport

文章目錄 前言一、問題描述二、解決方案三、軟件開發&#xff08;源碼&#xff09;四、項目展示五、資源鏈接 前言 我能抽象出整個世界&#xff0c;但是我不能抽象你。 想讓你成為私有常量&#xff0c;這樣外部函數就無法訪問你。 又想讓你成為全局常量&#xff0c;這樣在我的…

給定計算預算下的最佳LLM模型尺寸與預訓練數據量分配

給定計算預算下的最佳LLM模型尺寸與預訓練數據量分配 FesianXu 20250304 at Wechat Search Team 前言 如果給定了計算預算 C C C&#xff0c;如何分配LLM的模型尺寸 N N N和訓練的數據量 D D D&#xff0c;才能使得模型的效果 L L L最好呢&#xff1f;筆者在此介紹一篇經典的文…

青訓營:簡易分布式爬蟲

一、項目介紹 該項目是一個簡易分布式爬蟲系統&#xff0c;以分布式思想為基礎&#xff0c;通過多節點協作的方式&#xff0c;將大規模的網頁抓取任務分解&#xff0c;從而高效、快速地獲取網絡數據 。 項目地址&#xff1a;https://github.com/yanchengsi/distributed_crawle…

任務9:交換機基礎及配置

CSDN 原創主頁&#xff1a;不羈https://blog.csdn.net/2303_76492156?typeblog 一、交換機基礎 交換機的概念&#xff1a;交換機是一種網絡設備&#xff0c;用于連接多臺計算機或網絡設備&#xff0c;實現數據包在局域網內的快速交換。交換機基于MAC地址來轉發數據包&#x…

YOLOv8改進------------SPFF-LSKA

YOLOv8改進------------SPFF-LSKA 1、LSAK.py代碼2、添加YAML文件yolov8_SPPF_LSKA.yaml3、添加SPPF_LSKA代碼4、ultralytics/nn/modules/__init__.py注冊模塊5、ultralytics/nn/tasks.py注冊模塊6、導入yaml文件訓練 1、LSAK.py代碼 論文 代碼 LSKA.py添加到ultralytics/nn/…

[Lc(2)滑動窗口_1] 長度最小的數組 | 無重復字符的最長子串 | 最大連續1的個數 III | 將 x 減到 0 的最小操作數

目錄 1. 長度最小的字數組 題解 代碼 ?2.無重復字符的最長子串 題解 代碼 3.最大連續1的個數 III 題解 代碼 4.將 x 減到 0 的最小操作數 題解 代碼 1. 長度最小的字數組 題目鏈接&#xff1a;209.長度最小的字數組 題目分析: 給定一個含有 n 個 正整數 的數組…

安卓binder驅動內核日志調試打印開放及原理(第一節)

背景&#xff1a; 經常有學員朋友在做系統開發時候&#xff0c;有時候遇到binder相關的一些問題&#xff0c;這個時候可能就需要比較多的binder相關日志&#xff0c;但是正常情況下這些binder通訊的的內核日志都是沒有的打印的&#xff0c;因為經常binder通訊太過于頻繁&#…

docker 安裝達夢數據庫(離線)

docker安裝達夢數據庫&#xff0c;官網上已經下載不了docker版本的了&#xff0c;下面可通過百度網盤下載 通過網盤分享的文件&#xff1a;dm8_20240715_x86_rh6_rq_single.tar.zip 鏈接: https://pan.baidu.com/s/1_ejcs_bRLZpICf69mPdK2w?pwdszj9 提取碼: szj9 上傳到服務…

MWC 2025 | 紫光展銳聯合移遠通信推出全面支持R16特性的5G模組RG620UA-EU

2025年世界移動通信大會&#xff08;MWC 2025&#xff09;期間&#xff0c;紫光展銳聯合移遠通信&#xff0c;正式發布了全面支持5G R16特性的模組RG620UA-EU&#xff0c;以強大的靈活性和便捷性賦能產業。 展銳芯加持&#xff0c;關鍵性能優異 RG620UA-EU模組基于紫光展銳V62…

達夢適配記錄-檢查服務器

service DmServicedmdb status 查看是否開啟&#xff0c;沒有配置systemctl&#xff0c;查看《DM8_Linux 服務腳本使用手冊》2.1.2.2 1 &#xff0e;拷貝服務模板文件&#xff08; DmService &#xff09;到目錄&#xff08; /opt/dmdbms/bin &#xff09;&#xff0c;并將新文…

Pipeline模式詳解:提升程序處理效率的設計模式

文章目錄 Pipeline模式詳解&#xff1a;提升程序處理效率的設計模式引言Pipeline的基本概念Pipeline的工作原理Pipeline的優勢Pipeline的應用場景1. 數據處理2. DevOps中的CI/CD3. 機器學習4. 圖像處理 常見的Pipeline實現方式1. 函數式編程中的Pipeline2. 基于消息隊列的Pipel…

STM32單片機芯片與內部115 DSP-FIR IIR低通 高通 帶通 帶阻 中值 自適應 濾波器 逐個數據實時 樣條插值擬合

目錄 一、FIR 低通、高通、帶通、帶阻 1、FIR濾波器特點 2、濾波器結構 3、濾波器系數 4、濾波實現 5、FIR 濾波后的群延遲 二、IIR 低通、高通、帶通、帶阻 1、IIR濾波器特點 2、濾波器結構 3、濾波器系數 4、濾波實現 5、IIR濾波后的群延遲 三、中值濾波 1、中值…

C語言_圖書管理系統_借閱系統管理

?? 歡迎大家來到小傘的大講堂?? &#x1f388;&#x1f388;養成好習慣&#xff0c;先贊后看哦~&#x1f388;&#x1f388; 所屬專欄&#xff1a;數據結構與算法 小傘的主頁&#xff1a;xiaosan_blog 本文所需對順序表的理解&#xff1a; 注&#xff1a;由于順序表實現圖書…