ListAdapter를 사용하여 ScrollView 레이아웃 내부의 LinearLayout 채우기


95

저는 매우 일반적인 문제에 직면하고 있습니다. 활동을 배치했는데 이제이 안에 몇 가지 항목이 표시되어야합니다 ScrollView. 이를 수행하는 일반적인 방법은 기존을 사용하여 ListAdaptera에 연결 ListView하고 BOOM 에 항목 목록이있는 것입니다.

그러나 스크롤링을 망치기 때문에 중첩 ListView을 배치해서는 안됩니다. ScrollView심지어 Android Lint도 이에 대해 불평합니다.

그래서 여기 내 질문이 있습니다.

ListAdapter를 a LinearLayout또는 비슷한 것에 어떻게 연결 합니까?

이 솔루션이 많은 항목에 대해 확장되지는 않지만 목록이 매우 짧기 때문에 (<10 항목) 뷰를 재사용 할 필요가 없다는 것을 알고 있습니다. 성능 측면에서 모든 뷰를 LinearLayout.

내가 생각 해낸 한 가지 해결책은 기존 활동 레이아웃을 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>

업데이트 2 이해하기 쉽도록 헤드 라인을 변경했습니다 (비정 표를 받았습니다, doh!).


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 개의 중요하지 않은 목록 항목을 표시해야 할 때이 접근 방식을 거부했습니다. 매우 느립니다. Nexus 4는 허용되지 않는 "목록"을 표시하는 데 약 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.
    }
  }
});

그래, 내가 가진 두 번째 생각했지만, 난 확실히의 아니었다 ListAdapter그리고는 ListView내가 수동으로하지 않는 화면 뒤에 더 마법을 수행합니다.
Stefan Hoth 2012 년

물론 ListView는 적어도 요소 사이의 구분선을 사용하여 몇 가지 마술을 수행합니다. 수동으로 추가해야 할 것 같습니다. ListAdapter (올바르게 구현 된 경우)는 더 이상 마술을하지 않아야합니다.
smok

2
이것은 나를위한 반대표입니다. 메모리 문제는 누구입니까? 우리가 어댑터를 사용하는 이유가 있습니다 ...
Kevin Parker

1
다른 사람이 어댑터의 notifyDataSetChanged방법을 사용하여 뷰를 업데이트하는 데 문제가 있습니까? 이것은 내가 연결 한 다른 어댑터에서 잘 작동 ListView하지만 내가 연결 한 어댑터 에서는 작동하지 않는 것 같습니다 LinearLayout.
jokeefe nov.

2
이것이 제가 안드로이드를 싫어하는 이유 중 하나입니다. 복잡한 솔루션을 구현해야하는 이유나 성능 문제가있는 이유를 모르겠습니다.
IARI

6

나는 헤더 뷰 솔루션을 고수 할 것입니다. 아무 문제가 없습니다. 현재 나는 똑같은 접근 방식을 사용하여 활동을 구현합니다.

분명히 "항목 부분"은 정적 (다양한 항목 수 대 수정 항목 수 등)보다 더 동적입니다. 그렇지 않으면 어댑터 사용에 대해 전혀 생각하지 않을 것입니다. 따라서 어댑터가 필요할 때 ListView를 사용하십시오.

어댑터에서 LinearLayout을 채우는 솔루션을 구현하는 것은 결국 사용자 지정 레이아웃으로 ListView를 구축하는 것입니다.

내 2 센트.


이 접근 방식의 한 가지 단점은 거의 전체 활동 레이아웃 (위 참조)을 다른 레이아웃으로 이동해야한다는 것입니다. 그러면이를 ListHeaderView로 참조하고 ListView. 이 패치 작업이 마음에 들지 않습니다.
Stefan Hoth 2012 년

1
네, 무슨 뜻인지 압니다.이 접근 방식을 사용하기 전에 활동 레이아웃을 여러 파일로 분리하는 것도 마음에 들지 않았습니다. 그러나 전체 레이아웃이 예상대로 보이는 것을 보았을 때 두 개의 파일로만 분리되어 있기 때문에 분리에 대해 신경 쓰지 않았습니다. 주의해야 할 한 가지는 목록 항목이 없을 때 목록 헤더를 숨길 수 있으므로 ListView에 빈보기가 없어야합니다.
Flo

3
또한 한 페이지에 여러 목록을 갖고 싶다면 실제로 작동하지 않습니다. 항목을 "플랫"목록, 즉 스크롤하지 않는 목록으로 출력하는 사용자 정의 어댑터보기의 예가있는 사람이 있습니까?
murtuza

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로 목록을 채우고 싶지 않습니다.
플로

"ListAdapter를 LinearLayout 또는 이와 유사한 것에 어떻게 연결합니까?" 다만 영업 이익은 무엇을 요구 응답
fasheikh

2
아니 그는 ListView를 사용하고 싶지 않습니다. 그는 어댑터를 사용하고이를 사용하여 LinearLayout에 항목을 채우려 고합니다.
플로

답변 해 주셔서 감사하지만 @Flo가 정확합니다. 내가 할 수없는 외부 레이아웃이 이미있는 ScrollView이기 때문에 ListView를 사용합니다. 내 텍스트와 샘플 레이아웃을 확인하십시오.
Stefan Hoth 2012 년

왜 그 scrollView가 필요합니까?
fasheikh

0

나는와 어댑터 기능을 복제 다음 코드를 사용하십시오 ViewGroupTabLayout. 이것에 대한 좋은 점은 목록을 변경하고 다시 바인딩하면 변경된 항목에만 영향을 미친다는 것입니다.

용법:

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.