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)
        }
    }
}
相关推荐
LuiChun18 分钟前
django的model.py admin.py views.py 中 的可循环遍历的 精简案例
android·数据库·django
工程师老罗35 分钟前
Android笔试面试题AI答之SQLite(3)
android·jvm·sqlite
小Tomkk2 小时前
火山引擎FORCE:智算能力全面升级
android·数据库·火山引擎
码狂☆8 小时前
源码编译llama.cpp for android
android·人工智能·llama
Steve_XiaoHai9 小时前
AndroidStudio XML不识别自定义控件
android·自定义控件
csKQL9 小时前
ctf文件包含
android
2401_8576363910 小时前
SSM 寝室管理系统:为住宿生活保驾护航
android·数据库·生活
校园卡已办14 小时前
PHP木马编写
android·开发语言·php
每天进步一大步15 小时前
webSokect安卓和web适配的Bug 适用实时语音场景
android·前端·bug·web
avi911115 小时前
Unity开发哪里下载安卓Android-NDK-r21d,外加Android Studio打包实验
android·unity·游戏引擎