看图
样例代码
layout.xml
<com.XXXXX.utils.GraphBendLine android:id="@+id/ghost_view" android:layout_width="200dp" android:layout_height="200dp" android:layout_margin="40dp" app:node_bar_line_color="#F9FEFD" app:node_bar_line_dan_color="#BAEFE6" app:node_color="#D7F5F0" app:node_line_color="#8BE4D4" />
attr.xml
<declare-styleable name="nodeLine"> <!--节点默认颜色--> <attr name="node_color" format="color" /> <!--曲线颜色--> <attr name="node_bar_line_color" format="color" /> <!--曲线颜色浅色--> <attr name="node_bar_line_dan_color" format="color" /> <!--进度条默认颜色--> <attr name="node_line_color" format="color" /> </declare-styleable>
activity
GraphBendLine mLine = (GraphBendLine) findViewById(R.id.ghost_view); timeList.add(80f); timeList.add(0f); timeList.add(50f); timeList.add(20f); timeList.add(60f); timeList.add(20f); timeList.add(90f); dataList.add("3.15"); dataList.add("3.16"); dataList.add("3.17"); dataList.add("3.18"); dataList.add("3.19"); dataList.add("3.20"); dataList.add("3.21"); mLine.updateTime(timeList, dataList);
画布类
public class GraphBendLine extends View { private float sumHeight;//总控件的高度 private float sumWidth;//总空间的宽度 private float maxY;//y轴最大值 private float minY;//y轴最小值 private float centerY;//y轴中间值 private String unitY;//y轴值单位 private float maxTime;//最大的时间 用来划分单位的 最小就是20 X1.2是为了给上方和下方预留空间 private Paint linePaint;//线的画笔 private Paint shadowPaint;//阴影的画笔 private Paint mPaint;//曲线画笔 private Paint circlePaint;//圆点画笔 private Paint circlePaint2;//圆点画笔 private Paint scorePaint; private Paint textPaint;//文字的画笔 private ArrayList<Float> dataList;//监测值 private ArrayList<String> timeList;//底部的时间 private float oneHeight; //每一个小段所要分成的高 private float oneWidth;//每一个小段所要分成的宽 private float buttomHeiht; //给底部一排日期预留出的时间 private Path baseLinePath;//折线路径 private float smoothness = 0.36f; //折线的弯曲率 private Paint baseShadow;//折线下的阴影的画笔 private String lineColor = "#CBF2ED";//节点默认颜色 "#17CAAA" private String barLineColor = "#BAEFE6";//曲线覆盖区颜色 private String barLineDanColor = "#D7F5F0";//曲线覆盖区浅颜色颜色 "#F9FEFD" private String nodeColor = "#8BE4D4";//节点默认颜色 private ArrayList<PointF> xyList;//储存定好的坐标点的集合 public GraphBendLine(Context context) { super(context); initPaint(); } public GraphBendLine(Context context, AttributeSet attrs) { super(context, attrs); // mContext = context; TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.nodeLine); lineColor = ta.getString(R.styleable.nodeLine_node_line_color); nodeColor = ta.getString(R.styleable.nodeLine_node_color); barLineColor = ta.getString(R.styleable.nodeLine_node_bar_line_color); barLineDanColor = ta.getString(R.styleable.nodeLine_node_bar_line_dan_color); ta.recycle(); initPaint(); } /** * 初始化画笔 * * @linpaint 线条画笔 * @shadowPaint 阴影画笔 */ private void initPaint() { //画线的画笔 linePaint = new Paint(); linePaint.setColor(Color.parseColor("#333333")); linePaint.setAntiAlias(true); linePaint.setTextSize(dp2px(getContext(), 9)); linePaint.setStrokeWidth(dp2px(getContext(), 1)); //画背景的画笔 shadowPaint = new Paint(); shadowPaint.setColor(Color.parseColor(lineColor)); shadowPaint.setAntiAlias(true); //画最下方文字的画笔 textPaint = new Paint(); textPaint.setColor(Color.parseColor("#333333")); textPaint.setAntiAlias(true); textPaint.setTextSize(dp2px(getContext(), 8)); circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); circlePaint.setColor(Color.WHITE); circlePaint.setStrokeWidth(dp2px(getContext(), 2)); circlePaint.setStyle(Paint.Style.STROKE); circlePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG); circlePaint2.setColor(Color.parseColor(lineColor)); circlePaint2.setStyle(Paint.Style.FILL); baseShadow = new Paint(); baseShadow.setAntiAlias(true); baseShadow.setColor((Color.WHITE & 0x40FFFFFF) | 0x10000000); baseShadow.setStyle(Paint.Style.FILL); buttomHeiht = dp2px(35);//线距离底部高度 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.parseColor(lineColor)); mPaint.setStrokeWidth(dp2px(getContext(), 2)); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeCap(Paint.Cap.ROUND); scorePaint = new Paint(Paint.ANTI_ALIAS_FLAG); scorePaint.setStyle(Paint.Style.STROKE); scorePaint.setStrokeCap(Paint.Cap.ROUND); scorePaint.setColor(Color.parseColor("#DDDDDD")); scorePaint.setStrokeWidth(dp2px(0.5f)); baseLinePath = new Path(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); sumHeight = getMeasuredHeight(); sumWidth = getMeasuredWidth(); } private void measure() { maxTime = maxY;//最大为300 for (int i = 0; i < dataList.size(); i++) { if (maxTime <= dataList.get(i)) { maxTime = dataList.get(i); } } if (maxTime < 20) { maxTime = 20; } String text = "V"; Rect rect = new Rect(); textPaint.getTextBounds(text, 0, text.length(), rect); oneHeight = ((sumHeight - buttomHeiht - 2 * rect.height()) / maxTime); oneWidth = sumWidth / 28; } /** * 设置最大最小值 */ public void setMaxMin(float max, float min, float center, String unit) { this.maxY = max; this.minY = min; this.centerY = center; this.unitY = unit; } /** * 更新阅读时间 */ public void updateTime(ArrayList<Float> timeList, ArrayList<String> bottomList) { this.dataList = timeList; this.timeList = bottomList; if (this.dataList != null && this.dataList.size() > 0 && timeList != null && timeList.size() > 0) { invalidate(); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (dataList == null || timeList == null) return; maxTime = getMaxTime(dataList); measure(); toGetXy();//获取x和y的坐标 toDrawLine(canvas); } private void toGetXy() { int leftWidth = dp2px(13);//距离右边宽度 xyList = new ArrayList<>(); for (int i = 0; i < dataList.size(); i++) { float x = oneWidth + i * 4 * oneWidth; float time = dataList.get(i);//每点时间 float y = (sumHeight - (oneHeight * time)); xyList.add(new PointF(x + leftWidth, y - buttomHeiht)); } } /** * 画线 */ private void toDrawLine(Canvas canvas) { if (xyList == null || xyList.size() == 0) { return; } List<PointF> NewPoints = new ArrayList<>(); NewPoints.addAll(xyList); float lX = 0; float lY = 0; baseLinePath.reset(); baseLinePath.moveTo(NewPoints.get(0).x, NewPoints.get(0).y); for (int i = 1; i < NewPoints.size(); i++) { PointF p = NewPoints.get(i); PointF firstPointF = NewPoints.get(i - 1); float x1 = firstPointF.x + lX; float y1 = firstPointF.y + lY; PointF secondPointF = NewPoints.get(i + 1 < NewPoints.size() ? i + 1 : i); lX = (secondPointF.x - firstPointF.x) / 2 * smoothness; lY = (secondPointF.y - firstPointF.y) / 2 * smoothness; float x2 = p.x - lX; float y2 = p.y - lY; if (y1 == p.y) { y2 = y1; } baseLinePath.cubicTo(x1, y1, x2, y2, p.x, p.y); } canvas.drawPath(baseLinePath, mPaint); if (timeList.size() > 1) { for (int i = 0; i < timeList.size(); i++) { float x = NewPoints.get(i).x; float y = NewPoints.get(i).y; float time = dataList.get(i); String mThumbText = String.valueOf(time); Rect rect = new Rect(); linePaint.getTextBounds(mThumbText, 0, mThumbText.length(), rect); canvas.drawText(mThumbText, x - rect.width() / 2, y - dp2px(6), linePaint);//画最上面字体 canvas.drawCircle(x, y, dp2px(3), circlePaint); canvas.drawCircle(x, y, dp2px(2), circlePaint2); Rect rectf = new Rect(); textPaint.getTextBounds(timeList.get(i), 0, timeList.get(i).length(), rectf); canvas.drawText(timeList.get(i), x - rectf.width() / 2, sumHeight - dp2px(2), textPaint);//画最下方的字体 } drawArea(canvas, NewPoints); } drawBottomLine(canvas); drawLine(canvas); } /** * 分割线 * * @param canvas */ private void drawLine(Canvas canvas) { // String text = minY + ""; Rect rect = new Rect(); scorePaint.getTextBounds(text, 0, text.length(), rect); //0 canvas.drawText(text, 0, sumHeight - buttomHeiht - rect.height() / 2, textPaint); //60 text = centerY + ""; float y = (sumHeight - (oneHeight * centerY)); canvas.drawText(text, 0, y - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint); canvas.drawLine(0, y - buttomHeiht, sumWidth, y - buttomHeiht, scorePaint); //100 text = maxY + "(" + unitY + ")"; float y2 = (sumHeight - (oneHeight * maxY)); canvas.drawText(text, 0, y2 - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint); canvas.drawLine(0, y2 - buttomHeiht, sumWidth, y2 - buttomHeiht, scorePaint); } /** * 底部标线 * * @param canvas */ private void drawBottomLine(Canvas canvas) { Rect rect = new Rect(); scorePaint.getTextBounds("0", 0, "0".length(), rect); canvas.drawLine(0, sumHeight - dp2px(15) - rect.height() / 2, sumWidth, sumHeight - dp2px(15) - rect.height() / 2, scorePaint);//画底部灰线 } /** * 阴影层叠 * * @param canvas * @param Points */ private void drawArea(Canvas canvas, List<PointF> Points) { LinearGradient mShader = new LinearGradient(0, 0, 0, getMeasuredHeight(), new int[]{Color.parseColor(nodeColor), Color.parseColor(barLineColor), Color.parseColor(barLineDanColor)}, new float[]{0.5f, 0.65f, 0.85f}, Shader.TileMode.REPEAT); baseShadow.setShader(mShader); if (Points.size() > 0 && xyList != null && xyList.size() > 0) { baseLinePath.lineTo(xyList.get(Points.size() - 1).x, sumHeight - buttomHeiht); baseLinePath.lineTo(xyList.get(0).x, sumHeight - buttomHeiht); baseLinePath.close(); canvas.drawPath(baseLinePath, baseShadow); } } public int dp2px(float dp) { final float scale = this.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } /** * 取出时间里面的最大的一个用来计算总长度 * * @param timeList * @return */ public float getMaxTime(List<Float> timeList) { maxTime = 0; for (int i = 0; i < timeList.size(); i++) { if (maxTime < timeList.get(i)) { maxTime = timeList.get(i); } } if (maxTime <= 10) { maxTime = 10; } maxTime *= 1.2; return maxTime; } public int dp2px(Context context, float dp) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } public float dp2px(Resources resources, float dp) { final float scale = resources.getDisplayMetrics().density; return dp * scale + 0.5f; } public float sp2px(Resources resources, float sp) { final float scale = resources.getDisplayMetrics().scaledDensity; return sp * scale; } }
以上就是全部实现