Android – Appuyez sur le bouton pour répéter l'action

Bonjour à tous,

J'admettrai tout de suite que je suis novice dans le développement et j'essaie de m'adresser à Android. J'ai essayé de rechercher le «net pour trouver des conseils sur la façon d'implémenter un« bouton de maintien pour répéter l'action »- J'ai créé un clavier numérique personnalisé à partir des boutons et je souhaite un comportement en arrière-plan. Jusqu'à présent, j'ai appelé un ami qui n'a pas codé Android avant, mais a fait beaucoup de C # / Java et semble savoir ce qu'il fait.

  • Protéger la constante de chaîne contre l'ingénierie inverse
  • Applink ne peut pas être résolu dans Facebook SDK
  • Comment résoudre CursorIndexOutOfBoundsException
  • Comment résoudre l'erreur échec de la synchronisation Gradle (DefaultMavenFactory)?
  • Quelle est la différence entre l'opérateur sim et l'opérateur de réseau
  • Stockage de l'objet List List Array dans SharedPreferences
  • Le code ci-dessous fonctionne très bien, mais je pense que cela pourrait être fait plus soigneusement. Je m'excuse si j'ai oublié les bits, mais j'espère que cela explique mon approche. Je pense que l'onTouchListener est ok, mais la façon dont les threads sont traités ne se sent pas bien.

    Existe-t-il un moyen meilleur ou plus simple de le faire?

    Merci,

    M

    public class MyApp extends Activity { private boolean deleteThreadRunning = false; private boolean cancelDeleteThread = false; private Handler handler = new Handler(); public void onCreate(Bundle icicle) { super.onCreate(icicle); //May have missed some declarations here... Button_Del.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { handleDeleteDown(); return true; } case MotionEvent.ACTION_UP: { handleDeleteUp(); return true; } default: return false; } } private void handleDeleteDown() { if (!deleteThreadRunning) startDeleteThread(); } private void startDeleteThread() { Thread r = new Thread() { @Override public void run() { try { deleteThreadRunning = true; while (!cancelDeleteThread) { handler.post(new Runnable() { @Override public void run() { deleteOneChar(); } }); try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException( "Could not wait between char delete.", e); } } } finally { deleteThreadRunning = false; cancelDeleteThread = false; } } }; // actually start the delete char thread r.start(); } }); } private void handleDeleteUp() { cancelDeleteThread = true; } private void deleteOneChar() { String result = getNumberInput().getText().toString(); int Length = result.length(); if (Length > 0) getNumberInput().setText(result.substring(0, Length-1)); //I've not pasted getNumberInput(), but it gets the string I wish to delete chars from } 

  • Android studio ne se télécharge pas complètement
  • Codage de chaînes Android et conversion de l'entité html
  • ERROR: partition de redimensionnement e2fsck a échoué avec le code de sortie 8
  • Le studio Android ne s'ouvre pas sur Windows
  • Comment montrer une disposition sur l'autre par programme dans mon cas?
  • Modifier plusieurs propriétés avec un seul ObjectAnimator?
  • 8 Solutions collect form web for “Android – Appuyez sur le bouton pour répéter l'action”

    Il s'agit d'une implémentation plus indépendante, utilisable avec n'importe quelle vue, qui prend en charge l'événement tactile

     import android.os.Handler; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; /** * A class, that can be used as a TouchListener on any view (eg 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 OnTouchListener { private Handler handler = new Handler(); private int initialInterval; private final int normalInterval; private final OnClickListener clickListener; private Runnable handlerRunnable = new Runnable() { @Override public void run() { handler.postDelayed(this, normalInterval); clickListener.onClick(downView); } }; private View downView; /** * @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, 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); downView = view; downView.setPressed(true); clickListener.onClick(view); return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: handler.removeCallbacks(handlerRunnable); downView.setPressed(false); downView = null; return true; } return false; } } 

    Usage:

     Button button = new Button(context); button.setOnTouchListener(new RepeatListener(400, 100, new OnClickListener() { @Override public void onClick(View view) { // the code to execute repeatedly } })); 

    Voici une classe simple appelée AutoRepeatButton qui, dans de nombreux cas, peut être utilisée comme remplacement déroulant pour la classe Button standard:

     package com.yourdomain.yourlibrary; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.Button; public class AutoRepeatButton extends Button { private long initialRepeatDelay = 500; private long repeatIntervalInMilliseconds = 100; private Runnable repeatClickWhileButtonHeldRunnable = new Runnable() { @Override public void run() { //Perform the present repetition of the click action provided by the user // in setOnClickListener(). performClick(); //Schedule the next repetitions of the click action, using a faster repeat // interval than the initial repeat delay interval. postDelayed(repeatClickWhileButtonHeldRunnable, repeatIntervalInMilliseconds); } }; private void commonConstructorCode() { this.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); if(action == MotionEvent.ACTION_DOWN) { //Just to be sure that we removed all callbacks, // which should have occurred in the ACTION_UP removeCallbacks(repeatClickWhileButtonHeldRunnable); //Perform the default click action. performClick(); //Schedule the start of repetitions after a one half second delay. postDelayed(repeatClickWhileButtonHeldRunnable, initialRepeatDelay); } else if(action == MotionEvent.ACTION_UP) { //Cancel any repetition in progress. removeCallbacks(repeatClickWhileButtonHeldRunnable); } //Returning true here prevents performClick() from getting called // in the usual manner, which would be redundant, given that we are // already calling it above. return true; } }); } public AutoRepeatButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); commonConstructorCode(); } public AutoRepeatButton(Context context, AttributeSet attrs) { super(context, attrs); commonConstructorCode(); } public AutoRepeatButton(Context context) { super(context); commonConstructorCode(); } } 

    Votre implémentation de base est sonore. Cependant, je voudrais encapsuler cette logique dans une autre classe afin que vous puissiez l'utiliser à d'autres endroits sans dupliquer le code. Voir par exemple cette implémentation de la classe "RepeatListener" qui fait la même chose que vous voulez faire, à l'exception d'une barre de recherche.

    Voici un autre fil avec une solution alternative , mais c'est très semblable à celui de votre premier.

    Le RepeatListenerClass de Oliv est assez bon, mais il ne gère pas "MotionEvent.ACTION_CANCEL", de sorte que le gestionnaire ne supprime pas l'appel dans cette action. Cela crée des problèmes dans PagerAdapter , etc. J'ai donc ajouté ce cas d'événement.

     private Rect rect; // Variable rect to hold the bounds of the view public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: handler.removeCallbacks(handlerRunnable); handler.postDelayed(handlerRunnable, initialInterval); downView = view; rect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); clickListener.onClick(view); break; case MotionEvent.ACTION_UP: handler.removeCallbacks(handlerRunnable); downView = null; break; case MotionEvent.ACTION_MOVE: if (!rect.contains(view.getLeft() + (int) motionEvent.getX(), view.getTop() + (int) motionEvent.getY())) { // User moved outside bounds handler.removeCallbacks(handlerRunnable); downView = null; Log.d(TAG, "ACTION_MOVE...OUTSIDE"); } break; case MotionEvent.ACTION_CANCEL: handler.removeCallbacks(handlerRunnable); downView = null; break; } return false; } 

    La classe de Carl est autonome et fonctionne bien.

    Je rendrais le délai initial et l'intervalle de répétition configurable. Faire cela,

    Attrs.xml

     <resources> <declare-styleable name="AutoRepeatButton"> <attr name="initial_delay" format="integer" /> <attr name="repeat_interval" format="integer" /> </declare-styleable> </resources> 

    AutoRepeatButton.java

      public AutoRepeatButton(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoRepeatButton); int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.AutoRepeatButton_initial_delay: initialRepeatDelay = a.getInt(attr, DEFAULT_INITIAL_DELAY); break; case R.styleable.AutoRepeatButton_repeat_interval: repeatIntervalInMilliseconds = a.getInt(attr, DEFAULT_REPEAT_INTERVAL); break; } } a.recycle(); commonConstructorCode(); } 

    Alors vous pouvez utiliser la classe comme celle-ci

      <com.thepath.AutoRepeatButton xmlns:repeat="http://schemas.android.com/apk/res/com.thepath" android:id="@+id/btn_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/selector_btn_delete" android:onClick="onBtnClick" android:layout_weight="1" android:layout_margin="2dp" repeat:initial_delay="1500" repeat:repeat_interval="150" /> 

    La classe de Carl est très bonne, voici une modification qui permettra l'accélération (plus longtemps vous maintenez la fonction de clic plus rapide est exécutée:

     package com.yourdomain.yourlibrary; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.Button; public class AutoRepeatButton extends Button { private long initialRepeatDelay = 500; private long repeatIntervalInMilliseconds = 100; // speedup private long repeatIntervalCurrent = repeatIntervalInMilliseconds; private long repeatIntervalStep = 2; private long repeatIntervalMin = 10; private Runnable repeatClickWhileButtonHeldRunnable = new Runnable() { @Override public void run() { // Perform the present repetition of the click action provided by the user // in setOnClickListener(). performClick(); // Schedule the next repetitions of the click action, // faster and faster until it reaches repeaterIntervalMin if (repeatIntervalCurrent > repeatIntervalMin) repeatIntervalCurrent = repeatIntervalCurrent - repeatIntervalStep; postDelayed(repeatClickWhileButtonHeldRunnable, repeatIntervalCurrent); } }; private void commonConstructorCode() { this.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { // Just to be sure that we removed all callbacks, // which should have occurred in the ACTION_UP removeCallbacks(repeatClickWhileButtonHeldRunnable); // Perform the default click action. performClick(); // Schedule the start of repetitions after a one half second delay. repeatIntervalCurrent = repeatIntervalInMilliseconds; postDelayed(repeatClickWhileButtonHeldRunnable, initialRepeatDelay); } else if (action == MotionEvent.ACTION_UP) { // Cancel any repetition in progress. removeCallbacks(repeatClickWhileButtonHeldRunnable); } // Returning true here prevents performClick() from getting called // in the usual manner, which would be redundant, given that we are // already calling it above. return true; } }); } public AutoRepeatButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); commonConstructorCode(); } public AutoRepeatButton(Context context, AttributeSet attrs) { super(context, attrs); commonConstructorCode(); } public AutoRepeatButton(Context context) { super(context); commonConstructorCode(); } } 

    Voici une réponse basée sur Oliv's avec les ajustements suivants:

    • Au lieu de faire appel à un clic et d' onClick directement, il appelle performClick ou performLongClick sur la vue. Cela déclenchera un comportement de clic standard, comme un retour haptique sur un long clic.
    • Il peut être configuré soit pour déclencher l' onClick immédiatement (comme l'original), soit uniquement sur ACTION_UP et uniquement si aucun événement de clic n'a été déclenché (plus comme la façon onClick fonctionne onClick ).
    • Un constructeur non-arg alternatif qui met immediateClick Cliquer sur false et utilise le délai d' attente prolongé standard du système pour les deux intervalles. Pour moi, cela ressemble le mieux à ce que serait une "répression répétée" standard, si elle existait.

    C'est ici:

     import android.os.Handler; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; /** * A class that can be used as a TouchListener on any view (eg a Button). * It either calls performClick once, or performLongClick repeatedly on an interval. * The performClick can be fired either immediately or on ACTION_UP if no clicks have * fired. The performLongClick is fired once after initialInterval and then repeatedly * after 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. * * Based on http://stackoverflow.com/a/12795551/642160 */ public class RepeatListener implements OnTouchListener { private Handler handler = new Handler(); private final boolean immediateClick; private final int initialInterval; private final int normalInterval; private boolean haveClicked; private Runnable handlerRunnable = new Runnable() { @Override public void run() { haveClicked = true; handler.postDelayed(this, normalInterval); downView.performLongClick(); } }; private View downView; /** * @param immediateClick Whether to call onClick immediately, or only on ACTION_UP * @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( boolean immediateClick, int initialInterval, int normalInterval) { if (initialInterval < 0 || normalInterval < 0) throw new IllegalArgumentException("negative interval"); this.immediateClick = immediateClick; this.initialInterval = initialInterval; this.normalInterval = normalInterval; } /** * Constructs a repeat-listener with the system standard long press time * for both intervals, and no immediate click. */ public RepeatListener() { immediateClick = false; initialInterval = android.view.ViewConfiguration.getLongPressTimeout(); normalInterval = initialInterval; } public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: handler.removeCallbacks(handlerRunnable); handler.postDelayed(handlerRunnable, initialInterval); downView = view; if (immediateClick) downView.performClick(); haveClicked = immediateClick; return true; case MotionEvent.ACTION_UP: // If we haven't clicked yet, click now if (!haveClicked) downView.performClick(); // Fall through case MotionEvent.ACTION_CANCEL: handler.removeCallbacks(handlerRunnable); downView = null; return true; } return false; } } 

    La classe de Carl est bonne pour moi. Mais il y a un problème lorsque vous appuyez sur le bouton et que vous faites glisser. En cas de sortir de la zone du bouton, toujours en cliquant sur un événement.

    Ajoutez un code sur ACTION_MOVE comme ' Android: Détectez si l'utilisateur touche et arrête la région du bouton? '

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.