Android RecyclerView 中多Item的EditText焦點混亂,資料錯亂的解決方法(更新)
最近在做畢業設計,使用了RecyclerView來做了以下個人資料的編輯介面。RecyclerView中包含了多種不同的Item,例如圖片裡面的 姓名,公司,部門,職位,郵箱的Item 裡面都包含一個EditText。
RecyclerView中EditText的監聽處理
這個介面的主要邏輯是監聽每個EditText的輸入,然後儲存到個人物件裡,最後儲存到伺服器上。RecyclerView中Item裡EditText的監聽可以按如下程式碼實現:
首先定義個回撥介面:
public interface onTextChangeListener {
void onTextChanged(int pos,String str);
}
在RecyclerView的自定義Adapter中新增一個如上自定義的介面成員變數mTextListener,然後在onBindViewHolder裡面來新增Item中EditText的輸入監聽,即EditText.addTextChangedListener(),然後在監聽事件回撥裡的afterTextChanged 方法中 呼叫 mTextListener.onTextChanged 來達到介面回撥的目的。
public class EditListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private Context mContext;
private LayoutInflater mInflater;
private ArrayList<EditListBean> mList;
private onTextChangeListener mTextListener;
public EditListAdapter(Context context, ArrayList<EditListBean> list){
this.mContext=context;
mInflater=LayoutInflater.from (mContext);
this.mList=list;
}
//設定自定義介面成員變數
public void setOnTextChangeListener(onTextChangeListener onTextChangeListener){
this.mTextListener=onTextChangeListener;
}
//建立ViewHolder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder=new
itemHolder2(mInflater.inflate(R.layout.edit_recyclerview_item2,parent,false));
return holder;
}
//繫結ViewHolder
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int
position) {
final itemHolder2 holder2= (itemHolder2) holder;
holder2.title.setText(mList.get(position).getKey());
holder2.input.setText((String) mList.get(position).getValue());
holder2.input.setHint(mList.get(position).getHint());
//新增EditText的監聽事件
holder2.input.addTextChangedListener(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) {
//通過介面回撥將資料傳遞到Activity中
mTextListener.onTextChanged(position,holder2.input.getText().toString());
}
});
//內部類 ViewHolder
class itemHolder2 extends RecyclerView.ViewHolder{
TextView title;
EditText input;
public itemHolder2(View itemView) {
super(itemView);
title= (TextView) itemView.findViewById(R.id.EditActivity_item2_title);
input= (EditText) itemView.findViewById(R.id.EditActivity_item2_text);
}
}
@Override
public int getItemCount() {
return mList.size();
}
@Override
public int getItemViewType(int position){
return mList.get(position).getType();
}
}
Activity中的程式碼實現,在mAdapter中新增匿名內部類來監聽EditText中的介面回撥資料:
mAdapter.setOnTextChangeListener(new onTextChangeListener() {
@Override
public void onTextChanged(int pos, String str) {
//在這裡拿到RecyclerView中Item的position和EditText中的變化
mPedit.setList(pos,str);
Log.e(String.format("%d is ---->",pos),str);
}
});
OK 上面的監聽事件處理完了 我們來試試效果怎麼樣。首先點選姓名裡面的EditText,在馬化騰後面輸入哈哈哈哈 然後收起鍵盤, 可以發現竟然下面幾個Item的EditText也觸發了回撥事件了,也就是我編輯了Item2的EditText,然後Item7 , 9 , 11的EditText也觸發了onTextChanged的監聽事件了。如圖,出現了監聽事件錯亂的重大的Bug。
Recycler中 多Item中EditText焦點混亂的網上方法
2017年12月11號更新下,現在很多網上的解決方法是使用EditText的TAG來解決問題,EditText中有setTag和getTag可以儲存Object物件作為標籤,然後把textWatcher作為標籤設定TAG來作為標記表明這個item的editText是否已經設定textChange監聽了。在Adapter的onBindViewHolder中每次都先判斷editText的Tag是否有textWatcher物件,有的話就呼叫removeTextChangedListener來移除由於檢視複用之前繫結的textWatcher,然後就設定editText的內容setText,最後再給editText設定監聽器addTextChangedListener,並把這個textWatcher加入到editText的TAG標籤中。總結來說就是在介面卡裡先移除被複用事件,再新增新事件。
//避免複用
EditText editText = helper.getView(R.id.et_comment);
if (editText.getTag() instanceof TextWatcher) {
editText.removeTextChangedListener((TextWatcher) editText.getTag());
}
helper.setText(R.id.et_comment, item.content);
TextWatcher watcher = 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) {
item.content = s.length() > 0 ? s.toString() : "";
}
};
editText.addTextChangedListener(watcher);
editText.setTag(watcher);
這種方式並不優雅,而且還要進行型別判斷和強制型別轉型,對效能有一定的開銷,下面來介紹真正的優雅解決EditText焦點混亂的解決方法:
## Recycler中 多Item中EditText焦點混亂,資料錯亂的正確優雅的解決方法 !!##
首先要知道EditText焦點錯亂的問題就是RecyclerView的檢視回收與複用問題,例如在位置0有item1檢視中的editText已經綁定了textWatcher1監聽器,但是又沒有移除監聽器,在位置6時又複用了item1檢視,editText又再次綁定了textWatcher2監聽器,此時item1檢視中的editText已經綁定了2個textWatcher監聽器了,當在位置6進行輸入資料時,必定會導致位置0的資料變化了,令位置0的資料跟位置6的資料一模一樣,也就是資料錯亂了。原因是editText中的addTextChangedListener是可以繫結多個監聽器的,來看看它的實現:
public void addTextChangedListener(TextWatcher watcher) {
if (mListeners == null) {
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
}
可以看到mListeners 是一個ArrayList陣列,當多次呼叫addTextChangedListener時,一個editText可以繫結多個textWatcher監聽器,所以要記住在每次呼叫addTextChangedListener完之後必須呼叫removeTextChangedListener來接觸繫結。
那呼叫這兩個方法的時間節點是在哪裡的呢?首先我們要知道如果一個editText要獲取輸入值那它就首先必須要獲取到焦點了,沒錯,答案就是設定setOnFocusChangeListener監聽器來判斷焦點的變化從而設定addTextChangedListener和removeTextChangedListener。
程式碼如下:
final itemHolder2 holder2= (itemHolder2) holder;
//holder2.input為EditTextView
holder2.input.setText((String) mList.get(position).getValue());
holder2.input.setHint(mList.get(position).getHint());
final TextWatcher textWatcher=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) {
Log.e("textWatcher",holder.getAdapterPosition()+"");
if(holder2.input.hasFocus()){//判斷當前EditText是否有焦點在
//通過介面回撥將資料傳遞到Activity中
mTextListener.onTextChanged(position,holder2.input.getText().toString());
}
}
};
//設定EditText的焦點監聽器判斷焦點變化,當有焦點時addTextChangedListener,失去焦點時removeTextChangedListener
holder2.input.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){
holder2.input.addTextChangedListener(textWatcher);
Log.e("addTextChanged",position+"");
}else{
holder2.input.removeTextChangedListener(textWatcher);
Log.e("removeTextChanged",position+"");
}
}
});
通過設定setOnFocusChangeListener來監聽焦點就能完美的解決了RecyclerView中EditText資料監聽textWatcher導致的資料錯亂問題了。目前來說是最優雅的一種方式了,也不需要用到Tag了。