mpandroidchart在x轴标签旋转时,根据数据集更改垂直调整大小

r1zk6ea1  于 2021-06-29  发布在  Java
关注(0)|答案(1)|浏览(374)

问题是当我更改数据集时,我的mpandroidchart条形图会垂直调整大小。我的底部x轴标签是45度旋转,这是什么原因的问题。如果不旋转底部x标签-则不会调整大小。

我附上了一张柱状图在更新数据集前后的外观图片。
有人知道如何摆脱图表的无意调整大小吗?

编辑(+代码)

在此,我附上说明问题的代码:

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.formatter.DefaultValueFormatter;
import com.github.mikephil.charting.formatter.ValueFormatter;
import com.github.mikephil.charting.renderer.XAxisRenderer;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.Utils;
import com.github.mikephil.charting.utils.ViewPortHandler;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Random;

public class MainActivityB extends AppCompatActivity {

    private BarChart barChart;
    private BarDataSet barDataSet;

    private Button button;

    private Random random = new Random();
    private int xEntries = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_b);

        setupStackedBarChart();
        initButton();
    }

    private void initButton() {
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                chartsRenew();
            }
        });
    }

    private void setupStackedBarChart(){
        barChart = findViewById(R.id.stackedbarchart_statistics_b);

        barChart.animateY(600);
        barChart.getDescription().setEnabled(false); // disable description text
        barChart.setPinchZoom(true);
        barChart.getLegend().setEnabled(false);
        barChart.setMinimumHeight(600);

        ArrayList<BarEntry> barEntries = fillBarEntries();

        // Create Data Set
        barDataSet = new BarDataSet(barEntries, "Bar Set");
        barDataSet.setAxisDependency(YAxis.AxisDependency.LEFT);
        barDataSet.setColors(createColors());
        barDataSet.setValueFormatter(new MyValueFormatterBarChart(0)); // rounds off values on the bars
        barDataSet.setValueTextSize(14);

        // Refine the chart
        BarData barData = new BarData(barDataSet);
        barChart.setData(barData);

        // Formatter for the top x-axis
        barChart.setXAxisRenderer(new DoubleXLabelAxisRenderer(
                barChart.getViewPortHandler(),
                barChart.getXAxis(),
                barChart.getTransformer(YAxis.AxisDependency.LEFT),
                new ValueFormatter() {
                    @Override
                    public String getFormattedValue(float value) {
                        return "blahbla";
                    }
                })
        );

        // Formatter for the bottom x-axis
        ValueFormatter bottomXFormatter = new ValueFormatter() {

            private DecimalFormat mFormat = new DecimalFormat("###,###,##0");

            @Override
            public String getFormattedValue(float value) {

                int MAX_LENGTH = 15;

                if ((int) value >= xEntries) return ""; // Sometimes formatter fails. This check helps.

                String number = mFormat.format(value + 1) + ") ";

                int repetitions = 1 + random.nextInt(16);
                String name = new String(new char[repetitions]).replace("\0", "a");

                String xLabel = number + name;

                if (xLabel.length() > MAX_LENGTH){
                    xLabel = xLabel.substring(0, MAX_LENGTH - 2) + "..";
                    Log.d("try", "size >: " + xLabel.length());
                }

                return xLabel;
            }

        };

        final XAxis xAxis = barChart.getXAxis();
        xAxis.setPosition(XAxis.XAxisPosition.BOTH_SIDED);
        //xAxis.setGranularity(1f);
        xAxis.setGranularityEnabled(true); // To remove the extra labels appearing when zooming into the bar chart
        xAxis.setValueFormatter(bottomXFormatter);
        xAxis.setLabelRotationAngle(-45);

        final YAxis leftYAxis = barChart.getAxisLeft();
        leftYAxis.setAxisMinimum(0);
        leftYAxis.setGranularity(1f);
        leftYAxis.setTextSize(12f);

        final YAxis rightYAxis = barChart.getAxisRight();
        rightYAxis.setEnabled(false);

        barChart.setVisibleXRangeMaximum(3f); // Needs invalidate() before changes take place
        barChart.moveViewToX(barEntries.size()); // Includes invalidate in it
        barChart.setDragDecelerationFrictionCoef(0.6f);

    }

    private void chartsRenew(){
        ArrayList<BarEntry> barEntries = fillBarEntries();

        if (barEntries.isEmpty()) {
            barChart.clear();
        } else {
            barDataSet.setValues(barEntries);
            barChart.setData(new BarData(barDataSet));
        }

        barChart.notifyDataSetChanged();
        barChart.fitScreen(); // Without it the following methods work faulty
        barChart.setVisibleXRangeMaximum(3f); // Needs invalidate() before changes take place
        barChart.moveViewToX(barEntries.size()); // Includes invalidate in it

        Log.d("try", "0: " + barChart.getXAxis().mLabelRotatedHeight);
        Log.d("try", "0: " + barChart.getXAxis().mLabelHeight);

        Log.d("try", "Y: " + barChart.getAxisLeft().mAxisMaximum);

        Log.d("try", "0: " + barChart.getXAxis().getSpaceMax());
        Log.d("try", "0: " + barChart.getXAxis().getSpaceMin());
        Log.d("try", "1: " + barChart.getViewPortHandler().getContentRect().left);
        Log.d("try", "2: " + barChart.getViewPortHandler().getContentRect().top);
        Log.d("try", "3: " + barChart.getViewPortHandler().getContentRect().right);
        Log.d("try", "4: " + barChart.getViewPortHandler().getContentRect().bottom);
        Log.d("try", "bug: " + barChart.getViewPortHandler().getContentRect().height()); //TODO here it is !!!! bug
        //barChart.getViewPortHandler().getContentRect().set(43.25f, 100f, 988.625f, 500f);

        //barChart.invalidate();
    }

    private ArrayList<BarEntry> fillBarEntries() {
        ArrayList<BarEntry> barEntries = new ArrayList<>();

        xEntries = random.nextInt(7);
        for (int i = 0; i < xEntries; i++) {
            barEntries.add(new BarEntry(i, random.nextInt(4)));
        }

        return barEntries;
    }

    //Additional Method-----------------------------------------------------------------------------
    public int[] createColors(){
        int[] colors = new int[11];

        colors[0] = Color.parseColor("#006884");
        colors[1] = Color.parseColor("#00909E");
        colors[2] = Color.parseColor("#80DBEC");
        colors[3] = Color.parseColor("#FFD09D");
        colors[4] = Color.parseColor("#B00051");
        colors[5] = Color.parseColor("#F68370");
        colors[6] = Color.parseColor("#FEABB9");
        colors[7] = Color.parseColor("#6E006C");
        colors[8] = Color.parseColor("#CE9C48");
        colors[9] = Color.parseColor("#CF97D7");
        colors[10] = Color.parseColor("#FEDD27");
        //colors[11] = Color.parseColor("#91278F");

        return colors;
    }

    //CLASSES--------------------------------------------------------------------------------------
    public class MyValueFormatterBarChart extends DefaultValueFormatter {
        private DecimalFormat mFormat;

        public MyValueFormatterBarChart(int digits) {
            super(digits);
            mFormat = new DecimalFormat("###,###,###,##0");
        }

        @Override
        public String getFormattedValue(float value) {
            return mFormat.format(value);
        }

    }

    public class DoubleXLabelAxisRenderer extends XAxisRenderer {

        private ValueFormatter topValueFormatter;

        public DoubleXLabelAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer transformer, ValueFormatter topValueFormatter) {
            super(viewPortHandler, xAxis, transformer);
            this.topValueFormatter = topValueFormatter;
        }

        @Override
        public void renderAxisLabels(Canvas c) {

            if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled())
                return;

            float yOffset = mXAxis.getYOffset();

            mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
            mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
            mAxisLabelPaint.setColor(mXAxis.getTextColor());

            MPPointF pointF = MPPointF.getInstance(0, 0);
            if (mXAxis.getPosition() == XAxis.XAxisPosition.TOP) {
                pointF.x = 0.5f;
                pointF.y = 1.0f;
                drawLabels(c, mViewPortHandler.contentTop() - yOffset, pointF);

            } else if (mXAxis.getPosition() == XAxis.XAxisPosition.TOP_INSIDE) {
                pointF.x = 0.5f;
                pointF.y = 1.0f;
                drawLabels(c, mViewPortHandler.contentTop() + yOffset + mXAxis.mLabelRotatedHeight, pointF);

            } else if (mXAxis.getPosition() == XAxis.XAxisPosition.BOTTOM) {
                pointF.x = 0.5f;
                pointF.y = 0.0f;
                drawLabels(c, mViewPortHandler.contentBottom() + yOffset, pointF);

            } else if (mXAxis.getPosition() == XAxis.XAxisPosition.BOTTOM_INSIDE) {
                pointF.x = 0.5f;
                pointF.y = 0.0f;
                drawLabels(c, mViewPortHandler.contentBottom() - yOffset - mXAxis.mLabelRotatedHeight, pointF);

            } else { // BOTH SIDED
                pointF.x = 0.5f;
                pointF.y = 1.0f;
                drawLabelsTop(c, mViewPortHandler.contentTop() - yOffset, pointF);
                pointF.x = 0.5f;
                pointF.y = 0.0f;
                drawLabels(c, mViewPortHandler.contentBottom() + yOffset, pointF);
            }
            MPPointF.recycleInstance(pointF);
        }

        private void drawLabelsTop(Canvas c, float pos, MPPointF anchor) {

            final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
            boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled();

            float[] positions = new float[mXAxis.mEntryCount * 2];

            for (int i = 0; i < positions.length; i += 2) {

                // only fill x values
                if (centeringEnabled) {
                    positions[i] = mXAxis.mCenteredEntries[i / 2];
                } else {
                    positions[i] = mXAxis.mEntries[i / 2];
                }
            }

            mTrans.pointValuesToPixel(positions);

            for (int i = 0; i < positions.length; i += 2) {

                float x = positions[i];

                if (mViewPortHandler.isInBoundsX(x)) {

                    String label = topValueFormatter.getFormattedValue(mXAxis.mEntries[i / 2]);

                    if (mXAxis.isAvoidFirstLastClippingEnabled()) {

                        // avoid clipping of the last
                        if (i == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) {
                            float width = Utils.calcTextWidth(mAxisLabelPaint, label);

                            if (width > mViewPortHandler.offsetRight() * 2
                                    && x + width > mViewPortHandler.getChartWidth())
                                x -= width / 2;

                            // avoid clipping of the first
                        } else if (i == 0) {

                            float width = Utils.calcTextWidth(mAxisLabelPaint, label);
                            x += width / 2;
                        }
                    }

                    drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees);
                }
            }
        }
    }
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingStart="10dp"
    android:paddingEnd="10dp"
    android:paddingBottom="16dp">

    <com.github.mikephil.charting.charts.BarChart
        android:id="@+id/stackedbarchart_statistics_b"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:layout_marginTop="226dp"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Refresh data set"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="100dp"/>

</FrameLayout>

添加 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' 在grad尔。

pokxtpni

pokxtpni1#

就我的知识和测试结果而言,如果不修改库,这里似乎没有任何简单的解决方案。不精确的解决办法是 all XAxis label 在类似宽度(不能始终100%精确)中: Left padding 带有非空格字符的xaxis标签文本
应用合理的最大yaxis值通过 leftYAxis.setAxisMaximum() 使用 Monospace 字体,这样至少对于字母数字字符,宽度会相似。
测试代码更新:

....
        String number = mFormat.format(value + 1) + ")";
        int repetitions = 1 + random.nextInt(16);
        String name = new String(new char[repetitions]).replace("\0", "a");
        String xLabel = number + name;

        //Update Starts Here: Left padding with '*'
        xLabel = String.format("%17s", xLabel).replace(' ', '*');
        //Ends Here:

        if (xLabel.length() > MAX_LENGTH){
        ....

...
        final YAxis rightYAxis = barChart.getAxisRight();
        rightYAxis.setEnabled(false);

        //Update Starts Here:
        leftYAxis.setAxisMaximum(3f);
        xAxis.setGranularityEnabled(true);
        xAxis.setTextSize(10f);
        xAxis.setTypeface(Typeface.MONOSPACE); //Use monospace
        //Ends Here:

        barChart.setVisibleXRangeMaximum(3f); // Needs invalidate() before changes take place
        ...

相关问题