在上一篇文章中,咱們學習了商城購物車加減控件的簡單封裝,知道了封裝的思路過程和使用方法。尚未看過上一篇文章的朋友,建議先去閱讀 商城購物車加減控件的簡單封裝 。這段時間收到一些小夥伴的反饋,在ListView或者是RecyclerView中存在item複用致使數據錯亂的問題,這篇文章就重點解決item複用致使數據錯亂的問題和在ListView或者RecyclerView中的用法。下面爲了方便咱們以ListView爲例(RecyclerView是同樣的)。git
Github地址:github.com/Jmengfei/Ad…github
有過開發經驗的人都知道ListView的緩存複用機制雖然提高了它的性能,可是一樣也帶來了其餘問題,複用item致使數據錯亂就是其中一個。在這裏咱們不討論爲何會這樣。由於若是展開說這個問題的話就失去了本文章的重點。這裏主要說下解決方法的思路。緩存
解決這個問題的思路不少,網上通常給出這兩種辦法bash
上來就說是由於convertview對象共用的緣由,不能用convetView,而是每次getView()的時候都new一個對象的view出來.這種辦法大概是用屁股想出來的.ide
即然錯亂,那我就本身再弄一個集合保存checkBox的狀態,再錯亂,弄死你.即然adapter裏有一個list集合裏保存checkBox的狀態了,爲何還要本身再保存一次checkBox的狀態呢,不是畫蛇添足嗎?函數
這裏咱們先看一個例子佈局
這是再正常不過的例子了,item佈局很是簡單。點擊item條目改變CheckBox的選中狀態。看下adapter中getView()的代碼性能
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView == null) {
convertView = View.inflate(mContext, R.layout.lv_item,null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvItem.setText(datas.get(position));
// 點擊事件
holder.rlItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.cbItem.setChecked(!holder.cbItem.isChecked());
}
});
return convertView;
}複製代碼
如今我們修改datas數據bean的數據,增長一個控制checkbox的字段學習
public class ListBean {
// 爲了便於組裝數據
public ListBean(String title, boolean itemChecked) {
this.title = title;
this.itemChecked = itemChecked;
}
private String title;
private boolean itemChecked; // 新增字段控制是否被點擊,並設置get,set方法
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public boolean isItemChecked() {
return itemChecked;
}
public void setItemChecked(boolean itemChecked) {
this.itemChecked = itemChecked;
}
}複製代碼
如今我們利用bean中的屬性進行控制checkbox的選中狀態,如今再來看getView()中代碼gradle
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView == null) {
convertView = View.inflate(mContext, R.layout.lv_item,null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ListBean listBean = datas.get(position);
holder.tvItem.setText(listBean.getTitle());
holder.cbItem.setChecked(listBean.isItemChecked());
// 點擊事件
holder.rlItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
datas.get(position).setItemChecked(!(listBean.isItemChecked()));
notifyDataSetChanged();
}
});
holder.cbItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
boolean ischecked = holder.cbItem.isChecked();
datas.get(position).setItemChecked(ischecked);
}
});
return convertView;
}複製代碼
如今咱們再次運行起來,發現已經解決了那個問題。
思路很簡單,就是利用集合數據中新增字段itemChecked來控制,當咱們點擊的時候要把checkbox的屬性也同步到數據集合中。而後利用數據集合中的itemChecked來設置checkbox的狀態。記住一點,同步集合中的狀態是解決的關鍵點。
對於項目中,複用的是item中的當前數量,我們正好是要經過數量來控制數據,因此不用更改集合樣式,這簡直太棒了有沒有?對於外層,我們須要知道當前輸入框的值,怎麼時時獲取呢,這裏我經過接口的方式來傳遞數據,並且爲了適應不在ListView中的狀況,我這裏從新聲明瞭一個接口,代碼以下:
// 當數量改變的接口
public interface OnChangeValueListener {
void onChangeValue(int value,int position);
}複製代碼
我們在輸入框數量改變的監聽函數裏面來調用:
/**
* 監聽輸入的數據變化
*/
private void onNumberInput() {
//當前數量
int count = getNumber();
if (count < mBuyMin) {
//手動輸入
inputValue = mBuyMin;
etInput.setText(inputValue + "");
if(mOnChangeValueListener != null) {
mOnChangeValueListener.onChangeValue(inputValue,mPosition);
}
return;
}
int limit = Math.min(mBuyMax, inventory);
if (count > limit) {
if (inventory < mBuyMax) {
//庫存不足
warningForInventory();
} else {
//超過最大購買數
warningForBuyMax();
}
}else{
inputValue = count;
if(mOnChangeValueListener != null) {
mOnChangeValueListener.onChangeValue(inputValue,mPosition);
}
}
}複製代碼
這裏有人會問?爲何要傳一個position過來,這個position又是什麼呢?
你們都知道數據的複用是由於convertView的複用致使了position位置的數據也跟着亂了,假如我知道這個位置,而後用這個位置的數據來賦值給該位置的數據的話,不就解決了,這裏就是利用這一點。咱們新加入一個字段,而且實現get、set方法,注意set方法返回該類的實例(這樣作是爲了鏈式調用),代碼以下:
private int mPosition = 0; // 設置改變的位置,默認是0; //集合數據中會用到
public AddSubUtils setPosition(int position) {
mPosition = position;
return this;
}
public int getPosition() {
return mPosition;
}複製代碼
到這裏庫中的代碼已經修改完畢了,如今我們看看怎麼使用
dependencies {
compile 'com.mengfei:AddSubUtils:1.5.0'
}複製代碼
@Override
public View getView( int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = View.inflate(ListViewActivity.this, R.layout.listview_item, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
AddSubBean dataBean = mList.get(position);
holder.tv_item.setText(dataBean.getName());
Glide.with(ListViewActivity.this).load(dataBean.getImageId()).into(holder.iv_item);
holder.list_item_utils
.setStep(1)
.setBuyMax(30)
.setPosition(position) // 傳入當前位置,必定要傳,否則數據會錯亂
.setCurrentNumber(dataBean.getValue())
.setInventory(50)
.setOnWarnListener(new AddSubUtils.OnWarnListener() {
@Override
public void onWarningForInventory(int inventory) {
Toast.makeText(ListViewActivity.this, "不能超過當前庫存:" + inventory, Toast.LENGTH_SHORT).show();
}
@Override
public void onWarningForBuyMax(int max) {
Toast.makeText(ListViewActivity.this, "超過最大購買數:" + max, Toast.LENGTH_SHORT).show();
}
@Override
public void onWarningForBuyMin(int min) {
Toast.makeText(ListViewActivity.this, "低於最小購買數:" + min, Toast.LENGTH_SHORT).show();
}
})
.setOnChangeValueListener(new AddSubUtils.OnChangeValueListener() {
@Override
public void onChangeValue(int value,int position) {
setValue(position,value); // 使用傳回來的position設置數據
}
});
return convertView;
}
}複製代碼
代碼裏的兩處標註是重點。固然,若是你不在ListView中使用,那就還和以前同樣,是不用傳入position的,也不用實現AddSubUtils.OnChangeValueListener接口。setValue()方法就簡單了:
private void setValue(final int position, int inputValue) {
mList.get(position).setValue(inputValue);
}複製代碼
到這裏你已經解決了在ListView中item複用致使數據錯亂的問題,固然,RecyclerView一樣適用,這裏我就再也不給出。是否是用起來很簡單?若是你還在爲商城購物車加減控件的各類問題而煩惱,那麼你不妨試一下這個庫,能夠幫助你快速的完成這塊的工做。須要源碼的請到Github上下載,AddSubUtils ,你若是還有其餘的需求,歡迎留言,咱們共同改進。若是以爲對你有幫助,歡迎star。
Github地址:github.com/Jmengfei/Ad…
csdn地址:blog.csdn.net/sinat_36668…