ListAdapterを使用してScrollViewレイアウト内のLinearLayoutを塗りつぶします


95

私は非常に一般的な問題に直面しています。アクティビティをレイアウトしましたが、この中にいくつかの項目が表示されるはずScrollViewです。それを行うには、通常の方法では、既存のを使用することですListAdapter、Aに接続ListViewし、BOOM、私はアイテムの私のリストを持っていると思います。

しかし、スクロールをめちゃくちゃListViewにするScrollViewので入れ子に入れるべきではありません-Android Lintでさえ不満があります。

だから私の質問です:

どうやって接続するのですListAdapterへのLinearLayoutあるいは似たような?

このソリューションは多くのアイテムに対してスケーリングされないことを知っていますが、私のリストは非常に短いため(<10アイテム)、ビューの再利用は実際には必要ありません。パフォーマンスに関しては、すべてのビューをに直接配置することで対応できますLinearLayout

私が思いついた1つの解決策は、既存のアクティビティレイアウトをのheaderViewセクションに配置することですListView。しかし、これはこのメカニズムを悪用するような感じなので、より明確な解決策を探しています。

アイデア?

更新:正しい方向に向けるために、サンプルレイアウトを追加して問題を示します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/news_detail_layout"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical"
              android:visibility="visible">


    <ScrollView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#FFF"
            >

        <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:orientation="vertical"
                android:paddingLeft="@dimen/news_detail_layout_side_padding"
                android:paddingRight="@dimen/news_detail_layout_side_padding"
                android:paddingTop="@dimen/news_detail_layout_vertical_padding"
                android:paddingBottom="@dimen/news_detail_layout_vertical_padding"
                >

            <TextView
                    android:id="@+id/news_detail_date"
                    android:layout_height="wrap_content"
                    android:layout_width="fill_parent"
                    android:gravity="center_horizontal"
                    android:text="LALALA"
                    android:textSize="@dimen/news_detail_date_height"
                    android:textColor="@color/font_black"
                    />

            <Gallery
                    android:id="@+id/news_detail_image"
                    android:layout_height="wrap_content"
                    android:layout_width="fill_parent"
                    android:paddingTop="5dip"
                    android:paddingBottom="5dip"
                    />

            <TextView
                    android:id="@+id/news_detail_headline"
                    android:layout_height="wrap_content"
                    android:layout_width="fill_parent"
                    android:gravity="center_horizontal"
                    android:text="Some awesome headline"
                    android:textSize="@dimen/news_detail_headline_height"
                    android:textColor="@color/font_black"
                    android:paddingTop="@dimen/news_detail_headline_paddingTop"
                    android:paddingBottom="@dimen/news_detail_headline_paddingBottom"
                    />

            <TextView
                    android:id="@+id/news_detail_content"
                    android:layout_height="wrap_content"
                    android:layout_width="fill_parent"
                    android:text="Here comes a lot of text so the scrollview is really needed."
                    android:textSize="@dimen/news_detail_content_height"
                    android:textColor="@color/font_black"
                    />

            <!---
                HERE I NEED THE LIST OF ITEMS PROVIDED BY THE EXISTING ADAPTER. 
                They should be positioned at the end of the content, so making the scrollview smaller is not an option.
            ---->                        

        </LinearLayout>
    </ScrollView>
</LinearLayout>

UPDATE 2わかりやすくするために見出しを変更しました(反対票を投じました!)。


1
この問題のきれいな解決策を見つけたことがありますか?GoogleがPlayストアでレビューに使用しているようです。誰もがそれをどのように行うか知っていますか?
Zapnologica 2014年

回答:


96

おそらくアイテムを手動で追加する必要がありますLinearLayout

LinearLayout layout = ... // Your linear layout.
ListAdapter adapter = ... // Your adapter.

final int adapterCount = adapter.getCount();

for (int i = 0; i < adapterCount; i++) {
  View item = adapter.getView(i, null, null);
  layout.addView(item);
}

編集:約200個の重要なリストアイテムを表示する必要があるときにこのアプローチを拒否しました。非常に遅いです。Nexus4が「リスト」を表示するのに約2秒必要でしたが、これは受け入れられませんでした。そこで、ヘッダーを使用したFloのアプローチに目を向けました。リストビューは、ビューの作成時ではなく、ユーザーがスクロールしたときにオンデマンドで作成されるため、はるかに高速に動作します。

再開:レイアウトにビューを手動で追加すると、コーディングが簡単になります(そのため、可動部品やバグが少なくなる可能性があります)が、パフォーマンスの問題が発生するため、50以上のビューがある場合は、ヘッダーアプローチを使用することをお勧めします。

例。基本的に、アクティビティ(またはフラグメント)のレイアウトは次のように変換されます(ScrollViewは不要になります)。

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

次にonCreateView()(フラグメントの例を使用します)で、ヘッダービューを追加してからアダプターを設定する必要があります(ヘッダーリソースIDはであると想定していますheader_layout)。

ListView listView = (ListView) inflater.inflate(R.layout.my_top_layout, container, false);
View header = inflater.inflate(R.layout.header_layout, null);
// Initialize your header here.
listView.addHeaderView(header, null, false);

BaseAdapter adapter = // ... Initialize your adapter.
listView.setAdapter(adapter);

// Just as a bonus - if you want to do something with your list items:
view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    // You can just use listView instead of parent casted to ListView.
    if (position >= ((ListView) parent).getHeaderViewsCount()) {
      // Note the usage of getItemAtPosition() instead of adapter's getItem() because
      // the latter does not take into account the header (which has position 0).
      Object obj = parent.getItemAtPosition(position);
      // Do something with your object.
    }
  }
});

はい、それは2番目のアイデアでしたが、確信が持てませんでした。ListAdapterまたListView、スクリーンの背後で、手作業では行わない魔法を使っています。
Stefan Hoth 2012

もちろん、ListViewにはいくつかの魔法があり、少なくとも要素間の仕切りはあります。手動で追加する必要があると思います。ListAdapter(正しく実装されている場合)はこれ以上魔法をかけるべきではありません。
smok

2
これは私にとって反対票です。記憶に問題がありますか?アダプターを使用する理由があります...
Kevin Parker

1
他の誰かが、アダプターのnotifyDataSetChangedメソッドを使用してビューを更新することに問題がありますか?これは、に接続した別のアダプタで正常に機能しますが、に接続したアダプタListViewでは動作しないようですLinearLayout
jokeefe 2013年

2
これは私がアンドロイドを嫌う理由の一つです。なぜ複雑なソリューションを実装しなければならないのか、パフォーマンスの問題に悩まされているのかわかりません。
IARI 2017

6

ヘッダービューソリューションを使用します。何も問題はありません。現時点では、まったく同じ方法でアクティビティを実装しています。

明らかに、「アイテムパーツ」は静的よりも動的です(アイテム数の変化とアイテム数の修正など)。それ以外の場合は、アダプターの使用をまったく考えません。したがって、アダプターが必要な場合は、ListViewを使用します。

アダプタからLinearLayoutを生成するソリューションを実装することは、カスタムレイアウトでListViewを構築することに他なりません。

ちょうど私の2セント。


このアプローチの欠点の1つは、ほぼすべてのアクティビティレイアウト(上記を参照)を別のレイアウトに移動して、ListHeaderViewとして参照し、だけを含む別のレイアウトを作成する必要があることListViewです。私はこのパッチがうまくいくかどうかわかりません。
Stefan Hoth 2012

1
はい、私はあなたが何を意味するのかを知っています。このアプローチを使い始める前は、アクティビティレイアウトを複数のファイルに分割するという考えも好きではありませんでした。しかし、全体のレイアウトが予想したとおりに見えることを確認したとき、2つのファイルにのみ分離されているため、分離については気にしませんでした。注意する必要があることの1つは、リストアイテムがない場合にリストヘッダーが非表示になるため、ListViewには空のビューがあってはなりません。
Flo

3
また、ページに複数のリストが必要な場合、これは実際には機能しません。アイテムを「フラット」リスト、つまりスクロールしないリストに出力するカスタムアダプタービューの例はありますか。
murtuza 14

0

ビューをmain.xml onCreateに設定し、row.xmlからインフレートします

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="450dp" >

        <ListView
            android:id="@+id/mainListView"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_above="@+id/size"
            android:layout_below="@+id/editText1"
            android:gravity="fill_vertical|fill_horizontal"
            android:horizontalSpacing="15dp"
            android:isScrollContainer="true"
            android:numColumns="1"
            android:padding="5dp"
            android:scrollbars="vertical"
            android:smoothScrollbar="true"
            android:stretchMode="columnWidth" >

</ListView>

    <TextView
        android:id="@+id/size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:background="#ff444444"
        android:gravity="center"
        android:text="TextView"
        android:textColor="#D3D3D3"
        android:textStyle="italic" />

    </EditText>

</RelativeLayout> 

row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" 
  android:paddingTop="3dp">

<TextView
  android:id="@+id/rowTextView"
  android:layout_width="0dip"
  android:layout_height="41dp"
  android:layout_margin="4dp"
  android:layout_weight="2.83"
  android:ellipsize="end"
  android:gravity="center_vertical"
  android:lines="1"
  android:text="John Doe"
  android:textColor="@color/color_white"
  android:textSize="23dp" >
</TextView>

</LinearLayout>

質問を理解できなかったと思います。リストにLinearLayoutsを設定したくありません。
Flo

「ListAdapterをLinearLayoutなどに接続するにはどうすればよいですか?」OPの質問に答える
fasheikh 2012

2
いいえ、彼はリストビューを使いたくないのです。彼は、アダプターを使用し、それを使用して、LinearLayoutに項目を設定したいだけです。
Flo

回答ありがとうございます。@ Floは正解です。私がすることはできません外側レイアウトがすでにScrollViewであるため、リストビューを使用します。私のテキストとサンプルレイアウトを確認してください。
Stefan Hoth 2012

なぜscrollViewが必要なのですか?
fasheikh 2012

0

私は、次のコードで複製アダプター機能を使用ViewGroupしてTabLayout。これの良い点は、リストを変更して再度バインドすると、変更されたアイテムにのみ影響するということです。

使用法:

val list = mutableListOf<Person>()
layout.bindChildren(list, { it.personId }, { bindView(it) }, {d, t ->bindView(d, t)})
list.removeAt(0)
list+=newPerson
layout.bindChildren(list, { it.personId }, { bindView(it) }, {d, t ->bindView(d, t)})

の場合ViewGroups

fun <Item, Key> ViewGroup.bindChildren(items: List<Item>, id: (Item) -> Key, view: (Item) -> View, bind: (Item, View) -> Unit) {
    val old = children.map { it.tag as Key }.toList().filter { it != null }
    val new = items.map(id)

    val add = new - old
    val remove = old - new
    val keep = new.intersect(old)

    val tagToChildren = children.associateBy { it.tag as Key }
    val idToItem = items.associateBy(id)

    remove.forEach { tagToChildren[it].let { removeView(it) } }
    keep.forEach { bind(idToItem[it]!!, tagToChildren[it]!!) }
    add.forEach { id -> view(idToItem[id]!!).also { it.tag = id }.also { addView(it, items.indexOf(idToItem[id])) } }
}

以下のためにTabLayout私はこれを持っています:

fun <Item, Key> TabLayout.bindTabs(items: List<Item>, toKey: (Item) -> Key, tab: (Item) -> TabLayout.Tab, bind: (Item, TabLayout.Tab) -> Unit) {
    val old = (0 until tabCount).map { getTabAt(it)?.tag as Key }
    val new = items.map(toKey)

    val add = new - old
    val remove = old - new
    val keep = new.intersect(old)

    val tagToChildren = (0 until tabCount).map { getTabAt(it) }.associateBy { it?.tag as Key }
    val idToItem = items.associateBy(toKey)

    remove.forEach { tagToChildren[it].let { removeTab(it) } }
    keep.forEach { bind(idToItem[it]!!, tagToChildren[it]!!) }
    add.forEach { key -> tab(idToItem[key]!!).also { it.tag = key }.also { addTab(it, items.indexOf(idToItem[key])) } }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.