Android 使用 ListAdapter 在 ScrollView 布局中填充 LinearLayout
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12405575/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using a ListAdapter to fill a LinearLayout inside a ScrollView layout
提问by Stefan Hoth
I'm facing a very common problem:
I layed out an activity and now it turns out it should display a few items within this ScrollView
. The normal way to do that would be to use the existing ListAdapter
, connect it to a ListView
and BOOMI'd have my list of items.
我面临一个非常常见的问题:我布置了一个活动,现在结果它应该在这个ScrollView
. 这样做的正常方法是使用现有的ListAdapter
,将其连接到 aListView
和BOOM我会有我的项目列表。
BUTYou should not place a nested ListView
in a ScrollView
as it screws up the scrolling - even Android Lint complains about it.
但是您不应该ListView
在 a 中放置嵌套,ScrollView
因为它会破坏滚动 - 甚至 Android Lint 也抱怨它。
So here's my question:
所以这是我的问题:
How do I connect a ListAdapter
to a LinearLayout
or something similar?
如何将 a 连接ListAdapter
到 aLinearLayout
或类似的东西?
I know this solution won't scale for a lot of items but my lists is very short (< 10 items) so reusage of views is not really needed. Performance wise I can live with placing all views directly into the LinearLayout
.
我知道这个解决方案不会针对很多项目进行扩展,但我的列表非常短(< 10 个项目),因此并不真正需要重复使用视图。性能方面,我可以忍受将所有视图直接放入LinearLayout
.
One solution I came up with would be to place my existing activity layout in the headerView section of the ListView
. But this feels like abusing this mechanism so I'm looking for a cleaner solution.
我想出的一个解决方案是将我现有的活动布局放在ListView
. 但这感觉就像滥用这种机制,所以我正在寻找一个更清洁的解决方案。
Ideas?
想法?
UPDATE:In order to inspire the right direction I add a sample layout to show my problem:
更新:为了激发正确的方向,我添加了一个示例布局来显示我的问题:
<?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 2I changed the headline to make it easier to understand (got a downvote, doh!).
更新 2我更改了标题以使其更易于理解(得到了反对票,哦!)。
回答by smok
You probably should just manually add your items to LinearLayout
:
您可能应该手动将您的项目添加到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: I rejected this approach when I needed to display about 200 non-trivial list items, it is very slow - Nexus 4 needed about 2 seconds to display my "list", that was unacceptable. So I turned to Flo's approach with headers. It works much faster because list views are created on demand when user scrolls, not at the time the view is created.
编辑:当我需要显示大约 200 个非平凡列表项时,我拒绝了这种方法,它非常慢 - Nexus 4 需要大约 2 秒才能显示我的“列表”,这是不可接受的。所以我转向了 Flo 的标题方法。它的工作速度要快得多,因为列表视图是在用户滚动时按需创建的,而不是在创建视图时创建。
Resume:The manual addition of views to layout is easier to code (thus potentially less moving parts and bugs), but suffers from performance problems, so if you have like 50 views or more, I advise to use the header approach.
简历:将视图手动添加到布局更容易编码(因此可能会减少移动部件和错误),但会遇到性能问题,因此如果您有 50 个或更多视图,我建议使用标题方法。
Example. Basically the activity (or fragment) layout transforms to something like this (no ScrollView needed anymore):
例子。基本上,活动(或片段)布局转换为这样的(不再需要 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"/>
Then in onCreateView()
(I'll use an example with a fragment) you need to add a header view and then set an adapter (I assume the header resource ID is header_layout
):
然后在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.
}
}
});
回答by Flo
I would stick with the header view solution. There's nothing wrong with it. At the moment I implementing an activity using the exact same approach.
我会坚持使用标题视图解决方案。它没有任何问题。目前我使用完全相同的方法实施一项活动。
Obviously the "item part" is more dynamically than static (varying item count vs. fix item count etc.) otherwise you won't think about using an adapter at all. So when you need an adapter then use the ListView.
显然,“项目部分”比静态更动态(变化项目计数与固定项目计数等),否则您根本不会考虑使用适配器。因此,当您需要适配器时,请使用 ListView。
Implementing a solution which populates a LinearLayout from an adapter is in the end nothing else than building a ListView with a custom layout.
实现从适配器填充 LinearLayout 的解决方案最终无非是构建具有自定义布局的 ListView。
Just my 2 cents.
只有我的 2 美分。
回答by M-WaJeEh
I use following code which replicate adapter functionality with ViewGroup
and TabLayout
. Good thing about this is that if you change your list and bind again, it will only affect changed items:
我使用以下代码复制适配器功能ViewGroup
和TabLayout
。这样做的好处是,如果您更改列表并再次绑定,它只会影响更改的项目:
Usage:
用法:
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)})
For ViewGroups
:
对于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])) } }
}
For TabLayout
I have this:
因为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])) } }
}
回答by fasheikh
Set your view to main.xml onCreate, then inflate from row.xml
将您的视图设置为 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
<?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>