5333


現在ListViewを使用してアプリを作っているのですが、adapterの更新で不可解な箇所がありましたので質問させていただきます。  
ArrayAdapterを継承したMyArrayAdapterクラス内で

[code]

    public void setData(MessageData mData) {
        this.messageData = mData;
        if (messageData.getMessageList().isEmpty()) {
            this.messageDetailList = messageData.getMessageList();
        }
        notifyDataSetChanged();
    }
    
[/code]

としています。

データの更新があったら`setData`を呼び出してMyArrayAdapterを更新しているのですが、更新される場合とされない場合があります。

  * MethodAから呼ぶと更新される: `getView`が呼ばれてListViewに反映される
  * MethodBから呼ぶと更新されない: 更新されたデータだけ`getView`が呼ばれず、ListViewに反映されない(元々入っていた要素に関しては'getView'が呼ばれている)
  * MethodA、MethodBはMainThreadから実行している(各メソッド内でThread確認済み)

ほとんど同じ処理であり、Listデータの数も更新されたものが渡されています。MyArrayAdapterが保持するListデータもMethodAの場合もMethodBの場合もsizeが更新されて数が増えているのですが、MethodBで実行すると新規で追加したデータに関してのみListViewに更新がかからない状態です。  
MethodBで呼び出した場合は新規データの箇所のみMyArrayAdapterのgetViewが呼ばれず、  
既存のデータに関してはgetViewが呼ばれます。  
つまりMethodBの場合は表示が変更されません。

MethodBの実装  
Hogeクラス内で

[code]

    public void MethodB(JSONArray array){
        //JSONからListを取り出す
        ListmessageList=jsonUtil.getMessageDataList(array);
        if(messageList.size()!=0){
            userData.addMessageDataAll(messageList);
            userData=saveDataUtil.setUserData(userData);
            messageData=userData.getMessageData("ID");
            if(messageData!=null){
                //この時点では新規データが入っており更新されている
                messageData.getMessageList().size()="+messageData.getMessageList().size());
                adapter.setData(messageData);
                //ここでlist.getCount()したがサイズが増えていない。
                listView.setSelection(listView.getCount()-1);
            }else{
                Log.e(TAG,"messageData==null");
            }
        }
    
[/code]

MethodAの実装

[code]

    public void MethodA(JSONArray array){
                List detailList=jsonUtil.getMessageDetailList(array);
                Log.d(TAG,"送信したMessageDetail messageDetailList.size="+detailList.size());
                if(detailList.size()!=0){
                    MessageDetail detail=detailList.get(0);
                    messageData.addMessageList(detail);
                    userData.setMessageData(messageData);
                    userData=saveDataUtil.setUserData(userData);//userData保存
                    messageData=userData.getMessageData("ID");
                    if(messageData!=null){
                        adapter.setData(messageData);
                    }else{
                        Log.e(TAG,"userData.messageData==null");
                    }
                    listView.setSelection(listView.getCount()-1);
                }
    
[/code]

MethodAとMethodBは共にVolleyのJSONArray取得メソッドから呼び出されています。  
この場合どのような状況が考えられるのでしょうか？


CC BY-SA 3.0
2015-01-23T09:33:07.073
5151
2015-01-26T11:43:27.400
2015-01-26T02:07:49.590
297
297
question
android
notifyDataSetChangedでListViewが更新される場合とされない場合がある


問題を再現できるコードが無いので、想像ですが：  
原因として思いつくのは、以下です。

  * ListViewに追加したつもりの要素が、実際には追加できていない
  * 実はMethodAからの呼出でもgetViewが呼び出されている（が、確認方法がまずいなどの理由でgetViewが呼び出されていないと勘違いしている）
  * ListViewに追加する処理が非同期で実行されており、notifyDataSetChanged()を呼び出したタイミングではまだ追加処理が完了していない（そのためgetView呼出されない）


CC BY-SA 3.0
2015-01-24T08:15:41.090
5196
2015-01-24T08:15:41.090
7572
5151
answer

[code]

    public void setData(MessageData mData) {
        this.messageData = mData;
        if (messageData.getMessageList().isEmpty()) {
            this.messageDetailList = messageData.getMessageList();
        }
        notifyDataSetChanged();
    }
    
[/code]

`adapter.messageData.getMessageList()`(代入しているので`mData.getMessageList()`)が空の場合のみ、その中身を
adapter.messageDetailListに置き換えている為、`mData`の内容が、adapter.messageDataに追加されることは無いのでは、と思いました。

[code]

    public void setData(MessageData mData) {
        this.messageData = mData;
        if (!messageData.getMessageList().isEmpty()) {
            this.messageDetailList.addAll(messageData.getMessageList());
        }
        notifyDataSetChanged();
    }
    
[/code]

が正しいのではないでしょうか。


CC BY-SA 3.0
2015-01-26T09:29:54.197
5326
2015-01-26T09:48:59.940
2015-01-26T09:48:59.940
7572
7572
5151
answer


推測になってしまうのですが、単に参照の問題だと思います。

`ArrayAdapter`はコンストラクタで受け取ったリスト（配列のオーバーロードコンストラクタでは、`Arrays.asList()`を利用してリストに変換する）を、`ListView`などに表示するアダプタです。

サブクラスを作る場合、「
**`ArrayAdapter`が管理する`List<T>`は`private`であり、サブクラスから参照を変更できない**」という点に注意する必要があります。

`ArrayAdapter`は、[Class
Reference](http://developer.android.com/reference/android/widget/ArrayAdapter.html)にあるように一度保持したリストに対して、`add()`、`clear()`、`remove()`などのメソッドで操作するアダプタクラスになります。リストそのものの参照を外部から置き換えたい場合には、[`BaseAdapter`](http://developer.android.com/reference/android/widget/BaseAdapter.html)を継承した方が楽です。

表題の事象が起こり得るとすれば、以下のような原因かと思います。

  * `MethodA()`の呼び出し側で更新しているリストは、`ArrayAdapter`のコンストラクタに渡したリストの参照なので、`ArrayAdapter`が内部で管理しているリスト＝`ListView`が更新される。
  * `MethodB()`の呼び出し側ではリストを新規に生成して、`MyArrayAdapter`のメンバ変数を更新しているだけなので`ListView`は更新されない


CC BY-SA 3.0
2015-01-26T11:43:27.400
5333
2015-01-26T11:43:27.400
5337
5151
answer