废话不多说先上图咯

图一

至于怎么做呢 咱们可以先获取下折线图数据分析一波

{

"code": 200,

"message": "",

"data": {

"list": [

{

"day": "2020-10-22",

"readCount": 0,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-21",

"readCount": 0,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-20",

"readCount": 1,

"okGroupCount": 0,

"streamCount": 17,

"buyCount": 0

},

{

"day": "2020-10-19",

"readCount": 0,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-18",

"readCount": 0,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-17",

"readCount": 1,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-16",

"readCount": 4,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-15",

"readCount": 3,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-14",

"readCount": 1,

"okGroupCount": 0,

"streamCount": 2,

"buyCount": 0

},

{

"day": "2020-10-13",

"readCount": 4,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-12",

"readCount": 2,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-11",

"readCount": 0,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

},

{

"day": "2020-10-10",

"readCount": 3,

"okGroupCount": 0,

"streamCount": 0,

"buyCount": 0

}

]

}

}

获取到数据后 我对数据先进行了稍微处理 循环遍历获取他们里面最大值,当做表格的上限,当然 我最后表格的上限是用的最大值又乘了1.1

图二

javaBean AdvLineChart

//广告折线图bean

public class AdvLineChart {

private String day;

private long readCount; //阅读人数

private long okGroupCount; //拼成数量

private long streamCount; //引流量

private long buyCount; //下单量

private String dayDetail; //自己转换的日期 不带年

private float x;

private int position;

public String getDay() {

return day;

}

public void setDay(String day) {

this.day = day;

}

public long getReadCount() {

return readCount;

}

public void setReadCount(long readCount) {

this.readCount = readCount;

}

public long getOkGroupCount() {

return okGroupCount;

}

public void setOkGroupCount(long okGroupCount) {

this.okGroupCount = okGroupCount;

}

public long getStreamCount() {

return streamCount;

}

public void setStreamCount(long streamCount) {

this.streamCount = streamCount;

}

public long getBuyCount() {

return buyCount;

}

public void setBuyCount(long buyCount) {

this.buyCount = buyCount;

}

public String getDayDetail() {

return dayDetail;

}

public void setDayDetail(String dayDetail) {

this.dayDetail = dayDetail;

}

public float getX() {

return x;

}

public void setX(float x) {

this.x = x;

}

public int getPosition() {

return position;

}

public void setPosition(int position) {

this.position = position;

}

}

处理过数据后 我们就开始绘制了,我们可以把list里的每一个数据 当做一条竖线 for循环挨个绘制他们

下面我直接贴出自定义view的代码了 里面注释还是蛮清晰的:

里面有一些缺少的类 就是一些dp、数量转换 可以自己定义

//广告折线图

public class AdvLineChartView extends View {

private final static int PADDING_START_ADN_END = 10; //距离左右边距

private float mWidth;

private float mHeight;

private Rect mTopTextRect;

private Rect mBottomTextRect;

private Rect mTipTextRect;

private Paint mLinePaint;

private Paint mTopTextPaint;

private Paint mBottomTextPaint;

private Paint mTipTextPaint;

private Paint mTipTextBgPaint;

private long advMax; //最大价格值

private float max; //最大1.1

private float downX, downY; //按下的位置

private float moveY; //移动的y点

private float lineHeight; //线高

private List advList = new ArrayList<>(); //各个点信息

private AdvLineChart tipViewData;

private CallBack mCallBack;

public AdvLineChartView(Context context) {

this(context, null);

}

public AdvLineChartView(Context context, @Nullable AttributeSet attrs) {

this(context, attrs, 0);

}

public AdvLineChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

init();

}

private void init() {

mTopTextRect = new Rect();

mBottomTextRect = new Rect();

mTipTextRect = new Rect();

mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mLinePaint.setStrokeWidth(2);

mTopTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mTopTextPaint.setColor(getContext().getResources().getColor(R.color.c_1D1D1D));

mTopTextPaint.setTextSize(DensityUtil.dip2px(getContext(), 8));

mBottomTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mBottomTextPaint.setColor(getContext().getResources().getColor(R.color.c_1D1D1D));

mBottomTextPaint.setTextSize(DensityUtil.dip2px(getContext(), 7));

mTipTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mTipTextBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mTipTextBgPaint.setStrokeWidth(2);

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

mWidth = getMeasuredWidth();

mHeight = getMeasuredHeight();

}

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

//点击记录位置

downX = event.getX();

downY = event.getY();

moveY = event.getY();

return true;

case MotionEvent.ACTION_MOVE:

moveY = event.getY();

AdvLineChart clickOnPoint1 = getClickOnPoint(event.getX());

if (null != clickOnPoint1) {

//绘制提示view

tipViewData = clickOnPoint1;

invalidate();

}

//父布局不拦截子布局事件

getParent().requestDisallowInterceptTouchEvent(true);

return true;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

//松开时判断 松开位置在点击位置的有效范围内

if (Math.abs(downX - event.getX()) <= 2 && Math.abs(downY - event.getY()) <= 2) {

AdvLineChart clickOnPoint2 = getClickOnPoint(event.getX());

if (null != clickOnPoint2) {

//绘制提示view

tipViewData = clickOnPoint2;

invalidate();

return true;

} else {

return super.onTouchEvent(event);

}

} else {

return super.onTouchEvent(event);

}

}

return super.onTouchEvent(event);

}

@SuppressLint("DrawAllocation")

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

float paddingStartAndEnd = DensityUtil.dip2px(getContext(), PADDING_START_ADN_END);

for (int i = 0; i < advList.size(); i++) {

AdvLineChart data = advList.get(i);

String topText = data.getDay();

String bottomText = data.getDayDetail();

mTopTextPaint.setColor(getContext().getResources().getColor(R.color.c_1D1D1D));

mTopTextPaint.getTextBounds(topText, 0, topText.length(), mTopTextRect);

mBottomTextPaint.getTextBounds(bottomText, 0, bottomText.length(), mBottomTextRect);

//计算各条线x点

float x = calculationPositionX(i);

//竖线高度

lineHeight = mHeight - mTopTextRect.height() - mBottomTextRect.height() - DensityUtil.dip2px(getContext(), 16);

/** 绘制竖线 **/

drawLine(canvas, x, 0, x, lineHeight);

/** 绘制文字 **/

canvas.drawText(topText, x - mTopTextRect.width() / 2f,

mHeight - DensityUtil.dip2px(getContext(), 9) - mBottomTextRect.height(), mTopTextPaint);

canvas.drawText(bottomText, x - mBottomTextRect.width() / 2f,

mHeight - DensityUtil.dip2px(getContext(), 7), mBottomTextPaint);

/** 绘制横线 只绘制一次 **/

if (i == 0) {

drawLine(canvas, paddingStartAndEnd, 1, mWidth - paddingStartAndEnd, 1);

drawLine(canvas, paddingStartAndEnd, lineHeight / 2, mWidth - paddingStartAndEnd, lineHeight / 2);

drawLine(canvas, paddingStartAndEnd, lineHeight, mWidth - paddingStartAndEnd, lineHeight);

}

/** 绘制分数线 **/

if (i < advList.size() - 1 && max >= 0) {

AdvLineChart nextData = advList.get(i + 1);

//下一个x点

float nextX = calculationPositionX(i + 1);

/** 阅读人数 **/

drawLine(canvas, x, calculationPositionY(lineHeight, data.getReadCount()),

nextX, calculationPositionY(lineHeight, nextData.getReadCount()),

getResources().getColor(R.color.c_BF383E));

/** 拼成数量 **/

drawLine(canvas, x, calculationPositionY(lineHeight, data.getOkGroupCount()),

nextX, calculationPositionY(lineHeight, nextData.getOkGroupCount()),

getResources().getColor(R.color.c_FFC90D));

/** 引流数量 **/

drawLine(canvas, x, calculationPositionY(lineHeight, data.getStreamCount()),

nextX, calculationPositionY(lineHeight, nextData.getStreamCount()),

getResources().getColor(R.color.lightpink));

/** 下单数量 **/

drawLine(canvas, x, calculationPositionY(lineHeight, data.getBuyCount()),

nextX, calculationPositionY(lineHeight, nextData.getBuyCount()),

getResources().getColor(R.color.darkviolet));

//设置当前位置数据的坐标

data.setX(x);

data.setPosition(i);

//最后一个了 把nextX加进去

if (i == advList.size() - 2) {

nextData.setX(nextX);

nextData.setPosition(i + 1);

//默认显示成第一个

if (null == tipViewData) {

tipViewData = advList.get(advList.size() - 1);

moveY = 1;//calculationPositionY(lineHeight, advList.get(advList.size() - 1).getReadCount());

}

}

}

}

//绘制提示view

if (null != tipViewData) {

moveY = moveY < 1 ? 1 : moveY > lineHeight ? lineHeight : moveY;

String title = tipViewData.getDay() + "-" + tipViewData.getDayDetail().replace("/", "-");

String readStr = "阅读";

String read = StringUtil.getLastNum(tipViewData.getReadCount()) + "人";

String okGroupStr = "拼成";

String okGroup = StringUtil.getLastNum(tipViewData.getOkGroupCount()) + "个";

String streamStr = "引流";

String stream = StringUtil.getLastNum(tipViewData.getStreamCount()) + "次";

String buyStr = "下单";

String buy = StringUtil.getLastNum(tipViewData.getBuyCount()) + "笔";

mTipTextPaint.setTextSize(DensityUtil.dip2px(getContext(), 12));

mTipTextPaint.getTextBounds(title, 0, title.length(), mTipTextRect);

//标题高度

int titleHeight = mTipTextRect.height();

mTipTextPaint.setTextSize(DensityUtil.dip2px(getContext(), 10));

mTipTextPaint.getTextBounds(read, 0, read.length(), mTipTextRect);

//其他内容的高度

int otherHeight = mTipTextRect.height();

//阅读宽度

int readWidth = mTipTextRect.width();

mTipTextPaint.getTextBounds(okGroup, 0, okGroup.length(), mTipTextRect);

//拼成宽度

int okGroupWidth = mTipTextRect.width();

mTipTextPaint.getTextBounds(stream, 0, stream.length(), mTipTextRect);

//引流宽度

int streamWidth = mTipTextRect.width();

mTipTextPaint.getTextBounds(buy, 0, buy.length(), mTipTextRect);

//下单宽度

int buyWidth = mTipTextRect.width();

//x点位置

float tipViewX;

if (tipViewData.getX() + DensityUtil.dip2px(getContext(), 80) > mWidth - paddingStartAndEnd) {

tipViewX = mWidth - paddingStartAndEnd - DensityUtil.dip2px(getContext(), 80);

} else {

tipViewX = tipViewData.getX();

}

//框高度 padding上下7 标题padding下3 其他3个文字padding上下3 最后其他文字的上3 加起来就是38

int rectHeight = DensityUtil.dip2px(getContext(), 38) + titleHeight + otherHeight * 4;

//y点位置

if (moveY + rectHeight > lineHeight) {

moveY = lineHeight - rectHeight;

}

/** 绘制白色覆盖背景 **/

RectF rectF = new RectF(tipViewX, moveY,

tipViewX + DensityUtil.dip2px(getContext(), 80), moveY + rectHeight);

mTipTextBgPaint.setStyle(Paint.Style.FILL);

mTipTextBgPaint.setColor(Color.WHITE);

canvas.drawRoundRect(rectF, 0, 0, mTipTextBgPaint);

/** 绘制背景边框 **/

mTipTextBgPaint.setStyle(Paint.Style.STROKE);

mTipTextBgPaint.setColor(getResources().getColor(R.color.c_1D1D1D));

canvas.drawRoundRect(rectF, 0, 0, mTipTextBgPaint);

//strText前面距离

int strText = DensityUtil.dip2px(getContext(), 4);

/** 标题 **/

//标题y位置

float titleY = moveY + DensityUtil.dip2px(getContext(), 7) + titleHeight;

mTipTextPaint.setColor(getContext().getResources().getColor(R.color.c_BF383E));

canvas.drawText(title, tipViewX + strText, titleY, mTipTextPaint);

/** 阅读 **/

//阅读y位置

float readY = titleY + DensityUtil.dip2px(getContext(), 6) + otherHeight;

mTipTextPaint.setColor(getContext().getResources().getColor(R.color.c_BF383E));

canvas.drawText(readStr, tipViewX + strText, readY, mTipTextPaint);

canvas.drawText(read, rectF.right - readWidth - strText, readY, mTipTextPaint);

/** 拼成 **/

//拼成y位置

float okGroupY = readY + DensityUtil.dip2px(getContext(), 6) + otherHeight;

mTipTextPaint.setColor(getContext().getResources().getColor(R.color.c_FFC90D));

canvas.drawText(okGroupStr, tipViewX + strText, okGroupY, mTipTextPaint);

canvas.drawText(okGroup, rectF.right - okGroupWidth - strText, okGroupY, mTipTextPaint);

/** 引流 **/

//引流y位置

float streamY = okGroupY + DensityUtil.dip2px(getContext(), 6) + otherHeight;

mTipTextPaint.setColor(getContext().getResources().getColor(R.color.lightpink));

canvas.drawText(streamStr, tipViewX + strText, streamY, mTipTextPaint);

canvas.drawText(stream, rectF.right - streamWidth - strText, streamY, mTipTextPaint);

/** 下单 **/

//下单y位置

float buyY = streamY + DensityUtil.dip2px(getContext(), 6) + otherHeight;

mTipTextPaint.setColor(getContext().getResources().getColor(R.color.darkviolet));

canvas.drawText(buyStr, tipViewX + strText, buyY, mTipTextPaint);

canvas.drawText(buy, rectF.right - buyWidth - strText, buyY, mTipTextPaint);

/** 绘制选中的竖线 **/

drawLine(canvas, tipViewData.getX() < paddingStartAndEnd ? paddingStartAndEnd :

tipViewData.getX() > mWidth - paddingStartAndEnd ? mWidth - paddingStartAndEnd : tipViewData.getX(),

0, tipViewData.getX() < paddingStartAndEnd ? paddingStartAndEnd :

tipViewData.getX() > mWidth - paddingStartAndEnd ? mWidth - paddingStartAndEnd : tipViewData.getX(),

tipViewData.getX() > rectF.left && tipViewData.getX() < rectF.right ? rectF.top - 2 : lineHeight,

getResources().getColor(R.color.c_ED1C24), 3);

/** 绘制下半截的竖线 **/

if (tipViewData.getX() > rectF.left && tipViewData.getX() < rectF.right) {

drawLine(canvas, tipViewData.getX() < paddingStartAndEnd ? paddingStartAndEnd :

tipViewData.getX() > mWidth - paddingStartAndEnd ? mWidth - paddingStartAndEnd : tipViewData.getX(),

rectF.bottom + 2, tipViewData.getX() < paddingStartAndEnd ? paddingStartAndEnd :

tipViewData.getX() > mWidth - paddingStartAndEnd ? mWidth - paddingStartAndEnd : tipViewData.getX(), lineHeight,

getResources().getColor(R.color.c_ED1C24), 3);

}

if (null != mCallBack) {

mCallBack.pointClick(tipViewData, moveY);

}

}

}

/**

* 设置数据

*/

public void setData(List shopList, long shopMax) {

this.advMax = shopMax;

if (null != shopList && shopList.size() > 0) {

this.advList.clear();

this.advList.addAll(shopList);

max = (float) (this.advMax * 1.1);

invalidate();

}

}

/**

* 移动tipsView pre是否往前 num数量

*/

public void moveTipsView(boolean pre, int num) {

if (null != tipViewData) {

//前移 因为数据是从最新的开始排的 所以往后选

if (pre) {

if (tipViewData.getPosition() + num < advList.size()) {

tipViewData = advList.get(tipViewData.getPosition() + num);

} else {

tipViewData = advList.get(advList.size() - 1);

}

invalidate();

} else { //后移

if (tipViewData.getPosition() - num >= 0) {

tipViewData = advList.get(tipViewData.getPosition() - num);

} else {

tipViewData = advList.get(0);

}

invalidate();

}

}

}

private void drawLine(Canvas canvas, float startX, float startY, float endX, float endY) {

drawLine(canvas, startX, startY, endX, endY, getResources().getColor(R.color.c_1D1D1D), 2);

}

private void drawLine(Canvas canvas, float startX, float startY, float endX, float endY, int color) {

drawLine(canvas, startX, startY, endX, endY, color, 2);

}

private void drawLine(Canvas canvas, float startX, float startY, float endX, float endY, int color, int strokeWidth) {

mLinePaint.setColor(color);

mLinePaint.setStrokeWidth(strokeWidth);

canvas.drawLine(startX, startY, endX, endY, mLinePaint);

}

//计算x位置

private float calculationPositionX(int i) {

//当前数组是从大到小的 计算x点时从最后面开始算

return (mWidth - DensityUtil.dip2px(getContext(), PADDING_START_ADN_END * 2)) /

(advList.size() - 1) * (advList.size() - 1 - i) +

DensityUtil.dip2px(getContext(), PADDING_START_ADN_END);

}

//计算y位置

private float calculationPositionY(float lineHeight, long currentNum) {

float currentPositionY = lineHeight * advMax / max;

float scale = (1 - currentNum / (float) advMax);

return scale == 1 ? lineHeight : lineHeight - currentPositionY + currentPositionY * scale;

}

//计算当前点击是否在点上 是的话返回点对象 否则null

private AdvLineChart getClickOnPoint(float x) {

AdvLineChart advLineChartReturn = null;

for (AdvLineChart advLineChart : advList) {

if (Math.abs(x - advLineChart.getX()) < mWidth / advList.size() / 2) {

advLineChartReturn = advLineChart;

break;

}

}

return advLineChartReturn;

}

public void setOnMoveListener(CallBack callBack) {

this.mCallBack = callBack;

}

public interface CallBack {

void pointClick(AdvLineChart advLineChart, float rawY);

}

}

android自定义曲线控件,Android自定义折线图(可拖动显示)相关推荐

  1. android自定义曲线控件,Android自定义view进阶-- 神奇的贝塞尔曲线

    上一篇介绍了自定义view需要知道的基本函数.新开一篇献给借给我vpn的深圳_奋斗小哥. 转载请注明出处:http://blog.csdn.net/wingichoy/article/details/ ...

  2. android xml图片缩放,Android通过自定义ImageView控件实现图片的缩放和拖动的实现代码...

    概述:通过自定义ImageView控件,在xml布局里面调用自定的组件实现图片的缩放. /** * 自定义的ImageView控制,可对图片进行多点触控缩放和拖动 * * @author qiuwan ...

  3. android studio 画控件,Android Studio 基础控件使用

    TextView android:gravity="center" //文字对其方式 top bottom left right center android:textColor= ...

  4. android 自定义switch控件,Android中switch自定义样式

    android 原生开关按钮控件 Switch 提供样式自定义方式,可供我们修改为适合我们开发使用的样式控件,自定义样式过程如下: 自定义switch切换drawable 新建swith_thumb. ...

  5. android 自定义view控件,Android 自定义View——自定义View控件

    Android给我们提供了大量的View控件,但这还是远远满足不了我们的要求,有时候开发所需要的控件形式是在Android提供的控件中是不存在,这就需要我们自己去定义一个.那么如何自定义控件? 学习自 ...

  6. android 条码扫描控件,Android Zxing条码扫描自定义控件(附代码)

    团队要做一个项目,里面要用到条码扫描,搜了一下,知道了Zxing.这是一个开源的条码扫描程序.官方网站有完整的Android程序可下载.但是,如果想将扫描功能融合在自己开发的程序里,则需要理清设计的思 ...

  7. android 自定义刷新控件,Android开发中MJRefresh自定义刷新动画效果

    有时候我们对自己开发的项目经常不满意,但是我们要达到自定义刷新动画的效果有一定的难度,别着急,下面爱站技术频道和大家分享Android开发中MJRefresh自定义刷新动画效果,一起来学习吧! [一] ...

  8. android自定义选年控件,Android精美日历控件CalendarView自定义使用完全解析

    项目github地址 此框架采用组合的方式,各个模块互相独立,可自由采用各种提供的控件组合,完全自定义自己需要的UI,周视图和月视图可通过简单自定义任意自由绘制,不怕美工提需求!!!下面教程将介绍如何 ...

  9. android遥控杆控件,Android自定义滑杆控件SeekBar多功能版本

    在应用开发中有没有遇到过通过滑杆控件选择一些区间条件实现参数变化?今天我们就来自定义View实现一个多功能又实用的版本SeekBar. Paste_Image.png ](http://upload- ...

最新文章

  1. css游戏代码_介绍CSSBattle-第一个CSS代码搜寻游戏
  2. jquery如何对多个对象绑定同一事件
  3. Java学习从入门到精通-旧版
  4. 厉害了,如何通过双 key 来解决缓存并发问题?
  5. JDBC的批处理操作
  6. 北邮OJ 102. 最远距离 北邮2012网研院复试上机题
  7. 第1次作业:这是我的一个响亮的标题X!
  8. jvm高并发_在JVM上对高并发HTTP服务器进行基准测试
  9. 单调栈 leetcode整理(三)
  10. javaee 中遇到的jdk自带的异常(Exception)
  11. 载 Kubernetes和OpenStack到底是什么关系?先搞清楚,再系列学习
  12. (一)STL体系结构基础介绍
  13. php中result的值,PHP 返回所取得行中字段的值
  14. log4cpp 概述与基本使用实例(一)
  15. Javc处理图片各种效果的类库Java Image Filters
  16. echarts常见图形-时间轴(五)
  17. 新版TCGA的突变SNP数据添加临床信息
  18. MathType如何编辑商标标志
  19. ffmpeg学习:滤镜(实现视频缩放,裁剪,水印等) -
  20. vue3内置组件(Teleport组件,Fragment组件)-传送组件,减少层级

热门文章

  1. 用速腾16线激光雷达跑gmapping
  2. SylixOS 系统工作队列
  3. Python、C语言技能树测评
  4. 微信小程序模板template的使用
  5. 风影ASP.NET基础教学 4 验证控件
  6. 软件企业 双软认定好处、条件及具体内容
  7. 253Echarts - 3D 曲面(皮革材质)
  8. Bugku - 代码审计 | md5函数
  9. css如何实现鼠标移至图片上显示遮罩层及文字
  10. Intellij idea创建.xml文件