继续练习自定义View,这次带来的是简易折线图,支持坐标点点击监听,效果如下:
画坐标轴、画刻度、画点、连线。。x、y轴的数据范围是写死的 1 <= x <= 7 ,1 <= y <= 70 。。写活的话涉及到坐标轴刻度的动态计算、坐标点的坐标修改,想想就头大,这里只练习自定义View。
1、在res/values文件夹下新建attrs.xml文件,编写自定义属性:
<&#63;xml version="1.0" encoding="utf-8"&#63;>
2、新建LineChartView继承View,重写构造方法:
public LineChartView(Context context) { this(context, null); } public LineChartView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
3、在第三个构造方法中获取自定义属性的值:
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LineChartView, defStyleAttr, 0); mTextColor = ta.getColor(R.styleable.LineChartView_textColor, 0xff381a59); mLineColor = ta.getColor(R.styleable.LineChartView_lineColor, 0xff8e29fa); mPointColor = ta.getColor(R.styleable.LineChartView_pointColor, 0xffff5100); mPointRadius = DensityUtils.dp2px(context, 3); ta.recycle();
4、创建画图所使用的对象,如Paint、Path:
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setStyle(Paint.Style.FILL); mTextPaint.setColor(mTextColor); mTextPaint.setTextSize(40); mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mLinePaint.setStyle(Paint.Style.STROKE); mLinePaint.setColor(mLineColor); mLinePaint.setStrokeWidth(DensityUtils.dp2px(context, 2)); mLinePaint.setStrokeCap(Paint.Cap.ROUND); mXyPath = new Path(); mPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPointPaint.setStyle(Paint.Style.FILL); mPointPaint.setColor(mPointColor); mPointCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPointCirclePaint.setStyle(Paint.Style.STROKE); mPointCirclePaint.setStrokeWidth(DensityUtils.dp2px(context, 2)); mPointCirclePaint.setColor(mLineColor);
5、重写onMeasure()方法,计算自定义View的宽高:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measuredDimension(widthMeasureSpec), measuredDimension(heightMeasureSpec)); } private int measuredDimension(int measureSpec) { int result; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); if (mode == MeasureSpec.EXACTLY) { result = size; } else { result = 500; if (mode == MeasureSpec.AT_MOST) { result = Math.min(result, size); } } return result; }
6、暴露一个设置x、y数据集合的方法:
/** * 设置数据 * * @param xList x轴数据集合 * @param yList y轴数据集合 */ public void setDataList(ListxList, List yList) { if (xList == null || yList == null || xList.size() == 0 || yList.size() == 0) { throw new IllegalArgumentException("没有数据"); } if (xList.size() != yList.size()) { throw new IllegalArgumentException("x、y轴数据长度不一致"); } setPointData(xList, yList); setPointAnimator(); } /** * 设置坐标点的数据、坐标 * * @param xList x轴数据集合 * @param yList y轴数据集合 */ private void setPointData(List xList, List yList) { mPointList = new ArrayList<>(); for (int i = 0; i 0) { anim = ValueAnimator.ofInt(mLastPointList.get(i).getY(), point.getY()); } else { anim = ValueAnimator.ofInt(getHeight() - xyMargin, point.getY()); } anim.setDuration(500); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int value = (int) animation.getAnimatedValue(); point.setY(value); invalidate(); } }); anim.start(); } //储存坐标点集合 mLastPointList = mPointList; }
7、重写onDraw()方法,绘制坐标轴、刻度,画点连线,注意坐标的计算:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mPointList == null || mPointList.size() == 0) { return; } mXyPath.reset(); mXyPath.moveTo(xyMargin, 0); mXyPath.lineTo(xyMargin, getHeight() - xyMargin); mXyPath.lineTo(getWidth(), getHeight() - xyMargin); canvas.drawPath(mXyPath, mLinePaint);//画x、y坐标轴 for (int i = 0; i
8、设置坐标点点击事件:
private OnPointClickListener mOnPointClickListener; /** * 坐标点点击监听 */ public interface OnPointClickListener { /** * @param index 当前坐标点在数据集中的下标 * @param point 当前坐标点对象 */ void onPointClick(int index, ChartPoint point); } public void setOnPointClickListener(OnPointClickListener onPointClickListener) { mOnPointClickListener= onPointClickListener; }
9、重写onTouchEvent()方法,判断当前点击的点是不是在坐标点范围内:
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //判断当前点击的点是否在坐标点范围内 int curX = (int) event.getX(); int curY = (int) event.getY(); for (int i = 0; i
10、在activity_main.xml布局文件中使用该View:
<&#63;xml version="1.0" encoding="utf-8"&#63;>
11、在MainActivity.java中传入数据集合,并设置坐标点点击监听:
btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ListxList = new ArrayList<>(); List yList = new ArrayList<>(); for (int i = 0; i <7; i++) { xList.add(i + 1); int y = (int) (Math.random() * 70 + 1); yList.add(y); } chartView.setDataList(xList, yList); } }); chartView.setOnPointClickListener(new LineChartView.OnPointClickListener() { @Override public void onPointClick(int position, ChartPoint point) { tv.setText("position:" + position + "\nx:" + point.getxData() + "\ny:" + point.getyData()); } });
致此大致步骤完成了,发现和上一篇步骤差不多。。代码已上传github:
https://github.com/MonkeyMushroom/LineChartView/tree/master
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。