自定义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)]
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
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
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
removeAllViews()
}
companion object {
const val TIME_TYPE_WEEK = 0
const val TIME_TYPE_MONTH = 1
fun dip2px(context: Context, dpValue: Float): Int {
val scale = context.resources.displayMetrics.density
return (dpValue * scale + 0.5f).toInt()
}
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.textSize = dip2px(context, 10f).toFloat()
var code2 = 50f
var nuber = 0
for (i in 0..4) {
canvas.drawText(
y_title[i],
code2,
263f,
timeLinePaint!!
)
code2 += 200f
}
}
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
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
a.roll(Calendar.DATE, -1)
return a[Calendar.DATE]
}
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) {
month--
day = getMonthLastDay(year, month)
} else {
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
}
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
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]
val value: Float = timeDifference(
(if (endHour in 21..23) date else getSubtractDay(date)) + " 22:00:00",
mData.sleepLatency)
if (value > 0) {
val dataWidth = mChartH / 600
left += value * dataWidth
}
val rightMax = mChartH / 600 * 5 * 120 + code
var right: Float
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!!)
break
}
canvas.drawRect(left, top1.toFloat(), right, mChartBottom, arcPaint!!)
left = right
}
}
}
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
fun getFontLength(paint: Paint, str: String?): Float {
return paint.measureText(str)
}
fun getFontHeight(paint: Paint): Float {
val fm = paint.fontMetrics
return fm.descent - fm.ascent
}
fun getFontLeading(paint: Paint): Float {
val fm = paint.fontMetrics
return fm.leading - fm.ascent
}
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
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.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.textSize = dip2px(
context,
if (timeType == LinChartLayout.TIME_TYPE_WEEK) 14f else 12.toFloat()
).toFloat()
textPaint.textAlign = Paint.Align.RIGHT
val tY: Float
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)
}
}
}
}
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]
val value: Float = DateUtil.timeDifference(
(if (endHour in 21..23) date else DateUtil.getSubtractDay(date)) + " 22:00:00",
mData.sleepLatency)
if (value > 0) {
val dataWidth = mChartH / 600
left += value * dataWidth
}
val rightMax = mChartH / 600 * 5 * 120 + code
var right: Float
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!!)
break
}
canvas.drawRect(left, top1.toFloat(), right, mChartH, 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")
}
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.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
canvas.drawText(sleepTimeStr, tX, tY, textPaint)
}
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
fun getFontLength(paint: Paint, str: String?): Float {
return paint.measureText(str)
}
fun getFontHeight(paint: Paint): Float {
val fm = paint.fontMetrics
return fm.descent - fm.ascent
}
fun getFontLeading(paint: Paint): Float {
val fm = paint.fontMetrics
return fm.leading - fm.ascent
}
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.*
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
if (mData!!.size > 7) {
val mMothData: MutableList<SleepLineChartData> =
ArrayList()
mMothData.add(SleepLineChartData(null, 0f, null))
mMothData.addAll(mData!!)
mData!!.clear()
mData!!.addAll(mMothData)
}
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
removeAllViews()
}
companion object {
const val TIME_TYPE_WEEK = 0
const val TIME_TYPE_MONTH = 1
fun dip2px(context: Context, dpValue: Float): Int {
val scale = context.resources.displayMetrics.density
return (dpValue * scale + 0.5f).toInt()
}
fun px2dip(context: Context, pxValue: Float): Int {
val scale = context.resources.displayMetrics.density
return (pxValue / scale + 0.5f).toInt()
}
}
}
|