私は非常に一般的な問題に直面しています:アクティビティをレイアウトすると、このScrollView
内にいくつかのアイテムが表示されるはずです。これを行う通常の方法は、既存のListAdapter
を使用し、ListView
と[〜#〜] boom [〜#〜]に接続することです。 アイテムのリストがあります。
[〜#〜] but [〜#〜]入れ子になったListView
をScrollView
に配置しないでください。スクロールが失敗する-偶数Android Lintはそれについて文句を言います。
だからここに私の質問があります:
ListAdapter
をLinearLayout
または類似のものに接続するにはどうすればよいですか?
このソリューションは多くのアイテムに対応しませんが、リストは非常に短い(<10アイテム)ため、ビューの再利用は実際には必要ありません。パフォーマンスに関しては、すべてのビューをLinearLayout
に直接配置することで対応できます。
私が思いついた解決策の1つは、既存のアクティビティレイアウトをListView
のheaderViewセクションに配置することです。しかし、これはこのメカニズムを悪用したような気がするので、よりクリーンなソリューションを探しています。
アイデア?
PDATE:正しい方向を促すために、問題を示すためにサンプルレイアウトを追加します。
<?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>
PDATE 2わかりやすくするために見出しを変更しました(下票を得ました!)。
おそらく手動でアイテムを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);
}
_
[〜#〜] edit [〜#〜]:約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.
}
}
});
_
ヘッダービューソリューションに固執します。それには何の問題もありません。現時点では、まったく同じアプローチを使用してアクティビティを実装しています。
明らかに、「アイテム部分」は静的よりも動的です(アイテム数の変化とアイテム数の修正など)。そうでない場合は、アダプターの使用をまったく考えません。したがって、アダプターが必要な場合は、ListViewを使用してください。
アダプタからLinearLayoutを生成するソリューションを実装することは、最終的に、カスタムレイアウトでListViewを構築すること以外は何もありません。
ちょうど私の2セント。
ビューを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>
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])) } }
}