1

以下是一些测试代码,用于重现一个奇怪的错误:从 ListView 中删除一些项目后,当数据无效时它会停止刷新。更多项目被删除,但列表不刷新。甚至 Log cat 也不会显示用于删除的调试消息。如果有人能找出问题所在,我将不胜感激。

物品布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
        android:orientation="horizontal">

    <TextView android:id="@+id/nameTextView"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              />
    <Button android:id="@+id/deleteButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Delete"
            />
</LinearLayout>

物品类别:

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class Item implements View.OnClickListener {
    private String name;
    private View itemView;
    private MyActivity owner;

    //--- getters--
    public String getName() {
        return name;
    }
    public View getView() {
        return itemView;
    }

    public Item(String n, Context c , MyActivity o)
    {
        //---store the name given--
        name = n;
        //---store reference to the owner activity--
        owner = o;

        //--- create a View for this item----
        LayoutInflater inflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        itemView = inflater.inflate(R.layout.item,null);

        //---set up data to show--
        TextView nameTextView = (TextView) itemView.findViewById(R.id.nameTextView);
        Button deleteButton = (Button) itemView.findViewById(R.id.deleteButton);
        nameTextView.setText(name);

        //---set up events to be handled--
        deleteButton.setOnClickListener(this);

        Log.d("My_Test","Item: Hello world, my name is " + name);
    }

    //----request owner to delete this item---
    @Override
    public void onClick(View view) {
        Log.d("My_Test","Item:"+name+" requesting owner to delete me");
        owner.deleteItem(this);
    }

活动布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
        >
<ListView android:id="@+id/myListView"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          />
</LinearLayout>

活动类:

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;

public class MyActivity extends Activity {
    private ArrayList<Item> myItems;
    private ListView myListView;
    private ArrayAdapter<Item> myArrayAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        //-----adapter for item list----
        //----since each item has its own view , it just returns the same---
        myArrayAdapter = new ArrayAdapter<Item>(this,0){

            @Override
            public View getView(final int position, View convertView, ViewGroup parent)      {
              Item item = getItem(position);
              Log.d("My_Test","Adapter : View for Item: " + item.getName() +"is requested." );
              return item.getView();
            }

        };

        //-----set up my list view with the adapter------
        myListView = (ListView) findViewById(R.id.myListView);
        myListView.setAdapter(myArrayAdapter);

        //------add items-------
        //----each item has its own view and a reference to this activity as their owner----
        myArrayAdapter.add(new Item("Sunday", this, this));
        myArrayAdapter.add(new Item("Monday", this, this));
        myArrayAdapter.add(new Item("Tuesday", this, this));
        myArrayAdapter.add(new Item("Wednesday", this, this));
        myArrayAdapter.add(new Item("Thursday", this, this));
        myArrayAdapter.add(new Item("Friday", this, this));
        myArrayAdapter.add(new Item("Saturday", this, this));

        myArrayAdapter.notifyDataSetChanged();

    }

    //----- called by items requesting to be deleted from the item list----
    public void deleteItem(Item item) {
        myArrayAdapter.remove(item);
        Log.d("My_Test","Owner : Deleted item :" + item.getName());
        myArrayAdapter.notifyDataSetChanged();
    }
}

看起来 ListView 停止重新绘制它自己。即使列表项不再在项数组中并被myAdapter.notifyDataSetInvalidated();调用,列表项仍然可见,进一步的代码执行有些受阻。

4

2 回答 2

1

使用 ArrayAdapter 来执行此操作。试试这样的东西......

    import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class MyActivity extends Activity{
    private ListView myListView;
    private ArrayAdapter<Item> myArrayAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        myArrayAdapter = new ArrayAdapter<Item>(this,R.layout.item){    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View returnedView = convertView;

                //inflate your view here
                if(returnedView == null){
                    LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    returnedView = inflater.inflate(R.layout.item,null);
                }

                final Item item = getItem(position);

                //set the views
                if(returnedView != null){
                    TextView nameTextView = (TextView) returnedView.findViewById(R.id.nameTextView);
                    nameTextView.setText(item.getName());
                    Button deleteButton = (Button) returnedView.findViewById(R.id.deleteButton);
                    deleteButton.setOnClickListener(new OnClickListener() {
                        public void onClick(View v) {
                            remove(item);
                            notifyDataSetChanged();
                        }
                    });

                }

                return returnedView;
            }
        };

        myArrayAdapter.add(new Item("Sunday"));
        myArrayAdapter.add(new Item("Monday"));
        myArrayAdapter.add(new Item("Tuesday"));
        myArrayAdapter.add(new Item("Wednesday"));
        myArrayAdapter.add(new Item("Thursday"));
        myArrayAdapter.add(new Item("Friday"));
        myArrayAdapter.add(new Item("Saturday"));

        myListView = (ListView) findViewById(R.id.myListView);
        myListView.setAdapter(myArrayAdapter);

    }
}

public class Item{

    private String name;

    public Item(String n){
        this.name = n;
    }

    public String getName() {
        return name;
    }
}
于 2012-08-15T18:55:14.880 回答
0

查看 ListViews 和 ListAdapters 的工作原理,我了解到有很多对象回收,特别是适配器产生的 List View Item Objects。以下是原始问题的解决方案以及经验教训:

  • 当 aListView必须绘制/显示一个列表项时,它会请求 a Viewfrom ListAdapter,并且有时(不总是)还提供一个旧View对象以供重用。对象的这种重用是为了提高性能,有一个内置的Re -CyclerListView可以做到这一点,为什么要为每个新列表项膨胀新的布局,当已经有一些属性可以修改以使它们看起来像新的视图项目。到目前为止,适配器可以更改旧视图上的一些文本并将它们返回,或者如果没有可用的回收视图可用,则创建新视图,甚至丢弃可回收视图并始终创建新视图。

  • 但是,如果您的列表项的状态不仅仅是 a 中的一些文本TextView,也就是说,另一个对象被注册onClickListener为您的列表项,或者您的列表项在某处引用了某个对象,反之亦然;适配器只改变可重用视图的外观或简单地丢弃它们是不行的。适配器必须更新可重用项目的整个状态。这包括取消注册旧的事件侦听器、重新注册新的事件侦听器以及更新对可能存在的外部对象的所有引用。

将适配器的方法更改getView()为:

@Override
public View getView(int position, View convertView, ViewGroup parent)      {

    Item item = getItem(position);
    Log.d("My_Test","Adapter : View for Item: " + item.getName() +"is requested." );

    if(convertView != null)
    {
        (convertView.findViewById(R.id.deleteButton))
                .setOnClickListener(item);
        ((TextView)convertView.findViewById(R.id.nameTextView))
                .setText(item.getName());
        return convertView;
    }
    else
    {
        return item.getView();
    }
}

注意:虽然在这种情况下始终创建新项目不会导致任何错误,但ListView无法检测到更改并重绘。利用回收物品似乎可以解决这个问题。

于 2012-08-23T20:43:26.317 回答