ArrayAdapter のサブクラス内の getView() の引数 position が 0 のままで加算されない


[`ArrayAdapter`](http://developer.android.com/reference/android/widget/ArrayAdapter.html)
のサブクラスで `getView` が呼び出される際、コンストラクタで渡した`ArrayList` のsize に関わらず position は 0
のままで、 `ListView` に `ArrayList` の2つ目以降のアイテムのViewが渡されません。

レイアウトは ListView1 が ListView2 を持つ入れ子の構造で、 ListView1 は問題なく動作しますが、 ListView2
は上記のような状態です。

[code]

    private class DayAdapter extends ArrayAdapter<DayData> {        
        private LayoutInflater inflater;
        private class ViewHolder{
            TextView textDate;
            ListView listItem;
            TextView textBalance;
    
            ViewHolder(View view){
                this.textDate = (TextView) view.findViewById(R.id.txtDate);
                this.listItem = (ListView) view.findViewById(R.id.lstItem);
                this.textBalance = (TextView) view.findViewById(R.id.txtBalance);
            }
        } 
    
        public DayAdapter(Context context, int textViewResourceId, List<DayData> objects) {
            super(context, textViewResourceId, objects);
            this.inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }
    
        @Override
        // こちらのpositionは更新される
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
    
            if(convertView == null){
                convertView = inflater.inflate(R.layout.day, null);
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
            }else{
                holder = (ViewHolder)convertView.getTag();
            }
    
            DayData day = (DayData)getItem(position);
            if(day != null){            
                holder.textDate.setText(day.GetStringDate());
                holder.textBalance.setText(day.GetStringBalance());
    
                Log.d("ItemAdapter", "size="+day.GetItemList().size());
                ItemAdapter adapter = new ItemAdapter(Diary.this, 0, day.GetItemList());
                holder.listItem.setAdapter(adapter);
            }
            return convertView;
        }
    }   
    
    private class ItemAdapter extends ArrayAdapter<ItemData> {
        private LayoutInflater inflater;
        private class ViewHolder{
            TextView textDate;
            TextView textItem;
            TextView textPrice;
    
            ViewHolder(View view){
                this.textDate = (TextView) view.findViewById(R.id.textView1);
                this.textItem = (TextView) view.findViewById(R.id.textView2);
                this.textPrice = (TextView) view.findViewById(R.id.textView3);
            }
        }
    
        public ItemAdapter(Context context, int textViewResourceId, List<ItemData> objects) {
            super(context, textViewResourceId, objects);
            this.inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }
    
        @Override
        // こちらのpositionは0のまま
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if(convertView == null){
                convertView = inflater.inflate(R.layout.row, null);
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
            }else{
                holder = (ViewHolder)convertView.getTag();
            }
    
            Log.d("getView()", "position="+position);
            ItemData item = (ItemData)getItem(position);
            if(item != null){   
                holder.textDate.setText(item.GetStringDate());
                holder.textItem.setText(item.GetItem());  
    
                String sign = "";
                if(item.GetPrice() > 0) sign = "+";
                holder.textPrice.setText(sign + Integer.toString(item.GetPrice()) + "円");
            }
            return convertView;
        }
    }
    
[/code]

day.xml

[code]

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
    <TextView 
        android:textSize="16sp" 
        android:id="@+id/txtDate" 
        android:layout_height="wrap_content" 
        android:layout_width="match_parent"
        android:background="#003300"
        android:textColor="#FFFFFF" />
    
    <ListView
        android:id="@+id/lstItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>
    
    <TextView 
        android:textSize="16sp" 
        android:id="@+id/txtBalance" 
        android:layout_height="wrap_content" 
        android:layout_width="match_parent"
        android:background="#33CC00" />
    
    </LinearLayout>
    
[/code]

row.xml

[code]

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    
    <TextView 
        android:textSize="16sp" 
        android:id="@+id/textView1" 
        android:layout_height="wrap_content" 
        android:layout_width="0dp"
        android:layout_weight="1"/> 
    <TextView 
        android:textSize="16sp" 
        android:id="@+id/textView2" 
        android:layout_height="wrap_content" 
        android:layout_width="0dp"
        android:layout_weight="1"/>
    <TextView  
        android:textSize="16sp" 
        android:id="@+id/textView3" 
        android:layout_height="wrap_content" 
        android:layout_width="0dp"
        android:layout_weight="1"/>
    
    </LinearLayout>
    
[/code]

この場合どのような原因が考えられるでしょうか？




## なぜか

`position`値がゼロのままになる理由を説明します。  
ListViewにセットされた`ArrayAdapter`の`getView()`が呼び出されるのは、ListViewがスクロールされて画面内にはいってきた子要素に対応する`View`が必要になったタイミングです。  
したがって、`ListView`の初期状態に要素1個しか表示できない状態の場合、スクロールしないかぎり次の`getView()`は呼び出されません。

## どのようにすればよいか

今回は、内側の`ListView`をスクロールしたくないので、簡単にするために`LinearView`に置き換えれば良いです。

`day.xml`レイアウトを変更。

[code]

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical" >
        <TextView
                android:textSize="16sp"
                android:id="@+id/txtDate"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:background="#003300"
                android:textColor="#FFFFFF" />
        <!-- スクロールもさせないし中身の再利用もできないのであればLinearLayoutで良い -->
        <LinearLayout
                android:orientation="vertical"
                android:id="@+id/lstItem"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" >
        </LinearLayout>
        <TextView
                android:textSize="16sp"
                android:id="@+id/txtBalance"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:background="#33CC00" />
    </LinearLayout>
    
[/code]

`DayAdapter#getView()`を以下のように修正(ブロックコメントの箇所のみ)。

[code]

    @Override
    // こちらのpositionは更新される
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
    
        if(convertView == null){
            convertView = inflater.inflate(R.layout.day, null);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder)convertView.getTag();
        }
    
        DayData day = (DayData)getItem(position);
        if(day != null){
            holder.textDate.setText(day.GetStringDate());
            holder.textBalance.setText(day.GetStringBalance());
    
            Log.d("ItemAdapter", "size="+day.GetItemList().size());
            ItemAdapter adapter = new ItemAdapter(Diary.this, 0, day.GetItemList());
    
            /* listItemをクリアしてからadapterでrowレイアウトを必要なだけ作って追加する */
            holder.listItem.removeAllViews();
            for (int i = 0; i<adapter.getCount(); i++) {
                holder.listItem.addView(
                    adapter.getView(i, null, holder.listItem)
                );
            }
        }
        return convertView;
    }
    
[/code]

実害は無いと思いますが、制限事項が１つあります。  
上記のようにすると、外側の`ListView`の子要素の大きさが変わるたびにスクロールバーの長さを再計算するので、スクロールすると、スクロールバーが伸びたり縮んだりします。




## なぜか

`position`値がゼロのままになる理由を説明します。  
ListViewにセットされた`ArrayAdapter`の`getView()`が呼び出されるのは、ListViewがスクロールされて画面内にはいってきた子要素に対応する`View`が必要になったタイミングです。  
したがって、`ListView`の初期状態に要素1個しか表示できない状態の場合、スクロールしないかぎり次の`getView()`は呼び出されません。

## どのようにすればよいか

今回は、内側の`ListView`をスクロールしたくないので、簡単にするために`LinearView`に置き換えれば良いです。

`day.xml`レイアウトを変更。

[code]

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical" >
        <TextView
                android:textSize="16sp"
                android:id="@+id/txtDate"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:background="#003300"
                android:textColor="#FFFFFF" />
        <!-- スクロールもさせないし中身の再利用もできないのであればLinearLayoutで良い -->
        <LinearLayout
                android:orientation="vertical"
                android:id="@+id/lstItem"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" >
        </LinearLayout>
        <TextView
                android:textSize="16sp"
                android:id="@+id/txtBalance"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:background="#33CC00" />
    </LinearLayout>
    
[/code]

`DayAdapter#getView()`を以下のように修正(ブロックコメントの箇所のみ)。

[code]

    @Override
    // こちらのpositionは更新される
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
    
        if(convertView == null){
            convertView = inflater.inflate(R.layout.day, null);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder)convertView.getTag();
        }
    
        DayData day = (DayData)getItem(position);
        if(day != null){
            holder.textDate.setText(day.GetStringDate());
            holder.textBalance.setText(day.GetStringBalance());
    
            Log.d("ItemAdapter", "size="+day.GetItemList().size());
            ItemAdapter adapter = new ItemAdapter(Diary.this, 0, day.GetItemList());
    
            /* listItemをクリアしてからadapterでrowレイアウトを必要なだけ作って追加する */
            holder.listItem.removeAllViews();
            for (int i = 0; i<adapter.getCount(); i++) {
                holder.listItem.addView(
                    adapter.getView(i, null, holder.listItem)
                );
            }
        }
        return convertView;
    }
    
[/code]

実害は無いと思いますが、制限事項が１つあります。  
上記のようにすると、外側の`ListView`の子要素の大きさが変わるたびにスクロールバーの長さを再計算するので、スクロールすると、スクロールバーが伸びたり縮んだりします。

