最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android?自定義View?加?lifecycle?簡單使用詳解

 更新時(shí)間:2025年03月06日 09:46:16   作者:夢鑰  
本文介紹了自定義View的基本使用方法,包括onMeasure、onDraw、自定義樣式和lifecycle的使用,通過了解MeasureSpec的作用和lifecycle的控制,可以更好地管理View的生命周期,避免內(nèi)存泄露問題,感興趣的朋友一起看看吧

前言

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

通過lifecycle來控制 動畫的狀態(tài)

一、onMeasure做了什么?

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

 測量View的寬 / 高

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

必須要了解 MeasureSpec 作用

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

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

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

 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的 寬高 獲取到是 0
        Log.e(TAG, "onMeasure: ${widthSize}-${width}__${heightSize}__${height}")
        val defWidth = 400
        val 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)
        }
    }

1、onLayout 做了什么

計(jì)算位置,里面包含子view 的情況下才會用到這個(gè)函數(shù)

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

2、onDraw 做了什么

繪制View自身,設(shè)置padding 時(shí)要在onDraw中計(jì)算

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

  override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.let {
            val pL = paddingLeft
            val pR = paddingRight
            val pT = paddingTop
            val pB = paddingBottom
            var mHeight = height - pT - pB
            var mWidth = width - pL - pR
            val 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
            )
        }
    }

3、lifecycle控制動畫的狀態(tài)

自定義view 繼承 DefaultLifecycleObserver 類 然后實(shí)現(xiàn)  生命周期=中的方法
    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中 進(jìn)行生命周期監(jiān)聽的綁定
  lifecycle.addObserver(customView)

4、代碼示例

自定義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 動畫實(shí)現(xiàn)改變顏色  然后 通過  lifecycle 控制動畫的狀態(tài):開始、暫停、恢復(fù)、取消
    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.BLUE
                        1 -> android.graphics.Color.YELLOW
                        2 -> android.graphics.Color.CYAN
                        3 -> android.graphics.Color.MAGENTA
                        4 -> android.graphics.Color.LTGRAY
                        else -> android.graphics.Color.TRANSPARENT
                    }
//                    @TODO 每次設(shè)置顏色后 調(diào)用postInvalidate 重新繪制View
                    postInvalidate()
                }
            })
//            動畫無線循環(huán)執(zhí)行
            repeatCount = ValueAnimator.INFINITE
//            間隔一秒執(zhí)行一次
            duration = 1000
        }
    }
    init {
        mPaint.color = Color.Blue.hashCode()
        mPaint.style = Paint.Style.FILL
        mPaint.strokeWidth = 20f
        context?.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的 寬高 獲取到是 0
        Log.e(TAG, "onMeasure: ${widthSize}-${width}__${heightSize}__${height}")
        val defWidth = 400
        val 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上時(shí)
//    override fun onAttachedToWindow() {
//        super.onAttachedToWindow()
//        Log.e(TAG, "onAttachedToWindow: ")
//        anim.start()
//    }
//在Act 銷毀時(shí)
//    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: ")
    }
    /**
     * 作用:根據(jù)給定的 Canvas 自動渲染View包括其所有子 View)。
     * 繪制過程:
     *   1. 繪制view背景
     *   2. 繪制view內(nèi)容
     *   3. 繪制子View
     *   4. 繪制裝飾(漸變框,滑動條等等)
     * 注:
     *    a. 在調(diào)用該方法之前必須要完成 layout 過程
     *    b. 所有的視圖最終都是調(diào)用 View 的 draw()繪制視圖( ViewGroup 沒有復(fù)寫此方法)
     *    c. 在自定義View時(shí),不應(yīng)該復(fù)寫該方法,而是復(fù)寫 onDraw(Canvas) 方法進(jìn)行繪制
     *    d. 若自定義的視圖確實(shí)要復(fù)寫該方法,那么需先調(diào)用 super.draw(canvas)完成系統(tǒng)的繪制,然后再進(jìn)行自定義的繪制
     */
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.let {
            val pL = paddingLeft
            val pR = paddingRight
            val pT = paddingTop
            val pB = paddingBottom
            var mHeight = height - pT - pB
            var mWidth = width - pL - pR
            val 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.MyView
        android: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 控制動畫的狀態(tài):開始、暫停、恢復(fù)、取消
        lifecycle.addObserver(customView)
    }
}

總結(jié)

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

通過 lifecycle控制view的生命周期,防止出現(xiàn)內(nèi)存泄露問題如在相應(yīng)的生命周期中操作動畫的執(zhí)行狀態(tài)

到此這篇關(guān)于Android 自定義View 加 lifecycle 簡單使用詳解的文章就介紹到這了,更多相關(guān)Android 自定義View內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android中Retrofit庫的高級使用與原理

    Android中Retrofit庫的高級使用與原理

    在 Android 開發(fā)中,網(wǎng)絡(luò)請求是一個(gè)極為關(guān)鍵的部分,Retrofit 作為一個(gè)強(qiáng)大的網(wǎng)絡(luò)請求庫,能夠簡化開發(fā)流程,提供高效的網(wǎng)絡(luò)請求能力,本文將深入介紹 Retrofit 的高級使用與原理,幫助讀者更全面地理解和應(yīng)用這一庫,需要的朋友可以參考下
    2023-08-08
  • Android中回調(diào)接口的使用介紹

    Android中回調(diào)接口的使用介紹

    回調(diào)接口在完成某些特殊的功能時(shí)還是蠻有用的,下面為大家分享下具體的使用方法,感興趣的朋友可以參考下哈
    2013-06-06
  • Android 幀動畫使用詳情

    Android 幀動畫使用詳情

    這篇文章主要介紹本文介紹使用AnimationDrawable類來實(shí)現(xiàn)動畫效果,需要的朋友可以參考下文
    2021-08-08
  • Android通過HTTP協(xié)議實(shí)現(xiàn)上傳文件數(shù)據(jù)

    Android通過HTTP協(xié)議實(shí)現(xiàn)上傳文件數(shù)據(jù)

    這篇文章主要為大家詳細(xì)介紹了Android通過HTTP協(xié)議實(shí)現(xiàn)上傳文件數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Jetpack?Compose狀態(tài)專篇精講

    Jetpack?Compose狀態(tài)專篇精講

    在今年的Google/IO大會上,亮相了一個(gè)全新的?Android?原生?UI?開發(fā)框架-Jetpack?Compose,?與蘋果的SwiftIUI一樣,Jetpack?Compose是一個(gè)聲明式的UI框架,這篇文章主要介紹了Jetpack?Compose狀態(tài)管理
    2022-10-10
  • Android超詳細(xì)介紹自定義多選框與點(diǎn)擊按鈕跳轉(zhuǎn)界面的實(shí)現(xiàn)

    Android超詳細(xì)介紹自定義多選框與點(diǎn)擊按鈕跳轉(zhuǎn)界面的實(shí)現(xiàn)

    這篇文章主要介紹了在Android開發(fā)中如何來實(shí)現(xiàn)自定義多選框以及如何實(shí)現(xiàn)點(diǎn)擊按鈕跳轉(zhuǎn)界面的功能,感興趣的朋友快來看看吧
    2022-03-03
  • Android自定義個(gè)性化的Dialog示例

    Android自定義個(gè)性化的Dialog示例

    這篇文章主要介紹了Android自定義個(gè)性化的Dialog,結(jié)合實(shí)例形式分析了自定義Dialog的功能、樣式、布局等相關(guān)操作技巧,需要的朋友可以參考下
    2017-02-02
  • 解決Android非SDK接口繞過限制的深度實(shí)踐

    解決Android非SDK接口繞過限制的深度實(shí)踐

    本文詳細(xì)介紹了Android 16的新更新帶來的非SDK接口限制,闡述了其對應(yīng)用穩(wěn)定性及隱私安全的影響,并提供了從依賴管理、代碼重構(gòu)到運(yùn)行時(shí)檢測的全方位解決方案,幫助開發(fā)者適應(yīng)這一變化,需要的朋友可以參考下
    2026-05-05
  • Android定時(shí)器和Handler用法實(shí)例分析

    Android定時(shí)器和Handler用法實(shí)例分析

    這篇文章主要介紹了Android定時(shí)器和Handler用法,實(shí)例分析了Android中的定時(shí)器與Handler相關(guān)使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • TabLayout+ViewPager實(shí)現(xiàn)切頁的示例代碼

    TabLayout+ViewPager實(shí)現(xiàn)切頁的示例代碼

    這篇文章主要介紹了TabLayout+ViewPager實(shí)現(xiàn)切頁的示例代碼,可實(shí)現(xiàn)左右滑動切換視圖界面和點(diǎn)擊切換,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2019-01-01

最新評論

类乌齐县| 罗山县| 拉萨市| 汝阳县| 南汇区| 韶关市| 措勤县| 鹤岗市| 庆安县| 额尔古纳市| 阿拉善右旗| 曲松县| 精河县| 平顺县| 左权县| 新乐市| 麦盖提县| 乌兰察布市| 毕节市| 古蔺县| 泸定县| 青神县| 安岳县| 平阴县| 南靖县| 定襄县| 巴中市| 吉水县| 达拉特旗| 宁夏| 吴忠市| 秀山| 荔浦县| 绩溪县| 邢台县| 阳西县| 陈巴尔虎旗| 余庆县| 盖州市| 资源县| 西乌珠穆沁旗|