Android table as calendar view for whole year

271 Views Asked by At

I want to create a garden calendar in android for 12 months for each plant, with color bars according to sow or harvest action.

Example:

growing calendar

Each month should be divided to 3 quarters, so finally I need 36 columns for one row. (After consideration, it will be enough to have 2 halves per month, so 24 columns).

First row is for Planting inside, second for sow outside and third harvest (ignore the description on example picture).

The simpliest way is to create such an image and just show it via ImageView for each plant. But, the users will have a possibility to set their custom dates of sowing or harvesting, so the calendar should be generated accordingly, can't be fixed image.

For now I've found only solution to use TableLayout (of course programmatically, as the dates will later come from user's database):

This is my java file:

 int i;
    TableRow tbl =  findViewById(R.id.tblayout);

    for (i=1;i<=36;i++) {
        TextView tv = new TextView(this);

        if (i>3 && i<6) {tv.setBackgroundColor(Color.RED);} else {tv.setBackgroundColor(Color.GRAY);}

        TableRow.LayoutParams paramsExample = new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT,1.0f);

        tv.setGravity(Gravity.CENTER);
        tv.setPadding(2, 2, 2, 2);
        tv.setLayoutParams(paramsExample);

        tbl.addView(tv);
    }

And this is my XML layout:

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:shrinkColumns="*"
    android:stretchColumns="*">

    <TableRow
        android:id="@+id/tblayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    </TableRow>

So basically I am generating TextViews in TableRow and if the i variable will met the date requirements, the textView will have a background color.

Currently my result is now:

enter image description here

which is close to my requirements (for now just one row), but still I am not sure if this is a good solution to have so many textViews, then each month should have a vertical divider (not sure how to do it in textView - maybe generate another View after each textView) and possibly second color or a dark line that will indicate today's date.

Can you please help if there is any simplier solution or if I can go this way? Or to use some kind of progressbar, but then the grid in the background will be gone.

2

There are 2 best solutions below

3
Abdo21 On BEST ANSWER

I think the best way is to create your custom view. Here is an example close to what you want, you can customize it according to your needs:

enter image description here

public class GardenCalendarView extends View {

    private Plant plant;

    private Rect bounds;
    private RectF boundsF;
    private Paint boundPaint;
    private Paint subLinePaint;
    private Paint textPaint;
    private Paint barPaint;

    private RectF startInsideRect;
    private RectF transplantRect;
    private RectF sowOutsideRect;

    private static final int MARGIN = 40;
    private static final int PADDING = 5;

    public GardenCalendarView(Context context) {
        super(context);
        init(context);
    }

    public GardenCalendarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public GardenCalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public void setPlant(Plant plant) {
        this.plant = plant;
        invalidate();
    }

    private void init(Context context) {
        bounds = new Rect();
        boundsF = new RectF();
        boundPaint = new Paint();
        boundPaint.setColor(Color.RED);
        boundPaint.setStyle(Paint.Style.STROKE);

        boundPaint.setAntiAlias(true);
        boundPaint.setStrokeWidth(1);
        boundPaint.setStrokeJoin(Paint.Join.ROUND);
        boundPaint.setStrokeCap(Paint.Cap.ROUND);

        subLinePaint = new Paint(boundPaint);
        subLinePaint.setColor(Color.GREEN);

        textPaint = new Paint(boundPaint);
        textPaint.setColor(Color.BLACK);
        textPaint.setTextSize(Utils.dpToPx(10f, context));

        startInsideRect = new RectF();
        transplantRect = new RectF();
        sowOutsideRect = new RectF();

        barPaint = new Paint();
        barPaint.setAntiAlias(true);
        barPaint.setStyle(Paint.Style.FILL_AND_STROKE);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (plant != null) {
            paint(canvas);
        }
    }

    private void paint(Canvas canvas) {
        getDrawingRect(bounds);

        Utils.reduceRectBy(bounds, MARGIN);
        canvas.drawRect(bounds, boundPaint);

        float partWidth = bounds.width() / 36f;

        // draw vertical lines
        for (int i = 0; i< 36; ++i) {
            if (i % 3 == 0) {
                canvas.drawLine(
                        bounds.left + partWidth *i,
                        bounds.top,
                        bounds.left + partWidth *i,
                        bounds.bottom, boundPaint);

                //Paint month label
                String month = Plant.MONTHS[i/3].toString();
                canvas.drawText(month, bounds.left + partWidth *i, bounds.top - 4, textPaint);

            } else {
                canvas.drawLine(
                        bounds.left + partWidth *i,
                        bounds.top,
                        bounds.left + partWidth *i,
                        bounds.bottom, subLinePaint);
            }
        }

        float partHeight = bounds.height() / 3f;

        float monthWidth = partWidth*3;

        // draw start inside bar
        startInsideRect.left = bounds.left + (plant.startInside.startMonth - 1)* monthWidth;
        startInsideRect.right = bounds.left + (plant.startInside.endMonth - 1)* monthWidth;
        startInsideRect.top = bounds.top + 0* partHeight + PADDING;
        startInsideRect.bottom = bounds.top + 1* partHeight - PADDING;

        barPaint.setColor(plant.startInside.color);
        canvas.drawRect(startInsideRect, barPaint);

        // draw transplant bar
        transplantRect.left = bounds.left + (plant.transplant.startMonth - 1)* monthWidth;
        transplantRect.right = bounds.left + (plant.transplant.endMonth - 1)* monthWidth;
        transplantRect.top = bounds.top + 1* partHeight + PADDING;
        transplantRect.bottom = bounds.top + 2* partHeight - PADDING;

        barPaint.setColor(plant.transplant.color);
        canvas.drawRect(transplantRect, barPaint);

        // draw sow outside bar
        sowOutsideRect.left = bounds.left + (plant.sowOutside.startMonth - 1)* monthWidth;
        sowOutsideRect.right = bounds.left + (plant.sowOutside.endMonth - 1)* monthWidth;
        sowOutsideRect.top = bounds.top + 2* partHeight + PADDING;
        sowOutsideRect.bottom = bounds.top + 3* partHeight - PADDING;

        barPaint.setColor(plant.sowOutside.color);
        canvas.drawRect(sowOutsideRect, barPaint);
    }
}

public class Plant {

    public String name;

    public Bar startInside;
    public Bar transplant;
    public Bar sowOutside;

    public static String[] MONTHS = new String[]{
            "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
            "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
    };

    public Plant(String name, Bar startInside, Bar transplant, Bar sowOutside) {
        this.name = name;
        this.startInside = startInside;
        this.transplant = transplant;
        this.sowOutside = sowOutside;
    }
}

public class Bar {

    public int startMonth;
    public int endMonth;
    public int color;

    public Bar(int start, int end, int color) {
        this.startMonth = start;
        this.endMonth = end;
        this.color = color;
    }
}

public class MainActivity extends AppCompatActivity {

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

        GardenCalendarView gardenCalendar = findViewById(R.id.gardenCalendar);

        Bar startInsideBar = new Bar(2, 5, Color.MAGENTA);
        Bar transplantBar = new Bar(3, 6, Color.BLUE);
        Bar sowOutsideBar = new Bar(4, 7, Color.YELLOW);

        Plant bacopaPlant = new Plant(
                "Bacopa",
                startInsideBar,
                transplantBar,
                sowOutsideBar);
        
        gardenCalendar.setPlant(bacopaPlant);
    }
}

public class Utils {

    public static void reduceRectBy(Rect rect, int dx) {
        rect.left += dx;
        rect.top += dx;
        rect.right -= dx;
        rect.bottom -= dx;
    }

    public static float dpToPx(float dp, Context context) {
        return TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                dp,
                context.getResources().getDisplayMetrics()
        );
    }
}

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".MainActivity">

    <com.abdo.mycalendar.GardenCalendarView
        android:id="@+id/gardenCalendar"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Edit: Optimize drawing by performing size related calculations in onSizeChanged() instead of in onDrow()

public class GardenCalendarView extends View {

    private Plant plant;

    private Rect bounds;
    private Paint boundPaint;
    private Paint subLinePaint;
    private Paint textPaint;
    private Paint barPaint;

    private RectF startInsideRect;
    private RectF transplantRect;
    private RectF sowOutsideRect;

    private static final int MARGIN = 40;
    private static final int PADDING = 5;

    private float partWidth;

    public GardenCalendarView(Context context) {
        super(context);
        init(context);
    }

    public GardenCalendarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public GardenCalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public void setPlant(Plant plant) {
        this.plant = plant;
        invalidate();
    }

    private void init(Context context) {
        bounds = new Rect();
        boundPaint = new Paint();
        boundPaint.setColor(Color.RED);
        boundPaint.setStyle(Paint.Style.STROKE);

        boundPaint.setAntiAlias(true);
        boundPaint.setStrokeWidth(1);
        boundPaint.setStrokeJoin(Paint.Join.ROUND);
        boundPaint.setStrokeCap(Paint.Cap.ROUND);

        subLinePaint = new Paint(boundPaint);
        subLinePaint.setColor(Color.GREEN);

        textPaint = new Paint(boundPaint);
        textPaint.setColor(Color.BLACK);
        textPaint.setTextSize(Utils.dpToPx(10f, context));

        startInsideRect = new RectF();
        transplantRect = new RectF();
        sowOutsideRect = new RectF();

        barPaint = new Paint();
        barPaint.setAntiAlias(true);
        barPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (plant != null) {
            paint(canvas);
        }
    }

    private void paint(Canvas canvas) {

        canvas.drawRect(bounds, boundPaint);

        // draw vertical lines
        for (int i = 0; i < 36; ++i) {
            if (i % 3 == 0) {
                canvas.drawLine(
                        bounds.left + partWidth * i,
                        bounds.top,
                        bounds.left + partWidth * i,
                        bounds.bottom, boundPaint
                );

                //Paint month label
                canvas.drawText(
                        Plant.MONTHS[i / 3], 
                        bounds.left + partWidth * i, 
                        bounds.top - 4, textPaint);

            } else {
                canvas.drawLine(
                        bounds.left + partWidth * i,
                        bounds.top,
                        bounds.left + partWidth * i,
                        bounds.bottom, subLinePaint);
            }
        }

        // draw start inside bar
        barPaint.setColor(plant.startInside.color);
        canvas.drawRect(startInsideRect, barPaint);

        // draw transplant bar
        barPaint.setColor(plant.transplant.color);
        canvas.drawRect(transplantRect, barPaint);

        // draw sow outside bar
        barPaint.setColor(plant.sowOutside.color);
        canvas.drawRect(sowOutsideRect, barPaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        int xpad = getPaddingLeft() + getPaddingRight();
        int ypad = getPaddingTop() + getPaddingBottom();

        int ww = w - xpad;
        int hh = h - ypad;

        bounds.set(0, 0, ww, hh);

        Utils.reduceRectBy(bounds, MARGIN);

        partWidth = bounds.width() / 36f;

        float partHeight = bounds.height() / 3f;

        float monthWidth = partWidth * 3;

        startInsideRect.set(
                bounds.left + (plant.startInside.startMonth - 1) * monthWidth,
                bounds.top + 0 * partHeight + PADDING,
                bounds.left + (plant.startInside.endMonth - 1) * monthWidth,
                bounds.top + 1 * partHeight - PADDING
        );

        transplantRect.set(
                bounds.left + (plant.transplant.startMonth - 1) * monthWidth,
                bounds.top + 1 * partHeight + PADDING,
                bounds.left + (plant.transplant.endMonth - 1) * monthWidth,
                bounds.top + 2 * partHeight - PADDING
        );

        sowOutsideRect.set(
                bounds.left + (plant.sowOutside.startMonth - 1) * monthWidth,
                bounds.top + 2 * partHeight + PADDING,
                bounds.left + (plant.sowOutside.endMonth - 1) * monthWidth,
                bounds.top + 3 * partHeight - PADDING
        );
    }
}

Edit2: To be able to update the chart at runtime and also draw any number of bars :

enter image description here

GardenCalendarView.java

public class GardenCalendarView extends View {

    private Plant plant;

    private Rect bounds;
    private Paint boundPaint;
    private Paint subLinePaint;
    private Paint textPaint;
    private Paint barPaint;

    private RectF barRect;

    private static final int MARGIN = 40;
    private static final int PADDING = 5;

    private float partWidth;

    private Rect textBound;

    public GardenCalendarView(Context context) {
        super(context);
        init(context);
    }

    public GardenCalendarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public GardenCalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public void setPlant(Plant plant) {
        this.plant = plant;
        invalidate();
    }

    private void init(Context context) {
        bounds = new Rect();
        boundPaint = new Paint();
        boundPaint.setColor(Color.RED);
        boundPaint.setStyle(Paint.Style.STROKE);

        boundPaint.setAntiAlias(true);
        boundPaint.setStrokeWidth(1);
        boundPaint.setStrokeJoin(Paint.Join.ROUND);
        boundPaint.setStrokeCap(Paint.Cap.ROUND);

        subLinePaint = new Paint(boundPaint);
        subLinePaint.setColor(Color.GREEN);

        textPaint = new Paint(boundPaint);
        textPaint.setColor(Color.BLACK);
        textPaint.setTextSize(Utils.dpToPx(10f, context));
        textPaint.setTypeface(Typeface.MONOSPACE);

        barRect = new RectF();

        barPaint = new Paint();
        barPaint.setAntiAlias(true);
        barPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        textBound = new Rect();
        textPaint.getTextBounds("JAN", 0, 3, textBound);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (plant != null) {
            paint(canvas);
        }
    }

    private void paint(Canvas canvas) {

        getDrawingRect(bounds);

        Utils.reduceRectBy(bounds, MARGIN);
        partWidth = bounds.width() / 36f;
        float partHeight = bounds.height() / 3f;
        float monthWidth = partWidth * 3;

        canvas.drawRect(bounds, boundPaint);

        // draw vertical lines
        for (int i = 0; i < 36; ++i) {
            if (i % 3 == 0) {
                canvas.drawLine(
                        bounds.left + partWidth * i,
                        bounds.top,
                        bounds.left + partWidth * i,
                        bounds.bottom, boundPaint
                );

                //Paint month label
                canvas.drawText(
                        Plant.MONTHS[i / 3],
                        bounds.left + partWidth * i,
                        bounds.top - 4, textPaint);

            } else {
                canvas.drawLine(
                        bounds.left + partWidth * i,
                        bounds.top,
                        bounds.left + partWidth * i,
                        bounds.bottom, subLinePaint);
            }
        }

        for (Bar bar: plant.bars) {
            switch (bar.type) {
                case Bar.STARTINDIDE: { // first row
                    barRect.set(
                            bounds.left + (bar.startMonth - 1) * monthWidth,
                            bounds.top + 0 * partHeight + PADDING,
                            bounds.left + (bar.endMonth - 1) * monthWidth,
                            bounds.top + 1 * partHeight - PADDING
                    );
                    break;
                }
                case Bar.TRANSPLANT: { //second row
                    barRect.set(
                            bounds.left + (bar.startMonth - 1) * monthWidth,
                            bounds.top + 1 * partHeight + PADDING,
                            bounds.left + (bar.endMonth - 1) * monthWidth,
                            bounds.top + 2 * partHeight - PADDING
                    );
                    break;
                }
                case Bar.SOWOUTSIDE: { //third row
                    barRect.set(
                            bounds.left + (bar.startMonth - 1) * monthWidth,
                            bounds.top + 2 * partHeight + PADDING,
                            bounds.left + (bar.endMonth - 1) * monthWidth,
                            bounds.top + 3 * partHeight - PADDING
                    );
                    break;
                }
            }

            barPaint.setColor(bar.color);
            canvas.drawRect(barRect, barPaint);
        }
    }
}

Bar.java

public class Bar {
    public static final int STARTINDIDE = 0;
    public static final int TRANSPLANT = 1;
    public static final int SOWOUTSIDE = 2;

    public int startMonth;
    public int endMonth;
    public int color;
    public int type;

    public Bar(int start, int end, int color, int type) {
        this.startMonth = start;
        this.endMonth = end;
        this.color = color;
        this.type = type;
    }
}

Plant.java

public class Plant {
    public String name;
    public List<Bar> bars;

    public static String[] MONTHS = new String[]{
            "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
            "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
    };

    public Plant(String name) {
        this.name = name;
        this.bars = new ArrayList<>();
    }

    public Plant(String name, List<Bar> bars) {
        this.name = name;
        this.bars = bars;
    }

    public void addBar(Bar bar){
        bars.add(bar);
    }

    public void deleteBar(Bar bar){
        bars.remove(bar);
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private int[] COLORS = {Color.RED, Color.GREEN, Color.BLUE};

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

        GardenCalendarView gardenCalendar = findViewById(R.id.gardenCalendar);

        Spinner spinner = findViewById(R.id.spinner);
        EditText startEditText = findViewById(R.id.startEditText);
        EditText endEditText = findViewById(R.id.endEditText);
        Button addButton = findViewById(R.id.addButton);

        // Create an ArrayAdapter using the string array and a default spinner layout
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
                R.array.parts, android.R.layout.simple_spinner_item);
        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        // Apply the adapter to the spinner
        spinner.setAdapter(adapter);

        Plant bacopaPlant = new Plant("Bacopa");
        gardenCalendar.setPlant(bacopaPlant);

        addButton.setOnClickListener(v -> {
            // we suppose here data are valid !!
            int start = Integer.parseInt(startEditText.getText().toString());
            int end = Integer.parseInt(endEditText.getText().toString());
            int selectedItem = spinner.getSelectedItemPosition();
            Bar bar = new Bar(start, end, COLORS[selectedItem], selectedItem);
            bacopaPlant.addBar(bar);

            gardenCalendar.setPlant(bacopaPlant);


            //clear values
            startEditText.setText("");
            endEditText.setText("");

            Toast.makeText(this, "Bar added successfully !", Toast.LENGTH_SHORT).show();
        });

    }
}

arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string-array name="parts">
        <item>Start Inside</item>
        <item>Transplant</item>
        <item>Sow outside</item>
    </string-array>

</resources>

activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/startEditText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="Start"
        android:layout_margin="8dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/endEditText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="End"
        android:layout_margin="8dp"
        app:layout_constraintStart_toEndOf="@id/startEditText"
        app:layout_constraintTop_toTopOf="@id/startEditText"
        app:layout_constraintBottom_toBottomOf="@id/startEditText"/>

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        app:layout_constraintBottom_toBottomOf="@id/addButton"
        app:layout_constraintEnd_toStartOf="@id/addButton"
        app:layout_constraintTop_toTopOf="@id/addButton"
        app:layout_constraintVertical_bias="0.666" />

    <Button
        android:id="@+id/addButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Bar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


    <com.abdo.mycall.GardenCalendarView
        android:id="@+id/gardenCalendar"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="24dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/addButton" />

</androidx.constraintlayout.widget.ConstraintLayout>
2
Andrew On

I think that using a TableLayout and Textviews is the wrong way to go.

If you are not going to put text in the coloured rounded rectangles, then it would be more efficient to use a plain views to set the background of.

But also TableLayout have a lot of code to size columns to show the content and then you would have to set grow and shrink on columns to adjust the size to match the screen size.

You would also have to have a drawable for start of the rounded rectangles and an end drawable and then would would have to work out to put in each cell a start, middle or end background.

The vertical dividers would really have to be there own column very thin column (though best to be non growable/shrinkable) so they don't too big or same.

This all adds to a lot of complexity in constructing the table and then complexity in what it has to then calculate to draw it.

I think a much better solution in to create your own custom view that dynamically draws the right size of rounded rectangles and text. A few calculations based on the view's size will tell you how to divide it up in to 12 months and then the quarter (or half) of a month.

A custom view would perform much better than a TableView and be simpler to implement

e.g. https://developer.android.com/training/custom-views/custom-drawing

Update

After some thought if you don't want to draw it yourself, this is really a horizontal bar chart.

So you could use MPAndroidChart and a stacked horizontal bar chart (use a bar of transparent colour) to shift the start.

Rough example

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Color;
import android.os.Bundle;

import com.github.mikephil.charting.charts.HorizontalBarChart;
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.ValueFormatter;

import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {

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

        HorizontalBarChart chart = findViewById(R.id.chart1);

        YAxis yl = chart.getAxisLeft();
        yl.setAxisMaximum(48f);
        yl.setAxisMinimum(0f);
        yl.setGranularity(4f);
        yl.setGranularityEnabled(true);
        yl.setLabelCount(12);

        yl.setValueFormatter(new ValueFormatter()
        {
            @Override
            public String getFormattedValue(float value) {
                String str="";
                if(value==0f)
                    str="Jan";
                else if(value<=4f)
                    str="Feb";
                else if(value<=8f)
                    str="Mar";
                else
                    str="";
                return str;
            }
        });

        chart.getLegend().setEnabled(false);
        chart.getDescription().setEnabled(false);
        chart.getXAxis().setEnabled(false);
        chart.getAxisRight().setEnabled(false);


        ArrayList<BarEntry> values = new ArrayList<>();

        values.add(new BarEntry(1,new float[]{ 4f, 4f, 4f }));
        values.add(new BarEntry(2,new float[]{ 2f, 8f }));
        values.add(new BarEntry(3,new float[]{ 12f, 12f }));

        BarDataSet set1;

        set1 = new BarDataSet(values, "Plant 1");
        // Three colours need for the first entry as it is a split entry
        set1.setColors(Color.RED, Color.TRANSPARENT,Color.RED,Color.TRANSPARENT,Color.BLUE,Color.TRANSPARENT,Color.GREEN,Color.TRANSPARENT);
        set1.setDrawValues(false);


        BarData data = new BarData(set1);

        chart.setData(data);
    }

Gives you

enter image description here