开发者

Android 自定义View 加 lifecycle 简单使用详解

开发者 https://www.devze.com 2025-03-23 12:08 出处:网络 作者: 夢鑰
目录前言一、onMeasure做了什么?1、onLayout 做了什么2、onDraw 做了什么3、lifecycle控制动画的状态4、代码示例总结前言
目录
  • 前言
  • 一、onMeasure做了什么?
    • 1、onLayout 做了什么
    • 2、onDraw 做了什么
    • 3、lifecycle控制动画的状态
    • 4、代码示例
  • 总结

    前言

     本文是自定义view中最简单的使用方法,分别进行 ‘onMeasure’、‘onDraw’、‘自定义样式’、‘lifecycle’的简单使用,了解自定义view的使用。

    通过lifecycle来控制 动画的状态

    Android 自定义View 加 lifecycle 简单使用详解

    一、onMeasure做了什么?

    在onMeasure中获取view 的宽和高 是 ‘0’

     测量View的宽 / 高

    • 在某些情况下,需要多次测量(measure)才能确定View最终的宽/高;
    • 该情况下,measure过程后得到的宽 / 高可能不准确;
    • 此处建议:在layout过程中onLayout()去获取最终的宽 / 高

    必须要了解 MeasureSpec 作用

    测量规格(MeasureSpec)是由测量模式(mode)和测量大小(size)组成,共32位(int类型),其中:

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

    Android 自定义View 加 lifecycle 简单使用详解

    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的 宽高 获取到是 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 做了什么

    计算位置,里面包含子view 的情况下才会用到这个函数

    一般继承自viewGroup或者重新写layout布局

    Android 自定义View 加 lifecycle 简单使用详解

    2、onDraw 做了什么

    绘制View自身,设置padding 时要在onDraw中计算

    1. 绘制view背景

    2. 绘制view内容

    3. 绘制子View

    4. 绘制装饰(渐变框,滑动条等等)

    Android 自定义View 加 lifecycle 简单使用详解

      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控制动画的状态

    自定义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)

    4、代码示例

    自定义View代码

     
    /**
     * @TODO 自定义view
     *
     *
     */
    class MyVhttp://www.devze.comiew(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.BLUE
                            1 -> android.graphics.Color.YELLOW
                            2 -> android.graphics.Color.CYAN
                            3 -> android.graphics.Color.MAGENTA
                            4 -&jsgt; android.graphics.Color.LTGRAY
                            else -> android.graphics.Color.TRANSPARENT
                        }
    //                    @TODO 每次设置颜色后 调用postInvalidate 重新绘制View
                        postInvalidate()
                    }
                })
    //            动画无线循环执行
                repeatCount = ValueAnimator.INFINITE
    //            间隔一秒执行一次
                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上时
    //    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(chOhIvuujKEanged, left, top, right, bottom)
            Log.e(TAG, "onLayout: ")
        }
        /**
         * 作用:根据给定的 Canvas 自动渲染View包括其所有子 View)。
         * 绘制过程:
         *   1. 绘制view背景
         *   2. 绘制view内容
         *   3. 绘制子View
         *   4. 绘制装饰(渐变框,滑动条等等)
         * 注:
         *    a. 在调用该方法之前必须要完成 layout 过程
         *    b. 所有的视图最OhIvuujKE终都是调用 View 的 draw()绘制视图( ViewGroup 没有复写此方法)
         *    c. 在自定义View时,不应该复写该方法,而是复写 onDraw(Canvas) 方法进行绘制
         *    d. 若自定义的视图确实要复写该方法,那么需先调用 super.draw(canvas)完成系统的绘制,然后再进行自定义的绘制
         */
        override fun onDraw(canvas: Canvas?) {
            super.onDraw(canvas)
            canvas?.let {
                val pL = paddingLeft
                val pR = paddingRight
                val pT = paddingTop
       javascript         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 控制动画的状态:开始、暂停、恢复、取消
            lifecycle.addObserver(customView)
        }
    }

    总结

     在自定义View中了解在 onMeasure中进行view 的测量,在onLayout中进行对view位置的控制,在onDraw中进行view的绘制。

    通过 lifecycle控制view的生命周期,防止出现内存泄露问题如在相应的生命周期中操作动画的执行状态

    到此这篇关于Android 自定义View 加 lifecycle 简单使用详解的文章就介绍到这了,更多相关Android 自定义View内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号