华为健康 有个图表,客户想要,,UI呢,竟然还给满足了。可辛苦了我等啊。 具体效果如下

知识点如下: 1,图形绘制 圆角矩形,线,三角形 2,渐变处理 Shader
初始化操作
public class SleepChartView extends View {
private List<SleepChartValueBean> mDataList;
private float mWidthPixels = 0;// 屏幕的宽高
private float mHeightPixels = 0;
private float mTotalTime = 100f;
private int width = 0; // view 的 整体宽度和高度
private int height = 0;
private int mViewWidth = 0; // 图标可用的宽度 -- 除去下方虚线 和 右侧竖线
private int mViewHeight = 0;
private int leftPadding = 5, rightPadding = 10, topPadding = 30, bottomPadding = 15;
private int elementHeight = 20; // 单独的标识的高度
private int elementLineHeight = 30;
private Paint mMainPaint = new Paint(); // 绘制矩形的paint
private Paint mLinePaint = new Paint();
private Paint mVerticalLinePaint = new Paint();
private Paint mDashPathEffectLinePaint = new Paint();
int roundRectR = ConvertUtils.dp2px(2); //圆角角度
private float currDrawLeftPoint = leftPadding;
public SleepChartView(Context context) {
this(context, null);
}
public SleepChartView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SleepChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mWidthPixels = getResources().getDisplayMetrics().widthPixels;
mHeightPixels = getResources().getDisplayMetrics().heightPixels;
mMainPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mMainPaint.setStrokeWidth(5);
mMainPaint.setAntiAlias(true);
// mMainPaint.setColor(Color.parseColor("#A374F7"));
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setStrokeWidth(5);
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(Color.parseColor("#A374F7"));
mVerticalLinePaint.setStyle(Paint.Style.STROKE);
mVerticalLinePaint.setStrokeWidth(ConvertUtils.dp2px(2));
mVerticalLinePaint.setAntiAlias(true);
mVerticalLinePaint.setColor(Color.parseColor("#CDE4E4"));
mDashPathEffectLinePaint.setStyle(Paint.Style.STROKE);
mDashPathEffectLinePaint.setStrokeWidth(ConvertUtils.dp2px(2));
mDashPathEffectLinePaint.setAntiAlias(true);
mDashPathEffectLinePaint.setColor(Color.parseColor("#806AA544"));
mDashPathEffectLinePaint.setPathEffect(new DashPathEffect(new float[]{12, 10,}, 0));
}
测量大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int measuredWidth = width, measuredHeight = height;
if (widthMode != MeasureSpec.EXACTLY) {
measuredWidth = (int) mWidthPixels;
}
if (heightMode != MeasureSpec.EXACTLY) {
measuredHeight = (int) (mHeightPixels / 3) + 40;
}
setMeasuredDimension(measuredWidth, measuredHeight);
}
View的大小,初始化每个节点和连接线的高度
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
mViewWidth = width - leftPadding - rightPadding;
mViewHeight = height - topPadding - bottomPadding;
elementHeight = mViewHeight / 4;
elementLineHeight = mViewHeight / 4 / 2;
}
绘制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.drawColor(Color.WHITE);
currDrawLeftPoint = leftPadding;
for (int i = 0; i < mDataList.size(); i++) {
SleepChartValueBean itemData = mDataList.get(i);
int currTimeLong = itemData.getTimeLong();
double timeScale = currTimeLong * 1.0 / mTotalTime;// 当前的时间占比
float currItemWidth = (float) (mViewWidth * timeScale);
int elementLevel = itemData.getElementLevel();
mMainPaint.setShader(null);
mLinePaint.setShader(null);
int currStatus = itemData.getStatus();
// 第一个特殊处理,因为连接线处是平角
if (0 == i) {
Path roundRectPath = new Path();
float left = currDrawLeftPoint;
float top = topPadding + elementLevel * elementHeight + elementLevel * elementLineHeight;
float right = left + currItemWidth;
float bottom = top + elementHeight;
RectF rectf = new RectF(left, top, right, bottom);
roundRectPath.addRoundRect(rectf, roundRectR, roundRectR, Path.Direction.CCW);
// 0,离床 1:清醒 2:浅水 3:深睡
int startColor = Color.parseColor("#FD886C");
int endColor = Color.parseColor("#A1B092");
if (0 == currStatus) {
int[] colors = {Color.parseColor("#FD886C"), Color.parseColor("#C49879"), Color.parseColor("#A1A281")};
float[] position = {0f, 0.7f, 1.0f};
LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
mMainPaint.setShader(linearGradient);
endColor = Color.parseColor("#A1A281");
}
if (1 == currStatus) {
int[] colors = {Color.parseColor("#4DBFE7"), Color.parseColor("#48BDC2"), Color.parseColor("#45BC9F")};
float[] position = {0f, 0.7f, 1.0f};
LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
mMainPaint.setShader(linearGradient);
endColor = Color.parseColor("#A1A281");
}
if (2 == currStatus) {
int[] colors = {Color.parseColor("#3DB964"), Color.parseColor("#3FBA7A"), Color.parseColor("#41BA89")};
float[] position = {0f, 0.5f, 1.0f};
LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
mMainPaint.setShader(linearGradient);
startColor = colors[2];
}
if (3 == currStatus) {
int[] colors = {Color.parseColor("#A274F6"), Color.parseColor("#7F8DC2"), Color.parseColor("#6E98AA")};
float[] position = {0f, 0.7f, 1.0f};
LinearGradient linearGradient = new LinearGradient(left, bottom, left, top, colors, position, Shader.TileMode.CLAMP);
mMainPaint.setShader(linearGradient);
startColor = colors[2];
}
canvas.drawPath(roundRectPath, mMainPaint);
int nextIndex = i + 1;
SleepChartValueBean nextItemData = mDataList.get(nextIndex);
if (nextItemData != null) {
int nextElementLevel = nextItemData.getElementLevel();
Path pathLine = new Path();
Path pathTriangle = new Path();
// 下一个的top
int nextTop = topPadding + nextElementLevel * elementHeight + nextElementLevel * elementLineHeight;
int nextBottom = nextTop + elementHeight;
if (nextElementLevel < elementLevel) {
// 右上
pathTriangle.moveTo(right - roundRectR, top);
pathTriangle.lineTo(right, top);
pathTriangle.lineTo(right, top + roundRectR);
pathLine.moveTo(right, top + roundRectR);
pathLine.lineTo(right, nextBottom - roundRectR);
if (nextItemData.getStatus() == 0) {
int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#9EA888")};
float[] position = {0f, 0.5f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
if (nextItemData.getStatus() == 1) {
int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#45BC9F")};
float[] position = {0f, 0.5f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
if (nextItemData.getStatus() == 2) {
int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#40BA7F")};
float[] position = {0f, 0.5f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
}
if (nextElementLevel > elementLevel) {
//右下
pathTriangle.moveTo(right - roundRectR, bottom);
pathTriangle.lineTo(right, bottom - roundRectR);
pathTriangle.lineTo(right, bottom);
pathLine.moveTo(right, bottom - roundRectR);
pathLine.lineTo(right, nextTop + roundRectR);
if (nextItemData.getStatus() == 2) {
int[] colors = {endColor, Color.parseColor("#5ABF8C"), Color.parseColor("#3FB975")};
float[] position = {0f, 0.6f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, bottom - roundRectR, right, nextTop, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
if (nextItemData.getStatus() == 3) {
int[] colors = {endColor, Color.parseColor("#5AAA8A"), Color.parseColor("#7990BA")};
float[] position = {0f, 0.6f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, bottom - roundRectR, right, nextTop, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
}
pathTriangle.close();
canvas.drawPath(pathLine, mLinePaint);
if (currItemWidth > roundRectR * 2) {
canvas.drawPath(pathTriangle, mMainPaint);
}
}
} else {
Path path = new Path();
float left = currDrawLeftPoint;
float top = topPadding + elementLevel * elementHeight + elementLevel * elementLineHeight;
float right = left + currItemWidth;
float bottom = top + elementHeight;
RectF rectf = new RectF(left, top, right, bottom);
path.addRoundRect(rectf, roundRectR, roundRectR, Path.Direction.CCW);
int startColor = Color.parseColor("#6E98AA");
int endColor = Color.parseColor("#A1B092");
// 0,离床 1:清醒 2:浅水 3:深睡
if (0 == currStatus) {
int[] colors = {Color.parseColor("#FD886C"), Color.parseColor("#C49879"), Color.parseColor("#A1A281")};
float[] position = {0f, 0.7f, 1.0f};
LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
mMainPaint.setShader(linearGradient);
endColor = Color.parseColor("#A1A281");
}
if (1 == currStatus) {
int[] colors = {Color.parseColor("#4DBFE7"), Color.parseColor("#48BDC2"), Color.parseColor("#45BC9F")};
float[] position = {0f, 0.7f, 1.0f};
LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
mMainPaint.setShader(linearGradient);
endColor = Color.parseColor("#45BC9F");
}
if (2 == currStatus) {
int[] colors = {Color.parseColor("#3DB964"), Color.parseColor("#3FBA7A"), Color.parseColor("#41BA89")};
float[] position = {0f, 0.5f, 1.0f};
LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
mMainPaint.setShader(linearGradient);
startColor = colors[2];
}
if (3 == currStatus) {
int[] colors = {Color.parseColor("#A274F6"), Color.parseColor("#7F8DC2"), Color.parseColor("#6E98AA")};
float[] position = {0f, 0.7f, 1.0f};
LinearGradient linearGradient = new LinearGradient(left, bottom, left, top, colors, position, Shader.TileMode.CLAMP);
mMainPaint.setShader(linearGradient);
startColor = colors[2];
}
canvas.drawPath(path, mMainPaint);
SleepChartValueBean afterItemData = mDataList.get(i - 1);
int afterLevel = afterItemData.getElementLevel();
Path pathTriangle = new Path();
if (elementLevel > afterLevel) {
pathTriangle.moveTo(left + roundRectR, top);//定位到 三角形右角
pathTriangle.lineTo(left + 1, top - roundRectR);
pathTriangle.lineTo(left + 1, top + roundRectR);
}
if (elementLevel < afterLevel) {
// 左下角 三角形
pathTriangle.moveTo(left + roundRectR, bottom);//定位到 三角形左角
pathTriangle.lineTo(left + 1, bottom - roundRectR);
pathTriangle.lineTo(left + 1, bottom + roundRectR);
}
pathTriangle.close();
if (currItemWidth > roundRectR * 2) {
canvas.drawPath(pathTriangle, mMainPaint);
}
if (i < mDataList.size() - 1) {
SleepChartValueBean nextItemData = mDataList.get(i + 1);
// 是否有下一个,如果有,则绘制抵达的直线
if (nextItemData != null) {
int nextElementLevel = nextItemData.getElementLevel();
Path pathLine = new Path();
Path nextPathTriangle = new Path();
// 下一个的top
int nextTop = topPadding + nextElementLevel * elementHeight + nextElementLevel * elementLineHeight;
int nextBottom = nextTop + elementHeight;
if (nextElementLevel < elementLevel) {
// 右上
nextPathTriangle.moveTo(right - roundRectR, top);
nextPathTriangle.lineTo(right - 1, top - roundRectR);
nextPathTriangle.lineTo(right - 1, top + roundRectR);
pathLine.moveTo(right, top + roundRectR);
pathLine.lineTo(right, nextBottom - roundRectR);
if (nextItemData.getStatus() == 0) {
int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#9EA888")};
float[] position = {0f, 0.5f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
if (nextItemData.getStatus() == 1) {
int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#45BC9F")};
float[] position = {0f, 0.5f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
if (nextItemData.getStatus() == 2) {
int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#40BA7F")};
float[] position = {0f, 0.5f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
}
if (nextElementLevel > elementLevel) {
//右下
nextPathTriangle.moveTo(right - roundRectR, bottom);
nextPathTriangle.lineTo(right - 1, bottom - roundRectR);
nextPathTriangle.lineTo(right - 1, bottom + roundRectR);
pathLine.moveTo(right, bottom - roundRectR);
pathLine.lineTo(right, nextTop + roundRectR);
if (nextItemData.getStatus() == 2) {
int[] colors = {endColor, Color.parseColor("#5ABF8C"), Color.parseColor("#3FB975")};
float[] position = {0f, 0.6f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, bottom - roundRectR, right, nextTop, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
if (nextItemData.getStatus() == 3) {
int[] colors = {endColor, Color.parseColor("#5AAA8A"), Color.parseColor("#7990BA")};
float[] position = {0f, 0.6f, 1.0f};
LinearGradient linearGradient = new LinearGradient(right, bottom - roundRectR, right, nextTop, colors, position, Shader.TileMode.CLAMP);
mLinePaint.setShader(linearGradient);
}
}
nextPathTriangle.close();
canvas.drawPath(pathLine, mLinePaint);
if (currItemWidth > roundRectR * 2) {
canvas.drawPath(nextPathTriangle, mMainPaint);
}
}
}
}
currDrawLeftPoint = currDrawLeftPoint + currItemWidth;
}
canvas.drawLine(currDrawLeftPoint + ConvertUtils.dp2px(2), 0, currDrawLeftPoint + ConvertUtils.dp2px(2), getHeight() - ConvertUtils.dp2px(1), mVerticalLinePaint);
Path bottomLinePath = new Path();
bottomLinePath.moveTo(getWidth(), getHeight() - ConvertUtils.dp2px(1));
bottomLinePath.lineTo(0, getHeight() - ConvertUtils.dp2px(1));
canvas.drawPath(bottomLinePath, mDashPathEffectLinePaint);
canvas.restore();
}
public void setData(List<SleepChartValueBean> list) {
if (list == null || list.isEmpty()) {
mDataList = new ArrayList<>();
} else {
mDataList = list;
}
postInvalidate();
}
public void setTotalTime(float totalTime) {
mTotalTime = totalTime;
}
}