Home > Back-end >  Android invalidate and redraw custom view
Android invalidate and redraw custom view

Time:08-10

I have a custom View in a Relative layout:

     <my.app.com.GardenCalendarView
         android:id="@ id/gardenCalendar"
         android:layout_width="match_parent"
         android:layout_height="84dp"
 />

which draws a calendar.

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 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();

    }

    public void init(Context context) {
        bounds = new Rect();
        boundPaint = new Paint();
        boundPaint.setColor(Color.parseColor("#5E8364"));
        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.parseColor("#B8CCBB"));

        textPaint = new Paint(boundPaint);
        textPaint.setColor(Color.DKGRAY);
        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   10   partWidth * i,
                        bounds.top - 8, 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 * 1; //partWidth * 3; if full month column

        startInsideRect.set(
                bounds.left   (plant.startInside.startMonth - 1) * monthWidth,
                bounds.top   0 * partHeight   PADDING,
                bounds.left   (plant.startInside.endMonth ) * monthWidth, //(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 ) * 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 ) * monthWidth,
                bounds.top   3 * partHeight - PADDING
        );
    }
}

and then Plant.java:

    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", "MÁJ", "JÚN",
                "JÚL", "AUG", "SEP", "OKT", "NOV", "DEC"
        };
    
        public Plant(String name, Bar startInside, Bar transplant, Bar sowOutside) {
            this.name = name;
            this.startInside = startInside;
            this.transplant = transplant;
            this.sowOutside = sowOutside;
        }
    }

and Bar.java:

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

Now I am calling this to draw the calendar:

    GardenCalendarView gardenCalendar = findViewById(R.id.gardenCalendar);
    
            Bar startInsideBar = new Bar(5, 8, "#CCff8040");
            Bar transplantBar = new Bar(9, 12, "#CC5E8364");
            Bar sowOutsideBar = new Bar(15, 18, "#CCd2d200");
    
            Plant myPlant = new Plant(
                    "plantName",
                    startInsideBar,
                    transplantBar,
                    sowOutsideBar);
    
            gardenCalendar.setPlant(myPlant);

This is working fine, the View is shown.

But i have a button and after clicking on it, I want to redraw that View with another parameters.

     public void ResetCalendar(View view)
        {
            GardenCalendarView gardenCalendar = findViewById(R.id.gardenCalendar);
            gardenCalendar.invalidate();

            Bar startInsideBar = new Bar(1, 5, "#CCff8040");
            Bar transplantBar = new Bar(20, 25, "#CC5E8364");
            Bar sowOutsideBar = new Bar(6, 8, "#CCd2d200");
    
            Plant myPlant = new Plant(
                    "platName2",
                    startInsideBar,
                    transplantBar,
                    sowOutsideBar);
    
            gardenCalendar.setPlant(myPlant);
        }

You can see, only the numbers changed in the Bars, but nothing happens, the View has the old values and did not redraw. I tried also to call gardenCalendar.invalidate(); with no luck.

I also tried to call init with context, but then the whole View disappeared and did not redraw.

CodePudding user response:

From the original question Android table as calendar view for whole year

Don't use the updated onSizeChanged optimisation as it won't recalculate the position of the bars unless the size of view has changed.

If you had used different colours for the bars you would have noticed that they were redrawn in the new colours.

So your paint method should look like

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);
    }

Also calling gardenCalendar.invalidate(); is unnecessary as it is done in setPlant

  • Related