7505


[`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]

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


CC BY-SA 3.0
2015-02-26T09:37:36.510
7143
2015-03-05T21:25:37.377
2015-03-05T21:25:37.377
5750
8560
question
android java android-layout
ArrayAdapter のサブクラス内の getView() の引数 position が 0 のままで加算されない


原因は、ListView2 (@id/lstItem) の layout_height が wrap_content
であるためではないでしょうか。リストビューの高さは子要素によって計算することができないため固定値または match_parent にする必要があります。

下に TextView を続けるなら RelativeLayout に変えて、以下のようにすればなんとかなるのではないかと思います。

[code]

    <ListView
        android:id="@+id/lstItem"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/txtDate"
        android:layout_above="@+id/txtBalance">
    </ListView>
    
    <TextView 
        android:textSize="16sp" 
        android:id="@+id/txtBalance" 
        android:layout_height="wrap_content" 
        android:layout_width="match_parent"
        android:layout_alignParentBottom="true"
        android:background="#33CC00" />
    
[/code]

ただ、リストビューの入れ子というのはスクロールの面で難儀しそうなので、他の方法を取られたほうが得策かなという気はしますね。


CC BY-SA 3.0
2015-02-26T10:36:09.127
7147
2015-02-26T10:46:18.497
2015-02-26T10:46:18.497
7558
7558
7143
answer


## なぜか

`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`の子要素の大きさが変わるたびにスクロールバーの長さを再計算するので、スクロールすると、スクロールバーが伸びたり縮んだりします。


CC BY-SA 3.0
2015-03-05T16:26:07.427
7505
2015-03-05T16:26:07.427
7572
7143
answer