前面介绍过一种实现不同区间不同颜色的方案,在手机上使用看不出什么问题,但是在大平板上使用的时候在颜色变换点的位置有一个断口。现在分享另一种实现方式,相对更简单一些。主要思路是将不同颜色区间的画板进行拆分。
废话不多说,直接上代码,下面这个是对LineChartRenderer的重写:
package com.example.myjavaandroid.view;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import com.github.mikephil.charting.animation.ChartAnimator;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.renderer.LineChartRenderer;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.util.HashMap;
import java.util.List;
public class MyLineChartRenderer extends LineChartRenderer {
private float yMin = -20f;
private float yMax = 20f;
private String label = "cyLineChart";
public MyLineChartRenderer(LineDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
mChart = chart;
mCirclePaintInner = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaintInner.setStyle(Paint.Style.FILL);
mCirclePaintInner.setColor(Color.WHITE);
}
@Override
protected void drawDataSet(Canvas c, ILineDataSet dataSet) {
if (dataSet.getEntryCount() < 1)
return;
mRenderPaint.setStrokeWidth(dataSet.getLineWidth());
mRenderPaint.setPathEffect(dataSet.getDashPathEffect());
switch (dataSet.getMode()) {
default:
case LINEAR:
case STEPPED:
if (dataSet.getLabel() == label) {
drawSplitLinear(c, dataSet);
} else {
drawLinear(c, dataSet);
}
break;
case CUBIC_BEZIER:
drawCubicBezier(dataSet);
break;
case HORIZONTAL_BEZIER:
drawHorizontalBezier(dataSet);
break;
}
mRenderPaint.setPathEffect(null);
}
private float[] mLineBuffer = new float[4];
private void drawSplitLinear(Canvas c, ILineDataSet dataSet) {
int entryCount = dataSet.getEntryCount();
final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled();
final int pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2;
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
float phaseY = mAnimator.getPhaseY();
mRenderPaint.setStyle(Paint.Style.STROKE);
Canvas canvas = null;
// if the data-set is dashed, draw on bitmap-canvas
if (dataSet.isDashedLineEnabled()) {
canvas = mBitmapCanvas;
} else {
canvas = c;
}
mXBounds.set(mChart, dataSet);
// if drawing filled is enabled
if (dataSet.isDrawFilledEnabled() && entryCount > 0) {
drawLinearFill(c, dataSet, trans, mXBounds);
}
// more than 1 color
if (dataSet.getColors().size() > 1) {
if (mLineBuffer.length <= pointsPerEntryPair * 2)
mLineBuffer = new float[pointsPerEntryPair * 4];
for (int j = mXBounds.min; j <= mXBounds.range mXBounds.min; j ) {
Entry e = dataSet.getEntryForIndex(j);
if (e == null) continue;
mLineBuffer[0] = e.getX();
mLineBuffer[1] = e.getY() * phaseY;
if (j < mXBounds.max) {
e = dataSet.getEntryForIndex(j 1);
if (e == null) break;
if (isDrawSteppedEnabled) {
mLineBuffer[2] = e.getX();
mLineBuffer[3] = mLineBuffer[1];
mLineBuffer[4] = mLineBuffer[2];
mLineBuffer[5] = mLineBuffer[3];
mLineBuffer[6] = e.getX();
mLineBuffer[7] = e.getY() * phaseY;
} else {
mLineBuffer[2] = e.getX();
mLineBuffer[3] = e.getY() * phaseY;
}
} else {
mLineBuffer[2] = mLineBuffer[0];
mLineBuffer[3] = mLineBuffer[1];
}
trans.pointValuesToPixel(mLineBuffer);
if (!mViewPortHandler.isInBoundsRight(mLineBuffer[0]))
break;
// make sure the lines don't do shitty things outside
// bounds
if (!mViewPortHandler.isInBoundsLeft(mLineBuffer[2])
|| (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler
.isInBoundsBottom(mLineBuffer[3])))
continue;
// get the color that is set for this line-segment
mRenderPaint.setColor(dataSet.getColor(j));
canvas.drawLines(mLineBuffer, 0, pointsPerEntryPair * 2, mRenderPaint);
}
} else { // only one color per dataset
if (mLineBuffer.length < Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2)
mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 4];
Entry e1, e2;
e1 = dataSet.getEntryForIndex(mXBounds.min);
if (e1 != null) {
int j = 0;
for (int x = mXBounds.min; x <= mXBounds.range mXBounds.min; x ) {
e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1));
e2 = dataSet.getEntryForIndex(x);
if (e1 == null || e2 == null) continue;
mLineBuffer[j ] = e1.getX();
mLineBuffer[j ] = e1.getY() * phaseY;
if (isDrawSteppedEnabled) {
mLineBuffer[j ] = e2.getX();
mLineBuffer[j ] = e1.getY() * phaseY;
mLineBuffer[j ] = e2.getX();
mLineBuffer[j ] = e1.getY() * phaseY;
}
mLineBuffer[j ] = e2.getX();
mLineBuffer[j ] = e2.getY() * phaseY;
}
if (j > 0) {
trans.pointValuesToPixel(mLineBuffer);
for (YArea yarea : YArea.values()) {
int saveId = canvas.save();
float top = (1 - ((yarea.max > yMax ? yMax : yarea.max) - yMin) / (yMax - yMin)) * mViewPortHandler.getChartHeight();
float bottom = (1 - ((yarea.min < yMin ? yMin : yarea.min) - yMin) / (yMax - yMin)) * mViewPortHandler.getChartHeight();
mRenderPaint.setColor(yarea.color);
final int size = Math.max((mXBounds.range 1) * pointsPerEntryPair, pointsPerEntryPair) * 2;
canvas.clipRect(new Rect(0, (int) top, (int) mViewPortHandler.getChartWidth(), (int) bottom));
mRenderPaint.setColor(yarea.color);
canvas.drawLines(mLineBuffer, 0, size, mRenderPaint);
canvas.restoreToCount(saveId);
}
}
}
}
mRenderPaint.setPathEffect(null);
}
//在这里控制不同区间的颜色,如果这个要经常标动的话,可以作为入参传入,可以提高通用性
private enum YArea {
LOW(-30, -4, Color.parseColor("#07A1F1")),
MEDIUM_LOW(-4, 2, Color.parseColor("#FF000000")),
MEDIUM(2, 8, Color.parseColor("#FFBB86FC")),
MEDIUM_HIGH(8, 16, Color.parseColor("#FF3700B3")),
HIGH(16, 50, Color.parseColor("#BC133A"));
private float min;
private float max;
private int color;
YArea(float min, float max, int color) {
this.min = min;
this.max = max;
this.color = color;
}
}
private HashMap<IDataSet, DataSetImageCache> mImageCaches = new HashMap<>();
private float[] mCirclesBuffer = new float[2];
@Override
protected void drawCircles(Canvas c) {
mRenderPaint.setStyle(Paint.Style.FILL);
float phaseY = mAnimator.getPhaseY();
mCirclesBuffer[0] = 0;
mCirclesBuffer[1] = 0;
List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();
for (int i = 0; i < dataSets.size(); i ) {
ILineDataSet dataSet = dataSets.get(i);
if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() ||
dataSet.getEntryCount() == 0)
continue;
mCirclePaintInner.setColor(dataSet.getCircleHoleColor());
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mXBounds.set(mChart, dataSet);
float circleRadius = dataSet.getCircleRadius();
float circleHoleRadius = dataSet.getCircleHoleRadius();
boolean drawCircleHole = dataSet.isDrawCircleHoleEnabled() &&
circleHoleRadius < circleRadius &&
circleHoleRadius > 0.f;
boolean drawTransparentCircleHole = drawCircleHole &&
dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE;
DataSetImageCache imageCache;
if (mImageCaches.containsKey(dataSet)) {
imageCache = mImageCaches.get(dataSet);
} else {
imageCache = new DataSetImageCache();
mImageCaches.put(dataSet, imageCache);
}
boolean changeRequired = imageCache.init(dataSet);
// only fill the cache with new bitmaps if a change is required
if (changeRequired) {
imageCache.fill(dataSet, drawCircleHole, drawTransparentCircleHole);
}
int boundsRangeCount = mXBounds.range mXBounds.min;
for (int j = mXBounds.min; j <= boundsRangeCount; j ) {
Entry e = dataSet.getEntryForIndex(j);
if (e == null) break;
mCirclesBuffer[0] = e.getX();
mCirclesBuffer[1] = e.getY() * phaseY;
trans.pointValuesToPixel(mCirclesBuffer);
if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0]))
break;
if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) ||
!mViewPortHandler.isInBoundsY(mCirclesBuffer[1]))
continue;
Bitmap circleBitmap = imageCache.getBitmap(j);
for (YArea yArea : YArea.values()) {
if (yArea.min < e.getY() && yArea.max >= e.getY()) {
mRenderPaint.setColor(yArea.color);
break;
}
}
if (circleBitmap != null) {
c.drawCircle(mCirclesBuffer[0], mCirclesBuffer[1], dataSet.getCircleRadius(), mRenderPaint);
}
}
}
}
private class DataSetImageCache {
private Path mCirclePathBuffer = new Path();
private Bitmap[] circleBitmaps;
/**
* Sets up the cache, returns true if a change of cache was required.
*
* @param set
* @return
*/
protected boolean init(ILineDataSet set) {
int size = set.getCircleColorCount();
boolean changeRequired = false;
if (circleBitmaps == null) {
circleBitmaps = new Bitmap[size];
changeRequired = true;
} else if (circleBitmaps.length != size) {
circleBitmaps = new Bitmap[size];
changeRequired = true;
}
return changeRequired;
}
/**
* Fills the cache with bitmaps for the given dataset.
*
* @param set
* @param drawCircleHole
* @param drawTransparentCircleHole
*/
protected void fill(ILineDataSet set, boolean drawCircleHole, boolean drawTransparentCircleHole) {
int colorCount = set.getCircleColorCount();
float circleRadius = set.getCircleRadius();
float circleHoleRadius = set.getCircleHoleRadius();
for (int i = 0; i < colorCount; i ) {
Bitmap.Config conf = Bitmap.Config.ARGB_4444;
Bitmap circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf);
Canvas canvas = new Canvas(circleBitmap);
circleBitmaps[i] = circleBitmap;
mRenderPaint.setColor(set.getCircleColor(i));
if (drawTransparentCircleHole) {
// Begin path for circle with hole
mCirclePathBuffer.reset();
mCirclePathBuffer.addCircle(
circleRadius,
circleRadius,
circleRadius,
Path.Direction.CW);
// Cut hole in path
mCirclePathBuffer.addCircle(
circleRadius,
circleRadius,
circleHoleRadius,
Path.Direction.CCW);
// Fill in-between
canvas.drawPath(mCirclePathBuffer, mRenderPaint);
} else {
canvas.drawCircle(
circleRadius,
circleRadius,
circleRadius,
mRenderPaint);
if (drawCircleHole) {
canvas.drawCircle(
circleRadius,
circleRadius,
circleHoleRadius,
mCirclePaintInner);
}
}
}
}
/**
* Returns the cached Bitmap at the given index.
*
* @param index
* @return
*/
protected Bitmap getBitmap(int index) {
return circleBitmaps[index % circleBitmaps.length];
}
}
}
在view部分将MyLineChartRenderer设置给LineChart:
package com.example.myjavaandroid.fragment;
import android.content.Context;
import android.os.Bundle;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.myjavaandroid.R;
import com.example.myjavaandroid.databinding.FragmentLineChartBinding;
import com.example.myjavaandroid.view.MyLineChartRenderer;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import java.util.ArrayList;
import java.util.List;
public class LineChartFragment extends Fragment {
private Context mContext;
private static LineChartFragment instance;
private FragmentLineChartBinding binding;
private LineChartFragment(Context context) {
mContext = context;
}
public static LineChartFragment getInstance(Context context) {
if (instance == null) {
instance = new LineChartFragment(context);
}
return instance;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_line_chart, null, false);
initData();
initView();
return binding.getRoot();
}
privatevoidinitData(){
......
}
private void initView() {
LineChart lineChart = binding.lineChart;
lineChart.setScaleEnabled(false);
lineChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
//将lineChart的LineChartRenderer设置为重写的MyLineChartRenderer
lineChart.setRenderer(new MyLineChartRenderer(lineChart, lineChart.getAnimator(), lineChart.getViewPortHandler()));
lineChart.getAxisLeft().setAxisMinimum(-20f);
lineChart.getAxisLeft().setAxisMaximum(20f);
}
}
这种实现方式更容易理解和使用,重写的内容也相对少一些。
最后的效果是这样的:
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved