600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Android自定义View 实现窗帘控件

Android自定义View 实现窗帘控件

时间:2020-09-14 10:08:29

相关推荐

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),W)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),W)}}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),W)}}}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

.07.04

- 添加新版本v1.0.2,支持设置图片作为窗帘的叶子,具体修改访问上面的github地址

CSDN下载地址

哈哈,终于写完了,感谢看我文章的人,有帮助的,点个赞,给个Star啊

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。