Thursday, 6 April 2023

Self-made canvas view in Android is not correctly displayed (any more)

I have a strange problem. I spent quite some time designing a self-made canvas view in Android which should be a water tank (that is the rim which insert via R.drawable.hot_water_tank_container) and then there is also a blue bar inside the rim which depicts the water.

I have been using this for many months and everything worked correct. The blue bar was always being displayed inside the water tank rim (and even drawing operations to decrease or increase the size of the water bar were all within the boundaries).

Now all of a sudden (without knowingly having changed anything), the blue bar is not displayed anymore at the correct position. Further, the size of the water tank was (autmatically?) decreased somehow. I tested it on many emulators and on real devices. What makes it also strange is that on the layout editor, the self-made canvas view looks totally correct (the blue bar is within the rims and also the size is bigger).

Can anyone think about a reason as to why this is happening and how to fix it?

Here is the screenshot from the Layout Editor: enter image description here

Here is the screenshot from the Emulator: enter image description here

Here is the XML layout file of the displaying fragment:

<?xml version="1.0" encoding="utf-8"?>
<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:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context=".MainActivity">


    <com.example.game.HotWaterTank
        android:id="@+id/hotWaterTank"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_percent="0.3"
        app:layout_constraintHorizontal_bias="0.529"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.895"
        app:layout_constraintWidth_percent="0.15" />



    <Button
        android:id="@+id/button_action"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="Action"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_percent="0.102"
        app:layout_constraintHorizontal_bias="0.373"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.745"
        app:layout_constraintWidth_percent="0.13" />

</androidx.constraintlayout.widget.ConstraintLayout>

Here is the Java file of the self-made water tank canvas view:

package com.example.game;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class HotWaterTank extends View {
    private Paint mInnerCirclePaint;
    private int innerCircleCenter;
    private int hotWaterColor = Color.parseColor("#327bff");
    private Bitmap bitmap;
    private int left;
    private int top;

    private int lineEndY;
    private int lineStartY;


    /*
    Set the variables for the positions of the water bar
     */

    double positionOfWaterBar = 0.5;

    //0.607= empty, - 0.04 = full,
    final double value_positionOfWaterBar_Empty = 0.607;
    final double value_positionOfWaterBar_Full = -0.04;

    final double value_positionOfWaterBar_upperLimit = -0.08;
    final double value_positionOfWaterBar_lowerLimit = 0.61;

    public HotWaterTank(Context context) {
        this(context, null);
    }

    public HotWaterTank(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HotWaterTank(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        if (attrs != null) {

            final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Thermometer, defStyle, 0);

            hotWaterColor = a.getColor(R.styleable.Thermometer_therm_color, hotWaterColor);

            a.recycle();
        }

        init();
    }

    private void init() {
        mInnerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mInnerCirclePaint.setColor(hotWaterColor);
        mInnerCirclePaint.setStyle(Paint.Style.FILL);



        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.hot_water_tank_container);
    }

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

        // init bitmap
        int scaledHeight;
        int scaledWidth;
        int width = getWidth();
        int height = getHeight();
        if (width > height) {
            scaledHeight = (int) (height * 0.5);
            scaledWidth = scaledHeight * bitmap.getWidth() / bitmap.getHeight();
        } else {
            scaledWidth = (int) (width * 0.5);
            scaledHeight = scaledWidth * bitmap.getHeight() / bitmap.getWidth();
        }
        mInnerCirclePaint.setStrokeWidth( (float) (scaledWidth * 0.9));

        bitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);

        innerCircleCenter = (left + left + bitmap.getWidth() + (Math.min(width, height) / 72));

        left = (getWidth() - bitmap.getWidth()) / 2;
        top = (getHeight() - bitmap.getHeight()) / 2;



        lineStartY = ((int)(bitmap.getHeight() / 4.6f) + top) + (int) (positionOfWaterBar * bitmap.getHeight());
        lineEndY = (top + bitmap.getHeight()) - (int)(bitmap.getHeight() / 7f);
        //lineStartY = (int)(4.4*((int)(bitmap.getHeight() / 4.6f) + top));
        //lineEndY = (int)(1.0* ((top + bitmap.getHeight()) - (int)(bitmap.getHeight() / 4f)));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawThermometer(canvas);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        //takes care of paddingTop and paddingBottom
        int paddingY = getPaddingBottom() + getPaddingTop();

        //get height and width
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        height += paddingY;

        setMeasuredDimension(width, height);
    }

    private void drawThermometer(Canvas canvas) {

        canvas.drawLine(innerCircleCenter , lineStartY, innerCircleCenter, lineEndY, mInnerCirclePaint);
        canvas.drawBitmap(bitmap, left, top, new Paint());
    }

    public void setThermometerColor(int thermometerColor) {
        this.hotWaterColor = thermometerColor;
        mInnerCirclePaint.setColor(hotWaterColor);
        invalidate();
    }

    public void changeVolumeBar( double percentageChangeOfTheWholeBar) {
        double appliedPercentageChangeOfTheWholeBar = percentageChangeOfTheWholeBar / 100;
        if (appliedPercentageChangeOfTheWholeBar>1) {
            appliedPercentageChangeOfTheWholeBar = 1;
        }
        if (appliedPercentageChangeOfTheWholeBar <-1) {
            appliedPercentageChangeOfTheWholeBar = -1;
        }


        double absolutValueSpanForTheWholeBar = value_positionOfWaterBar_Full - value_positionOfWaterBar_Empty;

        positionOfWaterBar = positionOfWaterBar + appliedPercentageChangeOfTheWholeBar * absolutValueSpanForTheWholeBar;
        if (positionOfWaterBar < value_positionOfWaterBar_upperLimit) {
            positionOfWaterBar = value_positionOfWaterBar_upperLimit;
        }

        if(positionOfWaterBar > value_positionOfWaterBar_lowerLimit) {
            positionOfWaterBar = value_positionOfWaterBar_lowerLimit;
        }

        lineStartY = ((int)(bitmap.getHeight() / 4.6f) + top) + (int) (positionOfWaterBar * bitmap.getHeight());
        lineEndY = (top + bitmap.getHeight()) - (int)(bitmap.getHeight() / 7f);
    }

    public void setVolumeBarPosition( double percentageOfTheWholeVolume) {
        double appliedVolume = percentageOfTheWholeVolume;
        if (appliedVolume < 0) {
            appliedVolume = 0;
        }
        if (appliedVolume >100) {
            appliedVolume = 100;
        }

        double absolutValueSpanForTheWholeBar = value_positionOfWaterBar_Full - value_positionOfWaterBar_Empty;

        positionOfWaterBar = value_positionOfWaterBar_Empty + (appliedVolume/100 )  * absolutValueSpanForTheWholeBar;

        lineStartY = ((int)(bitmap.getHeight() / 4.6f) + top) + (int) (positionOfWaterBar * bitmap.getHeight());
        lineEndY = (top + bitmap.getHeight()) - (int)(bitmap.getHeight() / 7f);
    }



}

Reminder 3: Does anyone have an idea as to why this is happening and how to tackle it? Do you need more information? Please tell me because I don't know what I can do to tackle this problem.



from Self-made canvas view in Android is not correctly displayed (any more)

No comments:

Post a Comment