How to Handle Repeated Click Instance Code Elegantly in Android

  • 2021-10-15 11:41:10
  • OfStack

Problem

Sometimes some actions are to prevent users from responding to the next one at the end of one response. But some test users have to be aggressive and crazy. Malice like this should be prevented.

For example, in the client, 1 button 1 needs to avoid repeated clicks, such as: purchase, payment, confirmation, submission, praise, collection, etc. Repeated clicks in these scenes in a short time will cause 1 problem.

The following words are not much to say, let's take a look at the detailed introduction

Previous treatment

It is possible to record the last click time manually, and then calculate the time interval to judge whether to repeat the click or not


 private long mLastClickTime = 0;
 public static final int TIME_INTERVAL = 1000;
 private Button mButton;

 private void initView() {
 mButton.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
  if (System.currentTimeMillis() - mLastClickTime >= TIME_INTERVAL) {
   //to do
   mLastClickTime = System.currentTimeMillis();
  } else {
   Toast.makeText(getActivity(), " Please do not repeat clicking ", Toast.LENGTH_LONG).show();
  }
  }
 });
 }

Or abstract processing is adopted under encapsulation 1


public abstract class IClickListener implements View.OnClickListener {
 private long mLastClickTime = 0;
 public static final int TIME_INTERVAL = 1000;

 @Override
 public final void onClick(View v) {
 if (System.currentTimeMillis() - mLastClickTime >= TIME_INTERVAL) {
  onIClick(v);
  mLastClickTime = System.currentTimeMillis();
 } else {
  onAgain(v);
 }
 }

 protected abstract void onIClick(View v);

 protected void onAgain(View v) {

 }
}

Use (repeat clicks without reminding)


 mButton.setOnClickListener(new IClickListener() {
  @Override
  protected void onIClick(View v) {
  
  }
 });

Or (need to remind you to click repeatedly)

mButton.setOnClickListener(new IClickListener() {
@Override
protected void onIClick(View v) {

}

@Override
protected void onAgain(View v) {

}
});
You can see that it is very convenient to use after packaging, but there are several disadvantages

Overinvasive-OnClickListener is completely replaced with subclass IClickListener Irreversible-it is not easy to restore to OnClickListener because it is not the same callback If it is a third-party control, it cannot handle repeated clicks It can only be written in the inner class mode-because of the single inheritance feature, we can only call back the inner class, and the code is not beautiful

Elegant handling

The problem of repeated clicks is how to dynamically control whether the original click events are generated, instead of enhancing the functions on the original click events; Combined with design patterns, we know that proxy patterns can deal with this problem well, rather than inheritance.

Agent


public class ClickProxy implements View.OnClickListener {

 private View.OnClickListener origin;
 private long lastclick = 0;
 private long timems = 1000;

 public ClickProxy(View.OnClickListener origin) {
  this.origin = origin;
 }

 @Override
 public void onClick(View v) {
  if (System.currentTimeMillis() - lastclick >= timems) {
   origin.onClick(v);
   lastclick = System.currentTimeMillis();
  }
 }
}

Original click event


  mButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    //to do
   }
  });

Agent uses


  mButton.setOnClickListener(new ClickProxy(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    //to do
   }
  }));

As you can see, the logic of the original code has not changed, only the proxy class has been added, which greatly reduces the intrusion

Of course, you can also expand 1 to provide callback of repeated clicks and custom interval time, and add 1 constructor


public class ClickProxy implements View.OnClickListener {

 private View.OnClickListener origin;
 private long lastclick = 0;
 private long timems = 1000; //ms
 private IAgain mIAgain;

 public ClickProxy(View.OnClickListener origin, long timems, IAgain again) {
  this.origin = origin;
  this.mIAgain = again;
  this.timems = timems;
 }

 public ClickProxy(View.OnClickListener origin) {
  this.origin = origin;
 }

 @Override
 public void onClick(View v) {
  if (System.currentTimeMillis() - lastclick >= timems) {
   origin.onClick(v);
   lastclick = System.currentTimeMillis();
  } else {
   if (mIAgain != null) mIAgain.onAgain();
  }
 }

 public interface IAgain {
  void onAgain();// Repeat clicking 
 }
}

How to handle the click event inside the third party View

Maybe we use a custom control, which has already consumed click events inside, but we need to avoid repeated clicks. We can't change the internal code or reset the click events, which will lose the internal processing logic; At this time, we can adopt reflection processing mode and combine agent to realize seamless replacement


// Provide 1 Static methods 
public class ClickFilter {
 public static void setFilter(View view) {
  try {
   Field field = View.class.getDeclaredField("mListenerInfo");
   field.setAccessible(true);
   Class listInfoType = field.getType();
   Object listinfo = field.get(view);
   Field onclickField = listInfoType.getField("mOnClickListener");
   View.OnClickListener origin = (View.OnClickListener) onclickField.get(listinfo);
   onclickField.set(listinfo, new ClickProxy(origin));
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

Use:


 private StateButton mStateButton;// Custom control 

 private void initView() {
  ClickFilter.setFilter(mStateButton);
 }

This dynamic replacement method is also suitable for ordinary scenarios. After setting click events, the filter can be set to handle repeated clicks (including click events bound by annotations such as butterknife)

Finally


Related articles: