自定义折线、曲线图,根据触摸滑动动态获取值
来源:互联网 发布:windows nt和windows 8 编辑:程序博客网 时间:2024/06/18 16:25
自定义折线、曲线图,性能一般,还没来得及优化,需要用的可以拿去试一下:
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.set(j, String.valueOf(currentValue <= 0 ? 0 : currentValue)); } else { showAlertTextValuesList.set(j, "0"); } } Log.e("抬起2:", "currentTounchX:" + currentTounchX); } Log.e("抬起3:", "currentTounchX:" + currentTounchX); } Log.e("抬起4:", "currentTounchX:" + currentTounchX); break a; } else { Log.e("抬起5:", "currentTounchX:" + Math.abs(currentTounchX - xListLineList.get(0).get(i)) + ">=" + pointSpace); } } else { Log.e("抬起6:", "currentTounchX:" + currentTounchX + "<=" + xListLineList.get(0).get(i)); } } } } } Log.e("onTouchEvent", "ACTION_UP moveLineTopText:" + moveLineTopText); isShowVerticalLine = true; isShowCoverRound = true; } private void dealTouchEventMove(MotionEvent event) { currentTounchX = event.getX(); moveLineTopText = ""; if (currentTounchX <= minTouchXPoint) { currentTounchX = minTouchXPoint; a: for (Map.Entry<Integer, Integer> entry : startTempList.entrySet()) { if (entry.getValue() == minTouchXPoint) { if (isShowTopTextIndexList.get(entry.getKey())) { moveLineTopText = xRawDatas.get(entry.getKey()); } else { moveLineTopText = ""; } break a; } } ArrayList<Integer> startIndexList = new ArrayList<>(); for (int i = 0; i < linesStartIndexMap.size(); i++) { startIndexList.add(linesStartIndexMap.get(i)); } int minIndex = 0; if (startIndexList.size() > 0) { minIndex = Collections.min(startIndexList); } for (int i = 0; i < yRawDataLineList.size(); i++) { if (minIndex < yRawDataLineList.get(i).size()) { Double minValue = yRawDataLineList.get(i).get(minIndex); showAlertTextValuesList.set(i, String.valueOf(minValue <= 0 ? 0 : minValue)); } else { showAlertTextValuesList.set(i, "0"); } } } else if (currentTounchX >= maxTouchXPoint) { currentTounchX = maxTouchXPoint; a: for (Map.Entry<Integer, Integer> entry : endTempList.entrySet()) { if (entry.getValue() == maxTouchXPoint) { if (isShowTopTextIndexList.get(entry.getKey())) { moveLineTopText = xRawDatas.get(entry.getKey()); } else { moveLineTopText = ""; } break a; } } ArrayList<Integer> endIndexList = new ArrayList<>(); for (int i = 0; i < linesEndIndexMap.size(); i++) { endIndexList.add(linesEndIndexMap.get(i)); } if (endIndexList.size() == 0) return; int maxIndex = Collections.max(endIndexList); for (int i = 0; i < yRawDataLineList.size(); i++) { if (maxIndex < yRawDataLineList.get(i).size()) { Double maxValue = yRawDataLineList.get(i).get(maxIndex); showAlertTextValuesList.set(i, String.valueOf(maxValue <= 0 ? 0 : maxValue)); } else { showAlertTextValuesList.set(i, "0"); } } } if (currentTounchX - blwidh < dip2px(coverRoundWidth + coverRoundMarginLeft)) { isShowLineRight = true; } else if (currentTounchX + dip2px(coverRoundWidth + coverRoundMarginLeft) > canvasWidth - blwidh) { isShowLineRight = false; } else { isShowLineRight = true; } isShowVerticalLine = true; isShowCoverRound = false; } private void dealTouchEventDown(MotionEvent event) { currentTounchX = event.getX(); moveLineTopText = ""; if (currentTounchX <= minTouchXPoint) { currentTounchX = minTouchXPoint; a: for (Map.Entry<Integer, Integer> entry : startTempList.entrySet()) { if (entry.getValue() == minTouchXPoint) { if (isShowTopTextIndexList.get(entry.getKey())) { moveLineTopText = xRawDatas.get(entry.getKey()); } else { moveLineTopText = ""; } break a; } } ArrayList<Integer> startIndexList = new ArrayList<>(); for (int i = 0; i < linesStartIndexMap.size(); i++) { startIndexList.add(linesStartIndexMap.get(i)); } int minIndex = 0; if (startIndexList.size() > 0) { minIndex = Collections.min(startIndexList); } for (int i = 0; i < yRawDataLineList.size(); i++) { if (minIndex < yRawDataLineList.get(i).size()) { Double minValue = yRawDataLineList.get(i).get(minIndex); showAlertTextValuesList.set(i, String.valueOf(minValue <= 0 ? 0 : minValue)); } else { showAlertTextValuesList.set(i, "0"); } } } else if (currentTounchX >= maxTouchXPoint) { currentTounchX = maxTouchXPoint; a: for (Map.Entry<Integer, Integer> entry : endTempList.entrySet()) { if (entry.getValue() == maxTouchXPoint) { if (isShowTopTextIndexList.get(entry.getKey())) { moveLineTopText = xRawDatas.get(entry.getKey()); } else { moveLineTopText = ""; } break a; } } ArrayList<Integer> endIndexList = new ArrayList<>(); for (int i = 0; i < linesEndIndexMap.size(); i++) { endIndexList.add(linesEndIndexMap.get(i)); } int maxIndex = Collections.max(endIndexList); for (int i = 0; i < yRawDataLineList.size(); i++) { if (maxIndex < yRawDataLineList.get(i).size()) { Double maxValue = yRawDataLineList.get(i).get(maxIndex); showAlertTextValuesList.set(i, String.valueOf(maxValue <= 0 ? 0 : maxValue)); } else { showAlertTextValuesList.set(i, "0"); } } } if (currentTounchX - blwidh < dip2px(coverRoundWidth + coverRoundMarginLeft)) { isShowLineRight = true; } else if (currentTounchX + dip2px(coverRoundWidth + coverRoundMarginLeft) > canvasWidth - blwidh) { isShowLineRight = false; } else { isShowLineRight = true; } isShowVerticalLine = true; isShowCoverRound = false; }// starX : blwidh + (canvasWidth - blwidh) / yRawDataLineList.get(i).size() * j// stopX : blwidh + (canvasWidth - blwidh) / yRawDataLineList.get(i).size() * j private void drawCirclePoints(Canvas canvas) { for (int i = 0; i < mPointsLineList.size(); i++) { for (int j = 0; j < mPointsLineList.get(i).length; j++) { if (paintList.size() == mPointsLineList.size()) { canvas.drawCircle(mPointsLineList.get(i)[j].x, mPointsLineList.get(i)[j].y, CIRCLE_SIZE / 2, paintList.get(i)); } else { canvas.drawCircle(mPointsLineList.get(i)[j].x, mPointsLineList.get(i)[j].y, CIRCLE_SIZE / 2, mPaint); } } } } /** * 画所有横向表格,包括X轴 */ private void drawAllXLine(Canvas canvas) { for (int i = 0; i < spacingHeight + 1; i++) { if (i != 0) { mPaint.setColor(res.getColor(R.color.color_99XLine)); mPaint.setStrokeWidth(2); } else { mPaint.setColor(res.getColor(R.color.color_XLine)); mPaint.setStrokeWidth(dip2px(1)); } canvas.drawLine(blwidh, bheight - (Float.valueOf(bheight) / spacingHeight) * i + marginTop, (canvasWidth - blwidh), bheight - (Float.valueOf(bheight) / spacingHeight) * i + marginTop, mPaint);// Y坐标 drawText(String.valueOf(averageValue * i), blwidh / 2, bheight - (Float.valueOf(bheight) / spacingHeight) * i + marginTop, canvas, R.color.color_LeftText, false); } } /** * 画所有纵向表格,包括Y轴 */ private void drawAllYLineList(Canvas canvas) { xListLineList.clear(); for (int i = 0; i < yRawDataLineList.size(); i++) { ArrayList<Float> xListItem = new ArrayList<>(); for (int j = 0; j < xRawDatas.size(); j++) { mPaint.setColor(res.getColor(R.color.color_YLine));// Log.e("xlistitem:", "" + (blwidh + (bwidth) / (xRawDatas.size() - 1) * j));// Log.e("drawAllYLineList打印:", "pointSpace:" + pointSpace + " canvasWidth:" + canvasWidth + " blwidh:" + blwidh + " bwidth:" + bwidth); xListItem.add(blwidh + (Float.valueOf(bwidth)) / (xRawDatas.size() - 1) * j); canvas.drawLine(blwidh + (Float.valueOf(bwidth)) / (xRawDatas.size() - 1) * j, marginTop, blwidh + (Float.valueOf(bwidth)) / (xRawDatas.size() - 1) * j, bheight + marginTop, mPaint);// Log.e("画文本:", xRawDatas.get(j) + "bheight:" + bheight + "canvasWidth:" + canvasWidth); if (i == 0) { drawText(isShowBottomRawDatas.get(j) ? xRawDatas.get(j) : "", blwidh + (Float.valueOf(bwidth)) / (xRawDatas.size() - 1) * j, bheight + dip2px(bottomTextHeight) + dip2px(bottomTextVerticalSpacing), canvas, R.color.color_BottomText, true);// X坐标 } } xListLineList.add(xListItem); } } private void drawScrollLine(Canvas canvas) { Point startp = new Point(); Point endp = new Point(); for (int i = 0; i < mPoints.length - 1; i++) { startp = mPoints[i]; endp = mPoints[i + 1]; int wt = (startp.x + endp.x) / 2; Point p3 = new Point(); Point p4 = new Point(); p3.y = startp.y; p3.x = wt; p4.y = endp.y; p4.x = wt; Path path = new Path(); path.moveTo(startp.x, startp.y); path.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y); canvas.drawPath(path, mPaint); } } Map<Integer, Integer> linesStartIndexMap = new HashMap<>(); Map<Integer, Integer> linesEndIndexMap = new HashMap<>(); private void drawScrollLineList(Canvas canvas) { linesStartIndexMap.clear(); linesEndIndexMap.clear(); for (int i = 0; i < mPointsLineList.size(); i++) { Point startp = new Point(); Point endp = new Point(); for (int j = 0; j < mPointsLineList.get(i).length - 1; j++) { startp = mPointsLineList.get(i)[j]; endp = mPointsLineList.get(i)[j + 1]; if (j == 0 && startp.y >= 0) { linesStartIndexMap.put(i, j); } if (startp.y < 0) { linesStartIndexMap.put(i, j + 1); continue; } linesEndIndexMap.put(i, j + 1); int wt = (startp.x + endp.x) / 2; Point p3 = new Point(); Point p4 = new Point(); p3.y = startp.y; p3.x = wt; p4.y = endp.y; p4.x = wt; Path path = new Path(); path.moveTo(startp.x, startp.y); path.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y); if (paintList.size() == mPointsLineList.size()) { canvas.drawPath(path, paintList.get(i)); } else { canvas.drawPath(path, mPaint); } } } } private void drawLine(Canvas canvas) { Point startp = new Point(); Point endp = new Point(); for (int i = 0; i < mPoints.length - 1; i++) { startp = mPoints[i]; endp = mPoints[i + 1]; canvas.drawLine(startp.x, startp.y, endp.x, endp.y, mPaint); } } private void drawLineList(Canvas canvas) { linesStartIndexMap.clear(); linesEndIndexMap.clear(); for (int i = 0; i < mPointsLineList.size(); i++) { Point startp = new Point(); Point endp = new Point(); for (int j = 0; j < mPointsLineList.get(i).length - 1; j++) { startp = mPointsLineList.get(i)[j]; endp = mPointsLineList.get(i)[j + 1]; if (j == 0 && startp.y >= 0) { linesStartIndexMap.put(i, j); } if (startp.y < 0) { linesStartIndexMap.put(i, j + 1); continue; } linesEndIndexMap.put(i, j + 1); if (paintList.size() == mPointsLineList.size()) { canvas.drawLine(startp.x, startp.y, endp.x, endp.y, paintList.get(i)); } else { canvas.drawLine(startp.x, startp.y, endp.x, endp.y, mPaint); } } } } private void drawText(String text, float x, float y, Canvas canvas, int colorRes, boolean isVerticalText) { drawText(text, x, y, canvas, colorRes, isVerticalText, 12); } private void drawText(String text, float x, float y, Canvas canvas, int colorRes, boolean isVerticalText, int textSizedp) { Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setTextSize(dip2px(textSizedp)); p.setColor(res.getColor(colorRes)); if (isVerticalText) { p.setTextAlign(Paint.Align.CENTER); canvas.drawText(text, x, y, p); } else { p.setTextAlign(Paint.Align.CENTER); canvas.drawText(text, x, y + Float.valueOf(p.getTextSize()) / 3, p); } } private Point[] getPoints() { Point[] points = new Point[yRawData.size()]; for (int i = 0; i < yRawData.size(); i++) { int ph = bheight - (int) (bheight * (yRawData.get(i) / maxValue)); points[i] = new Point(xList.get(i), ph + marginTop); } return points; } private int minTouchXPoint; // 有效触摸点 x轴坐标 起点 private int maxTouchXPoint; // 有效触摸点 x轴坐标 终点 // TODO 下面这个是 获得所有点的方法 /** * 现在需要得到 有效的 x 轴起点和终点 * 在这个方法中计算可得到 * <p> * 目前两条线的起点都为 从0 开始有值时 正常 * 目前其中一条线的起点为 从0 开始有值时 正常 * <p> * 需要处理的是 两条线的起点为 从0 都开始 无值时 603行会报错,因为 startXPointList 集合中无值 因为取值增加目前写的是 每条线 取角标为0(第一个)的加入 */ private ArrayList<Point[]> getPointsLineList() { startTempList.clear(); endTempList.clear(); ArrayList<Point[]> allPointsLineList = new ArrayList<>(); ArrayList<Integer> startXPointList = new ArrayList<>(); ArrayList<Integer> endXPointList = new ArrayList<>(); boolean isHaveValidStartXPoint = false; // 这个标记可用于 判断是否有 有效起点 boolean isHaveValidEndXPoint = false; // 这个标记可用于 判断是否有 有效终点 for (int i = 0; i < yRawDataLineList.size(); i++) { Point[] points = new Point[yRawDataLineList.get(i).size()]; for (int j = 0; j < yRawDataLineList.get(i).size(); j++) { if (yRawDataLineList.get(i).get(j) < 0) { // 为无效点 将不在画线范围 无效点 Y 轴给 -1 值 用于触摸显示 上面方框中的取值 points[j] = new Point(Math.round(xListLineList.get(i).get(j)), -1); } else { int ph = bheight - (int) (bheight * (yRawDataLineList.get(i).get(j) / maxValue));// Log.e("点坐标:", "x:" + xListLineList.get(i).get(j) + " y:" + ph + marginTop); points[j] = new Point(Math.round(xListLineList.get(i).get(j)), ph + marginTop); a: for (int k = 0; k < yRawDataLineList.get(i).size(); k++) { if (yRawDataLineList.get(i).get(k) >= 0) { startXPointList.add(Math.round(xListLineList.get(i).get(k))); startTempList.put(k, Math.round(xListLineList.get(i).get(k))); break a; } } if (j == yRawDataLineList.get(i).size() - 1) { endXPointList.add(Math.round(xListLineList.get(i).get(j))); endTempList.put(j, Math.round(xListLineList.get(i).get(j))); } } } allPointsLineList.add(points); } minTouchXPoint = Collections.min(startXPointList); maxTouchXPoint = Collections.max(endXPointList); Log.e("minTouchXPoint", "" + minTouchXPoint); Log.e("maxTouchXPoint", "" + maxTouchXPoint); return allPointsLineList; } Map<Integer, Integer> startTempList = new HashMap<>(); Map<Integer, Integer> endTempList = new HashMap<>(); public void setData(ArrayList<ArrayList<Double>> yRawData, ArrayList<String> xBottomRawData, ArrayList<Boolean> isShowBottomRawDatas, ArrayList<Boolean> isShowTopTextIndexList, float maxValue, float averageValue, ArrayList<Paint> paintList, ArrayList<String> showAlertTextList) { this.maxValue = maxValue; this.averageValue = averageValue; this.paintList = paintList; this.showAlertTextList = showAlertTextList; showAlertTextValuesList.clear(); for (int i = 0; i < (showAlertTextList.size() > yRawData.size() ? showAlertTextList.size() : yRawData.size()); i++) { showAlertTextValuesList.add("0"); }// this.mPoints = new Point[yRawData.size()]; ArrayList<Point[]> arrayList = new ArrayList<>(); for (int i = 0; i < yRawData.size(); i++) { arrayList.add(new Point[yRawData.get(i).size()]); } this.mPointsLineList = arrayList; this.xRawDatas = xBottomRawData; this.isShowBottomRawDatas = isShowBottomRawDatas; this.isShowTopTextIndexList = isShowTopTextIndexList;// this.yRawData = yRawData; this.yRawDataLineList = yRawData; this.spacingHeight = maxValue / averageValue; isShowCoverRound = false; isShowVerticalLine = false; // 第一次 初始值都为0 onSizeChanged 方法只会走一次, 走过之后就会计算有值 pointSpace = (canvasWidth - blwidh * 2) / (xRawDatas.size() - 1);// Log.e("setData打印:", "pointSpace:" + pointSpace + " canvasWidth:" + canvasWidth + " blwidh:" + blwidh + " bwidth:" + bwidth); // 重绘一下 postInvalidate(); } public void clearView(boolean isClearView) { this.clearView = isClearView; // 重绘一下 postInvalidate(); } public void clearView(boolean isClearView, String clearAfterShowText) { this.clearView = isClearView; this.clearAfterShowText = clearAfterShowText; // 重绘一下 postInvalidate(); } public void setTotalvalue(int maxValue) { this.maxValue = maxValue; } public void setPjvalue(int averageValue) { this.averageValue = averageValue; } public void setMargint(int marginTop) { this.marginTop = marginTop; } public void setMarginb(int marginBottom) { this.marginBottom = marginBottom; } public void setMstyle(Linestyle mStyle) { this.mStyle = mStyle; } public void setBheight(int bheight) { this.bheight = bheight; } /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public int dip2px(float dpValue) { return (int) (dpValue * dm.density + 0.5f); }
package com.zz.kotlintest;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;/** * Created by wangx on 2016/7/6. * 线程池管理器 */public class ThreadPoolManager { // 单例 饿汉式 (线程安全的) 懒汉式 private static ThreadPoolManager instance = new ThreadPoolManager(); private ThreadPoolProxy threadPoolProxy; private ThreadPoolProxy shortThreadPoolProxy; private ThreadPoolManager() { } /** * 获取线程池管理器对象 * * @return */ public static ThreadPoolManager getInstance() { return instance; } /** * 1 请求 2.读写文件 * 最大效率 cpu核心数: cpu * 2 + 1 */ public ThreadPoolProxy createThreadPool() { if (threadPoolProxy == null) threadPoolProxy = new ThreadPoolProxy(5, 5, 5000); return threadPoolProxy; } public ThreadPoolProxy createShortThreadPool() { if (shortThreadPoolProxy == null) shortThreadPoolProxy = new ThreadPoolProxy(3, 3, 5000); return shortThreadPoolProxy; } /** * 设置线程池 */ public class ThreadPoolProxy { private int corePoolSize; private int maximumPoolSize; private long keepAliveTime; private ThreadPoolExecutor threadPoolExecutor; public ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long keepAliveTime) { this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.keepAliveTime = keepAliveTime; } public void execute(Runnable runnable) { // 线程池 // corePoolSize 初始化的 线程数量 //maximumPoolSize 增加的最大数量 不包含初始化的线程数量 //keepAliveTime 存活的时长 // 时间单位 if (threadPoolExecutor == null) threadPoolExecutor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(10)); threadPoolExecutor.execute(runnable); } public void cancel(Runnable runnable) { // 1. 不为空 2. 不停止 3.没有崩溃 if (threadPoolExecutor != null && !threadPoolExecutor.isTerminated() && !threadPoolExecutor.isShutdown()) threadPoolExecutor.remove(runnable); } }}
package com.zz.kotlintest;import android.os.Handler;/** * Created by wangx on 2016/7/6. * 线程的工具类 1. 在子线程中执行的操作 2.在主线程中执行操作 */public class ThreadUtils { /** * 在子线程中执行 * @param runnable */ public static void runOnBackThread(Runnable runnable){ //线程池// new Thread( runnable).start(); ThreadPoolManager.getInstance().createThreadPool().execute(runnable); } private static Handler handler = new Handler(); /** * 主线程中执行操作 * @param runnable */ public static void runOnUiThread(Runnable runnable){ handler.post(runnable); }}
开始 xml 代码了:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/llContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.zz.kotlintest.view.LineGraphicView android:id="@+id/lineGraphicView" android:layout_width="match_parent" android:layout_height="300dp" /> <Button android:id="@+id/btnDataChanges" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:layout_gravity="center_horizontal" android:text="切换数据显示"/></LinearLayout>
开始Activity 代码了:
package com.zz.kotlintest;import android.graphics.DashPathEffect;import android.graphics.Paint;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v7.app.AppCompatActivity;import android.util.DisplayMetrics;import android.view.View;import android.widget.Button;import android.widget.LinearLayout;import android.widget.RelativeLayout;import com.zz.kotlintest.view.LineGraphicView;import com.zz.kotlintest.view.SplineChart01View;import java.util.ArrayList;import java.util.Map;/** * @Author xifangzheng * Created by zz on 2017/9/13 16:30. * class explain: * update: upAuthor: explain: */public class LineActivity extends AppCompatActivity { private Button btnDataChanges; boolean isDataA = true; private LineGraphicView lineGraphicView; private ArrayList<String> showAlertText; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_line); btnDataChanges = (Button) findViewById(R.id.btnDataChanges); lineGraphicView = (LineGraphicView) findViewById(R.id.lineGraphicView); showAlertText = new ArrayList<>(); showAlertText.add("蓝线"); showAlertText.add("绿线"); showAlertText.add("红线"); btnDataChanges.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { isDataA = !isDataA; if (isDataA) { lineGraphicView.setStyle(LineGraphicView.Linestyle.Line); dataA(lineGraphicView); } else { lineGraphicView.setStyle(LineGraphicView.Linestyle.Curve); dataB(lineGraphicView); } } }); dataA(lineGraphicView); } private void dataA(LineGraphicView lineGraphicView) { ArrayList<ArrayList<Double>> lineListA = new ArrayList<>(); ArrayList yList = new ArrayList<Double>(); yList.add((double) 2.103); yList.add(4.05); yList.add(6.60); yList.add(3.08); yList.add(4.32); yList.add(2.0);// yList.add(5.0);// yList.add(9.0);// yList1.add(5.0); lineListA.add(yList); ArrayList yList1 = new ArrayList<Double>(); yList1.add((double) 2.103); yList1.add(1.05); yList1.add(2.60); yList1.add(3.08); yList1.add(2.32); yList1.add(4.0);// yList1.add(6.0);// yList1.add(9.0);// yList1.add(5.0); lineListA.add(yList1); ArrayList<String> xRawDatas = new ArrayList<String>(); xRawDatas.add("1"); xRawDatas.add("2"); xRawDatas.add("3"); xRawDatas.add("4"); xRawDatas.add("5"); xRawDatas.add("6"); xRawDatas.add("7");// xRawDatas.add("05-26");// lineGraphicView.setData(yList, xRawDatas, 8, 2); ArrayList<Paint> paintList = new ArrayList<>(); Paint paint1 = new Paint(); paint1.setColor(getResources().getColor(R.color.color_4c88fc)); paint1.setStrokeWidth(lineGraphicView.dip2px(1)); paint1.setStyle(Paint.Style.STROKE);// paint1.setPathEffect(new DashPathEffect(new float[]{20, 20}, 0)); paintList.add(paint1); Paint paint2 = new Paint(); paint2.setColor(getResources().getColor(R.color.color_2bd63f)); paint2.setStrokeWidth(lineGraphicView.dip2px(1)); paint2.setStyle(Paint.Style.STROKE); paint2.setPathEffect(new DashPathEffect(new float[]{10, 10}, 0)); paintList.add(paint2); lineGraphicView.setDrawCirclePoints(false); lineGraphicView.setData(lineListA, xRawDatas, 20, 2, paintList, showAlertText); } private void dataB(LineGraphicView lineGraphicView) { ArrayList<ArrayList<Double>> lineListB = new ArrayList<>(); ArrayList<Double> yList = new ArrayList<Double>(); yList.add(Double.valueOf(-1)); yList.add(Double.valueOf(-1)); yList.add(Double.valueOf(-1)); for (int i = 0; i < 20; i++) { yList.add(Double.valueOf(Math.round(Math.random() * 1000))); }// yList.add(Double.valueOf(-1));// yList.add(Double.valueOf(-1)); lineListB.add(yList); ArrayList<Double> yList1 = new ArrayList<Double>(); yList1.add(Double.valueOf(-1)); yList1.add(Double.valueOf(-1));// yList1.add(Double.valueOf(-1)); for (int i = 0; i < 20; i++) { yList1.add(Double.valueOf(Math.round(Math.random() * 1000))); } lineListB.add(yList1); ArrayList<Double> yList2 = new ArrayList<Double>(); for (int i = 0; i < 20; i++) { yList2.add(Double.valueOf(Math.round(Math.random() * 1000))); } lineListB.add(yList2); ArrayList<String> xRawDatas = new ArrayList<String>();// lineGraphicView.setData(yList, xRawDatas, 8, 2); for (int i = 0; i < 25; i++) { if (i == 0) { xRawDatas.add("0"); } else if (i % 6 == 0) { xRawDatas.add(String.valueOf(i)); } else { xRawDatas.add(""); } } ArrayList<Paint> paintList = new ArrayList<>(); Paint paint1 = new Paint(); paint1.setColor(getResources().getColor(R.color.color_4c88fc)); paint1.setStrokeWidth(lineGraphicView.dip2px(1)); paint1.setStyle(Paint.Style.STROKE); paint1.setAntiAlias(true);// paint1.setPathEffect(new DashPathEffect(new float[]{20, 20}, 0)); paintList.add(paint1); Paint paint2 = new Paint(); paint2.setColor(getResources().getColor(R.color.color_2bd63f)); paint2.setStrokeWidth(lineGraphicView.dip2px(1)); paint2.setStyle(Paint.Style.STROKE); paint2.setPathEffect(new DashPathEffect(new float[]{10, 10}, 0)); paint2.setAntiAlias(true); paintList.add(paint2); Paint paint3 = new Paint(); paint3.setColor(getResources().getColor(R.color.color_DefaultDrawLine)); paint3.setStrokeWidth(lineGraphicView.dip2px(1)); paint3.setStyle(Paint.Style.STROKE); paint3.setPathEffect(new DashPathEffect(new float[]{10, 10}, 0)); paint3.setAntiAlias(true); paintList.add(paint3); lineGraphicView.setDrawCirclePoints(false); lineGraphicView.setData(lineListB, xRawDatas, 1000, 200, paintList,showAlertText); }}
阅读全文
2 0
- 自定义折线、曲线图,根据触摸滑动动态获取值
- Android 自定义View之绘制折线图、曲线图
- android 自定义View 动态绘制曲线图
- angularjs自定义折线图+滑动条指令
- 自定义android折线图,实现左右滑动和快速滑动
- 自定义曲线图
- 自定义曲线图
- 自定义曲线图
- Android获取触摸手势实现左右滑动
- iOS 中各种折线.曲线.图形绘制方法集锦(折线图.走势图.柱状图.动态曲线图.形状图.划线图)
- iOS 中各种折线.曲线.图形绘制方法集锦(折线图.走势图.柱状图.动态曲线图.形状图.划线图)
- jfreechar做好的折线+曲线图
- 折线 曲线图的实现,图表
- Android 实时曲线图/折线图
- Android自定义折线图和曲线图(曲线被渐变色填充)
- jQuery基于Canvas的微信运动折线图动态曲线图插件 - wcMotionChart
- 2048中根据触摸点来判断滑动方向
- 获取动态数据到echarts-折线图
- android Camera 数据流程分析
- Service基类--添加非空校验方法
- 关于Java对象
- Android
- mysql导入csv中文数据乱码问题分析与解决
- 自定义折线、曲线图,根据触摸滑动动态获取值
- 多重MVC
- 微信小程序wx.uploadfile 本地文件转base64
- js数组循环删除元素或对象
- NetWordUtils
- 自定义View实现箭头沿圆转动
- QT5 QThread
- Kotlin学习笔记(一)
- AsyncTask+请求数据