前言背景
在App开发过程中,搜索功能是必不可少的。一般对于搜索功能,要么是输入一段文字后自己手动点击搜索按钮进行搜索;要么是实时的搜索。如果输入框中每一个字符的改变都要去触发网络请求的话,会浪费用户的流量,增大服务器的负载,并且使页面比较卡顿。这显然是不可取的。举一个很简单的例子,用户想要搜索 "ABC" ,如果我们直接用TextWatcher监听里面的afterTextChanged(Editable s)方法来处理的话,会向服务器发送3次请求。显然这会造成差的用户体验并浪费网路资源。
失败的实现方式
显然我们还是要从TextWatcher 这个监听下做文章,下面是实时搜索失败的实现方式。
/**
* EditText 设置的监听
*
*/
private TextWatcher mSearchWatch = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String searchword = s.toString().trim();
Log.e("tianzhu", searchword);
startSearch(searchword);
}
};
/**
* 开始P层搜索
*
*/
private void startSearch(String keyword) {
if (TextUtils.isEmpty(keyword)) {
return;
}
mPresenter.searchData(keyword);
mSearchEdit.clearFocus();
}
在上面的方式中,每一个字符的改变都要去触发网络请求的话,会浪费用户的流量,增大服务器的负载,并且使页面比较卡顿。
优化的实现方式
我们可以思考,既然每次文本框的改变都会回调 afterTextChanged(Editable s) ,我进行优化的策略是当EditText中两次改变在 500ms 内不会触发搜索。当输入框文字在500ms 内未发生改变时,再触发搜索。
/**
* EditText 设置的监听
*
*/
private TextWatcher mSearchWatch = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String searchword = s.toString().trim();
//我们可以把每次输入框改变的字符串传给一个工具类,并让它来判断是否进行搜索
mOptionSearch.optionSearch(searchword);
Log.e("tianzhu", searchword);
}
};
/**
* 开始P层搜索
*
*/
private void startSearch(String keyword) {
if (TextUtils.isEmpty(keyword)) {
return;
}
mPresenter.searchData(keyword);
mSearchEdit.clearFocus();
}
@Override
public void getKeyword(String keyword) {
//开始搜索
if (!TextUtils.isEmpty(keyword) && mPresenter != null) {
startSearch(keyword);
}
}
**
* Created by tianzhu on 17/3/15.
* 搜索优化,快速点击不会一直触发搜索,这也是最关键的类
*/
public class OptionSearch implements CommonHandler.MessageHandler {
private String currentWord;
private IFinishListener mListener;
private MyRunnable myRunnable = new MyRunnable();
private CommonHandler mHandler;
/**
* 点击按钮响应间隔时间-毫秒
*/
private int INTERVAL_TIME = 500;
public OptionSearch(Looper looper) {
mHandler = new CommonHandler(looper, this);
}
/**
* 这一步就是实时搜索优化的关键代码了,当EditText中的文字发生改变时,我们先会将handle中的Callback移除掉。然后使用Handle发一个延时的消息。最后通过回调getKeyword,让Activity开始搜索
*/
public void optionSearch(String keyword) {
this.currentWord = keyword;
if (myRunnable != null) {
mHandler.removeCallbacks(myRunnable);
}
mHandler.postDelayed(myRunnable, INTERVAL_TIME);
}
public void setListener(IFinishListener listener) {
this.mListener = listener;
}
@Override
public void handleMessage(Message msg) {
if(mListener!=null){
mListener.getKeyword(currentWord);
}
}
public interface IFinishListener {
void getKeyword(String keyword);
}
private class MyRunnable implements Runnable {
@Override
public void run() {
mHandler.sendEmptyMessage(1);
}
}
}
/**
* Created by tianzhu on 2016/07/19.
* 防止内存泄露handle
*/
public class CommonHandler extends Handler {
public interface MessageHandler {
void handleMessage(Message msg);
}
private WeakReference<MessageHandler> mMessageHandler;
public CommonHandler(MessageHandler msgHandler) {
mMessageHandler = new WeakReference<MessageHandler>(msgHandler);
}
public CommonHandler(Looper looper, MessageHandler msgHandler) {
super(looper);
mMessageHandler = new WeakReference<MessageHandler>(msgHandler);
}
@Override
public void handleMessage(Message msg) {
MessageHandler realHandler = mMessageHandler.get();
if (realHandler != null) {
realHandler.handleMessage(msg);
}
}
}
总结
1.在用户快速输入的过程中不触发搜索
2.使用弱引用避免Handler内存泄露
3.使用Handler的 postDelayed 方法 发送延时消息。当用户快速输入时,使用mHandler.removeCallbacks(myRunnable),将当前Handler的中的Callback移除。重新发一个postDelayed。
这样就做到了实时搜索的,小伙伴们赶紧试试吧。
网友评论