自定义睡眠分段VIew

自定义view效果图
在这里插入图片描述

在这里插入图片描述

使用

   <HomeLinChartLayout
			android:id="@+id/lc_specific_sleep"
	       android:layout_width="match_parent"
          android:layout_height="100dp"
          android:layout_marginTop="60dp"
          android:layout_weight="1"
          android:orientation="vertical"
          android:padding="6dp" />
sleeps = [SleepData(sleepType=1, sleepTypeTime=62), SleepData(sleepType=2, sleepTypeTime=33), SleepData(sleepType=1, sleepTypeTime=154), SleepData(sleepType=2, sleepTypeTime=19), SleepData(sleepType=1, sleepTypeTime=102), SleepData(sleepType=2, sleepTypeTime=23), SleepData(sleepType=1, sleepTypeTime=71)] //不同睡眠类型的集合 0清醒 1浅睡 2深睡 
totalSleep.light.toFloat() = 389.0 //当前日期的睡眠时间
DateTools.date2Str(sleepLists[0].date //这个是入睡时间    	


    var chartList = mutableListOf<SleepLineChartData>()
      chartList.add(
                SleepLineChartData(
                    sleeps, totalSleep.light.toFloat()
                    , DateTools.date2Str(sleepLists[0].date, "yyyy-MM-dd HH:mm:ss")
                )
            )
  lc_specific_sleep.setData(
                chartList,
                UIUtils.getAndroiodScreenProperty(),
                HomeLinChartLayout.TIME_TYPE_WEEK
            )
 lc_specific_sleep.setView()
 


HomeLinChartLayout.kt
package com.oplayer.orunningplus.function.main.todaySpecific.linChart

import com.oplayer.orunningplus.view.LinChart.SleepLineChartData

import android.content.Context
import android.graphics.*
import android.text.TextUtils
import android.util.AttributeSet
import android.view.Gravity
import android.widget.LinearLayout

/**
 * @author bishiqiangan@yeah.net
 * 重写一个LinearLayout, 遍历添加LinChartView
 */
class HomeLinChartLayout : LinearLayout {
    /**
     * 列表的数据源
     */
    private var mData: MutableList<SleepLineChartData>? = null
    /**
     * 屏幕的宽
     */
    private var scrW = 0
    /**
     * 屏幕的高
     */
    private var scrH = 0
    /**
     * 时间类型
     */
    private var timeType = 0

    constructor(
        context: Context?,
        attrs: AttributeSet?,
        defStyleAttr: Int
    ) : super(context, attrs, defStyleAttr)

    constructor(context: Context?, attrs: AttributeSet?) : super(
        context,
        attrs
    )

    constructor(context: Context?) : super(context) {
        this.orientation = VERTICAL
        setView()
    }

    fun setView() {
        if (mData != null && mData!!.isNotEmpty()) {
            var textAreW = 20
            if (timeType == TIME_TYPE_WEEK) {
                var text_max_length = 10
                for (data in mData!!) { // 获取最长文字的个数
                    if (!TextUtils.isEmpty(data.sleepLatency)) {
                        if (text_max_length <= data.sleepLatency!!.length) {
                            text_max_length = data.sleepLatency.length
                        }
                    }
                }
                val wh = textWH
                // 文字区域的宽
                textAreW =
                    text_max_length * wh[0] + dip2px(context, 10f)
            }
            // 图形区域的宽
            val chartAreW: Int
            chartAreW =  scrW -200
            val layoutParams =
                LayoutParams(
                    scrW - dip2px(
                        context,
                        10f
                    ), dip2px(context, 32f), 1f
                )
            // 设置居中
            layoutParams.gravity = Gravity.CENTER
            // 遍历添加LinChartView
            for (i in mData!!.indices) {
                val sleepLineChartData = mData!![i]
                val chartView = HomeLinChartView(context)
                chartView.setData(chartAreW, sleepLineChartData, timeType, i)
                if (i == 0) {
                    if (timeType == TIME_TYPE_MONTH) {
                        val first =
                            LayoutParams(
                                scrW - dip2px(
                                    context,
                                    10f
                                ), dip2px(context, 40f), 1f
                            )
                        // 设置居中
                        first.gravity = Gravity.CENTER
                        this.addView(chartView, first)
                    } else {
                        this.addView(chartView, layoutParams)
                    }
                } else {
                    this.addView(chartView, layoutParams)
                }
            }
        }
    }
    /**
     * 获取单个字符的高和宽
     */
    private val textWH: IntArray
        private get() {
            val wh = IntArray(2)
            // 一个矩形
            val rect = Rect()
            val text = "M"
            val paint = Paint()
            // 设置文字大小
            paint.textSize = dip2px(context, 3f).toFloat()
            paint.getTextBounds(text, 0, text.length, rect)
            wh[0] = rect.width()
            wh[1] = rect.height()
            return wh
        }

    fun setData(
        d: MutableList<SleepLineChartData>?,
        scrw: Int,
        timeType: Int
    ) {
        mData = d
        scrW = scrw
        this.timeType = timeType
        //在添加view之前要把之前所有添加过的view都清除掉
        removeAllViews()
    }

    companion object {
        /**
         * 周
         */
        const val TIME_TYPE_WEEK = 0
        /**
         * 月
         */
        const val TIME_TYPE_MONTH = 1

        /**
         * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
         */
        fun dip2px(context: Context, dpValue: Float): Int {
            val scale = context.resources.displayMetrics.density
            return (dpValue * scale + 0.5f).toInt()
        }

        /**
         * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
         */
        fun px2dip(context: Context, pxValue: Float): Int {
            val scale = context.resources.displayMetrics.density
            return (pxValue / scale + 0.5f).toInt()
        }
    }
}

HomeLinChartView.kt

package com.oplayer.orunningplus.function.main.todaySpecific.linChart
import android.app.Service
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.DashPathEffect
import android.graphics.Paint
import android.text.TextPaint
import android.text.TextUtils
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.View
import android.view.WindowManager
import com.example.myapplication.R

import com.oplayer.orunningplus.view.LinChart.SleepLineChartData
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*

class HomeLinChartView : View {
    private lateinit var mData: SleepLineChartData
    private var mChartH = 0f
    private var mChartBottom = 0f
    private var arcPaint: Paint? = null
    private var timeType = 0
    private var top1 = 0
    private val code = 80
    val y_title = arrayOf("22:00","00:00", "02:00", "05:00","08:00")
    var mChartH1 = 0f
    var mChartH2 = 0f
    var mChartH3 = 0f
    var mChartTop1 = 0f
    var mChartTop2 = 0f
    var mChartTop3 = 0f
    private var position = 0
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(
        context,
        attrs
    )

    constructor(
        context: Context?,
        attrs: AttributeSet?,
        defStyleAttr: Int
    ) : super(context, attrs, defStyleAttr)
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //画睡眠时间轴
        drawTimeLine(canvas)
        //画睡眠虚线
        drawTextDate(canvas)
        //画图形
        drawLine(canvas)
    }
    fun getHour(date: Date?): Int {
        val c = Calendar.getInstance()
        c.time = date
        return c[Calendar.HOUR_OF_DAY]
    }
    private fun drawTimeLine(canvas: Canvas) {
        val timeLinePaint = Paint()
        //timeLinePaint.color = UIUtils.getColor(R.color.sleep_details_date) // 上方点颜色
        timeLinePaint.textSize = dip2px(context, 10f).toFloat()
        // 设置文字右对齐
        // timeLinePaint.textAlign = Paint.Align.CENTER
        var code2 = 50f
        var nuber = 0
        for (i in 0..4) {
            canvas.drawText(
                y_title[i],
                code2,
                263f,
                timeLinePaint!!
            )
            code2 +=  200f
        }
    }
    /**
     * 将字符串日期时间转换成java.util.Date类型 日期时间格式yyyy-MM-dd HH:mm:ss
     *
     * @param datetime
     * @return
     */
    private val datetimeFormat =
        SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    fun parseDatetime(datetime: String?): Date {
        return datetimeFormat.parse(datetime)
    }

    private fun getEndTime(
        startTime: Long,
        subTime: Int
    ): String {
        val time = subTime * 60000 + startTime
        var date = Date(time)
        return SimpleDateFormat("HH:mm").format(date)
    }
    private fun drawTextDate(canvas: Canvas) {
        //画左边的数据时间
        var mPaint :Paint = Paint(Paint.ANTI_ALIAS_FLAG)
        mPaint.color = resources.getColor(R.color.cardview_dark_background)


        mPaint.strokeWidth = 0.1f
        mPaint.pathEffect = DashPathEffect(floatArrayOf(3f, 3f), 0f)
        val centerY = height / 20
        val centerY2 = height
        //    setLayerType(LAYER_TYPE_SOFTWARE, null)
        canvas.drawLine(0f+60f, centerY.toFloat()-13f, width.toFloat()-55f, centerY.toFloat()-13f, mPaint)
        canvas.drawLine(0f+60f, centerY2.toFloat()-37f, width.toFloat()-55f, centerY2.toFloat()-37f, mPaint)
    }
    fun timeDifference(startTime: String?, endTime: String?): Float {
        val simpleFormat =
            SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
        var from: Long = 0
        var to: Long = 0
        try {
            from = simpleFormat.parse(startTime).time
            to = simpleFormat.parse(endTime).time
        } catch (e: ParseException) {
            e.printStackTrace()
        }
        return ((to - from) / (1000 * 60)).toFloat()
    }
    /**
     * 得到指定月的天数
     */
    public fun getMonthLastDay(year: Int, month: Int): Int {
        val a = Calendar.getInstance()
        a[Calendar.YEAR] = year
        a[Calendar.MONTH] = month - 1
        a[Calendar.DATE] = 1 //把日期设置为当月第一天
        //roll()函数处理,只会比相应的字段进行处理,不会智能的对其它字段也进行逻辑上的改变。但是add()函数会在逻辑上改变其它字段,使结果正确。
        a.roll(Calendar.DATE, -1) //日期回滚一天,也就是最后一天
        return a[Calendar.DATE]
    }
    /**
     * 获得某年某月某日的前一天
     *
     * @param year
     * @param month
     * @return yyyy-MM-dd
     */
    fun getPreDay(year: Int, month: Int, day: Int): String {
        var year = year
        var month = month
        var day = day
        if (day != 1) { // 判断是否为某月月初
            day--
        } else {
            if (month != 1) { // 如果不是1月的话,那么就是上月月末
                month--
                day = getMonthLastDay(year, month)
            } else { // 如果是1月的话,那么就是上年的12月31日
                year--
                month = 12
                day = 31
            }
        }
        return "$year-$month-$day"
    }
    //日期减一天格式化处理
    fun getSubtractDay(s: String): String? {
        val date = s.split("-").toTypedArray()
        val year = date[0].toInt()
        val month = date[1].toInt()
        val day = date[2].toInt()
        val s1: String = getPreDay(year, month, day)
        val date1 = s1.split("-").toTypedArray()
        var monthStr = date1[1]
        var dayStr = date1[2]
        if (monthStr.length == 1) {
            monthStr = "0$monthStr"
        }
        if (dayStr.length == 1) {
            dayStr = "0$dayStr"
        }
        return date1[0] + "-" + monthStr + "-" + dayStr
    }
    /**
     * 绘制图形
     *
     * @param canvas
     */
    private fun drawLine(canvas: Canvas) {
        val wm =
            UIUtils.getContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val dm = DisplayMetrics()
        wm.defaultDisplay.getMetrics(dm)
        val height = dm.heightPixels // 屏幕高度(像素)
        val sleepViewHeight = height / 3
        val yPoint = sleepViewHeight

        // 需要导入 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
        if(mData.sleepLatency!=null&& mData.sleepLatency?.split(" ")?.size!! >1){
            arcPaint = Paint()
            var left = code.toFloat()-20
            val endHour = Integer.valueOf(
                mData.sleepLatency!!.split(" ").toTypedArray()[1].split(":").toTypedArray()[0]
            )
            val date = mData.sleepLatency!!.split(" ").toTypedArray()[0]
            //计算时间差   以00:00为基准
            val value: Float = timeDifference(
                (if (endHour in 21..23) date else getSubtractDay(date)) + " 22:00:00",
                mData.sleepLatency)

            if (value > 0) { //600 是22点到8点的总时长 单位分钟
                val dataWidth = mChartH / 600
                left += value * dataWidth
            }
            val rightMax = mChartH / 600 * 5 * 120 + code
            var right: Float // 循环里面尽量不要创建变量,避免造成频繁的GC
            for (sleepData in mData.dataList!!) {
                when (sleepData.sleepType) {
                    SLEEP_TYPE_AWAKE-> {
                        arcPaint!!.color = UIUtils.getColor(
                            R.color.sleep_details_awake
                        )
                        mChartBottom= mChartH1
                        top1 = mChartTop1.toInt()
                    }
                    SLEEP_TYPE_LIGHT-> {
                        arcPaint!!.color = UIUtils.getColor(
                            R.color.sleep_details_light
                        )
                        mChartBottom= mChartH2
                        top1 = mChartTop2.toInt()
                    }
                    SLEEP_TYPE_DEEP-> {
                        arcPaint!!.color = UIUtils.getColor(
                            R.color.sleep_details_deep
                        )
                        mChartBottom= mChartH3
                        top1 = mChartTop3.toInt()
                    }
                }
                arcPaint!!.isAntiAlias = true // 去除锯齿
                //绘制矩形
                right = left + sleepData.sleepTypeTime  * (mChartH / 600)
                if (left > rightMax) {
                    break
                } else if (right > rightMax) {
                    canvas.drawRect(left, top1.toFloat(), rightMax, mChartBottom, arcPaint!!)
//                canvas.drawRoundRect(left, top1.toFloat(), rightMax, mChartH,50f,50f,arcPaint!!)
                    break
                }
                canvas.drawRect(left, top1.toFloat(), right, mChartBottom, arcPaint!!)

//
                left = right
            }
        }

    }
    /**
     * 设置数据
     *
     * @param chartW
     * @param data
     * @param timeType
     * @param position
     */
    fun setData(chartW: Int, data: SleepLineChartData, timeType: Int, position: Int) {
        mChartH = chartW.toFloat()
        mChartH1 = mChartH*0.0f
        mChartH2 = mChartH*0.14f
        mChartH3 = mChartH*0.21f
        mChartTop1= mChartH*0.05f
        mChartTop2= mChartH*0.05f
        mChartTop3= mChartH*0.14f
        mData = data
        this.timeType = timeType
        this.position = position
        this.postInvalidate()
    }

    companion object {
        const val SLEEP_TYPE_AWAKE = 0 //醒来时间
        const val SLEEP_TYPE_LIGHT = 1 //浅睡时间
        const val SLEEP_TYPE_DEEP = 2 //深睡时间
        /**
         * @return 返回指定笔和指定字符串的长度
         */
        fun getFontLength(paint: Paint, str: String?): Float {
            return paint.measureText(str)
        }

        /**
         * @return 返回指定笔的文字高度
         */
        fun getFontHeight(paint: Paint): Float {
            val fm = paint.fontMetrics
            return fm.descent - fm.ascent
        }

        /**
         * @return 返回指定笔离文字顶部的基准距离
         */
        fun getFontLeading(paint: Paint): Float {
            val fm = paint.fontMetrics
            return fm.leading - fm.ascent
        }

        /**
         * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
         */
        fun dip2px(context: Context, dpValue: Float): Int {
            val scale = context.resources.displayMetrics.density
            return (dpValue * scale + 0.5f).toInt()
        }

    }
}

SleepData.kt

package com.oplayer.orunningplus.view.LinChart

data class SleepData(var sleepType:Int, var sleepTypeTime:Int)

SleepLineChartData.kt

package com.oplayer.orunningplus.view.LinChart
/**
 * @author bishiqiangan@yeah.net
 * 数据模型类
 */
class SleepLineChartData(
    //不同睡眠类型的集合
    val dataList: List<SleepData>?,
    // 当前日期的睡眠时间
    val sleepTime: Float,
    //入睡時間
    val sleepLatency: String?
)

另外一种样式
在这里插入图片描述

LinChartVie.kt

package com.oplayer.orunningplus.view.LinChart

import android.app.Service
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.text.TextUtils
import android.util.AttributeSet
import android.view.View
import com.oplayer.orunningplus.R
import com.oplayer.orunningplus.function.themeColor.DataColorBean
import com.oplayer.orunningplus.manager.DataManager
import com.oplayer.orunningplus.utils.DateUtil
import com.oplayer.orunningplus.utils.PreferencesHelper
import com.oplayer.orunningplus.utils.Slog
import com.oplayer.orunningplus.utils.UIUtils

class LinChartView : View {
    private lateinit var mData: SleepLineChartData
    private var mChartH = 0f
    private var arcPaint: Paint? = null
    private var timeType = 0
    private var top1 = 0
    private val code = 80
    var themeColor : DataColorBean? = null
    private val weekStr = intArrayOf(
        R.string.reminders_repeat_sun,
        R.string.reminders_repeat_mon,
        R.string.reminders_repeat_tue,
        R.string.reminders_repeat_wed,
        R.string.reminders_repeat_thu,
        R.string.reminders_repeat_fri,
        R.string.reminders_repeat_sat
    )
    private var position = 0

    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(
        context,
        attrs
    )

    constructor(
        context: Context?,
        attrs: AttributeSet?,
        defStyleAttr: Int
    ) : super(context, attrs, defStyleAttr)
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //画睡眠时间轴
        drawTimeLine(canvas)
        if (timeType == LinChartLayout.TIME_TYPE_MONTH) {
            if (position == 0) {
                return
            }
        }
        Slog.d("onDraw  $mData")
        drawTextDate(canvas)

        if (!this::mData.isInitialized || mData.dataList==null) {
            return
        }

        top1 = if (timeType == LinChartLayout.TIME_TYPE_WEEK) { // 画文字
            drawText(canvas, mData.sleepTime)
            100
        } else {
            10
        }
        //画图形
        drawLine(canvas)
    }

    private fun drawTimeLine(canvas: Canvas) {
        val timeLinePaint = Paint()
        themeColor()
        if((themeColor?.globalTextColor) != null) {
            timeLinePaint.color = Color.parseColor(themeColor?.globalTextColor)
        }
        //timeLinePaint.color = UIUtils.getColor(R.color.sleep_details_date) // 上方点颜色
        timeLinePaint.textSize = dip2px(context, 10f).toFloat()
        // 设置文字右对齐
        timeLinePaint.textAlign = Paint.Align.RIGHT
        val i1 = mChartH / 600 * 120
        val cy = if (timeType == LinChartLayout.TIME_TYPE_MONTH) 50 else 60
        for (i in 0..5) {
            if (position == 0) {
                canvas.drawCircle(i1 * i + code, cy.toFloat(), 10f, timeLinePaint)
                if (i == 0) {
                    canvas.drawText("22 pm", i1 * i + 120, 30f, timeLinePaint)
                } else if (i == 5) {
                    canvas.drawText("8 am", i1 * i + 120, 30f, timeLinePaint)
                }
                canvas.drawLine(
                    i1 * i + code,
                    cy.toFloat(),
                    i1 * i + code,
                    height.toFloat(),
                    timeLinePaint
                )
            } else {
                canvas.drawLine(
                    i1 * i + code,
                    0f,
                    i1 * i + code,
                    height.toFloat(),
                    timeLinePaint
                )
            }
        }
    }


    private fun drawTextDate(canvas: Canvas) {
        val textPaint = Paint()
        //画左边的数据时间
        themeColor()
        if((themeColor?.globalTextColor) != null) {
            textPaint.color = Color.parseColor(themeColor?.globalTextColor)
        }
        //textPaint.color = UIUtils.getColor(R.color.sleep_details_date)
        textPaint.textSize = dip2px(
            context,
            if (timeType == LinChartLayout.TIME_TYPE_WEEK) 14f else 12.toFloat()
        ).toFloat()
        // 设置文字左对齐
        textPaint.textAlign = Paint.Align.RIGHT
        val tY: Float
        // 注意第二个参数,左对齐,文字是从右开始写的,那么  x 就是对齐处的X坐标
        if (timeType == LinChartLayout.TIME_TYPE_WEEK) {
            tY =
                (height - getFontHeight(textPaint)) / 2 + getFontLeading(
                    textPaint
                ) + 50
            canvas.drawText(UIUtils.getString(weekStr[position]).substring(0,1), 70f, tY, textPaint)
        } else {
            tY =
                (height - getFontHeight(textPaint)) / 2 + getFontLeading(
                    textPaint
                ) + 5
            if (!TextUtils.isEmpty(mData.sleepLatency)) {
                canvas.drawText(
                    mData.sleepLatency!!.split(" ").toTypedArray()[0].split("-").toTypedArray()[2],
                    70f,
                    tY,
                    textPaint
                )
            } else {
                if (position == 1 || position == 5 || position == 10 || position == 15 || position == 20 || position == 25) {
                    canvas.drawText(position.toString() + "", 70f, tY, textPaint)

                }
            }
        }
    }

    /**
     * 绘制图形
     *
     * @param canvas
     */
    private fun drawLine(canvas: Canvas) {
        if(mData.sleepLatency!=null&& mData.sleepLatency?.split(" ")?.size!! >1){
            arcPaint = Paint()
            var left = code.toFloat()
            val endHour = Integer.valueOf(
                mData.sleepLatency!!.split(" ").toTypedArray()[1].split(":").toTypedArray()[0]
            )
            val date = mData.sleepLatency!!.split(" ").toTypedArray()[0]
            //计算时间差   以00:00为基准
            val value: Float = DateUtil.timeDifference(
                (if (endHour in 21..23) date else DateUtil.getSubtractDay(date)) + " 22:00:00",
                mData.sleepLatency)

            if (value > 0) { //600 是22点到8点的总时长 单位分钟
                val dataWidth = mChartH / 600
                left += value * dataWidth
            }
            val rightMax = mChartH / 600 * 5 * 120 + code
            var right: Float // 循环里面尽量不要创建变量,避免造成频繁的GC
            for (sleepData in mData.dataList!!) {
                when (sleepData.sleepType) {
                    SLEEP_TYPE_AWAKE -> arcPaint!!.color = UIUtils.getColor(
                        R.color.sleep_details_awake
                    )
                    SLEEP_TYPE_LIGHT -> arcPaint!!.color = UIUtils.getColor(
                        R.color.sleep_details_light
                    )
                    SLEEP_TYPE_DEEP -> arcPaint!!.color = UIUtils.getColor(
                        R.color.sleep_details_deep
                    )
                }
                arcPaint!!.isAntiAlias = true // 去除锯齿
                //绘制矩形
                right = left + sleepData.sleepTypeTime  * (mChartH / 600)
                if (left > rightMax) {
                    break
                } else if (right > rightMax) {
                    canvas.drawRect(left, top1.toFloat(), rightMax, mChartH, arcPaint!!)
//                canvas.drawRoundRect(left, top1.toFloat(), rightMax, mChartH,50f,50f,arcPaint!!)
                    break
                }
                canvas.drawRect(left, top1.toFloat(), right, mChartH, arcPaint!!)
//            canvas.drawRoundRect(left, top1.toFloat(), right, mChartH,50f,50f, arcPaint!!)
                left = right
            }
        }
    }
    fun themeColor() {
        val sharedPreferences = UIUtils.getContext().getSharedPreferences("themeColor", Service.MODE_PRIVATE)
        var checkedThemeName= PreferencesHelper.isTheme()
        var themeName = sharedPreferences?.getString("themeName$checkedThemeName","")
        themeColor = DataManager.getThemeColor("$themeName")
    }
    /**
     * 绘制文字说明  右对齐
     *
     * @param canvas
     * @param sleepTime 睡眠時間
     */
    private fun drawText(canvas: Canvas, sleepTime: Float) {
        val x = width
        val y = height
        val sleepTimeStr = "${(sleepTime / 60).toInt()}:${(sleepTime % 60).toInt()}"
        val textPaint = Paint()
        //画右边周的数据时间
        themeColor()
        if((themeColor?.globalTextColor) != null) {
            textPaint.color = Color.parseColor(themeColor?.globalTextColor)
        }
        // textPaint.color = UIUtils.getColor(R.color.sleep_details_goal)
        textPaint.textSize = dip2px(context, 15f).toFloat()
        // 设置文字左对齐
        textPaint.textAlign = Paint.Align.LEFT
        val tX = x - getFontLength(textPaint, sleepTimeStr)-dip2px(context, 8f).toFloat()
        val tY = (y - getFontHeight(textPaint)) / 2 + getFontLeading(textPaint) + 50
        // 注意第二个参数,左对齐,文字是从右开始写的,那么  x 就是对齐处的X坐标
        canvas.drawText(sleepTimeStr, tX, tY, textPaint)
    }

    /**
     * 设置数据
     *
     * @param chartW
     * @param data
     * @param timeType
     * @param position
     */
    fun setData(chartW: Int, data: SleepLineChartData, timeType: Int, position: Int) {
        mChartH = chartW.toFloat()
        mData = data
        this.timeType = timeType
        this.position = position
        this.postInvalidate()
    }

    companion object {
        const val SLEEP_TYPE_AWAKE = 0 //醒来时间
        const val SLEEP_TYPE_LIGHT = 1 //浅睡时间
        const val SLEEP_TYPE_DEEP = 2 //深睡时间
        /**
         * @return 返回指定笔和指定字符串的长度
         */
        fun getFontLength(paint: Paint, str: String?): Float {
            return paint.measureText(str)
        }

        /**
         * @return 返回指定笔的文字高度
         */
        fun getFontHeight(paint: Paint): Float {
            val fm = paint.fontMetrics
            return fm.descent - fm.ascent
        }

        /**
         * @return 返回指定笔离文字顶部的基准距离
         */
        fun getFontLeading(paint: Paint): Float {
            val fm = paint.fontMetrics
            return fm.leading - fm.ascent
        }

        /**
         * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
         */
        fun dip2px(context: Context, dpValue: Float): Int {
            val scale = context.resources.displayMetrics.density
            return (dpValue * scale + 0.5f).toInt()
        }

    }
}

LinChartLayout.kt

package com.oplayer.orunningplus.view.LinChart

import android.content.Context
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.text.TextUtils
import android.util.AttributeSet
import android.view.Gravity
import android.widget.LinearLayout
import com.oplayer.orunningplus.utils.Slog
import java.util.*

/**
 * @author bishiqiangan@yeah.net
 * 重写一个LinearLayout, 遍历添加LinChartView
 */
class LinChartLayout : LinearLayout {
    /**
     * 列表的数据源
     */
    private var mData: MutableList<SleepLineChartData>? = null
    /**
     * 屏幕的宽
     */
    private var scrW = 0
    /**
     * 时间类型
     */
    private var timeType = 0

    constructor(
        context: Context?,
        attrs: AttributeSet?,
        defStyleAttr: Int
    ) : super(context, attrs, defStyleAttr)

    constructor(context: Context?, attrs: AttributeSet?) : super(
        context,
        attrs
    )

    constructor(context: Context?) : super(context) {
        this.orientation = VERTICAL
        setView()
    }

    fun setView() {
        if (mData != null && mData!!.isNotEmpty()) {
            var textAreW = 20
            if (timeType == TIME_TYPE_WEEK) {
                var text_max_length = 10
                for (data in mData!!) { // 获取最长文字的个数
                    if (!TextUtils.isEmpty(data.sleepLatency)) {
                        if (text_max_length <= data.sleepLatency!!.length) {
                            text_max_length = data.sleepLatency.length
                        }
                    }
                }
                val wh = textWH
                // 文字区域的宽
                textAreW =
                    text_max_length * wh[0] + dip2px(context, 10f)
            }
            // 图形区域的宽
            val chartAreW: Int
            chartAreW = if (textAreW == 20) { //月
                scrW - 200
            } else { //周
                scrW - textAreW - 60
            }
            val layoutParams =
                LayoutParams(
                    scrW - dip2px(
                        context,
                        10f
                    ), dip2px(context, 32f), 1f
                )
            // 设置居中
            layoutParams.gravity = Gravity.CENTER
            // 设置Margin
//            layoutParams.topMargin = dip2px(getContext(), 2);
//            layoutParams.bottomMargin = dip2px(getContext(), 2);
//            layoutParams.leftMargin = dip2px(getContext(), 2);
//            layoutParams.rightMargin = dip2px(getContext(), 2);
            if (mData!!.size > 7) {
                val mMothData: MutableList<SleepLineChartData> =
                    ArrayList()
                mMothData.add(SleepLineChartData(null, 0f, null))
                mMothData.addAll(mData!!)
                mData!!.clear()
                mData!!.addAll(mMothData)
            }
            // 遍历添加LinChartView
            for (i in mData!!.indices) {
                val sleepLineChartData = mData!![i]
                val chartView = LinChartView(context)
                chartView.setData(chartAreW, sleepLineChartData, timeType, i)
                if (i == 0) {
                    if (timeType == TIME_TYPE_MONTH) {
                        val first =
                            LayoutParams(
                                scrW - dip2px(
                                    context,
                                    10f
                                ), dip2px(context, 40f), 1f
                            )
                        // 设置居中
                        first.gravity = Gravity.CENTER
                        this.addView(chartView, first)
                    } else {
                        this.addView(chartView, layoutParams)
                    }
                } else {
                    this.addView(chartView, layoutParams)
                }
            }
        }
    }
    /**
     * 获取单个字符的高和宽
     */
    private val textWH: IntArray
        private get() {
            val wh = IntArray(2)
            // 一个矩形
            val rect = Rect()
            val text = "M"
            val paint = Paint()
            // 设置文字大小
            paint.textSize = dip2px(context, 3f).toFloat()
            paint.getTextBounds(text, 0, text.length, rect)
            wh[0] = rect.width()
            wh[1] = rect.height()
            return wh
        }

    fun setData(
        d: MutableList<SleepLineChartData>?,
        scrw: Int,
        timeType: Int
    ) {
        mData = d
        scrW = scrw
        this.timeType = timeType
        //在添加view之前要把之前所有添加过的view都清除掉
        removeAllViews()
    }

    companion object {
        /**
         * 周
         */
        const val TIME_TYPE_WEEK = 0
        /**
         * 月
         */
        const val TIME_TYPE_MONTH = 1

        /**
         * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
         */
        fun dip2px(context: Context, dpValue: Float): Int {
            val scale = context.resources.displayMetrics.density
            return (dpValue * scale + 0.5f).toInt()
        }

        /**
         * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
         */
        fun px2dip(context: Context, pxValue: Float): Int {
            val scale = context.resources.displayMetrics.density
            return (pxValue / scale + 0.5f).toInt()
        }
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

信工院李平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值