-
废话不多说先上图咯
图一 -
至于怎么做呢 咱们可以先获取下折线图数据分析一波
{
"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<AdvLineChart> 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<AdvLineChart> 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);
}
}
网友评论