在用户提交表单内容,请求接口的通用场景下,增删改查 crud 操作是比较频繁出现的操作。
对于改查操作来讲,并不会引起数据上的问题,影响的最多是接口重复调用;但是对于增删操作来讲,很容易引起数据重复添加,删除异常等问题。
对用户来讲,网络延时或者不稳定造成请求接口时出现的App反应迟缓,下意识的会进行重复提交;或者用户不小心进行的重复点击;又或者测试App自动化测试工具导致的短时间内大量重复点击。
这些场景都应该在代码实现的时候应该考虑的。
- 说是笨方法,当然也是出现重复提交问题时候,想到的最有效的方法。
对于每个点击按钮,初始状态 isPressed = FALSE 进行状态判定,点击一次记录状态 isPressed = TURE ,然后进行接口请求或者其它处理,接口请求完成之后初始化该按钮状态 isPressed = FALSE 。
- Android中怎样设置按钮不能点击?
其实,和上面的方法没什么区别,只不过借助了Button的属性设置来完成的。
- Button.setEnabled(false);//设置这个属性
思路和1.1一致,生了一个局部变量,依然有效!
其实,对于重复点击这类问题,只需要一个状态标记就能够解决,但是我们的之前的方法只适用于单个Button按钮,对于多个按钮,如果我们这么去做,必然是大量的 Ctrl+C 和 Ctrl+V,想想都心累,更别说我还真的这么干过。
如果有一种一劳永逸的方法岂不是很好,对了,然后我们想到了封装,从Button组件内部去判断岂不是美妙,这样我们只需要替换xml中的Button就可以完美的解决大量的按钮点击事件。
既然,Button按钮的事件点击是通过
- setOnClickListener(OnClickListener listener)
方法注册的,我们只需要在注册的时候,替换新的Listener,加上我们自定义事件判断就可以搞定。
比如:
- public class OnNoRepeatButton extends Button
- {
- public ButtonEx(final Context context)
- {
- super(context);
- }
- public ButtonEx(final Context context, final AttributeSet attrs)
- {
- super(context, attrs);
- }
- public ButtonEx(final Context context, final AttributeSet attrs, final int defStyleAttr)
- {
- super(context, attrs, defStyleAttr);
- }
- @Override
- public void setOnClickListener(final OnClickListener listener)
- {
- super.setOnClickListener(new OnNoRepeatClickListener(listener));
- }
- }
我们的OnNoRepeatClickListener:
- /**
- * 防止快速点击中多次触发事件的自定义OnClickListener
- */
- public class OnNoRepeatClickListener implements OnClickListener
- {
- private final OnClickListener mListener;
- private long lastClickTime = 0;
- public OnNoRepeatClickListener(final OnClickListener listener)
- {
- this.mListener = listener;
- }
- @Override
- public void onClick(final View v)
- {
- synchronized (this)
- {
- //判断当前点击时间与上一次点击时间的间隔
- if (System.currentTimeMillis() - this.lastClickTime > 1000)
- {
- if (this.mListener != null)
- this.mListener.onClick(v);
- //处理完响应事件后记录时间确保下次响应
- this.lastClickTime = System.currentTimeMillis();
- }
- }
- }
- }
Note: 代码中的1000单位是ms,具体的时间需要根据需求来定义。
Clever方法已经算是一个相对好的解决思路,但是如果我们的Textview ,ImageView , View等组件点击事件重复需要怎么解决呢?
也需要我们重写每一个的组件的
- setOnClickListener(OnClickListener listener)
来实现。
那么,如果我们在重写了Activity/Fragment中重写
- View.OnClickListener
方法,对点击事件时间进行判断,便可以减少对View组件的
- setOnClickListener(OnClickListener listener)
方法的重写。
上代码:
- interface IBaseView extends View.OnClickListener {
- /**
- * 初始化数据
- *
- * @param bundle 传递过来的bundle
- */
- void initData(final Bundle bundle);
- /**
- * 绑定布局
- *
- * @return 布局Id
- */
- int bindLayout();
- /**
- * 初始化view
- */
- void initView(final Bundle savedInstanceState, final View view);
- /**
- * 业务操作
- */
- void doBusiness();
- /**
- * 视图点击事件
- *
- * @param view 视图
- */
- void onWidgetClick(final View view);
- }
BaseActivity.java
- /**
- * <pre>
- * desc : Activity基类
- * </pre>
- */
- public abstract class BaseActivity extends AppCompatActivity implements IBaseView {
- /**
- * 当前Activity渲染的视图View
- */
- protected View contentView;
- /**
- * 上次点击时间
- */
- private long lastClick = 0;
- protected BaseActivity mActivity;
- @Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mActivity = this;
- Bundle bundle = getIntent().getExtras();
- initData(bundle);
- setBaseView(bindLayout());
- initView(savedInstanceState, contentView);
- doBusiness();
- }
- protected void setBaseView(@LayoutRes int layoutId) {
- setContentView(contentView = LayoutInflater.from(this).inflate(layoutId, null));
- }
- /**
- * 判断是否快速点击
- *
- * @return {@code true}: 是<br>{@code false}: 否
- */
- private boolean isFastClick() {
- long now = System.currentTimeMillis();
- if (now - lastClick >= 800) {
- lastClick = now;
- return false;
- }
- return true;
- }
- @Override public void onClick(final View view) {
- if (!isFastClick()) onWidgetClick(view);
- }
- }
当然,Smart方法也不是最佳的解决方案,这样来做需要组件采用
- setOnClickListener(this)
调用方式,让Activity/Fragment统一处理点击事件的响应,Clever和Smart的结合也是一个不错的选择。
- Have fun , enjoy Coding...
来源: https://juejin.im/post/5a02a76f51882529bb456240