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 のリサイクルがたくさんあることがわかりました。元の問題の解決策とともに学んだ教訓は次のとおりです。

  • ListViewがリスト項目を描画/表示する必要がある場合、 Viewfromを要求しListAdapter、場合によっては (常にではありません) 再利用する古いViewオブジェクトも提供します。オブジェクトのこの再利用は、パフォーマンスを向上させるためにあります。これを行うために組み込みのRe-CyclerListViewあります。新しいビュー アイテム。この時点までは、アダプターが古いビューの一部のテキストを変更して元に戻すか、リサイクルされたビューが利用できない場合は新しいビューを作成するか、リサイクル可能なビューを破棄して常に新しいビューを作成しても問題ありません。

  • ただし、リスト アイテムの状態が 内のテキスト以上のものである場合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 に答える