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