Android中自制画布视图无法正确显示(更多)

3lxsmp7m  于 2023-04-10  发布在  Android
关注(0)|答案(2)|浏览(97)

bounty还有3天到期,回答此问题可获得+50声望奖励,VanessaF希望引起更多关注此问题:由于我还没有收到任何答复或评论,而这个问题对我来说很重要,我不知道如何继续,我将奖励50分的奖金给一个有帮助的答案。我将感谢每一个评论。

我有一个奇怪的问题。我花了相当长的时间在Android中设计了一个自制的画布视图,它应该是一个水箱(即通过R.drawable.hot_water_tank_container插入的边缘),然后在边缘内还有一个描绘水的蓝色条。
我已经使用这个很多个月了,一切都正常工作。蓝色的酒吧总是显示在水箱边缘内(甚至绘图操作,以减少或增加水酒吧的大小都在边界内)。
现在突然间(在没有故意改变任何东西的情况下),蓝色条不再显示在正确的位置。(自动?)以某种方式减少了。我在许多模拟器和真实的设备上测试了它。同样奇怪的是,在布局编辑器上,自制画布视图看起来完全正确(蓝色条在边框内,尺寸也更大)。
有没有人能想一想为什么会发生这种情况以及如何解决它?
以下是布局编辑器的屏幕截图:

以下是来自模拟器的屏幕截图:

下面是显示片段的XML布局文件:

<?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>

下面是自制水箱画布视图的Java文件:

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


}

更新

我在一个回答中插入了建议:

innerCircleCenter = left + (bitmap.getWidth() / 2)
mInnerCirclePaint.setStrokeWidth((float) (scaledWidth * 0.5))

但结果更糟,正如你在这个场景中看到的:

这里要求的是热水箱容器文件:

jckbn6z7

jckbn6z71#

看起来问题出在innerCircleCenter的计算上。我的理解innerCircleCenter应该是水箱中心的X坐标,但是通过两次添加left,它稍微向右移动了一点。然后,蓝条的笔画宽度太大,无法容纳在水箱内,所以它被推到了外面。
我想你可以这样计算中心:
innerCircleCenter = left + (bitmap.getWidth() / 2)
然后将mInnerCirclePaint的笔划宽度修改为比实际宽度小一点,例如
mInnerCirclePaint.setStrokeWidth((float) (scaledWidth * 0.5))

9cbw7uwe

9cbw7uwe2#

因为你的hot_water_tank_container是一个很大的图像,为了简化计算,我使用AppCompatImageView作为父代,用xml硬编码android:layout_width,用java代码硬编码setAdjustViewBounds(true)

<com.example.game.HotWaterTank
    android:id="@+id/tank"
    android:layout_width="100dp"
    android:layout_height="wrap_content" />

这里的HotWaterTank

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatImageView;

public class HotWaterTank extends AppCompatImageView {
    private Paint mPaint;
    private Rect mRect;
    private int mColor = Color.parseColor("#327bff");

    private float percentage = 0f;

    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() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(mColor);
        mPaint.setStyle(Paint.Style.FILL);

        mRect = new Rect();

        setAdjustViewBounds(true);
        setImageResource(R.drawable.hot_water_tank_container);
    }

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

        mRect.set(0, 0, w, h);
        mRect.inset((int) (0.085f * w), (int) (0.18f * h)); /* can be refine more */
    }

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

        float top = mRect.bottom - (mRect.height() * percentage);
        canvas.drawRect(mRect.left, top, mRect.right, mRect.bottom, mPaint);
    }

    public void changeVolumeBar(float percentage) {
        this.percentage = percentage;
        invalidate();
    }
}

拍摄:

相关问题