关于 android listview 加载数据错位(错乱)问题
一般的關(guān)于Adapter中g(shù)etView的寫法不外乎以下形式:
public?
ViewgetView(int position, View convertView, ViewGroup parent)?
{ ?
ViewHolder holder; ?
if (convertView == null)?
{ ?
convertView = mLayout.inflate(R.layout....); ?
holder =new ViewHolder(); ?
holder.textView = (TextView) convertView.findViewById(R.id.textview); ?
... ... ?
convertView.setTag(holder); ?
}?
else { ?
holder = (ViewHolder) convertView.getTag(); ?
} ?
holder.textView.setText(mText + position); ?
return convertView;
}
在Android源碼中關(guān)于getView方法的實(shí)現(xiàn)就是采用的以上形式,如ArrayAdapter等。因?yàn)檫@種寫法的好處也是顯而易見的,如果該position的convertview曾經(jīng)被加載過,在數(shù)據(jù)集合未被改動(dòng)的前提下,系統(tǒng)會(huì)自動(dòng)將該position的convertview緩存起來,避免重復(fù)加載耗費(fèi)資源。
我自己的代碼:
@Override public View getView(int position, View view, ViewGroup parent) {final ViewHolder mViewHolder;if(null == view){mViewHolder = new ViewHolder();view = LayoutInflater.from(mContext).inflate(R.layout.fragment_new_order_list_item, null);mViewHolder.txtPaystatus = (TextView) view.findViewById(R.id.order_pay_status);mViewHolder.txtOrdertime = (TextView) view.findViewById(R.id.order_time);mViewHolder.txtCustomerName = (TextView) view.findViewById(R.id.customer_name);mViewHolder.txtCustomerAddress = (TextView) view.findViewById(R.id.customer_address);mViewHolder.txtOrderSendTime = (TextView) view.findViewById(R.id.customer_post_time);mViewHolder.txtOrderGoodsDes = (TextView) view.findViewById(R.id.customer_list_goods_des);mViewHolder.txtCustomerPhone = (TextView) view.findViewById(R.id.customer_phone);mViewHolder.btnOrderOk = (Button) view.findViewById(R.id.order_ok);mViewHolder.btnOrderCancel = (Button) view.findViewById(R.id.order_cancel);mViewHolder.listgoods = (ListView) view.findViewById(R.id.customer_list_goods);if(listOrder.get(position).getPaystatus() == 0){mViewHolder.txtPaystatus.setText("未付款");}else {mViewHolder.txtPaystatus.setText("已付款");}mViewHolder.txtOrdertime.setText(listOrder.get(position).getOrdertime());mViewHolder.txtCustomerName.setText(listOrder.get(position).getAcceptname());mViewHolder.txtCustomerAddress.setText(listOrder.get(position).getAcceptlocation());mViewHolder.txtOrderSendTime.setText(listOrder.get(position).getGoodsarrivetime());mViewHolder.txtOrderGoodsDes.setText(listOrder.get(position).getOrderSeller().getRemark());mViewHolder.txtCustomerPhone.setText(listOrder.get(position).getAcceptphonenum());OrderGoodsListItemAdapter mOrderGoodsListItemAdapter = new OrderGoodsListItemAdapter(mContext,listOrder.get(position).getOrderSeller().getLstOrderGoods());mViewHolder.listgoods.setAdapter(mOrderGoodsListItemAdapter);setListViewHeightOnChildren(mViewHolder.listgoods);final int index = position;mViewHolder.btnOrderOk.setOnClickListener(new View.OnClickListener(){@Override public void onClick(View v) {String sellerorderid = String.valueOf(listOrder.get(index).getOrderSeller().getSellerorderid());String token = App.getInstance().sellerInfo.getToken();orderService.modifyOrder(sellerorderid, "1", token).subscribe(new Action1<String>() {@Override public void call(String s) {Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();newOrderFragment.listOrder.clear();newOrderFragment.pageindex = 1;newOrderFragment.requestData();App.isNeedFreshData = true;App.isNeedFresShophData = true;}}, new Action1<Throwable>() {@Override public void call(Throwable throwable) {Toast.makeText(mContext, throwable.getMessage(), Toast.LENGTH_SHORT).show();}});}});mViewHolder.btnOrderCancel.setOnClickListener(new View.OnClickListener(){@Override public void onClick(View v) {String sellerorderid = String.valueOf(listOrder.get(index).getOrderSeller().getSellerorderid());String token = App.getInstance().sellerInfo.getToken();orderService.modifyOrder(sellerorderid, "2", token).subscribe(new Action1<String>() {@Override public void call(String s) {Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();newOrderFragment.listOrder.clear();newOrderFragment.pageindex = 1;newOrderFragment.requestData();App.isNeedFreshData = true;App.isNeedFresShophData = true;}}, new Action1<Throwable>() {@Override public void call(Throwable throwable) {Toast.makeText(mContext, throwable.getMessage(), Toast.LENGTH_SHORT).show();}});}});view.setTag(mViewHolder);}else {mViewHolder = (ViewHolder) view.getTag();}return view; }
然后問題就來了,當(dāng)時(shí)我就”自作小聰明“或者說“沒有注意”,覺得當(dāng)convertview==null時(shí)只是做了item布局的加載以及相關(guān)控件ID的綁定操作,為什么連內(nèi)容的加載操作也放入其中呢,這樣下次加載緩存是就省去內(nèi)容set的操作了,然后就出現(xiàn)了滑動(dòng)ListView后數(shù)據(jù)顯示錯(cuò)位的問題。
剖析原因:
后來看源碼發(fā)現(xiàn),原來AbListView中獲取getView()和滑動(dòng)操作是異步進(jìn)行的,其中滑動(dòng)操作在一個(gè)FlingRunnable的支線程中運(yùn)行,所以這就導(dǎo)致了在ListView在滑動(dòng)時(shí)可能已經(jīng)滑動(dòng)到了第十行,但可能第二行的數(shù)據(jù)這時(shí)就被直接使用了,這就是導(dǎo)致數(shù)據(jù)加載錯(cuò)亂的根本原因。
附上源碼中對FlingRunnable的注釋:
解決方法
所以唯一的解決方法就是只在convertview中緩存該ChildView的layout,但ChildView 中的數(shù)據(jù)必須每次都重新獲取并加載。
修改后的代碼:
@Override public View getView(int position, View view, ViewGroup parent) {final ViewHolder mViewHolder;if(null == view){mViewHolder = new ViewHolder();view = LayoutInflater.from(mContext).inflate(R.layout.fragment_new_order_list_item, null);mViewHolder.txtPaystatus = (TextView) view.findViewById(R.id.order_pay_status);mViewHolder.txtOrdertime = (TextView) view.findViewById(R.id.order_time);mViewHolder.txtCustomerName = (TextView) view.findViewById(R.id.customer_name);mViewHolder.txtCustomerAddress = (TextView) view.findViewById(R.id.customer_address);mViewHolder.txtOrderSendTime = (TextView) view.findViewById(R.id.customer_post_time);mViewHolder.txtOrderGoodsDes = (TextView) view.findViewById(R.id.customer_list_goods_des);mViewHolder.txtCustomerPhone = (TextView) view.findViewById(R.id.customer_phone);mViewHolder.btnOrderOk = (Button) view.findViewById(R.id.order_ok);mViewHolder.btnOrderCancel = (Button) view.findViewById(R.id.order_cancel);mViewHolder.listgoods = (ListView) view.findViewById(R.id.customer_list_goods);view.setTag(mViewHolder);}else {mViewHolder = (ViewHolder) view.getTag();}if(listOrder.get(position).getPaystatus() == 0){mViewHolder.txtPaystatus.setText("未付款");}else {mViewHolder.txtPaystatus.setText("已付款");}mViewHolder.txtOrdertime.setText(listOrder.get(position).getOrdertime());mViewHolder.txtCustomerName.setText(listOrder.get(position).getAcceptname());mViewHolder.txtCustomerAddress.setText(listOrder.get(position).getAcceptlocation());mViewHolder.txtOrderSendTime.setText(listOrder.get(position).getGoodsarrivetime());mViewHolder.txtOrderGoodsDes.setText(listOrder.get(position).getOrderSeller().getRemark());mViewHolder.txtCustomerPhone.setText(listOrder.get(position).getAcceptphonenum());OrderGoodsListItemAdapter mOrderGoodsListItemAdapter = new OrderGoodsListItemAdapter(mContext,listOrder.get(position).getOrderSeller().getLstOrderGoods());mViewHolder.listgoods.setAdapter(mOrderGoodsListItemAdapter);setListViewHeightOnChildren(mViewHolder.listgoods);final int index = position;mViewHolder.btnOrderOk.setOnClickListener(new View.OnClickListener(){@Override public void onClick(View v) {String sellerorderid = String.valueOf(listOrder.get(index).getOrderSeller().getSellerorderid());String token = App.getInstance().sellerInfo.getToken();orderService.modifyOrder(sellerorderid, "1", token).subscribe(new Action1<String>() {@Override public void call(String s) {Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();newOrderFragment.listOrder.clear();newOrderFragment.pageindex = 1;newOrderFragment.requestData();App.isNeedFreshData = true;App.isNeedFresShophData = true;}}, new Action1<Throwable>() {@Override public void call(Throwable throwable) {Toast.makeText(mContext, throwable.getMessage(), Toast.LENGTH_SHORT).show();}});}});mViewHolder.btnOrderCancel.setOnClickListener(new View.OnClickListener(){@Override public void onClick(View v) {String sellerorderid = String.valueOf(listOrder.get(index).getOrderSeller().getSellerorderid());String token = App.getInstance().sellerInfo.getToken();orderService.modifyOrder(sellerorderid, "2", token).subscribe(new Action1<String>() {@Override public void call(String s) {Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();newOrderFragment.listOrder.clear();newOrderFragment.pageindex = 1;newOrderFragment.requestData();App.isNeedFreshData = true;App.isNeedFresShophData = true;}}, new Action1<Throwable>() {@Override public void call(Throwable throwable) {Toast.makeText(mContext, throwable.getMessage(), Toast.LENGTH_SHORT).show();}});}});return view; }
其實(shí)ListView數(shù)據(jù)加載及數(shù)據(jù)緩存是比較復(fù)雜的,所以以后有機(jī)會(huì)還是要好好研讀源碼,這樣才能有助于提升自己開發(fā)Android的性能和對Android工作的原理的理解。
總結(jié)
以上是生活随笔為你收集整理的关于 android listview 加载数据错位(错乱)问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国移动与高通联合 vivo、小米等完成
- 下一篇: android 格式格式:YYYYMM