关于 android listview 加载数据错位(错乱)问题

来源:互联网 发布:windows优化大师64 编辑:程序博客网 时间:2024/05/19 18:45

一般的关于Adapter中getView的写法不外乎以下形式:

@Overridepublic 

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源码中关于getView方法的实现就是采用的以上形式,如ArrayAdapter等。因为这种写法的好处也是显而易见的,如果该position的convertview曾经被加载过,在数据集合未被改动的前提下,系统会自动将该position的convertview缓存起来,避免重复加载耗费资源。

我自己的代码:

@Overridepublic 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;}

然后问题就来了,当时我就”自作小聪明“或者说“没有注意”,觉得当convertview==null时只是做了item布局的加载以及相关控件ID的绑定操作,为什么连内容的加载操作也放入其中呢,这样下次加载缓存是就省去内容set的操作了,然后就出现了滑动ListView后数据显示错位的问题。


剖析原因:

后来看源码发现,原来AbListView中获取getView()和滑动操作是异步进行的,其中滑动操作在一个FlingRunnable的支线程中运行,所以这就导致了在ListView在滑动时可能已经滑动到了第十行,但可能第二行的数据这时就被直接使用了,这就是导致数据加载错乱的根本原因。
附上源码中对FlingRunnable的注释:

/** * Responsible for fling behavior. Use {@link #start(int)} to * initiate a fling. Each frame of the fling is handled in {@link #run()}. * A FlingRunnable will keep re-posting itself until the fling is done. * */private class FlingRunnable implements Runnable {    /**     * Tracks the decay of a fling scroll     */    private final OverScroller mScroller;    ... ...}

解决方法

所以唯一的解决方法就是只在convertview中缓存该ChildView的layout,但ChildView 中的数据必须每次都重新获取并加载。

修改后的代码:

@Overridepublic 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;}

其实ListView数据加载及数据缓存是比较复杂的,所以以后有机会还是要好好研读源码,这样才能有助于提升自己开发Android的性能和对Android工作的原理的理解。

0 0
原创粉丝点击