如何在Android中改变视图的颜色,只要按下repeatListener按钮而不闪烁

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

我有以下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

提醒:你知道我该怎么做吗?

java android android-animation android-button
1个回答
0
投票

我想要的是,只要按下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

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