看图
样例代码
layout.xml
<com.XXXXX.utils.GraphBendLineandroid: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();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);sumHeight = getMeasuredHeight();sumWidth = getMeasuredWidth();}private void measure() {maxTime = maxY;//最大为300for (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();}}@Overrideprotected 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);//0canvas.drawText(text, 0, sumHeight - buttomHeiht - rect.height() / 2, textPaint);//60text = 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);//100text = 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;} }
以上就是全部实现