日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android自定义View 实现窗帘控件

發布時間:2024/3/13 Android 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android自定义View 实现窗帘控件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

需求分析

這里只作簡單介紹,最后會分享源碼地址

  • 窗簾分為三部分,上面的窗簾桿,桿下面的窗簾布,以及布中間的滑塊,實現還是蠻簡單的,我們可以通過自定義view把這個窗簾畫出來
  • 窗簾桿是一個上面是圓角,下面是直角的矩形,窗簾的葉子是上面直角,下面圓角的矩形,這里我們可以通過canvas.drawRoundRect來進行圓角矩形的繪制,然后疊加在一起顯示。
  • 但是進行自定義view的繪制時,需要避免重復繪制 因此直接通過canvas.drawRoundRect來繪制在疊加在一起是不行,所以這里我是采用Path.addRoundRect方法來實現
  • 這里提供了一個自定義屬性curtain_type來設置這個窗簾時單開簾還是雙開簾
  • 實現

    定義好所需要的自定義view屬性,這樣可以支持定制化的顏色以及滑塊圖標

    <declare-styleable name="CurtainView"><!-- 窗簾最小范圍 --><attr name="min" format="integer" /><!-- 窗簾最大范圍 --><attr name="max" format="integer" /><!-- 窗簾當前進度 --><attr name="progress" format="integer" /><!-- 最小進度為窗簾兩邊的距離,應該要大于thumb一半的寬度--><attr name="min_progress" format="float" /><!-- 動畫時長 --><attr name="duration" format="integer" /><!-- 窗簾桿的顏色 --><attr name="curtain_rod_color" format="color" /><!-- 窗簾桿的高度 --><attr name="curtain_rod_height" format="dimension" /><!-- 窗簾葉子的顏色 --><attr name="curtain_leaves_color" format="color" /><!-- 窗簾滑塊 --><attr name="curtain_thumb" format="reference" /><!-- 窗簾類型 單開簾還是雙開簾 --><attr name="curtain_type" format="boolean" /></declare-styleable>

    編寫自定義View-CurtainView

    val obtainStyledAttributes =context.obtainStyledAttributes(attributeSet, R.styleable.CurtainView)setMin(obtainStyledAttributes.getInt(R.styleable.CurtainView_min, mMin))setMax(obtainStyledAttributes.getInt(R.styleable.CurtainView_max, mMax))setDurtain(obtainStyledAttributes.getInt(R.styleable.CurtainView_duration, mDuration))setProgress(obtainStyledAttributes.getInt(R.styleable.CurtainView_progress, mProgress))setMinProgress(obtainStyledAttributes.getFloat(R.styleable.CurtainView_min_progress,minProgress))setProgressColor(obtainStyledAttributes.getInt(R.styleable.CurtainView_curtain_leaves_color,mProgressColor))setRodColor(obtainStyledAttributes.getInt(R.styleable.CurtainView_curtain_rod_color,mRodColor))setRodHeight(obtainStyledAttributes.getDimension(R.styleable.CurtainView_curtain_rod_height,rodHeight))setThumb(obtainStyledAttributes.getDrawable(R.styleable.CurtainView_curtain_thumb))setDouble(obtainStyledAttributes.getBoolean(R.styleable.CurtainView_curtain_type, isDoubled))obtainStyledAttributes.recycle()mPaint.strokeCap = Paint.Cap.ROUNDmPaint.style = Paint.Style.FILLrodPath = Path()//這里就是繪制窗簾桿所需的PathleftRect = RectF()rightRect = RectF()thumbRect = Rect()leftPath = Path()//窗簾葉左邊路徑rightPath = Path()//窗簾葉右邊路徑

    將定義的自定義屬性添加設置進來,并初始化好一些需要用到的東西,如畫筆,所需的矩形、路徑等.實際上難點主要在窗簾位置的計算,ondraw方法反而很簡單

    mPaint.color = mProgressColorcanvas.drawPath(leftPath!!, mPaint)if (isDoubled) {canvas.drawPath(rightPath!!, mPaint)}mPaint.color = mRodColorcanvas.drawPath(rodPath, mPaint)mThumbDrawable?.apply {bounds = thumbRect!!draw(canvas)}

    因為窗簾桿的大小只需要計算一次,不需要修改,因此我們直接在onSizeChanged計算好就行了

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)mWidth = (w + paddingLeft + paddingRight).toFloat()mHeight = (h + paddingTop + paddingBottom).toFloat()middleLine = (mWidth / 2).toInt()thumbHeightHalf = mThumbDrawable!!.intrinsicHeight / 2//順時針方向添加圓角rodPath.addRoundRect(RectF(0F, 0F, mWidth, rodHeight),floatArrayOf(radius, radius, radius, radius, 0F, 0F, 0F, 0F),Path.Direction.CCW)computeProgressRect()}

    thumbHeightHalf 是我們設置滑塊的高度的一半,然后主要是窗簾葉位置的計算,代碼如下所示,矩形的大小根據設置的mMax,mProgress ,minProgress的三個值來決定

    /*** 矩形的大小根據設置的 minProgress,mMax,mProgress 的三個值來決定*/private fun computeProgressRect() {var rectMargin = if (!isDoubled) {(mWidth - minProgress) / (mMax - mMin) * mProgress + minProgress} else {(middleLine - minProgress) / (mMax - mMin) * mProgress + minProgress}if (isDoubled) {//矩形邊距不能超過中線if (rectMargin > middleLine) {rectMargin = middleLine.toFloat()}}leftRect?.let {it.set(0F, rodHeight,rectMargin, mHeight)leftPath?.apply {//每次設置路徑前都需要先重置,否則不會生效reset()addRoundRect(it,floatArrayOf(0F, 0F, 0F, 0F, radius, radius, radius, radius),Path.Direction.CCW)}}if (isDoubled) {rectMargin =mWidth - minProgress - (mWidth - minProgress - middleLine) / (mMax - mMin) * mProgressif (rectMargin < middleLine) {rectMargin = middleLine.toFloat()}rightRect?.let {it.set(rectMargin,rodHeight,mWidth,mHeight)rightPath?.apply {//每次設置路徑前都需要先重置,否則不會生效reset()addRoundRect(it,floatArrayOf(0F, 0F, 0F, 0F, radius, radius, radius, radius),Path.Direction.CCW)}}}if (isDoubled) {thumbRect?.set((leftRect!!.right - thumbHeightHalf).roundToInt(),((mHeight - rodHeight) / 2 - thumbHeightHalf + rodHeight).toInt(),(leftRect!!.right + thumbHeightHalf).roundToInt(),((mHeight - rodHeight) / 2 + thumbHeightHalf + rodHeight).toInt())} else {//單開簾時需要判斷滑塊是否超出view的寬度thumbRect?.set(if (leftRect!!.right > mWidth - thumbHeightHalf) {(leftRect!!.right - thumbHeightHalf * 2).roundToInt()} else {(leftRect!!.right - thumbHeightHalf).roundToInt()},((mHeight - rodHeight) / 2 - thumbHeightHalf + rodHeight).toInt(),if (leftRect!!.right > mWidth - thumbHeightHalf) {(leftRect!!.right).roundToInt()} else {(leftRect!!.right + thumbHeightHalf).roundToInt()},((mHeight - rodHeight) / 2 + thumbHeightHalf + rodHeight).toInt())}}

    mProgress 需要根據手指滑動計算出來這里重寫onTouchEvent方法

    override fun onTouchEvent(event: MotionEvent): Boolean {if (!isEnabled) {return false}when (event.action) {MotionEvent.ACTION_DOWN -> {moved = false//只有手指在滑塊中才能滑動mThumbDrawable?.let {if (it.bounds.contains(event.x.toInt(), event.y.toInt())) {isTouch = trueif (null != mOnProgressChangeListener) {mOnProgressChangeListener!!.onStartTrackingTouch(this)}}}}MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {if (null != mOnProgressChangeListener && moved) {isTouch = falsemOnProgressChangeListener!!.onStopTrackingTouch(this)}}MotionEvent.ACTION_MOVE -> {var x = event.xif (x > minProgress && isTouch) {if (isDoubled) {if (x > middleLine) {x = middleLine.toFloat()}} else {if (x > mWidth) {x = mWidth}}moved = trueval ctrlProgress = if (isDoubled) {((x - minProgress) * (mMax - mMin) / (middleLine - minProgress)).toInt()} else {((x - minProgress) * (mMax - mMin) / (mWidth - minProgress)).toInt()}if (ctrlProgress != mProgress) {mProgress = ctrlProgresscomputeProgressRect()//滑動時計算矩形,然后刷新postInvalidate()if (null != mOnProgressChangeListener) {mOnProgressChangeListener!!.onProgressChanged(this, ctrlProgress, true)}}}}}return true}

    實現了上面兩步之后,現在的窗簾就可以根據手指滑動而移動了
    然后我們給這個View添加上屬性動畫,當外部設置進度時,也能夠進行窗簾的移動了

    if (valueAnimator != null && valueAnimator!!.isRunning) {valueAnimator!!.cancel()}valueAnimator = ValueAnimator.ofInt(oldProgress, mProgress).apply {addUpdateListener { animation: ValueAnimator ->mProgress = animation.animatedValue as Intif (mProgress in mMin..mMax) {computeProgressRect()invalidate()}}}valueAnimator!!.duration = mDuration.toLong()valueAnimator!!.start()

    到這里就已經實現了一個可以跟隨手指滑動而移動,并可以自定義窗簾桿、葉子、滑塊以及控制是單開還是雙開的窗簾了。

    最后提供庫的使用,以及源碼地址,歡迎Star

    使用

  • 將JitPack存儲庫添加到您的構建文件
  • allprojects {repositories {...maven { url 'https://jitpack.io' }}}
  • 添加依賴
  • dependencies {implementation 'com.github.zhuwang0926:CurtainView:1.0.1'}
  • xml
  • <com.hnkj.curtainview.CurtainViewandroid:id="@+id/curtain"android:layout_width="300dp"android:layout_height="253dp"android:layout_marginTop="20dp"app:curtain_leaves_color="@color/curtain_leaves_color"app:curtain_rod_color="@color/curtain_rod_color"app:curtain_rod_height="40dp"app:curtain_thumb="@drawable/drag"app:curtain_type="true"app:duration="2500"app:max="100"app:min="0"app:min_progress="83"app:progress="100" />

    源碼中也有使用方法,可以直接查看
    源碼地址鏈接: 點擊這里,歡迎Star

    2022.07.04

    - 添加新版本v1.0.2,支持設置圖片作為窗簾的葉子,具體修改訪問上面的github地址

    CSDN下載地址

    哈哈,終于寫完了,感謝看我文章的人,有幫助的,點個贊,給個Star啊

    總結

    以上是生活随笔為你收集整理的Android自定义View 实现窗帘控件的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。