Android自定義View簡易折線圖控件(二)

繼續(xù)練習(xí)自定義View,這次帶來的是簡易折線圖,支持坐標(biāo)點點擊監(jiān)聽,效果如下:

站在用戶的角度思考問題,與客戶深入溝通,找到博樂網(wǎng)站設(shè)計與博樂網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:網(wǎng)站設(shè)計制作、網(wǎng)站設(shè)計、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、國際域名空間、虛擬主機、企業(yè)郵箱。業(yè)務(wù)覆蓋博樂地區(qū)。

Android自定義View簡易折線圖控件(二)

畫坐標(biāo)軸、畫刻度、畫點、連線。。x、y軸的數(shù)據(jù)范圍是寫死的 1 <= x <= 7 ,1 <= y <= 70 。。寫活的話涉及到坐標(biāo)軸刻度的動態(tài)計算、坐標(biāo)點的坐標(biāo)修改,想想就頭大,這里只練習(xí)自定義View。

1、在res/values文件夾下新建attrs.xml文件,編寫自定義屬性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="LineChartView">
 <attr name="textColor" format="color" />
 <attr name="lineColor" format="color" />
 <attr name="pointColor" format="color" />
 </declare-styleable>
</resources>

2、新建LineChartView繼承View,重寫構(gòu)造方法:

 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、在第三個構(gòu)造方法中獲取自定義屬性的值:

 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、創(chuàng)建畫圖所使用的對象,如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、暴露一個設(shè)置x、y數(shù)據(jù)集合的方法:

 /**
  * 設(shè)置數(shù)據(jù)
  *
  * @param xList x軸數(shù)據(jù)集合
  * @param yList y軸數(shù)據(jù)集合
  */
 public void setDataList(List<Integer> xList, List<Integer> yList) {
  if (xList == null || yList == null || xList.size() == 0 || yList.size() == 0) {
   throw new IllegalArgumentException("沒有數(shù)據(jù)");
  }
  if (xList.size() != yList.size()) {
   throw new IllegalArgumentException("x、y軸數(shù)據(jù)長度不一致");
  }
  setPointData(xList, yList);
  setPointAnimator();
 }

 /**
  * 設(shè)置坐標(biāo)點的數(shù)據(jù)、坐標(biāo)
  *
  * @param xList x軸數(shù)據(jù)集合
  * @param yList y軸數(shù)據(jù)集合
  */
 private void setPointData(List<Integer> xList, List<Integer> yList) {
  mPointList = new ArrayList<>();
  for (int i = 0; i < xList.size(); i++) {
   ChartPoint point = new ChartPoint();
   //設(shè)置坐標(biāo)點的xy數(shù)據(jù)
   point.setxData(xList.get(i));
   point.setyData(yList.get(i));
   //計算坐標(biāo)點的橫縱坐標(biāo)
   point.setX(xyMargin + xList.get(i) * (getWidth() - 2 * xyMargin) / maxX);
   point.setY(getHeight() - xyMargin - (getHeight() - 2 * xyMargin) * yList.get(i) / maxY);
   mPointList.add(point);
  }
 }

 /**
  * 設(shè)置坐標(biāo)點移動的動畫
  */
 private void setPointAnimator() {
  for (int i = 0; i < mPointList.size(); i++) {
   final ChartPoint point = mPointList.get(i);
   ValueAnimator anim;
   if (mLastPointList != null && mLastPointList.size() > 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();
  }
  //儲存坐標(biāo)點集合
  mLastPointList = mPointList;
 }

7、重寫onDraw()方法,繪制坐標(biāo)軸、刻度,畫點連線,注意坐標(biāo)的計算:

 @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坐標(biāo)軸

  for (int i = 0; i < mPointList.size(); i++) {
   //畫x軸刻度線
   int x = xyMargin + (i + 1) * (getWidth() - 2 * xyMargin) / mPointList.size();
   canvas.drawLine(x, getHeight() - xyMargin - graduatedLineLength, x, getHeight() - xyMargin, mLinePaint);
   //畫y軸刻度線
   int y = getHeight() - xyMargin - (i + 1) * (getHeight() - 2 * xyMargin) / mPointList.size();
   canvas.drawLine(xyMargin, y, xyMargin + graduatedLineLength, y, mLinePaint);
   //畫坐標(biāo)軸刻度文本
   canvas.drawText(String.valueOf(mPointList.get(i).getxData()), x, getHeight() - mTextPaint.getTextSize() / 4, mTextPaint);
   canvas.drawText(String.valueOf((i + 1) * 10), 0, y + mTextPaint.getTextSize() / 2, mTextPaint);
  }
  //畫連接線
  for (int i = 0; i < mPointList.size(); i++) {
   if (i != mPointList.size() - 1) {
    ChartPoint lastP = mPointList.get(i);
    ChartPoint nextP = mPointList.get(i + 1);
    canvas.drawLine(lastP.getX(), lastP.getY(), nextP.getX(), nextP.getY(), mLinePaint);
   }
  }
  //畫坐標(biāo)點
  for (int i = 0; i < mPointList.size(); i++) {
   ChartPoint point = mPointList.get(i);
   canvas.drawCircle(point.getX(), point.getY(), mPointRadius, mPointPaint);
   canvas.drawCircle(point.getX(), point.getY(), mPointRadius, mPointCirclePaint);
  }
 }

8、設(shè)置坐標(biāo)點點擊事件:

 private OnPointClickListener mOnPointClickListener;

 /**
  * 坐標(biāo)點點擊監(jiān)聽
  */
 public interface OnPointClickListener {
  /**
   * @param index 當(dāng)前坐標(biāo)點在數(shù)據(jù)集中的下標(biāo)
   * @param point 當(dāng)前坐標(biāo)點對象
   */
  void onPointClick(int index, ChartPoint point);
 }

 public void setOnPointClickListener(OnPointClickListener onPointClickListener) {
  mOnPointClickListener = onPointClickListener;
 }

9、重寫onTouchEvent()方法,判斷當(dāng)前點擊的點是不是在坐標(biāo)點范圍內(nèi):

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    //判斷當(dāng)前點擊的點是否在坐標(biāo)點范圍內(nèi)
    int curX = (int) event.getX();
    int curY = (int) event.getY();
    for (int i = 0; i < mPointList.size(); i++) {
     ChartPoint point = mPointList.get(i);
     double d1 = Math.pow(curX - point.getX(), 2);
     double d2 = Math.pow(curY - point.getY(), 2);
     //√ ̄(curX - cx)² + (curY - cy)² < R
     if (Math.sqrt(d1 + d2) < mPointRadius + 10) {//為了方便點擊,把坐標(biāo)點范圍增大了10像素
      if (mOnPointClickListener != null) {
       mOnPointClickListener.onPointClick(i, point);
      }
     }
    }
    break;
  }
  return super.onTouchEvent(event);
 }

10、在activity_main.xml布局文件中使用該View:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:lcv="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="center_horizontal"
 android:orientation="vertical"
 tools:context=".MainActivity">

 <com.monkey.linechartview.LineChartView
  android:id="@+id/chartView"
  android:layout_width="250dp"
  android:layout_height="250dp"
  android:layout_marginTop="@dimen/activity_vertical_margin"
  lcv:lineColor="#8e29fa"
  lcv:pointColor="#ff5100"
  lcv:textColor="#000000" />

 <Button
  android:id="@+id/btn"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginTop="@dimen/activity_vertical_margin"
  android:text="set data"
  android:textAllCaps="false" />
</LinearLayout>

11、在MainActivity.java中傳入數(shù)據(jù)集合,并設(shè)置坐標(biāo)點點擊監(jiān)聽:

 btn.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
   List<Integer> xList = new ArrayList<>();
   List<Integer> 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());
  }
});

致此大致步驟完成了,發(fā)現(xiàn)和上一篇步驟差不多。。代碼已上傳github:
https://github.com/MonkeyMushroom/LineChartView/tree/master

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

網(wǎng)站標(biāo)題:Android自定義View簡易折線圖控件(二)
分享網(wǎng)址:http://muchs.cn/article44/gpjiee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、電子商務(wù)網(wǎng)站維護、定制開發(fā)微信公眾號、面包屑導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

綿陽服務(wù)器托管