我有以下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
*/
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();
//Define the repeatListener
binding.repeatButton.setOnTouchListener((View.OnTouchListener) new RepeatListener(30, 30, new View.OnClickListener() {
@Override
public void onClick(View view) {
View_Game_Event_Rectangle activeElement = null;
for (int currentElement =0; currentElement <arrayList_GameEventRectangles.size(); currentElement++) {
activeElement = arrayList_GameEventRectangles.get(currentElement);
}
if (activeElement!=null) {
activeElement.setBackground(ContextCompat.getDrawable(getActivity(),R.drawable.game_event_rectangle_solar_2).mutate());
// Schedule the change back to R.drawable.game_event_rectangle_solar_1 after 10 milliseconds
Handler handler = new Handler();
View_Game_Event_Rectangle finalActiveElement = activeElement;
handler.postDelayed(new Runnable() {
@Override
public void run() {
// Change the background back to R.drawable.game_event_rectangle_solar_1
finalActiveElement.setBackground(ContextCompat.getDrawable(getActivity(), R.drawable.game_event_rectangle_solar_1).mutate());
}
}, 300);
}
}
}));
startGame();
return binding.getRoot();
}
public void startGame () {
startRound();
}
public void startRound () {
orangeRectangleValuesForTheLevel = new int[5000];
orangeRectangleValuesForTheLevel [54] = 20;
//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_solar_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);
}
}
这是 RepeatListener 的代码:
package com.example.game;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
/**
* A class, that can be used as a TouchListener on any view (e.g. a Button).
* It cyclically runs a clickListener, emulating keyboard-like behaviour. First
* click is fired immediately, next one after the initialInterval, and subsequent
* ones after the normalInterval.
*
* <p>Interval is scheduled after the onClick completes, so it has to run fast.
* If it runs slow, it does not generate skipped onClicks. Can be rewritten to
* achieve this.
*/
public class RepeatListener implements View.OnTouchListener {
private Handler handler = new Handler();
private int initialInterval;
private final int normalInterval;
private final View.OnClickListener clickListener;
private View touchedView;
private Runnable handlerRunnable = new Runnable() {
@Override
public void run() {
if(touchedView.isEnabled()) {
handler.postDelayed(this, normalInterval);
clickListener.onClick(touchedView);
} else {
// if the view was disabled by the clickListener, remove the callback
handler.removeCallbacks(handlerRunnable);
touchedView.setPressed(false);
touchedView = null;
}
}
};
/**
* @param initialInterval The interval after first click event
* @param normalInterval The interval after second and subsequent click
* events
* @param clickListener The OnClickListener, that will be called
* periodically
*/
public RepeatListener(int initialInterval, int normalInterval,
View.OnClickListener clickListener) {
if (clickListener == null)
throw new IllegalArgumentException("null runnable");
if (initialInterval < 0 || normalInterval < 0)
throw new IllegalArgumentException("negative interval");
this.initialInterval = initialInterval;
this.normalInterval = normalInterval;
this.clickListener = clickListener;
}
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
handler.removeCallbacks(handlerRunnable);
handler.postDelayed(handlerRunnable, initialInterval);
touchedView = view;
touchedView.setPressed(true);
clickListener.onClick(view);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
handler.removeCallbacks(handlerRunnable);
touchedView.setPressed(false);
touchedView = null;
return true;
}
return false;
}
}
发生的事情是一个简单的定制视图矩形
View_Game_Event_Rectangle
从右到左进行动画处理。然后我有一个重复侦听器。当按下重复侦听器时,自定义视图的背景颜色将从原来的 R.drawable.game_event_rectangle_solar_1
更改为新的 R.drawable.game_event_rectangle_solar_2
。一段时间后,处理程序会将颜色更改回来。问题是,当按住重复监听器时,定制的视图矩形会闪烁。我尝试更改处理程序的时间延迟,但这并没有解决问题(当延迟太高时,颜色会长时间保持变化)。我想要的是,只要按下 RepeatListener 按钮,定制视图矩形的颜色就会保持变化。不再按下 RepeatListener 按钮后,颜色应变回原来的颜色。重要的是,按下按钮时,定制的视图矩形不应闪烁,而应具有替代颜色R.drawable.game_event_rectangle_solar_2
。
提醒:你知道我该怎么做吗?
我想要的是,只要按下repeatListener按钮,自定义视图矩形的颜色就会保持变化。
矩形的颜色在 300 毫秒后变回原样,这就是您编码的内容。这可能会产生闪烁。
很难从提供的代码中判断到底哪里出了问题。您尚未提供
RepeatListener
的代码。我怀疑里面的代码:
public void onClick(View view) {
即使在第一次触摸按钮后也会执行。您应该在
event.getAction() == MotionEvent.ACTION_DOWN
时执行触摸代码,并在 event.getAction() == MotionEvent.ACTION_UP
时处理触摸操作。否则,您还将拦截 MotionEvent.ACTION_MOVE
事件,这些事件可能会给您带来闪烁。我认为这在 RepeatListener
中没有实现。
因此,当
MotionEvent.ACTION_DOWN
将颜色更改为您希望在用户触摸按钮时保持的颜色时。然后当 MotionEvent.ACTION_UP
出现时,将颜色改回来。我认为这里不需要postDelayed
。