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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 10:34:47  来源:igfitidea点击:

Using a ListAdapter to fill a LinearLayout inside a ScrollView layout

androidandroid-layoutandroid-arrayadapter

提问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 ListViewand BOOMI'd have my list of items.

我面临一个非常常见的问题:我布置了一个活动,现在结果它应该在这个ScrollView. 这样做的正常方法是使用现有的ListAdapter,将其连接到 aListViewBOOM我会有我的项目列表。

BUTYou should not place a nested ListViewin a ScrollViewas 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 ListAdapterto a LinearLayoutor 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 ViewGroupand TabLayout. Good thing about this is that if you change your list and bind again, it will only affect changed items:

我使用以下代码复制适配器功能ViewGroupTabLayout。这样做的好处是,如果您更改列表并再次绑定,它只会影响更改的项目:

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 TabLayoutI 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>