Draw 2 ColumnSeries for each date label


#1

Hi, Guys
I am trying to draw a chart with dateTimeAxis for x Axis and number for y Axis.
And I want to draw 2 columnSeries for each label on the x Axis. The problem is the width of the columnSeries does not take the whole space between each two ticks.
Is it possible to draw the 2 columSeries between 2 ticks and make them take the whole space between the ticks? And also, show all the month labels?

Following is part of the code. Thanks for the help.

public class MainActivity extends AppCompatActivity {

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

    List<MonthlyIO> list =  new MonthlyIO().getMonthlyIO();

    NumberRange returnNumberRange = new NumberRange(Double.valueOf(0.00), Double.valueOf(10000.00));
    DateRange dateRange = new DateRange(list.get(0).date, list.get(list.size() - 1).date);

    ChartFragment chartFragment =
            (ChartFragment) getFragmentManager().findFragmentById(R.id.chart);

    ShinobiChart shinobiChart = chartFragment.getShinobiChart();

    DateTimeAxis xAxis = new DateTimeAxis();
    xAxis.setDefaultRange(dateRange);
    xAxis.setLabelFormat(new SimpleDateFormat("MMM"));
    xAxis.getStyle().setInterSeriesPadding(0);
    xAxis.getStyle().setInterSeriesSetPadding(1);
    xAxis.setMajorTickFrequency(new DateFrequency(1, DateFrequency.Denomination.MONTHS));
    shinobiChart.setXAxis(xAxis);

    NumberAxis yAxis = new NumberAxis();
    shinobiChart.setYAxis(yAxis);

    yAxis.setDefaultRange(returnNumberRange);

    ColumnSeries columnSeries;
    DataAdapter<Date, Number> dataAdapter;

    try {
        for (int i = 0; i < list.size(); i++) {
                columnSeries = new ColumnSeries();
                columnSeries.getStyle().setAreaColor(Color.GREEN);
                columnSeries.getStyle().setLineWidth(2);

                dataAdapter = new SimpleDataAdapter<Date, Number>();
                dataAdapter.add(new DataPoint<Date, Number>(list.get(i).date, list.get(i).income));
                columnSeries.setDataAdapter(dataAdapter);
                shinobiChart.addSeries(columnSeries);

                columnSeries = new ColumnSeries();
                columnSeries.getStyle().setAreaColor(Color.RED);

                dataAdapter = new SimpleDataAdapter<Date, Number>();
                dataAdapter.add(new DataPoint<Date, Number>(list.get(i).date, list.get(i).outlay));
                columnSeries.setDataAdapter(dataAdapter);
                shinobiChart.addSeries(columnSeries);

        }
    } catch (Exception e) {
    }
    shinobiChart.redrawChart();
}

public class MonthlyIO {

    public Date date;
    public Number income;
    public Number outlay;

    public MonthlyIO() {

    }

    public MonthlyIO(Date date, Number income, Number outlay) {
        this.date = date;
        this.income = income;
        this.outlay = outlay;
    }

    public List<MonthlyIO> getMonthlyIO() {
        List<MonthlyIO> list = new ArrayList<MonthlyIO>();
        try {
            list.add(new MonthlyIO(toDate("05/31/2016 "), Double.parseDouble("4000"), Double.parseDouble("3000")));
            list.add(new MonthlyIO(toDate("06/30/2016"), Double.parseDouble("4000"), Double.parseDouble("3000")));
            list.add(new MonthlyIO(toDate("07/29/2016"), Double.parseDouble("4000"), Double.parseDouble("3000")));
            list.add(new MonthlyIO(toDate("08/31/2016"), Double.parseDouble("4000"), Double.parseDouble("3000")));
            list.add(new MonthlyIO(toDate("09/30/2016"), Double.parseDouble("4000 "), Double.parseDouble("3000")));
            list.add(new MonthlyIO(toDate("10/31/2016"), Double.parseDouble("4000"), Double.parseDouble("2000")));
            list.add(new MonthlyIO(toDate("11/30/2016"), Double.parseDouble("4000"), Double.parseDouble("2000")));
            list.add(new MonthlyIO(toDate("12/30/2016"), Double.parseDouble("4000"), Double.parseDouble("3000")));
            list.add(new MonthlyIO(toDate("01/31/2017"), Double.parseDouble("4000"), Double.parseDouble("2000")));
            list.add(new MonthlyIO(toDate("02/28/2017"), Double.parseDouble("5000"), Double.parseDouble("3000")));
            list.add(new MonthlyIO(toDate("03/31/2017"), Double.parseDouble("5000"), Double.parseDouble("2000")));
            list.add(new MonthlyIO(toDate("04/28/2017"), Double.parseDouble("5000"), Double.parseDouble("3000")));
        } catch (Exception e) {
        }
        return list;
    }

    private Date toDate(String dateString) throws Exception {
        SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy", Locale.US);

        return format.parse(dateString);
    }

}

}


#2

Hello,

I think the issue is that you are using multiple DataAdapters and Series, when in this case 2 DataAdapters and 2 Series may be better.

To help explain let’s first think about your first x axis data point which is for May. You start by creating 2 series, each with an x value of a date (in May) and a y value to represent income or outlay. At this point everything is fine. You repeat the process for June but critically you use 2 new series. When the axis decides how wide to make columns it considers all series associated with the axis regardless of if they actually have a data point at the given value. So in your case, for May, the column width is set so that the two series for the month of May will fit alongside all of the other 22 series for June onward, even though the latter contain no data for May.

I built your code and saw this behaviour demonstrated. To get around the issue I modified the code within the main loop so that only 2 series with two DataAdapters were used and added to the chart. For each item in the list I add a data point to the DataAdapter for the income or outlay as applicable. This resulted in a much better use of space, with very little space between the columns.

Following this exercise I did still see a small amount of space between some sets of columns which I expect to be due to the irregular length of months (28, 29, 30 or 31 days). A consideration to correct this would be to replace your DataTimeAxis for a CategoryAxis.

For background, a similar question was asked on StackOverflow which may help:

https://stackoverflow.com/questions/36231730/how-to-set-the-shinobicharts-column-width-fixed-in-android.

As for your labels, they will only be drawn when they can fit on the axis without overlapping. I notice in your screen shot you have a portrait orientation. I would expect them to be visible in landscape?

To get around this issue you would need to consider dynamically changing the labels depending upon the screen orientation. For example, in portrait mode you may choose to use shorter, less frequent labels such as M J J A S O or only show a label to represent the quarter, i.e. JUN, SEP, DEC, MAR.

We offer an API method which may help you with this:

https://www.shinobicontrols.com/docs/android/shinobicharts/latest/apidocs/reference/com/shinobicontrols/charts/ShinobiChart.OnTickMarkDrawListener.html

I hope that this proves useful.

Thanks,

Kai.