文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么在Android中自定义一个控件

2023-06-14 09:15

关注

怎么在Android中自定义一个控件?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

代码

class SleepDayChart(context: Context, attrs: AttributeSet?) : View(context, attrs) {  //屏幕宽高  private var scrWidth = 0f  private var scrHeight = 0f  private var xData: Array<String> = arrayOf("20:00", "02:00", "08:00", "14:00", "20:00")  private var sleepsData: Sleep? = null  private lateinit var paintLine: Paint  private lateinit var paintGradientLine: Paint  private lateinit var paintXText: Paint  private lateinit var paintSleep: Paint  private lateinit var paintPillar: Paint  private lateinit var paintRound: Paint  private lateinit var paintBessel: Paint  private var xSlider = 0f //滑块的x轴位置  private var mPath: Path  private val curveCircleRadius = 12f.dp  // the coordinates of the first curve  private val mFirstCurveStartPoint = Point()  private val mFirstCurveEndPoint = Point()  private val mFirstCurveControlPoint1 = Point()  private val mFirstCurveControlPoint2 = Point()  //the coordinates of the second curve  private var mSecondCurveStartPoint = Point()  private val mSecondCurveEndPoint = Point()  private val mSecondCurveControlPoint1 = Point()  private val mSecondCurveControlPoint2 = Point()  init {    setLayerType(LAYER_TYPE_SOFTWARE, null)    mPath = Path()    initPaint()  }    private fun initPaint() {    paintLine = Paint()    paintLine.style = Paint.Style.STROKE    paintLine.strokeWidth = 1f    paintLine.color = context.colorCompat(R.color.e6e6e6_2e2e2e)    paintGradientLine = Paint()    paintGradientLine.style = Paint.Style.STROKE    paintGradientLine.strokeWidth = 1f    paintXText = Paint()    paintXText.isAntiAlias = true    paintXText.strokeWidth = 1f    paintXText.textSize = 12f.sp    paintXText.textAlign = Paint.Align.CENTER    paintXText.color = context.colorCompat(R.color.color_on_surface)    paintSleep = Paint()    paintSleep.style = Paint.Style.FILL    paintSleep.isAntiAlias = true    paintSleep.color = context.colorCompat(R.color.blue_7fbeff)    paintPillar = Paint()    paintPillar.style = Paint.Style.FILL    paintPillar.isAntiAlias = true    paintPillar.color = context.colorCompat(R.color.blue_7fbeff)    paintRound = Paint()    paintRound.style = Paint.Style.FILL    paintRound.isAntiAlias = true    paintRound.color = context.colorCompat(R.color.ffffff_6e6e6e)    paintBessel = Paint()    paintBessel.style = Paint.Style.FILL    paintBessel.isAntiAlias = true    paintBessel.color = context.colorCompat(R.color.f2f2f2_1d1d1d)  }  override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {    super.onSizeChanged(w, h, oldw, oldh)    scrWidth = width.toFloat()    scrHeight = height.toFloat()    ySpacing = scrHeight / 8f //y轴分8份    //底部圆滑块可以滑动的范围    xWithStart = margin * 3    xWithEnd = scrWidth - margin * 3    xSlider = scrWidth / 2    xSpacing = (xWithEnd - xWithStart) / (xData.size - 1)  }  override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {    parent.requestDisallowInterceptTouchEvent(true)    return super.dispatchTouchEvent(ev)  }  private var mDownX = 0f  private var mDownY = 0f  private var isSlider = false  @SuppressLint("ClickableViewAccessibility")  override fun onTouchEvent(event: MotionEvent): Boolean {    when (event.action) {      MotionEvent.ACTION_DOWN -> {        mDownX = event.x        mDownY = event.y        isSlider = abs(event.x - xSlider) < 60f && abs(event.y - ySpacing * 7) < 60f        return isSlider      }      MotionEvent.ACTION_MOVE ->        if (abs(event.y - mDownY) < abs(event.x - mDownX)) {          if (isSlider) {            xSlider = event.x            if (xSlider < xWithStart) {              xSlider = xWithStart            }            if (xSlider > xWithEnd) {              xSlider = xWithEnd            }            invalidate()          }        }      MotionEvent.ACTION_UP -> {        if (!isSlider) {          if (abs(event.x - mDownX) < curveCircleRadius) {            xSlider = event.x            invalidate()          }        }      }    }    return true  }  private val margin = 20f.dp //左右两边距离  private var xWithStart = 0f //x轴的起始点  private var xWithEnd = 0f //x轴结束点  private var ySpacing = 0f //高度分割份数后间距  private var xSpacing = 0f //x轴分割份数后间距  @SuppressLint("DrawAllocation")  override fun onDraw(canvas: Canvas) {    super.onDraw(canvas)    //画柱子    drawPillar(canvas)    //垂直渐变线    drawGradientLine(canvas)    //底部    drawBessel(canvas)    //画x轴方向文字    drawX(canvas)  }  private fun drawX(canvas: Canvas) {    if (sleepsData == null) {      xData.forEachIndexed { index, s ->        val x = xWithStart + xSpacing * index        val dis = abs(x - xSlider)        var y = ySpacing * 7 - 10f.dp        if (dis < xSpacing / 2) {          paintXText.typeface = Typeface.DEFAULT_BOLD          y -= 10f.dp * (1 - dis / xSpacing)        } else {          paintXText.typeface = Typeface.DEFAULT        }        canvas.drawText(s, x, y, paintXText)        if (index == 0) {          canvas.drawText(startDay, x, y - 12f.dp, paintXText)        }        if (index == xData.size - 1) {          canvas.drawText(endDay, x, y - 12f.dp, paintXText)        }      }    } else {      sleepsData?.let {        val start = DateTime(it.items[0].timeStamp * 1000)        val asleep = start.hourOfDay * 60 + start.minuteOfHour        val end = DateTime(it.items.last().timeStamp * 1000)        val wakeUp = end.hourOfDay * 60 + end.minuteOfHour + it.items.last().duration        val s1 =          "${context.getString(R.string.bed_time)} ${asleep / 60}:${if (asleep % 60 < 10) "0" else ""}${asleep % 60}"        val dis1 = abs(xWithStart + paintXText.measureText(s1) / 2 - xSlider)        var y1 = ySpacing * 7 - 10f        if (dis1 < curveCircleRadius * 3) {          paintXText.typeface = Typeface.DEFAULT_BOLD          var temp = 1 - dis1 / curveCircleRadius * 2          if (temp < 0f || temp > 1f) {            temp = 1f          }          y1 -= 60f * temp        } else {          paintXText.typeface = Typeface.DEFAULT        }        canvas.drawText(s1, xWithStart, y1, paintXText)        canvas.drawText(startDay, xWithStart, y1 - 40f, paintXText)        val hour = "${if (wakeUp / 60 < 10) "0" else ""}${wakeUp / 60}"        val minute = "${if (wakeUp % 60 < 10) "0" else ""}${wakeUp % 60}"        val s2 =          "${context.getString(R.string.rise_time)} $hour:$minute"        val dis2 = abs(xWithEnd - paintXText.measureText(s2) / 2 - xSlider)        var y2 = ySpacing * 7 - 10f        if (dis2 < curveCircleRadius * 3) {          paintXText.typeface = Typeface.DEFAULT_BOLD          y2 -= 60f * (1 - dis2 / (xSlider - curveCircleRadius * 3))        } else {          paintXText.typeface = Typeface.DEFAULT        }        canvas.drawText(s2, xWithEnd, y2, paintXText)        canvas.drawText(endDay, xWithEnd, y2 - 40f, paintXText)      }    }  }  private fun drawPillar(canvas: Canvas) {    var top = 0f    var bottom = 0f    var preDuration = 0 //前一状态时长    var duration = 0 //时间累加    var tempTop = 0f    var tempBottom: Float    var startColor = 0    var endColor = 0    val colors = intArrayOf(startColor, endColor)    sleepsData?.let {      it.items.forEachIndexed { index, item ->        when (item.status) {          3, 4 -> { //清醒            endColor = Color.parseColor("#fdc221")            paintSleep.color = Color.parseColor("#fdc221")            paintPillar.color = Color.parseColor("#f9eec1")            top = 1f            bottom = 2f          }          12 -> { //快速眼动            endColor = Color.parseColor("#fd817c")            paintSleep.color = Color.parseColor("#fd817c")            paintPillar.color = Color.parseColor("#4dfd817c")            top = 2f            bottom = 3f          }          0, 1 -> { //浅            endColor = Color.parseColor("#c64be4")            paintSleep.color = Color.parseColor("#c64be4")            paintPillar.color = Color.parseColor("#e8c3f1")            top = 3f            bottom = 4f          }          2 -> { //深            endColor = Color.parseColor("#8a2be2")            paintSleep.color = Color.parseColor("#8a2be2")            paintPillar.color = Color.parseColor("#d6b9f1")            top = 4f            bottom = 5f          }        }        if (xSlider < xWithStart + xSpacing * (duration + item.duration) && xSlider > xWithStart + xSpacing * duration) {          onDaySelectListener?.invoke(index, item)          canvas.drawRect(            RectF(              xWithStart + xSpacing * duration,              ySpacing * top + 10f,              xWithStart + xSpacing * (duration + item.duration),              ySpacing * 7            ), paintPillar          )        }        canvas.drawRoundRect(          RectF(            xWithStart + xSpacing * duration - 1f,            ySpacing * top,            xWithStart + xSpacing * (duration + item.duration) + 1f,            ySpacing * bottom          ), 10f, 10f, paintSleep        )        if (index > 0 && index < it.items.size) {          if (tempTop < top) {            tempTop += 0.9f            tempBottom = bottom - 0.9f            colors[0] = startColor            colors[1] = endColor            if (xSpacing * preDuration > 10f) {              val path2 = Path()              path2.moveTo(xWithStart + xSpacing * duration, ySpacing * tempTop)              path2.lineTo(                xWithStart + xSpacing * duration - 8f,                ySpacing * tempTop + 6f              )              path2.lineTo(xWithStart + xSpacing * duration, ySpacing * tempTop + 12f)              path2.close()              paintSleep.color = startColor              canvas.drawPath(path2, paintSleep)            }            if (xSpacing * item.duration > 10f) {              val path3 = Path()              path3.moveTo(xWithStart + xSpacing * duration, ySpacing * tempBottom)              path3.lineTo(                xWithStart + xSpacing * duration + 8f,                ySpacing * tempBottom - 6f              )              path3.lineTo(                xWithStart + xSpacing * duration,                ySpacing * tempBottom - 12f              )              path3.close()              paintSleep.color = endColor              canvas.drawPath(path3, paintSleep)            }          } else {            tempBottom = tempTop + 0.1f            tempTop = bottom - 0.1f            colors[0] = endColor            colors[1] = startColor            if (xSpacing * preDuration > 10f) {              val path2 = Path()              path2.moveTo(xWithStart + xSpacing * duration, ySpacing * tempBottom)              path2.lineTo(                xWithStart + xSpacing * duration - 8f,                ySpacing * tempBottom - 6f              )              path2.lineTo(                xWithStart + xSpacing * duration,                ySpacing * tempBottom - 12f              )              path2.close()              paintSleep.color = startColor              canvas.drawPath(path2, paintSleep)            }            if (xSpacing * item.duration > 10f) {              val path3 = Path()              path3.moveTo(xWithStart + xSpacing * duration, ySpacing * tempTop)              path3.lineTo(                xWithStart + xSpacing * duration + 8f,                ySpacing * tempTop + 6f              )              path3.lineTo(xWithStart + xSpacing * duration, ySpacing * tempTop + 12f)              path3.close()              paintSleep.color = endColor              canvas.drawPath(path3, paintSleep)            }          }          val mLinearGradient = LinearGradient(            xWithStart + xSpacing * duration,            ySpacing * tempTop,            xWithStart + xSpacing * duration,            ySpacing * tempBottom, colors, null, Shader.TileMode.MIRROR          )          paintGradientLine.shader = mLinearGradient          canvas.drawLine(            xWithStart + xSpacing * duration,            ySpacing * tempTop,            xWithStart + xSpacing * duration,            ySpacing * tempBottom,            paintGradientLine          )        }        tempTop = top        tempBottom = bottom        preDuration = item.duration        duration += item.duration        startColor = endColor      }    }  }  private fun drawBessel(canvas: Canvas) {    // 第一条曲线开始点    mFirstCurveStartPoint[(xSlider - curveCircleRadius * 3).toInt()] = (ySpacing * 7).toInt()    // 第一条曲线结束点    mFirstCurveEndPoint[xSlider.toInt()] =      (ySpacing * 7 - curveCircleRadius - curveCircleRadius / 4).toInt()    // 第二条开始点    mSecondCurveStartPoint = mFirstCurveEndPoint    mSecondCurveEndPoint[(xSlider + curveCircleRadius * 3).toInt()] = (ySpacing * 7).toInt()    // 第一条控制点    mFirstCurveControlPoint1[(mFirstCurveStartPoint.x + curveCircleRadius + curveCircleRadius / 4).toInt()] =      mFirstCurveStartPoint.y    mFirstCurveControlPoint2[(mFirstCurveEndPoint.x - curveCircleRadius * 2 + curveCircleRadius).toInt()] =      mFirstCurveEndPoint.y    // 第二条控制点    mSecondCurveControlPoint1[(mSecondCurveStartPoint.x + curveCircleRadius * 2 - curveCircleRadius).toInt()] =      mSecondCurveStartPoint.y    mSecondCurveControlPoint2[(mSecondCurveEndPoint.x - curveCircleRadius - curveCircleRadius / 4).toInt()] =      mSecondCurveEndPoint.y    mPath.reset()    mPath.moveTo(0f, ySpacing * 7)    mPath.lineTo(mFirstCurveStartPoint.x.toFloat(), mFirstCurveStartPoint.y.toFloat())    mPath.cubicTo(      mFirstCurveControlPoint1.x.toFloat(), mFirstCurveControlPoint1.y.toFloat(),      mFirstCurveControlPoint2.x.toFloat(), mFirstCurveControlPoint2.y.toFloat(),      mFirstCurveEndPoint.x.toFloat(), mFirstCurveEndPoint.y.toFloat()    )    mPath.cubicTo(      mSecondCurveControlPoint1.x.toFloat(), mSecondCurveControlPoint1.y.toFloat(),      mSecondCurveControlPoint2.x.toFloat(), mSecondCurveControlPoint2.y.toFloat(),      mSecondCurveEndPoint.x.toFloat(), mSecondCurveEndPoint.y.toFloat()    )    mPath.lineTo(scrWidth, ySpacing * 7)    mPath.lineTo(scrWidth, scrHeight)    mPath.lineTo(0f, scrHeight)    mPath.close()    //底部灰色    canvas.drawPath(mPath, paintBessel)    //底部滑块    canvas.drawCircle(xSlider, ySpacing * 7 + 5f, curveCircleRadius, paintRound)  }  private var startDay = ""  private var endDay = ""  fun setValue(value: Sleep?, startDay: String, endDay: String): SleepDayChart {    this.startDay = startDay    this.endDay = endDay    this.sleepsData = value    if (sleepsData == null) {      xSpacing = (xWithEnd - xWithStart) / (xData.size - 1)    } else {      sleepsData?.let {        xSpacing = (xWithEnd - xWithStart) / it.total //时间段分割成分钟      }    }    postInvalidate()    return this  }  private fun drawGradientLine(canvas: Canvas) {    if (sleepsData == null) {      canvas.drawText(        context.getString(R.string.no_sleep_data),        scrWidth / 2f,        scrHeight / 2f,        paintXText      )    } else {      val mLinearGradient = LinearGradient(        xSlider, ySpacing, xSlider, ySpacing * 6,        intArrayOf(          context.colorCompat(R.color.ffffff_262626), Color.parseColor("#0e83ff"),          context.colorCompat(R.color.ffffff_262626)        ), null, Shader.TileMode.MIRROR      )      paintGradientLine.shader = mLinearGradient      if (ySpacing > 0) {        canvas.drawLine(xSlider, ySpacing, xSlider, ySpacing * 6, paintGradientLine)      }    }  }  private var onDaySelectListener: ((index: Int, item: SleepItem) -> Unit)? = null  fun setOnDaySelectListener(l: ((index: Int, item: SleepItem) -> Unit)): SleepDayChart {    this.onDaySelectListener = l    return this  }}

看完上述内容,你们掌握怎么在Android中自定义一个控件的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网行业资讯频道,感谢各位的阅读!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯