1. 程式人生 > >自定義折線、曲線圖,根據觸控滑動動態獲取值

自定義折線、曲線圖,根據觸控滑動動態獲取值

自定義折線、曲線圖,效能一般,還沒來得及優化,需要用的可以拿去試一下:

package com.zz.kotlintest.view;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.os.Build;
import 
android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import com.zz.kotlintest.R; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import
java.util.Map; /** * @Author xifangzheng * Created by zz on 2017/9/13 17:32. *   class explain: 曲線 折線分佈圖 *     update: upAuthor: explain: */ public class LineGraphicView extends View {
    /**
     * 公共部分
     */
private static final int CIRCLE_SIZE = 6;   // 圓點的直徑
private boolean isDrawCirclePoints 
= true; //是否畫每個圓點 private ArrayList<ArrayList<Double>> yRawDataLineList; // TODO 自己新增 線的集合 private ArrayList<Paint> paintList; private float pointSpace; // 兩個點之間的 x 軸間距 private ArrayList<String> showAlertTextList; private ArrayList<Boolean> isShowBottomRawDatas; private ArrayList<Boolean> isShowTopTextIndexList; private boolean clearView; private String clearAfterShowText = "暫無資料"; public static enum Linestyle { Line, //折線 Curve; //曲線 } private Context mContext; private Paint mPaint; private Resources res; private DisplayMetrics dm; /** * data */ private Linestyle mStyle = Linestyle.Line; public void setStyle(Linestyle mStyle) { this.mStyle = mStyle; } private int canvasHeight; private int canvasWidth; private int bheight = 0; // 表的高度 private int bwidth = 0; // 表的寬度 private int blwidh; // 表的左右邊距 private boolean isMeasure = true; /** * Y軸最大值 */ private float maxValue; /** * Y軸間距值 */ private float averageValue; private int marginTop = 40; private int marginBottom = 20; int bottomTextVerticalSpacing = 4; int bottomTextHeight = 24; // 這個可讓畫布增加底部文字的高度 /** * 曲線上總點數 */ private Point[] mPoints; private ArrayList<Point[]> mPointsLineList; // TODO 新增用來存放所有線的點的集合 /** * 縱座標值 */ private ArrayList<Double> yRawData; /** * 橫座標值 */ private ArrayList<String> xRawDatas; private ArrayList<Integer> xList = new ArrayList<Integer>();// 記錄每個x的值 private ArrayList<ArrayList<Float>> xListLineList = new ArrayList<>();// 記錄每個x的值 // TODO 新增 private float spacingHeight; public boolean isDrawCirclePoints() { return isDrawCirclePoints; } public void setDrawCirclePoints(boolean drawCirclePoints) { isDrawCirclePoints = drawCirclePoints; } public LineGraphicView(Context context) { this(context, null); } public LineGraphicView(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; initView(); } private void initView() { this.res = mContext.getResources(); this.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setAntiAlias(true); dm = new DisplayMetrics(); WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(dm); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (isMeasure) { // this.canvasHeight = getHeight(); // 高度 this.canvasHeight = getHeight() - dip2px(bottomTextHeight) - dip2px(bottomTextVerticalSpacing); // 減的bottomTextHeight是文字的高度 this.canvasWidth = getWidth(); if (bheight == 0) { bheight = (int) (canvasHeight - marginBottom); } blwidh = dip2px(19); this.bwidth = canvasWidth - blwidh * 2; isMeasure = false; // pointSpace = (canvasWidth - blwidh * 2) / (xRawDatas.size() - 1); } if (xRawDatas == null) { return; } pointSpace = (canvasWidth - blwidh * 2) / (xRawDatas.size() - 1); // Log.e("onSizeChanged列印:", "pointSpace:" + pointSpace + " canvasWidth:" + canvasWidth + " blwidh:" + blwidh + " bwidth:" + bwidth); } @Override protected void onDraw(Canvas canvas) { if (clearView) { mPaint.setColor(res.getColor(R.color.color_DefaultDrawLine)); mPaint.setStrokeWidth(dip2px(0)); mPaint.setTextSize(dip2px(15)); mPaint.setStyle(Paint.Style.STROKE); mPaint.setFlags(Gravity.CENTER_HORIZONTAL); canvas.drawText(clearAfterShowText, bwidth / 2 + blwidh - 2 * mPaint.getTextSize(), bheight / 2, mPaint); return; } mPaint.setColor(res.getColor(R.color.color_DefaultDrawLine)); mPaint.setStrokeWidth(dip2px(1)); mPaint.setStyle(Paint.Style.STROKE); if (yRawDataLineList == null) { return; } drawAllXLine(canvas); // 畫直線(縱向) // drawAllYLine(canvas); drawAllYLineList(canvas); // 點的操作設定 // mPoints = getPoints(); // 新增 mPointsLineList = getPointsLineList(); // mPaint.setPathEffect(new DashPathEffect(new float[]{20, 20}, 0)); if (mStyle == Linestyle.Curve) { // drawScrollLine(canvas); drawScrollLineList(canvas); } else { // drawLine(canvas); drawLineList(canvas); } mPaint.setStyle(Paint.Style.FILL); if (isDrawCirclePoints) { drawCirclePoints(canvas); } if (isShowVerticalLine) { mPaint.setStrokeWidth(2); mPaint.setTextSize(dip2px(16)); mPaint.setColor(res.getColor(R.color.color_DefaultMoveLine)); // canvas.drawLine(currentTounchX, marginTop, currentTounchX, canvasHeight, mPaint); if (isCanShowMoveLineTopText) { drawText(moveLineTopText, currentTounchX, marginTop - dip2px(moveLineTopTextSpace), canvas, R.color.color_DefaultCoverRoundText, true, moveLineTopTextTextSize); } canvas.drawLine(currentTounchX, marginTop, currentTounchX, bheight + marginTop, mPaint); if (isShowCoverRound) { if (isShowLineRight) { int rightAddWidth = 0; int maxLengthIndex = 0; int maxLength = 0; for (int i = 0; i < showAlertTextValuesList.size(); i++) { float textNum = Float.valueOf(showAlertTextValuesList.get(i)); String textValue = (textNum == Math.round(textNum) ? String.valueOf(Math.round(textNum)) : String.valueOf(textNum)); if (textValue.length() >= maxLength) { maxLengthIndex = i; maxLength = textValue.length(); } } if (maxLength >= valueLength) { float textNum = Float.valueOf(showAlertTextValuesList.get(maxLengthIndex)); String textValue = (textNum == Math.round(textNum) ? String.valueOf(Math.round(textNum)) : String.valueOf(textNum)); rightAddWidth = (textValue.contains(".") ? dip2px(pointAddWidth) : 0) + (textValue.replace(".", "").length() - valueLength) * dip2px(everyAddWidth); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mPaint.setColor(res.getColor(R.color.colorWhite)); mPaint.setStyle(Paint.Style.FILL); canvas.drawRoundRect(currentTounchX + dip2px(coverRoundMarginLeft), dip2px(coverRoundMarginTop), currentTounchX + dip2px(coverRoundWidth) + dip2px(coverRoundMarginLeft) + rightAddWidth,dip2px(coverRoundMarginTop + coverRoundTextMarginTop * (2) + 2 + coverRoundTextVerticalSpacing * (showAlertTextList.size() - 1) + coverRoundTextSize * showAlertTextList.size()), coverRoundRadius, coverRoundRadius, mPaint); mPaint.setColor(res.getColor(R.color.color_cccccc)); mPaint.setStyle(Paint.Style.STROKE); canvas.drawRoundRect(currentTounchX + dip2px(coverRoundMarginLeft + 1), dip2px(coverRoundMarginTop + 1), currentTounchX + dip2px(coverRoundWidth) + dip2px(coverRoundMarginLeft) + rightAddWidth,dip2px(coverRoundMarginTop + coverRoundTextMarginTop * (2) + 2 + coverRoundTextVerticalSpacing * (showAlertTextList.size() - 1) + coverRoundTextSize * showAlertTextList.size()), coverRoundRadius, coverRoundRadius, mPaint); } if (showAlertTextList != null && showAlertTextList.size() > 0) { mPaint.setColor(res.getColor(R.color.color_DefaultCoverRoundText)); mPaint.setTextSize(dip2px(coverRoundTextSize)); mPaint.setStyle(Paint.Style.FILL); mPaint.setTextAlign(Paint.Align.LEFT); for (int i = 0; i < showAlertTextList.size(); i++) { float textNum = Float.valueOf(showAlertTextValuesList.get(i)); String textValue = (textNum == Math.round(textNum) ? String.valueOf(Math.round(textNum)) : String.valueOf(textNum)); // canvas.drawText(showAlertTextList.get(i) + ": " + showAlertTextValuesList.get(i), canvas.drawText(showAlertTextList.get(i) + ": " + textValue, currentTounchX + dip2px(coverRoundMarginLeft + coverRoundTextMarginLeft + 1), dip2px(coverRoundMarginTop + coverRoundTextMarginTop + coverRoundTextVerticalSpacing * i + coverRoundTextSize * (i + 1)), mPaint); // canvas.drawText(showAlertTextList.get(i) + ": " + textMy, currentTounchX + dip2px(coverRoundMarginLeft + coverRoundTextMarginLeft + 1), dip2px(coverRoundMarginTop + coverRoundTextMarginTop + coverRoundTextSize * (i + 1)), mPaint); // canvas.drawText("同行: " + textOther, currentTounchX + dip2px(coverRoundMarginLeft + coverRoundTextMarginLeft + 1), dip2px(coverRoundMarginTop + coverRoundTextMarginTop + coverRoundTextVerticalSpacing + coverRoundTextSize * 2), mPaint); } } } else { int leftAddWidth = 0; int maxLengthIndex = 0; int maxLength = 0; for (int i = 0; i < showAlertTextValuesList.size(); i++) { float textNum = Float.valueOf(showAlertTextValuesList.get(i)); String textValue = (textNum == Math.round(textNum) ? String.valueOf(Math.round(textNum)) : String.valueOf(textNum)); if (textValue.length() >= maxLength) { maxLengthIndex = i; maxLength = textValue.length(); } } if (maxLength >= valueLength) { float textNum = Float.valueOf(showAlertTextValuesList.get(maxLengthIndex)); String textValue = (textNum == Math.round(textNum) ? String.valueOf(Math.round(textNum)) : String.valueOf(textNum)); leftAddWidth = (textValue.contains(".") ? dip2px(pointAddWidth) : 0) + (textValue.replace(".", "").length() - valueLength) * dip2px(everyAddWidth); } // Log.e("leftAddWidth", "" + leftAddWidth); // Log.e("Width", "" + ((currentTounchX - dip2px(coverRoundMarginLeft)) - (currentTounchX - dip2px(coverRoundMarginLeft) - dip2px(coverRoundWidth) - leftAddWidth))); // Log.e("變數", "" + ((currentTounchX - dip2px(coverRoundMarginLeft)) + " " + (currentTounchX - dip2px(coverRoundMarginLeft) - dip2px(coverRoundWidth) - leftAddWidth))); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mPaint.setColor(res.getColor(R.color.colorWhite)); mPaint.setStyle(Paint.Style.FILL); canvas.drawRoundRect(currentTounchX - dip2px(coverRoundMarginLeft) - dip2px(coverRoundWidth) - leftAddWidth, dip2px(coverRoundMarginTop), currentTounchX - dip2px(coverRoundMarginLeft), // dip2px(coverRoundMarginTop + coverRoundHeight), dip2px(coverRoundMarginTop + coverRoundTextMarginTop * (2) + 2 + coverRoundTextVerticalSpacing * (showAlertTextList.size() - 1) + coverRoundTextSize * showAlertTextList.size()), coverRoundRadius, coverRoundRadius, mPaint); mPaint.setColor(res.getColor(R.color.color_cccccc)); mPaint.setStyle(Paint.Style.STROKE); // canvas.drawRoundRect(currentTounchX - dip2px(coverRoundMarginLeft + 1) - dip2px(coverRoundWidth ) - leftAddWidth, dip2px(coverRoundMarginTop + 1), canvas.drawRoundRect(currentTounchX - dip2px(coverRoundMarginLeft) - dip2px(coverRoundWidth) - leftAddWidth, dip2px(coverRoundMarginTop + 1), // currentTounchX - dip2px(coverRoundMarginLeft + 1), dip2px(coverRoundMarginTop + coverRoundHeight), currentTounchX - dip2px(coverRoundMarginLeft), // dip2px(coverRoundMarginTop + coverRoundHeight), dip2px(coverRoundMarginTop + coverRoundTextMarginTop * (2) + 2 + coverRoundTextVerticalSpacing * (showAlertTextList.size() - 1) + coverRoundTextSize * showAlertTextList.size()), coverRoundRadius, coverRoundRadius, mPaint); } mPaint.setColor(res.getColor(R.color.color_DefaultCoverRoundText)); mPaint.setTextSize(dip2px(coverRoundTextSize)); mPaint.setStyle(Paint.Style.FILL); mPaint.setTextAlign(Paint.Align.LEFT); for (int i = 0; i < showAlertTextList.size(); i++) { float textNum = Float.valueOf(showAlertTextValuesList.get(i)); String textValue = (textNum == Math.round(textNum) ? String.valueOf(Math.round(textNum)) : String.valueOf(textNum)); // canvas.drawText(showAlertTextList.get(i) + ": " + showAlertTextValuesList.get(i), canvas.drawText(showAlertTextList.get(i) + ": " + textValue, currentTounchX - dip2px(coverRoundMarginLeft + coverRoundWidth) + dip2px(coverRoundTextMarginLeft) - leftAddWidth, dip2px(coverRoundMarginTop + coverRoundTextMarginTop + coverRoundTextVerticalSpacing * i + coverRoundTextSize * (i + 1)), mPaint); // canvas.drawText(showAlertTextList.get(i) + ": " + textMy, currentTounchX - dip2px(coverRoundMarginLeft + coverRoundWidth) + dip2px(coverRoundTextMarginLeft) - leftAddWidth, dip2px(coverRoundMarginTop + coverRoundTextMarginTop + coverRoundTextSize * (i + 1)), mPaint); // canvas.drawText("同行: " + textOther, currentTounchX - dip2px(coverRoundMarginLeft + coverRoundWidth) + dip2px(coverRoundTextMarginLeft) - leftAddWidth, dip2px(coverRoundMarginTop + coverRoundTextMarginTop + coverRoundTextVerticalSpacing + coverRoundTextSize * 2), mPaint); } } } } } private String moveLineTopText = ""; // 移動的線頂部的文字 private boolean isCanShowMoveLineTopText; // 是否顯示移動的線頂部的文字 private int moveLineTopTextSpace = 3; // 移動的線頂部的文字 底部間距 private int moveLineTopTextTextSize = 11; // 移動的線頂部的文字 文字大小 dp值 private ArrayList<String> showAlertTextValuesList = new ArrayList<>(); boolean isShowVerticalLine; // 是否展示觸控後的豎線 boolean isShowCoverRound; // 是否展示覆蓋框 boolean isShowLineRight = true; // 展示覆蓋框 是否線上的右邊 float currentTounchX; int coverRoundTextSize = 12; // 頂部 我的、同行 覆蓋區域文字的高度 int coverRoundHeight = coverRoundTextSize * 2 + 15; // 頂部 我的、同行 覆蓋區域高度 int coverRoundWidth = 60; // 頂部 我的、同行 覆蓋區域寬度 int coverRoundMarginTop = 0; // 頂部 我的、同行 覆蓋區域 上邊距 int coverRoundMarginLeft = 8; // 頂部 我的、同行 覆蓋區 ?左邊距 int coverRoundTextMarginLeft = 6; // 頂部 我的、同行 覆蓋區 ?左內邊距 控制與文字的左間距 int coverRoundTextMarginTop = 3; // 頂部 我的、同行 覆蓋區 ?左內邊距 控制與文字的上間距 int coverRoundTextVerticalSpacing = 3; // 頂部 我的、同行 覆蓋區 ?左內邊距 文字的上下間距 int coverRoundRadius = 5; // 頂部 我的、同行 覆蓋區 ?圓角半徑 int everyAddWidth = 6; // 頂部 我的、同行 覆蓋區 數字長度每增加一位 增加的寬度 int pointAddWidth = 3; // 頂部 我的、同行 覆蓋區 包含小數點時 增加的寬度 int valueLength = 2; // 頂部 我的、同行 覆蓋區 數字長度判斷底值 DecimalFormat decimalFormat = new DecimalFormat("#0.00"); public void setCoverRoundMarginLeft(int coverRoundMarginLeft) { this.coverRoundMarginLeft = coverRoundMarginLeft; } public void setCanShowMoveLineTopText(boolean isCanShowMoveLineTopText) { this.isCanShowMoveLineTopText = isCanShowMoveLineTopText; } @Override public boolean onTouchEvent(MotionEvent event) { if (yRawDataLineList == null) { return super.onTouchEvent(event); } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: dealTouchEventDown(event); postInvalidate(); // Log.e("onTouchEvent", "ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: final MotionEvent finalEvent = event; ThreadUtils.runOnBackThread(new Runnable() { @Override public void run() { dealTouchEventMove(finalEvent); ThreadUtils.runOnUiThread(new Runnable() { @Override public void run() { postInvalidate(); } }); } }); // dealTouchEventMove(event); // postInvalidate(); // Log.e("onTouchEvent", "ACTION_MOVE"); break; case MotionEvent.ACTION_UP: // Log.e("ACTION_UP", "pointSpace:" + pointSpace); dealTouchEventUp(); postInvalidate(); break; case MotionEvent.ACTION_CANCEL: // isTounch = true; dealTouchEventUp(); postInvalidate(); Log.e("onTouchEvent", "ACTION_CANCEL"); break; } // return super.onTouchEvent(event); return true; } private void dealTouchEventUp() { a: if (xListLineList != null && xListLineList.size() > 0) { if (xListLineList.get(0) != null && xListLineList.get(0).size() > 0) { if (currentTounchX >= xListLineList.get(0).get(xListLineList.get(0).size() - 1)) { Log.e("擡起6:", "currentTounchX:" + currentTounchX + ">" + xListLineList.get(0).get(xListLineList.get(0).size() - 1)); currentTounchX = xListLineList.get(0).get(xListLineList.get(0).size() - 1); // TODO 線上面的文字 if (isShowTopTextIndexList.get(xListLineList.get(0).size() - 1)) { moveLineTopText = xRawDatas.get(xListLineList.get(0).size() - 1); } else { moveLineTopText = ""; } } else { for (int i = 0; i < xListLineList.get(0).size(); i++) { if (currentTounchX >= xListLineList.get(0).get(i)) { String[] strings = String.valueOf(pointSpace).split("/."); String pattern = "#0.0"; if (strings.length > 1) { int length = strings[1].length(); for (int j = 0; j < length; j++) { pattern += "0"; } } decimalFormat.applyPattern(pattern); String sub = decimalFormat.format(currentTounchX - xListLineList.get(0).get(i)); // LogUtils.e("decimalFormat : " + Float.valueOf(sub.substring(0, sub.length() - 1))); if (Math.abs(Float.valueOf(sub.substring(0, sub.length() - 1))) <= pointSpace) { if (currentTounchX - xListLineList.get(0).get(i) <= pointSpace / 2) { currentTounchX = xListLineList.get(0).get(i); // TODO 線上面的文字 if (isShowTopTextIndexList.get(i)) { moveLineTopText = xRawDatas.get(i); } else { moveLineTopText = ""; } for (int j = 0; j < yRawDataLineList.size(); j++) { if (i <= yRawDataLineList.get(j).size() - 1) { Double currentValue = yRawDataLineList.get(j).get(i); showAlertTextValuesList.set(j, String.valueOf(currentValue <= 0 ? 0 : currentValue)); } else { showAlertTextValuesList.set(j, "0"); } } Log.e("擡起1:", "currentTounchX:" + currentTounchX); } else { if (i + 1 < xListLineList.get(0).size()) { currentTounchX = xListLineList.get(0).get(i + 1); // TODO 線上面的文字 if (isShowTopTextIndexList.get(i + 1)) { moveLineTopText = xRawDatas.get(i + 1); } else { moveLineTopText = ""; } for (int j = 0; j < yRawDataLineList.size(); j++) { if (i + 1 <= yRawDataLineList.get(j).size() - 1) { Double currentValue = yRawDataLineList.get(j).get(i + 1); showAlertTextValuesList<