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 - list[i] * 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( "${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)
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)
        }
    }
}
相关推荐
ECT-OS-JiuHuaShan40 分钟前
功夫不负匠心人,渡劫代谢舞沧桑
android·开发语言·人工智能·算法·机器学习·kotlin·拓扑学
ZC跨境爬虫2 小时前
移动端爬虫工具Fiddler完整配置流程:PC+安卓模拟器全覆盖,零基础一次配置成功
android·前端·爬虫·测试工具·fiddler
巴德鸟2 小时前
DaVinci 常用技巧 关键帧 自动字幕 追踪 音频 冻结帧 快捷键 多轨道字幕 扩充边缘
android·编辑器·音视频·视频·davinci·davin
学习使我健康3 小时前
Android 广播介绍详情
android·开发语言·kotlin
dalancon3 小时前
AudioTrack Start 执行流程分析
android
众少成多积小致巨4 小时前
Android 初始化语言入门
android·linux·c++
Carson带你学Android4 小时前
谁才是地表最强 Android Agent 大模型?Google官方测评来了!
android·openai
followYouself4 小时前
ASM开源库实现函数耗时插桩
android·asm·asm插桩·字节码插桩
TO_ZRG5 小时前
Android Content Provider 基础
android·jvm·oracle
studyForMokey5 小时前
【Android面试】数据库
android·数据库·面试