Has written a chart before lib, but the pace of development, the most difficult to keep up with the steps of product demand change, so changed the original graph library, support chart can integrate the table below shows the corresponding category, use curve to replace the broken line, support for multiple curve shows that increased the animation display, and added some customizable attributes, Supports horizontal and stacked bar charts, as well as multiple graphs and pie charts

1. The rendering

2. The use of various charts

1. Pie chart This is the same as the original use, but with an added animation, see the previous article.Pie chart use.

2. Horizontal multi-bar chart

2.1 the XML layout

 <wellijohn.org.varchart.hor_bar_with_line_chart.ChartLine
        android:id="@+id/chartline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/white"
        app:default_x_visible_num="4.2"// How many columns to display on a screen app:y_interval="40dp"//Y spacing app:y_num_text_max_width="56dp"Y_visible_num: how many columns should be displayed on the y axisCopy the code

2.2 Data Settings

Public class HorBarActivity extends appactivity {private ChartLine mChartline; public class HorBarActivity extends appActivity {private ChartLine mChartline; Private List<List<DotVo>> mMulListDisDots; Private String[] mXdots = new String[]{"08/18"
            , "08/19"."08/20"."08/21"."08/22"."08/23"."08/24"."08/25"."08/26"."08/27"."08/28"."08/29"."09/01"."09/02"."09/23"}; private double mMax = 44; private Random rand = new Random(); private List<CategoryVo> mCategoryList; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_hor_bar);

        initView();
        initMulTestData();
        initCategoryList();
        try {
            mChartline.setYAxisMaxValue(mMax).setXdots(mXdots).setAnimationOpen(true).setListDisDots(mMulListDisDots).
                    setCategoryList(mCategoryList).reDraw();
        } catch (YCoordinateException e) {
            Log.d("MainActivity"."onCreate: "); e.printStackTrace(); * CategoryVo: {* categoryName * private String categoryName; * categoryName * categoryName; * Private List<String> categoryValueList; * } */ private voidinitCategoryList() { mCategoryList = new ArrayList<>(); mCategoryList.add(new CategoryVo()); mCategoryList.add(new CategoryVo()); mCategoryList.add(new CategoryVo()); } /** * private List<List<DotVo>> mMulListDisDots; List<DotVo>> is a graph, */ private voidinitMulTestData() {
        mMulListDisDots = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            ArrayList<DotVo> temp = new ArrayList();
            DotVo tempDotVo = new DotVo("08/18", rand.nextInt((int) mMax));
            temp.add(tempDotVo);
            DotVo tempDotVo1 = new DotVo("08/19", rand.nextInt((int) mMax));
            temp.add(tempDotVo1);
            DotVo tempDotVo2 = new DotVo("08/20", rand.nextInt((int) mMax));
            temp.add(tempDotVo2);
            DotVo tempDotVo3 = new DotVo("08/21", rand.nextInt((int) mMax));
            temp.add(tempDotVo3);
            DotVo tempDotVo4 = new DotVo("08/22", rand.nextInt((int) mMax));
            temp.add(tempDotVo4);
            DotVo tempDotVo5 = new DotVo("08/23", rand.nextInt((int) mMax));
            temp.add(tempDotVo5);
            DotVo tempDotVo6 = new DotVo("09/02", rand.nextInt((int) mMax));
            temp.add(tempDotVo6);

            mMulListDisDots.add(temp);
        }


    }


    private void initView() { mChartline = findViewById(R.id.chartline); }}Copy the code

3. Stacking bar charts

3.1 the XML layout

<wellijohn.org.varchart.overlay_bar_with_line_chart.OverLayBarChartLine
        android:id="@+id/overlay_chart_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:visibility="visible"
        app:overlay_default_x_visible_num="4.2"
        app:overlay_y_interval="40dp"
        app:overlay_y_num_text_max_width="56dp" />
Copy the code

3.2 Data Setting, as in 2.2

3. Key points of implementation

3.1 The width needs to be rewritten, onMeasure, because the width of the control is larger than the width of the screen. The width is composed of the points and spacing displayed on the X axis and the width distance occupied by the text on the Y axis.

int widthParentMeasureMode = MeasureSpec.getMode(widthMeasureSpec); int widthParentMeasureSize = MeasureSpec.getSize(widthMeasureSpec); int heightParentMeasureMode = MeasureSpec.getMode(heightMeasureSpec); int heightParentMeasureSize = MeasureSpec.getSize(heightMeasureSpec); int resultWidthSize = 0; int resultHeightSize = 0; int resultWidthMode = MeasureSpec.EXACTLY; // Int resultHeightMode = MeasureSpec.EXACTLY; int paddingWidth = getPaddingLeft() + getPaddingRight(); int paddingHeight = getPaddingTop() + getPaddingBottom(); ViewGroup.LayoutParams thisLp = getLayoutParams(); Switch (widthParentMeasureMode) {// The parent class is not restricted to the child classcaseMeasureSpec.UNSPECIFIED: // This represents the width of the layout written outif (thisLp.width > 0) {
                    resultWidthSize = thisLp.width;
                    resultWidthMode = MeasureSpec.EXACTLY;
                } else {
                    resultWidthSize = (int) (getYMaxTextWidth() + mXinterval * mXdots.length);
                    resultWidthMode = MeasureSpec.UNSPECIFIED;
                }
                break;
            caseMeasureSpec.AT_MOST: // This represents the width written in the layoutif (thisLp.width > 0) {
                    resultWidthSize = thisLp.width;
                    resultWidthMode = MeasureSpec.EXACTLY;
                } else if (thisLp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                    resultWidthSize = Math.max(0, widthParentMeasureSize - paddingWidth);
                    resultWidthMode = MeasureSpec.AT_MOST;
                } else if (thisLp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
                    resultWidthSize = (int) (getYMaxTextWidth() + mXinterval * mXdots.length);
                    resultWidthMode = MeasureSpec.AT_MOST;
                }
                break;
            caseMeasureSpec.EXACTLY: // This represents the width in the layoutif (thisLp.width > 0) {
                    resultWidthSize = Math.min(widthParentMeasureSize, thisLp.width);
                    resultWidthMode = MeasureSpec.EXACTLY;
                } else if (thisLp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                    resultWidthSize = widthParentMeasureSize;
                    resultWidthMode = MeasureSpec.EXACTLY;
                } else if (thisLp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
                    resultWidthSize = (int) (getYMaxTextWidth() + mXinterval * mXdots.length);
                    resultWidthMode = MeasureSpec.AT_MOST;
                }
                break;
        }


        setMeasuredDimension(MeasureSpec.makeMeasureSpec(resultWidthSize, resultWidthMode),
                MeasureSpec.makeMeasureSpec(resultHeightSize, resultHeightMode));
Copy the code

3.2 Planning a fixed area, the part beyond the area is not visible, this was used in the previous bitmap to achieve, always feel awkward, later read the official source code, understand the Canvas clipRect method, we draw this piece of time, onDraw method call

int clipRestoreCount = canvas.save(); canvas.clipRect(mContentRect); // called before drawingdoDraw(); // Start a series of events with the desired canvas. RestoreToCount (clipRestoreCount); // After drawing, restoreToCount is called to return to the state before drawingCopy the code

3.3 We can use ValueAnimator for almost all animations, such as pie charts: one of his drawings is a 0-360 Angle transformation, so we can

    private void startPathAnim(long duration) {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 360);
        valueAnimator.setDuration(duration);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mDrawAngle = (float) animation.getAnimatedValue(); ViewCompat.postInvalidateOnAnimation(CirclePercentChart.this); }}); valueAnimator.start(); }Copy the code

Then use mDrawAngle to control the Angle of each drawing, so that you can have the feeling of drawing from 0 to 360 degrees, the animation of the bar chart is the same, to be constant should be changeable.

3.4 Algorithm of Bessel curve drawing

    ifMoveTo (mDots[0], mDots[1] + (mlasthorliney-mdots [1]) * mPhaseY); / / start}else {
        floatCPX = preX + (mDots[0] -prex) / 2.0f; path.cubicTo(cpx, preY + (mLastHorLineY - preY) * mPhaseY, cpx, mDots[1] + (mLastHorLineY - mDots[1]) * mPhaseY, mDots[0], mDots[1] + (mLastHorLineY - mDots[1]) * mPhaseY); }Copy the code

In Bessel curve drawing, I carefully check the rule for calculating the control points, according to three points, to calculate the two control points, but such mapped inside the three point curve is very smooth, but the fourth point of cohesion in the following, the feeling is not very good, so I still use the above calculation method to calculate the control points, algorithm I posted, The parameters are the x and y coordinates and bending coefficients of 1, 2, and 3 respectively

public static ControlPonits getControlPoints(double x0, double y0, double x1, double y1, double x2, double y2, double paramCoefficient) {
        double d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2));
        double d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
        double fa = paramCoefficient * d01 / (d01 + d12);   // scaling factor for triangle Ta
        double fb = paramCoefficient * d12 / (d01 + d12);   // ditto for Tb, simplifies to fb=t-fa
        double p1x = x1 - fa * (x2 - x0);    // x2-x0 is the width of triangle T
        double p1y = y1 - fa * (y2 - y0);    // y2-y0 is the height of T
        double p2x = x1 + fb * (x2 - x0);
        double p2y = y1 + fb * (y2 - y0);
        ControlPonits tempControlPoints = new ControlPonits();

        tempControlPoints.beforeControlPointX = (float) p1x;
        tempControlPoints.beforeControlPointY = (float) p1y;
        tempControlPoints.afterControlPointX = (float) p2x;
        tempControlPoints.afterControlPointY = (float) p2y;
        return tempControlPoints;
    }
Copy the code

If you have good ideas, I hope you can exchange them. If you think the article is helpful for you to customize the view, I hope you can help to like or pay attention to it. Thank you