为什么具有相同属性的自定义视图在 Android 的不同运行中具有不同的大小

问题描述 投票:0回答:2

我有一个自制的自定义视图

View_Game_Event_Rectangle
,它只是一个橙色矩形,开头有一个图标。我有一个小片段,只是从屏幕的右到左对这些矩形进行动画处理。真正奇怪的是,如果我多次运行相同的代码,每次运行时矩形的长度可能会有所不同,尽管我不会更改任何内容,正如您在屏幕截图中看到的那样。

这有点奇怪,因为它们具有相同的属性,有时它们看起来相同,但有时如果您比较不同的运行,它们就不同了。您知道为什么会发生这种情况以及如何解决这个问题吗?

这里是动画片段类的Java文件:

package com.example.game;

import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.example.game.databinding.FragmentTestBinding;
import java.util.ArrayList;


public class Test extends Fragment {
    /*
    Game variables
     */

    public static final int DELAY_IN_MILLIS = 100;
    public static final double DIFFICULTY_SPEED_MULTIPLICATOR_INCREMENT_PER_LEVEL = 0.3;
    public static final int TIME_OF_A_LEVEL_IN_SECONDS = 90;
    private Handler handler = new Handler();

    private int numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 = 8000;

    private int currentTimeSlot;
    
    private float verticalBiasOfEventElementToBeInTheLine = 0.049f;
    private float percentageHeightOfEventElement = 0.071f;

    int widthDisplay;
    int heightDisplay;

    //Type of View_Game_Events
    public static final String VIEW_EVENT_RECTANGLE_SOLAR = "Solar";
    
    private FragmentTestBinding binding;

    private ConstraintLayout constraintLayout;
    ConstraintSet constraintSet ;

    //Variables for the single view event
    View_Game_Event_Rectangle[] viewEvent;
    boolean [] isViewEventActive;
    Drawable[] drawingsForTheViewEvents;
    private static int nextFreeIndexForViewEvent;
    private static int numberOfViewEventInArray = 10;
    ArrayList<View_Game_Event_Rectangle> arrayList_GameEventRectangles;

    private int [] orangeRectangleValuesForTheLevel;
    
    private boolean fragmentViewHasBeenCreated = false;

    private CountDownTimer cdt;

    private  final long DELAY_COUNT_DOWN_TIMER = 100; //100ms

    private int numberOfTimeSlotsUntilTheEndOfScreen = (int)(numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 * 2/(DELAY_COUNT_DOWN_TIMER));


    public Test() {
        // Required empty public constructor
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        nextFreeIndexForViewEvent = 0;
        viewEvent = new View_Game_Event_Rectangle[numberOfViewEventInArray];
        drawingsForTheViewEvents = new Drawable[numberOfViewEventInArray];
        arrayList_GameEventRectangles = new ArrayList<View_Game_Event_Rectangle>();
        isViewEventActive = new boolean[numberOfViewEventInArray];

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        binding = FragmentTestBinding.inflate(inflater, container, false);

        WindowManager wm = (WindowManager) getActivity().getWindowManager();
        Display display = wm.getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        widthDisplay = size.x;
        heightDisplay = size.y;


        container.getContext();
        constraintLayout= binding.constraintLayout;


        fragmentViewHasBeenCreated = true;
        getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        constraintLayout = binding.constraintLayout;
        constraintSet = new ConstraintSet();
        startGame();
        return binding.getRoot();
    }

    public void startGame () {
        startRound();
    }

    public void startRound () {
        orangeRectangleValuesForTheLevel = new int[5000];
        orangeRectangleValuesForTheLevel [74] = 25;
        orangeRectangleValuesForTheLevel [83] = 25;
        orangeRectangleValuesForTheLevel [92] = 25;
        orangeRectangleValuesForTheLevel [101] = 25;


        //Create the array list with the Game_Event_Rectangles
        for (int i =0; i<orangeRectangleValuesForTheLevel.length; i++) {
            if (orangeRectangleValuesForTheLevel[i] >0) {
                arrayList_GameEventRectangles.add(new View_Game_Event_Rectangle(getActivity(), VIEW_EVENT_RECTANGLE_SOLAR, i+1, orangeRectangleValuesForTheLevel[i]));
            }
        }

        countDownTime();

    }

    private void updateScreen() {

        /*
        Iterate through all elements
         */
        for (int currentElement =0; currentElement <arrayList_GameEventRectangles.size(); currentElement++) {


            //Create view and set
            if (currentTimeSlot == arrayList_GameEventRectangles.get(currentElement).getStartingTimeSlot() - 15) {

                arrayList_GameEventRectangles.get(currentElement).setActive(true);


                //Set the parameters and the backgorund of the view element
                arrayList_GameEventRectangles.get(currentElement).setLayoutParams(new ViewGroup.LayoutParams(0, 0));

                if(arrayList_GameEventRectangles.get(currentElement).getEventType().equals(VIEW_EVENT_RECTANGLE_SOLAR)) {
                    arrayList_GameEventRectangles.get(currentElement).setBackground(ContextCompat.getDrawable(getActivity(),R.drawable.game_event_rectangle_bulb_1).mutate());
                }


                arrayList_GameEventRectangles.get(currentElement).setId(View.generateViewId());

                //Make the view invisible (before it's appearence time)
                arrayList_GameEventRectangles.get(currentElement).getBackground().setAlpha(0);

                // Set the ConstraintLayout programatically for the view
                constraintLayout.addView(arrayList_GameEventRectangles.get(currentElement));
                constraintSet.clone(constraintLayout);
                constraintSet.constrainPercentHeight(arrayList_GameEventRectangles.get(currentElement).getId(), percentageHeightOfEventElement);

                float widthConstrainPercentage_element1 = (float)(arrayList_GameEventRectangles.get(currentElement).getDuration() / 100.0);
                float duration = arrayList_GameEventRectangles.get(currentElement).getDuration();


                constraintSet.constrainPercentWidth(arrayList_GameEventRectangles.get(currentElement).getId(), widthConstrainPercentage_element1);
                constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(),ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID,ConstraintSet.BOTTOM,0);
                constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(),ConstraintSet.TOP,ConstraintSet.PARENT_ID ,ConstraintSet.TOP,0);
                constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(),ConstraintSet.LEFT,ConstraintSet.PARENT_ID ,ConstraintSet.LEFT,0);
                constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(),ConstraintSet.RIGHT,ConstraintSet.PARENT_ID ,ConstraintSet.RIGHT,0);

                float horizontalBias = 1.0f ;
                constraintSet.setHorizontalBias(arrayList_GameEventRectangles.get(currentElement).getId(), horizontalBias);
                constraintSet.setVerticalBias(arrayList_GameEventRectangles.get(currentElement).getId(), verticalBiasOfEventElementToBeInTheLine);
                constraintSet.applyTo(constraintLayout);

            }
            
            //Shift the view to the right border of the display
            if (currentTimeSlot == arrayList_GameEventRectangles.get(currentElement).getStartingTimeSlot() - 10) {


                arrayList_GameEventRectangles.get(currentElement).setTranslationX(arrayList_GameEventRectangles.get(currentElement).getWidth());
            }


            //Animate view element
            if (currentTimeSlot == arrayList_GameEventRectangles.get(currentElement).getStartingTimeSlot()) {
                arrayList_GameEventRectangles.get(currentElement).getBackground().setAlpha(255);
                View rectangle = arrayList_GameEventRectangles.get(currentElement);
                int rectangleWidth = rectangle.getWidth();
                float distanceToCover_current = widthDisplay + rectangleWidth;
                float distanceToCover_normalizedObject = widthDisplay + 20;
                double ratioDistanceDifference = distanceToCover_current /distanceToCover_normalizedObject;
                long durationForTheAnimation = (long)(numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 * ratioDistanceDifference);

                arrayList_GameEventRectangles.get(currentElement).animate().setDuration(durationForTheAnimation).translationX(widthDisplay*(-1)).setInterpolator(new LinearInterpolator()).start();

            }
        }

    }

    private void countDownTime(){


        cdt = new CountDownTimer(100000, DELAY_COUNT_DOWN_TIMER) {
            boolean delay = true;
            public void onTick(long millisUntilFinished) {
                if(delay) {
                    delay = false;
                } else {
                    currentTimeSlot++;

                    updateScreen();
                    delay = true;
                }
            }
            public void onFinish() {
                updateScreen();
            }
        }.start();
    }
    
    @Override
    public void onDestroyView() {
        super.onDestroyView();

        // Reset your variable to false
        fragmentViewHasBeenCreated = false;

        // And clean up any postDelayed callbacks that are waiting to fire
        cdt.cancel();
        handler.removeCallbacksAndMessages(null);
    }
}

还有自定义视图的Java文件:

package com.example.game;

import android.content.Context;
import android.view.View;

public class View_Game_Event_Rectangle extends View {

    private boolean isActive;
    private int numberOfTimeSlotsAfterFinishing;
    private String eventType;
    private int startingTimeSlot;
    private int duration;

    public View_Game_Event_Rectangle(Context context, String eventType, int startingTimeSlot, int duration ) {
        super(context);
        this.eventType = eventType;
        this.startingTimeSlot = startingTimeSlot;
        this.duration = duration;
    }

    public boolean isActive() {
        return isActive;
    }

    public void setActive(boolean active) {
        isActive = active;
    }

    public int getNumberOfTimeSlotsAfterFinishing() {
        return numberOfTimeSlotsAfterFinishing;
    }

    public void setNumberOfTimeSlotsAfterFinishing(int numberOfTimeSlotsAfterFinishing) {
        this.numberOfTimeSlotsAfterFinishing = numberOfTimeSlotsAfterFinishing;
    }

    public void incrementNumberOfTimeSlotsAfterFinishing() {
        this.numberOfTimeSlotsAfterFinishing = this.numberOfTimeSlotsAfterFinishing + 1;
    }

    public String getEventType() {
        return eventType;
    }

    public void setEventType(String eventType) {
        this.eventType = eventType;
    }

    public int getStartingTimeSlot() {
        return startingTimeSlot;
    }

    public void setStartingTimeSlot(int startingTimeSlot) {
        this.startingTimeSlot = startingTimeSlot;
    }

    public int getDuration() {
        return duration;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }
}

提醒:有什么建议我可以做些什么来摆脱这个问题吗?为了解决这个问题,我需要了解为什么会发生这种情况。目前我还没有任何线索。

android view android-animation
2个回答
0
投票

创建game_layout.xml

game_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_weight="1"
    android:layout_margin="8dp"
    android:gravity="center"
    android:background="#FF03FF72">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_add_24" />

</LinearLayout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:id="@+id/rootLL"/>

</LinearLayout>

MainActivity.java

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

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

        LinearLayout rootLL = findViewById(R.id.rootLL);

        for (int i = 0; i < 5; i++) {
            View gameView = getLayoutInflater().inflate(R.layout.game_layout, rootLL,false);
            rootLL.addView(gameView);
        }
    }
}

输出:


-1
投票

我注意到该问题可能源于在

getWidth()
方法中使用
View_Game_Event_Rectangle
视图的
updateScreen()
方法。在布局膨胀过程中或首次创建视图时,视图的宽度不会立即可用。

为了确保您拥有正确的宽度,您可以推迟宽度的检索,直到视图被测量和布局之后。这是

updateScreen()
方法的更新版本,考虑到了这一点:

private void updateScreen() {
/*
Iterate through all elements
 */
for (int currentElement = 0; currentElement < arrayList_GameEventRectangles.size(); currentElement++) {
    // ...

    // Set the ConstraintLayout programatically for the view
    constraintLayout.addView(arrayList_GameEventRectangles.get(currentElement));
    constraintSet.clone(constraintLayout);

    arrayList_GameEventRectangles.get(currentElement).post(() -> {
        // Get the width of the view after it has been measured and laid out
        int viewWidth = arrayList_GameEventRectangles.get(currentElement).getWidth();

        float widthConstrainPercentage_element1 = (float) (arrayList_GameEventRectangles.get(currentElement).getDuration() / 100.0);
        float duration = arrayList_GameEventRectangles.get(currentElement).getDuration();

        constraintSet.constrainPercentWidth(arrayList_GameEventRectangles.get(currentElement).getId(), widthConstrainPercentage_element1);
        constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(), ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 0);
        constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(), ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0);
        constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(), ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.LEFT, 0);
        constraintSet.connect(arrayList_GameEventRectangles.get(currentElement).getId(), ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, 0);

        float horizontalBias = 1.0f;
        constraintSet.setHorizontalBias(arrayList_GameEventRectangles.get(currentElement).getId(), horizontalBias);
        constraintSet.setVerticalBias(arrayList_GameEventRectangles.get(currentElement).getId(), verticalBiasOfEventElementToBeInTheLine);
        constraintSet.applyTo(constraintLayout);

        // Shift the view to the right border of the display
        if (currentTimeSlot == arrayList_GameEventRectangles.get(currentElement).getStartingTimeSlot() - 10) {
            arrayList_GameEventRectangles.get(currentElement).setTranslationX(viewWidth);
        }

        // Animate view element
        if (currentTimeSlot == arrayList_GameEventRectangles.get(currentElement).getStartingTimeSlot()) {
            arrayList_GameEventRectangles.get(currentElement).getBackground().setAlpha(255);
            View rectangle = arrayList_GameEventRectangles.get(currentElement);
            int rectangleWidth = viewWidth;
            float distanceToCover_current = widthDisplay + rectangleWidth;
            float distanceToCover_normalizedObject = widthDisplay + 20;
            double ratioDistanceDifference = distanceToCover_current / distanceToCover_normalizedObject;
            long durationForTheAnimation = (long) (numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 * ratioDistanceDifference);

            arrayList_GameEventRectangles.get(currentElement).animate().setDuration(durationForTheAnimation).translationX(widthDisplay * (-1)).setInterpolator(new LinearInterpolator()).start();
        }
    });
}
}

通过使用

post()
方法,Runnable中的代码将在视图测量和布局后执行,确保您获得正确的宽度。

请尝试更新此代码,看看它是否解决了不同运行中矩形长度不同的问题。

© www.soinside.com 2019 - 2024. All rights reserved.