目录
目录
效果展示
实现原理
- 轴线:轴线的实现原理比较简单,就是使用线拼出X、Y轴的路径。
- 柱形图:柱形图的实现相对复杂,首先需要根据X轴的长度和数据的size计算出每个矩形的宽度,然后根据传入的最大值计算出每个柱形图的高度公式为:柱形图的绘制高度 = Y轴长度 / 传入的最大值 * 柱形图代表的值的大小,然后将计算出的每一个柱形图(矩形Rect)添加到集合当中去。
- 柱形图动画:柱形图动画使用的是ValueAnimator,与普通的柱形图不一样的是在每次添加矩形的时候都需要为每一个矩形增加一个动画。
- 柱形图文字描述:在向柱形图集合中添加矩形的时候,需要根据矩形当前位置同时创建一个用来画文字描述的点的对象,然后根据这些点的位置来画出相应的文字描述。
代码展示
/**
*
* @ProjectName: CustomViewDemo
* @Package: com.hehuidai.customview.one
* @ClassName: HistogramView
* @Description: java类作用描述 :直方图控件
* @Author: 作者名:lml
* @CreateDate: 2019/7/8 15:28
* @UpdateUser: 更新者:
* @UpdateDate: 2019/7/8 15:28
* @UpdateRemark: 更新说明:
* @Version: 1.0
*/
class HistogramView : View {
private var paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var mPaintText:Paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var mMargen: Float = 40F//直方图的边距
private var mCsysPath: Path = Path()//坐标系的路径
private var mRectList:ArrayList<HistogramRectItem>? = null//存储矩形形状的集合
private var mDescTextSize:Float = 20F//每个柱状的文字描述的文字大小
private var mIsOpenAnim:Boolean = false //是否开启动画
private var mHistogramList:ArrayList<RectF>? = null //存储柱状图的集合
private var mRectColor:Int = Color.BLUE //柱形图的颜色
private var mAxisColor:Int = Color.BLUE //轴线的颜色
private var mRectDescTextColor:Int = Color.BLUE //柱形图文字描述的颜色
init {
paint.strokeWidth = 3F
paint.color = mAxisColor
paint.strokeCap = Paint.Cap.ROUND
paint.strokeJoin = Paint.Join.ROUND
paint.style = Paint.Style.STROKE
mPaintText.textSize = mDescTextSize
mPaintText.textAlign = Paint.Align.CENTER
mPaintText.strokeWidth = 3F
mPaintText.color = mRectDescTextColor
}
constructor(context: Context) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs){
initAttr(attrs)
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr){
initAttr(attrs)
}
/**
* 加载自定义属性
*/
@SuppressLint("Recycle")
fun initAttr(attrs: AttributeSet?){
val ta = context.obtainStyledAttributes(attrs, R.styleable.HistogramView)
mAxisColor = ta.getColor(R.styleable.HistogramView_axisColor, mAxisColor)
mRectColor = ta.getColor(R.styleable.HistogramView_rectColor,mRectColor)
mRectDescTextColor = ta.getColor(R.styleable.HistogramView_rectDescTextColor,mRectDescTextColor)
mDescTextSize = ta.getFloat(R.styleable.HistogramView_rectTextSize,mDescTextSize)
mPaintText.textSize = mDescTextSize
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
initCsysPath()
}
/**
* 加载X、Y轴路径
*/
private fun initCsysPath(){
mCsysPath.reset()//重置路径
// ==========画X轴的箭头===========
mCsysPath.moveTo(mMargen - 10, mMargen + 10)
mCsysPath.lineTo(mMargen, mMargen)
mCsysPath.lineTo(mMargen + 10, mMargen + 10)
mCsysPath.moveTo(mMargen, mMargen)
// ================================
// ==========画X轴=================
mCsysPath.lineTo(mMargen,height - mMargen)
// ================================
// ==========画Y轴=================
mCsysPath.lineTo(width - mMargen,height - mMargen)
// ================================
// ==========画Y轴箭头=============
mCsysPath.lineTo(width - mMargen - 10,height - mMargen - 10)
mCsysPath.moveTo(width - mMargen,height - mMargen)
mCsysPath.lineTo(width - mMargen - 10,height - mMargen + 10)
// ================================
}
/**
* 设置轴线颜色
*/
fun setAxisColor(color:Int){
mAxisColor = color
invalidate()//重绘
}
/**
* 设置柱形图颜色
*/
fun setRectColor(color:Int){
mRectColor = color
invalidate()//重绘
}
/**
* 设置柱形图底部描述文字颜色
*/
fun setRectDescTextColor(color:Int){
mPaintText.color = color
invalidate()//重绘
}
/**
* 设置柱形图底部描述文字大小
*/
fun setDescTextSize(textSize:Float){
mDescTextSize = textSize
mPaintText.textSize = mDescTextSize
for(histioramitem in mRectList!!){
histioramitem.pointF!!.y = height - mMargen + mDescTextSize //重新设置文字距离轴线的距离
}
invalidate()//重绘
}
/**
* 加载柱形图路径
* @param rectList 柱形图的数据
* @param maxVal 柱形图的最大高度
* @param isOpenAnim 是否开启动画
* @param animDuration 动画的时长 不开启动画填0即可
*/
fun initRectPath(rectList:List<HistogramRectItem>,maxVal:Float,isOpenAnim:Boolean,animDuration:Long){
mIsOpenAnim = isOpenAnim
mRectList = rectList as ArrayList<HistogramRectItem>
val yLength = width - (mMargen*2)//Y轴的长度
val xLength = height - (mMargen*2)//X轴的长度
val yRealUseLength = yLength - (20*(rectList.size+1))//Y轴实际可用长度为总长度减去各个柱形的间隔20
val rectItemWidth = yRealUseLength/rectList.size//每个柱形图的宽度
val partHeight = xLength/maxVal//一段柱形图的高度
if(mHistogramList == null){
mHistogramList = ArrayList()
}
mHistogramList!!.clear()//清空柱形图
//将计算出来的柱形图添加到路径当中
if(mIsOpenAnim){//判断是否是开启动画的状态
for ((i,rect) in mRectList!!.withIndex()){
val left = mMargen + 20 + 20 * i + i * rectItemWidth
val top = height - partHeight * rect.rectValue - mMargen
val right = left + rectItemWidth
val bottom = height - mMargen
val rectF = RectF(left,top,right,bottom)
rect.pointF = PointF(left+rectItemWidth/2,bottom + mDescTextSize)
mHistogramList!!.add(rectF)
// 设置柱形图的动画
val valueAnimator = ValueAnimator.ofFloat(height - mMargen,top)
valueAnimator.duration = animDuration
valueAnimator.addUpdateListener {
val value:Float = it.animatedValue as Float
rectF.top = value
invalidate()
}
valueAnimator.start()
}
}else{
for ((i,rect) in mRectList!!.withIndex()){
val left = mMargen + 20 + 20 * i + i * rectItemWidth
val top = height - partHeight * rect.rectValue - mMargen
val right = left + rectItemWidth
val bottom = height - mMargen
val rectF = RectF(left,top,right,bottom)
rect.pointF = PointF(left+rectItemWidth/2,bottom + mDescTextSize)
mHistogramList!!.add(rectF)
}
invalidate()
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.style = Paint.Style.STROKE
paint.color = mAxisColor //将画笔颜色设置为轴线颜色
canvas.drawPath(mCsysPath, paint)
paint.style = Paint.Style.FILL
paint.color = mRectColor //将画笔颜色设置为柱形图颜色
if(mHistogramList!=null){
for(rect in mHistogramList!!){
canvas.drawRect(rect,paint)
}
}
// 画柱状图文字描述
if(mRectList!=null){
for(histogramitem in mRectList!!){
canvas.drawText(histogramitem.rectText,histogramitem.pointF!!.x,histogramitem.pointF!!.y,mPaintText)
}
}
}
}
- 柱形图Item实体类(HistogramRectItem)
/**
*
* @ProjectName: CustomViewDemo
* @Package: com.hehuidai.customview.one.histogramview
* @ClassName: HistogramRectItem
* @Description: java类作用描述 :柱形图的实体类
* @Author: 作者名:lml
* @CreateDate: 2019/7/8 16:17
* @UpdateUser: 更新者:
* @UpdateDate: 2019/7/8 16:17
* @UpdateRemark: 更新说明:
* @Version: 1.0
*/
class HistogramRectItem {
var rectValue:Float = 0f
var rectText:String = ""
var pointF:PointF? = null
constructor()
constructor(rectValue:Float,rectText:String){
this.rectValue = rectValue
this.rectText = rectText
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="HistogramView">
<!--轴线颜色-->
<attr name="axisColor" format="color"/>
<!--柱形图的颜色-->
<attr name="rectColor" format="color"/>
<!--柱形图描述文字的颜色-->
<attr name="rectDescTextColor" format="color"/>
<!--柱形图描述文字的大小-->
<attr name="rectTextSize" format="float"/>
</declare-styleable>
</resources>
网友评论