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)
        }
    }
}
相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone2 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui