如何在 Android 中精确检查动画元素的位置

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

我想开发一个小游戏,其中橙色矩形在显示屏上从右到左动画,当它位于中间(绿色矩形)时,用户应按下按钮并在他/她成功时获得积分(参见截屏):

这里是游戏类的代码

public class Test extends Fragment implements View.OnClickListener {


    /*
    Game variables
     */

    public static final int DELAY_IN_MILLIS = 100;
    ArrayList<View_Game_Event_Rectangle> arrayList_GameEventRectangles;
    private int currentTimeSlot;
    private boolean gameIsRunning;
    private Handler handler = new Handler();
    private int timeslotsEventElementUntilTheMiddleOfTheScreen_Level1 = (int)(5.0 * (1000.0/DELAY_IN_MILLIS));
    private int numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 = 5000;

    //Fix parameters of the UI elements
    private float horizontalPercentageOfScoreRectangle_End = 0.491f;
    private float horizontalPercentageOfScoreRectangle_Start = 0.567f;
    private float verticalBiasOfEventElementToBeInTheLine = 0.049f;
    private float percentageHeightOfEventElement = 0.071f;


    int widthDisplay;
    int heightDisplay;
    int helpCounterRun =0;
    int helpCounterUpdateScreen =0;
    int helpCounterCountDownTime =0;

    private FragmentTestBinding binding;
    private ConstraintLayout constraintLayout;
    ConstraintSet constraintSet ;

    //Variables for the single view event
    View_Game_Event_Rectangle[] viewEvent;

    private boolean fragmentViewHasBeenCreated = false;
    private CountDownTimer cdt;
    private  final long DURATION = 900000L;
    private  final long DELAY_COUNT_DOWN_TIMER = 100;

    private int numberOfTimeSlotsUntilTheEndOfScreen = (int)(numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 * 2/(DELAY_COUNT_DOWN_TIMER));
    private int currentLevel;
    public Test() {
        // Required empty public constructor
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        arrayList_GameEventRectangles = new ArrayList<View_Game_Event_Rectangle>();
        currentLevel = 1;
    }

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


    @Override
    public void onClick(View view) {

    }


    public void startGame () {
        gameIsRunning = true;
        Log.e("LogTag_El", "-------Start Game------" );

        //Create rectangle-element that should be animated from the right to the left on the screen
        arrayList_GameEventRectangles.add(new View_Game_Event_Rectangle(getActivity(), "Bulb Rectangle", 100, 20));
        countDownTime();
    }


    private void updateScreen() {

        for (int currentElement =0; currentElement <arrayList_GameEventRectangles.size(); currentElement++) {

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

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


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

                if(arrayList_GameEventRectangles.get(currentElement).getEventType().equals("Bulb Rectangle")) {
                    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() - 40) {
                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);
                long durationForTheAnimation = (long)(numberOfMillisecondsUntilTheMiddleOfTheScreen_Level1 * 1);

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

            }
            helpCounterUpdateScreen++;
            checkPositionsOfActiveElements();

        }

    }

    private void countDownTime(){

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

                    helpCounterCountDownTime++;
                    currentTimeSlot++;

                    updateScreen();
                    delay = true;
                }
            }
            public void onFinish() {
                updateScreen();
            }
        }.start();

    }

    public void checkPositionsOfActiveElements () {
        for (int currentElement =0; currentElement <arrayList_GameEventRectangles.size(); currentElement++) {
            //Check if the view is still running
            if (arrayList_GameEventRectangles.get(currentElement).isActive() == true && currentElement==0) {
                Log.e("LogTag_FGa_CP", "currentTimeSlot: " + currentTimeSlot);
                Log.e("LogTag_FGa_CP", "currentElement.getX(): " + arrayList_GameEventRectangles.get(currentElement).getX());
            }
        }
    }

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

这是 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">


    <View
        android:id="@+id/imageView_TargetRectangle"
        android:layout_width="0dp"
        android:layout_height="0dp"


        android:background="@drawable/rectangle"
        app:layout_constraintBottom_toBottomOf="parent"

        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_percent="0.16"
        app:layout_constraintHorizontal_bias="0.535"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.01"
        app:layout_constraintWidth_percent="0.10" />


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

为视图设置动画并倒计时我使用处理程序

CountDownTimer
。实际上动画本身是有效的(我首先将不可见的橙色矩形移到最右边,然后在一定时间后使其可见并设置动画)。问题是,我无法准确确定橙色矩形的位置。我只能在一定时间跨度后确定它,这对我想做的事情来说还不够。

这是 LogTag 的输出:

2023-03-14 18:37:40.318 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 100
2023-03-14 18:37:40.320 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 2028.0
2023-03-14 18:37:40.617 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 101
2023-03-14 18:37:40.618 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 2013.0167
2023-03-14 18:37:40.857 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 102
2023-03-14 18:37:40.857 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1972.158
2023-03-14 18:37:41.149 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 103
2023-03-14 18:37:41.150 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1883.3052
2023-03-14 18:37:41.403 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 104
2023-03-14 18:37:41.403 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1780.0686
2023-03-14 18:37:41.668 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 105
2023-03-14 18:37:41.669 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1634.5706
2023-03-14 18:37:41.931 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 106
2023-03-14 18:37:41.936 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1495.0557
2023-03-14 18:37:42.185 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 107
2023-03-14 18:37:42.186 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1317.382
2023-03-14 18:37:42.481 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 108
2023-03-14 18:37:42.481 15544-15544/com.example.game E/LogTag_FGa_CP: currentElement.getX(): 1088.4066
2023-03-14 18:37:42.760 15544-15544/com.example.game E/LogTag_FGa_CP: currentTimeSlot: 109

如您所见,当我检查它时,橙色矩形的 x 位置减少了大量像素(例如 1972.158、1883.3052、1780.0686)。我真正想要的是一种方法,它可以返回橙色矩形的每个位置,意思是:1972、1971、1970、1969 等。这样,我可以确保 - 在按下按钮后 - 得到橙色矩形的确切位置。

提醒:您是否需要更多信息,或者为什么没有人至少给我一个可能的解决方案的建议。我会非常感激。

java android android-animation game-engine
2个回答
0
投票

除了使用 x 和 y,您还可以获得屏幕上的确切位置:

Java

    var location = new int[2];
    view.getLocationOnScreen(location);

科特林

    val location = IntArray(2)
    view.getLocationOnScreen(location)

0
投票

我真正想要的是一种方法,可以返回橙色矩形的每个位置,意思是:1972、1971、1970、1969 等。

矩形不一定会出现在这些位置(1972、1971 等),即使动画跨越这些位置。 Android 设备以每秒 60 帧的速率 (Frame Rate)(对于某些设备可能为 90 FPS 或 120 FPS)组成显示内容。对于 60 FPS,每 16 毫秒左右显示一帧。

让我们考虑一个动画,它在一秒钟内将视图的 x 位置从 0 像素移动到 1,000 像素。动画有这三个约束:

  1. 动画必须持续一秒;
  2. 动画必须移动1000像素;
  3. 动画必须坚持 60 FPS 显示速率。

为了满足这些条件,假设速度恒定,动画必须将视图平均移动 1,000/16 像素或每帧 62.5 像素。

所以,人们会期望 x 值遵循序列“0、62.5、125、187.5,...”,这会跳过很多位置。这与电影放映机的操作方式没有什么不同。电影的每一帧都代表时间的量化飞跃。

考虑到所有这些,一个解决方案是根据上述内容确定构成成功点击按钮的位置范围。您也可以创建一次移动一个像素的动画,但是由于帧速率限制,动画可能太慢,因为您每秒最多可以移动 60 个像素。



以下是一些可能有帮助的附加信息:

  • 一旦动画开始,您可以随时通过检查View#getX()来确定动画视图的位置。

此视图的视觉 x 位置,以像素为单位。这相当于 translationX 属性加上当前的 left 属性。

这是正在动画的

translationX
属性。

  • 在点击监听器中,如上所述检索动画视图的“x”位置,并将其与对应于用户应该尝试点击的区域的范围进行比较。

这是大纲,尽管您可能需要对代码进行其他调整以适应这种方法。

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