观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

版权声明

本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/9708588.html

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

前言

  此篇博客提供一个个人实现的柱状图View,并且相关功能已经封装好,可以直接复制代码使用。

效果图

静态效果图

动态效果图

代码


import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.View
import java.util.concurrent.atomic.AtomicBoolean

/**
 * 柱状图View
 */
class HistogramView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
    private val mProgressList = mutableListOf<Pair<String, Float>>()
    private val mRectList = mutableListOf<RectF>()
    private var mWidth = 0
    private var mHeight = 0
    private var mRoundX = 0f
    private var mRoundY = 0f
    private val mPaint by lazy { Paint() }
    private val mTextPaint by lazy { Paint() }
    private var mIsSetItemWidth = false
    private var mSetItemWidth = 0
    private var mInterval = 0
    private var mTextSize = 14f
    private var mTextAndHistogramTopMargin = 5f
    private var mIsAnimation = AtomicBoolean(false)
    private var mAnimationSpeed = 25L
    private var mAnimationValue = 0.01f

    init {
        mPaint.color = Color.GREEN
        mPaint.setFlags(Paint.ANTI_ALIAS_FLAG)
        mTextPaint.color = Color.BLACK
        mTextPaint.textSize = mTextSize
        mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG)
        mTextPaint.textAlign = Paint.Align.CENTER
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        mWidth = MeasureSpec.getSize(widthMeasureSpec)
        mHeight = MeasureSpec.getSize(heightMeasureSpec)
        if (mIsSetItemWidth) {
            calculatedCoordinates(mSetItemWidth, mInterval)
        } else {
            calculatedCoordinates()
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (mIsAnimation.get()) {
            drawAnimationHistogram(canvas)
        } else {
            drawHistogram(canvas)
        }
    }

    /**
     * 刷新内容进度
     * 第一个参数 是item柱状图下面的文字名称
     * 第二个参数 是每个柱状图的进度 进度范围0.00-1
     */
    fun refreshProgressList(list: List<Pair<String, Float>>?) {
        mProgressList.clear()
        list?.let {
            mProgressList.addAll(list)
        }
        postInvalidate()
    }

    /**
     * 设置item的文字颜色
     */
    fun setItemTextColor(color: Int) {
        mTextPaint.color = color
        postInvalidate()
    }

    /**
     * 设置item的文字大小
     */
    fun setItemTextSize(textSize: Float) {
        mTextPaint.textSize = textSize
        postInvalidate()
    }

    /**
     * 设置文字与柱状图的间距
     */
    fun setTextAndHistogramTopMargin(margin: Float) {
        mTextAndHistogramTopMargin = margin
        postInvalidate()
    }

    /**
     * 设置柱状图圆角
     */
    fun setHistogramRound(roundX: Float, roundY: Float) {
        mRoundX = roundX
        mRoundY = roundY
        postInvalidate()
    }

    /**
     * 设置柱状图颜色
     */
    fun setHistogramColor(color: Int) {
        mPaint.color = color
        postInvalidate()
    }

    /**
     * 设置柱状图渐变色
     */
    fun setHistogramColor(startColor: Int, endColor: Int) {
        val linearGradient = LinearGradient(0f, 100f, 1000f, mHeight.toFloat(), startColor, endColor, Shader.TileMode.CLAMP)
        mPaint.shader = linearGradient
        postInvalidate()
    }

    /**
     * 设置柱状图渐变色
     */
    fun setHistogramColor(colors: IntArray, positions: FloatArray) {
        val linearGradient = LinearGradient(0f, 100f, 1000f, mHeight.toFloat(), colors, positions, Shader.TileMode.CLAMP)
        mPaint.shader = linearGradient
        postInvalidate()
    }

    /**
     * 设置柱状图宽度与间隔宽度
     * 单位DP
     */
    fun setHistogramWidthDpAndInterval(itemWidthDp: Float, intervalDp: Float) {
        mIsSetItemWidth = true
        mSetItemWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, itemWidthDp, resources.displayMetrics).toInt()
        mInterval = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, intervalDp, resources.displayMetrics).toInt()
        calculatedCoordinates(mSetItemWidth, mInterval)
        postInvalidate()
    }

    /**
     * 设置无需设置item宽度,自动均分View的宽度计算获得Item宽度
     */
    fun setAverageItemWidth() {
        mIsSetItemWidth = false
        calculatedCoordinates()
        postInvalidate()
    }

    /**
     * 启用动画
     * [speed] 动画的执行速度,建议设置范围100-25毫秒之间
     */
    fun enableAnimation(speed:Long){
        mIsAnimation.set(true)
        mAnimationSpeed = speed
        postInvalidate()
    }

    /**
     * 取消动画
     */
    fun cancelAnimation(){
        mIsAnimation.set(false)
        postInvalidate()
    }

    /**
     * 以均分宽度的方式计算坐标值
     */
    private fun calculatedCoordinates() {
        mRectList.clear()
        val intervalCount = mProgressList.size * 2
        val itemWidth = mWidth / intervalCount
        //文字的总高度 文字大小 + 文字与柱状图的之间的间隔
        val textHeight = mTextSize + mTextAndHistogramTopMargin
        //柱状图的总高度 View的高度 - 文字的高度
        val histogramHeight = mHeight - textHeight
        for (i in 0..intervalCount) {
            if (i % 2 != 0) {
                val rect = RectF()
                val position = i / 2 //获取除数商,i是奇数除以2的商正好是数据的位置
                rect.top = histogramHeight - (histogramHeight.toFloat() * mProgressList[position].second)
                rect.bottom = histogramHeight
                rect.left = i * itemWidth.toFloat()
                rect.right = (i + 1) * itemWidth.toFloat()
                mRectList.add(rect)
            }
        }
        Log.e("zh", "以均分宽度的方式计算坐标值,计算完成")
    }

    /**
     * 以设置item的宽度与间隔的方式计算坐标值
     */
    private fun calculatedCoordinates(width: Int, interval: Int) {
        mRectList.clear()
        val intervalCount = mProgressList.size * 2
        val horizontalMargin = ((mWidth - intervalCount * width) + intervalCount * interval) / 2
        //文字的总高度 文字大小 + 文字与柱状图的之间的间隔
        val textHeight = mTextSize + mTextAndHistogramTopMargin
        //柱状图的总高度 View的高度 - 文字的高度
        val histogramHeight = mHeight - textHeight
        for (i in 0..intervalCount) {
            if (i % 2 != 0) {
                val rect = RectF()
                val position = i / 2 //获取除数商,i是奇数除以2的商正好是数据的位置
                rect.top = histogramHeight - (histogramHeight.toFloat() * mProgressList[position].second)
                rect.bottom = histogramHeight
                rect.left = i * width.toFloat() + horizontalMargin
                rect.right = (i + 1) * width.toFloat() + horizontalMargin
                //减少间隔距离
                if (i > 0) {
                    rect.left = rect.left - i * interval
                    rect.right = rect.right - i * interval
                }
                mRectList.add(rect)
            }
        }
        Log.e("zh", "以设置item的宽度与间隔的方式计算坐标值,计算完成")
    }

    /**
     * 绘制动画的柱状图
     */
    private fun drawAnimationHistogram(canvas: Canvas){
        mRectList.forEachIndexed { index, rectF ->
            val top = rectF.bottom - ((rectF.bottom - rectF.top) * mAnimationValue)
            canvas.drawRoundRect(rectF.left, top, rectF.right, rectF.bottom, mRoundX, mRoundY, mPaint)
            val textX = rectF.centerX()
            //因为文字会显示不完整,所以高度-2
            canvas.drawText(mProgressList[index].first, textX, mHeight.toFloat() - 2, mTextPaint)
        }
        mAnimationValue += 0.1f
        if (mAnimationValue >= 1){
            mIsAnimation.set(false)
        }
        postInvalidateDelayed(mAnimationSpeed)
    }

    /**
     * 绘制柱状图
     */
    private fun drawHistogram(canvas: Canvas) {
        mRectList.forEachIndexed { index, rectF ->
            canvas.drawRoundRect(rectF, mRoundX, mRoundY, mPaint)
            val textX = rectF.centerX()
            //因为文字会显示不完整,所以高度-2
            canvas.drawText(mProgressList[index].first, textX, mHeight.toFloat() - 2, mTextPaint)
        }
    }
}

 

end

posted on 2018-08-04 11:12  观心静  阅读(549)  评论(0编辑  收藏  举报