Android自定义柱状图

先上ui效果图

1. 分析UI稿 先定义一些属性

java 复制代码
private var mouthTextColor = Color.BLACK  //底部月份文字颜色
private var bottomEndColor = Color.BLACK //柱状图渐变结束颜色
private var bottomStartColor = Color.BLACK //柱状图渐变起始颜色
private var chatTopTextColor = Color.BLACK//顶部月份文字颜色
//字体大小 
private var mouthTextSize: Float=DensityUtils.sp2px(context,12f).toFloat()
//月份最高31天  高度为 天数*倍数  31*4 
private val multiple:Int=DensityUtils.dp2px( 4f)
//柱状图宽度 
private var mChartWidth = DensityUtils.dp2px( 8f)
//柱状图底部距离布局底部距离 
private var mChartMarginBottom =DensityUtils.dp2px( 27f)
//柱状图顶部距离顶部文本的距离 
private var mChartMarginTopTitle =DensityUtils.dp2px( 4f)
//柱状图圆角 
private var mChartRadius =DensityUtils.dp2px( 8f).toFloat()
// 柱状图左右间距 
private var mStartWidth = DensityUtils.dp2px( 30f)

2. 测量出布局所需的width和height

高度:月份打卡数最高为31 依据蓝湖比例 一天等于4dp 柱状图最高 31*4 (dp)然后加上margin+padding+底部的月份文字间距

宽度:因为如果一屏显示不全,需要左右滑动,需要外层嵌套一个HorizontalScrollView 柱状图宽度12+左边间距12+最右边间距+margin+padding

kotlin 复制代码
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    val widthSize = MeasureSpec.getSize(widthMeasureSpec)
    val widthMode = MeasureSpec.getMode(widthMeasureSpec)
    val heightSize = MeasureSpec.getSize(heightMeasureSpec)
    val heightMode = MeasureSpec.getMode(heightMeasureSpec)

    var width = widthSize     
	var height = heightSize      
	// 如果宽度是 wrap_content 或者不是精确尺寸,需要计算实际需要的宽度     
	if (widthMode != MeasureSpec.EXACTLY) {
        width = calculateDesiredWidth()
    }

    // 高度如果不是精确尺寸,可以根据内容或者默认高度进行计算     
	if (heightMode != MeasureSpec.EXACTLY) {
        height = calculateDesiredHeight()
    }
    setMeasuredDimension(width, height)
}
private fun calculateDesiredWidth(): Int {
    val numColumns = 12 // 假设有12个月份     
	val columnWidth = mChartWidth  // 每个柱状图的宽度加上间距    
	// 计算内容所需的总宽度     
	val contentWidth = numColumns * (columnWidth + mStartWidth)
    // 加上空隙最右侧 宽度     
	val desiredWidth = contentWidth +mStartWidth+marginLeft+marginRight+paddingStart+paddingEnd      
	return desiredWidth 
	}

private fun calculateDesiredHeight(): Int {
    // 根据内容计算需要的高度,最高的为31个月  每月multiple4dp  再加上文本12sp 和间距4dp     
	val maxMouthDay = 31 // 31个月     r
	eturn ((maxMouthDay * multiple)+calculateTextHeight()+mChartMarginTopTitle+mChartMarginBottom+marginTop+
	marginBottom+paddingStart+paddingEnd).toInt()// 这里需要根据实际情况计算 
	}

开始画布局

1.先画柱状图 先定义出柱状图线性渐变 然后遍历12次 因为月份有12个月 startMargin 等于 startMargin+mStartWidth左边间距+ mChartWidth柱状图宽度 依次累加

  • var startMargin=0
  • startMargin += mStartWidth + mChartWidth
  • val rectF = RectF()
  • rectF.left = startMargin-mChartWidth.toFloat()
  • rectF.right = (startMargin ).toFloat()
  • rectF.bottom = (mHeight - mChartMarginBottom).toFloat()
  • rectF.top = (mHeight - mChartMarginBottom - listi * multiple).toFloat()
  • canvas.drawRoundRect(rectF, mChartRadius, mChartRadius, mChartPaint)

2.底部月份文本 先测量出文本的宽高 在通过mBound获取

  • mPaint.getTextBounds( "${i + 1}", 0, i.toString().length, mBound)
  • drawText (String text, float x, float y, Paint paint)
  • x 是文本左侧的 X 坐标。
  • y 是文本基线的 Y 坐标
  • canvas.drawText("${i + 1}月", ( startMargin-mChartWidth * 1 / 2 ).toFloat(), (mHeight - mBound.height() * 1 / 2).toFloat(), mPaint)

画柱状图顶部数字 和月份同理

  • val topTextBound = Rect()
  • mPaint.getTextBounds( "${listi}", 0, listi.toString().length, topTextBound)
  • canvas.drawText("${listi}", ( startMargin-mChartWidth * 1 / 2).toFloat(), (mHeight - (listi * multiple)-topTextBound.height() * 1 / 2-mChartMarginBottom-mChartMarginTopTitle).toFloat(), mPaint)
scss 复制代码
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    var startMargin=0     for (i in 0..11) {
        mChartPaint.style = Paint.Style.FILL         
		if (list.isNotEmpty()) {
            startMargin += mStartWidth + mChartWidth        
			var mBound = Rect()
            //画月份数字             
			mPaint.color = mouthTextColor            
			mPaint.textSize = mouthTextSize            
			mPaint.textAlign = Paint.Align.CENTER           
			mPaint.getTextBounds( "${i + 1}", 0, i.toString().length, mBound)
            canvas.drawText("${i + 1}月", ( startMargin-mChartWidth * 1 / 2 ).toFloat(),
                (mHeight  - mBound.height() * 1 / 2).toFloat(), mPaint)


            //画柱状图顶部数字            
			mTopTitlePaint.textSize = mouthTextSize            
			mPaint.textAlign = Paint.Align.CENTER            
			mPaint.color = chatTopTextColor             
			val topTextBound = Rect()
            mPaint.getTextBounds( "${list[i]}", 0, list[i].toString().length, topTextBound)
            canvas.drawText("${list[i]}", ( startMargin-mChartWidth * 1 / 2).toFloat(),
                (mHeight  -  (list[i] * multiple)-topTextBound.height() * 1 / 2-
				mChartMarginBottom-mChartMarginTopTitle).toFloat(), mPaint)

            //画柱状图             
			val lg=LinearGradient(
                mChartWidth.toFloat(),
                (mHeight - mChartMarginBottom).toFloat(),
                mChartWidth.toFloat(),
                (mHeight - mChartMarginBottom - list[i] * multiple).toFloat(),
                bottomStartColor,
                bottomEndColor,
                Shader.TileMode.CLAMP             
				)
            mChartPaint.setShader(lg)
            val rectF = RectF()
            rectF.left = startMargin-mChartWidth.toFloat()
			rectF.right = (startMargin ).toFloat()
            rectF.bottom = (mHeight - mChartMarginBottom).toFloat()
            rectF.top = (mHeight - mChartMarginBottom - list[i] * multiple).toFloat()
            canvas.drawRoundRect(rectF, mChartRadius, mChartRadius, mChartPaint)
        }
    }
}
相关推荐
驰骋工作流7 小时前
流程时效考核的表结构:从待办时限到 WF_CH 考核结果驰骋BPM驰骋低代码低代码工作流引擎
android·低代码·rxjava
传奇开心果编程9 小时前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
方白羽12 小时前
Android 双开环境底层剖析与风控检测落地
android·安全·app
weixin_5633714713 小时前
Android Studio 2024 版 Device Explorer 打开方法
android·ide·android studio
AIsoft_868915 小时前
职场音频文件怎么整理?录音APP有哪些用法
android·音视频
booboosui15 小时前
第一批个人 Agent 用户正在卸载,问题不在模型,在它住在哪儿
android
洞见前行15 小时前
点击图标到内容出现,你的 App 在这几秒里做了什么:Android 冷启动全链路优化实战
android·性能优化·android studio
凯基迪科技16 小时前
芯片UID绑定修改 加密与逆向修改思路分析
android
Liben17 小时前
Weex开发踩坑记录
android·前端·ios
救救孩子把19 小时前
结构化输出为何“格式正确、答案错误”?JSON Schema 的能力边界
android·java·json·agent·agent服务