ListView가 접히지 않고 어떻게 ScrollView에 넣을 수 있습니까?


351

이 문제에 대한 해결책을 찾아 보았는데 찾을 수있는 유일한 대답은 " ListView를 ScrollView에 넣지 마십시오 "입니다. 나는 왜 그런지에 대한 실제 설명을 아직 보지 못했습니다 . 내가 찾을 수있는 유일한 이유는 Google이 그렇게하고 싶지 않다고 생각하기 때문입니다. 글쎄, 그렇게 했어.

문제는 ListView를 최소 높이로 축소하지 않고 어떻게 ListView를 ScrollView에 배치 할 수 있습니까?


11
장치가 터치 스크린을 사용하는 경우 두 개의 중첩 된 스크롤 가능 컨테이너를 구별하는 좋은 방법이 없기 때문입니다. 스크롤바를 사용하여 컨테이너를 스크롤하는 기존 데스크탑에서 작동합니다.
Romain Guy

57
물론입니다. 내부를 만지면 스크롤하십시오. 내부가 너무 크면 UI 디자인이 좋지 않습니다. 그렇다고 일반적으로 나쁜 생각은 아닙니다.
DougW

5
내 태블릿 앱 에서이 문제를 발견했습니다. 스마트 폰에서는 더 나빠 보이지는 않지만 태블릿에서 사용자가 외부 또는 내부 ScrollView를 스크롤할지 여부를 쉽게 결정할 수있는 것은 끔찍합니다. @DougW : 끔찍한 디자인, 동의합니다.
해파리

4
@Romain-이것은 상당히 오래된 게시물이므로 여기의 문제가 이미 해결되었는지 또는 ScrollView 내부에서 ListView를 사용하는 좋은 방법이 없는지 궁금합니다 (또는 수동 코딩이 필요없는 대안) ListView의 모든 nicities를 LinearLayout으로)?
Jim

3
@Jim : "또는 ListView의 모든 특성을 LinearLayout에 수동으로 코딩하지 않는 대안"-안에 모든 것을 넣습니다 ListView. ListView여러 행 스타일과 선택 불가능한 행을 가질 수 있습니다.
CommonsWare

답변:


196

a ListView를 사용하여 스크롤하지 않도록하려면 비용이 많이 들고 전체 목적에 위배 ListView됩니다. 당신은해야 하지 이렇게. LinearLayout대신 사용하십시오 .


69
지난 2 ~ 3 년 동안 ListView를 담당 한 이래로 소스가 될 것입니다. :) ListView는 어댑터 사용을 최적화하기 위해 많은 추가 작업을 수행합니다. 이 문제를 해결하려고하면 여전히 ListView가 LinearLayout이 할 필요가없는 많은 작업을 수행하게됩니다. ListView의 구현을 설명하기에 충분한 공간이 없기 때문에 자세한 내용을 다루지 않겠습니다. 이것은 터치 이벤트와 관련하여 ListView가 작동하는 방식을 해결하지 못합니다. 또한 오늘날 해킹이 작동하더라도 향후 릴리스에서도 작동한다는 보장은 없습니다. LinearLayout을 사용하는 것은 실제로 많은 일을하지 않을 것입니다.
Romain Guy

7
글쎄요, 그것은 합리적인 반응입니다. 피드백을 공유 할 수 있다면 훨씬 더 iPhone 경험이 풍부하며 성능 특성 및 이와 같은 유스 케이스 (또는 안티 패턴)와 관련하여 Apple의 문서가 훨씬 더 잘 작성되었음을 알 수 있습니다. 전반적으로 Android 문서는 훨씬 더 분산되어 있고 덜 집중되어 있습니다. 나는 그 뒤에 몇 가지 이유가 있음을 이해하지만, 그것은 긴 토론이므로, 당신이 그것에 대해 이야기해야한다고 느끼면 알려주십시오.
DougW

6
어댑터에서 LinearLayout을 작성하는 정적 메소드를 쉽게 작성할 수 있습니다.
Romain Guy

125
이것은 대답이 아닙니다 How can I put a ListView into a ScrollView without it collapsing?... 당신은 그것을해서는 안된다는 것을 말할 수 있지만 어떻게 해야하는지에 대한 대답을 할 수 있습니다. 좋은 대답입니다. ListView에는 LinearLayout에없는 스크롤 기능 외에 다른 멋진 기능이 있습니다.
Jorge Fuentes González

44
ListView + 다른 뷰를 포함하는 영역이 있고 모든 것을 하나로 스크롤하려면 어떻게해야합니까? 이것은 ScrollView 내에 ListView를 배치하기위한 합리적인 사용 사례처럼 보입니다. ListView를 LinearLayout으로 바꾸는 것은 솔루션을 사용할 수 없기 때문에 해결책이 아닙니다.
Barry Fruitman

262

여기 내 해결책이 있습니다. 나는 안드로이드 플랫폼에 익숙하지 않으며, 특히 .measure를 직접 호출하고 LayoutParams.height속성을 직접 설정하는 것과 관련하여 약간 해킹 적이라고 확신 하지만 작동합니다.

당신이해야 할 일은 전화 Utility.setListViewHeightBasedOnChildren(yourListView)이며 항목의 높이를 정확하게 수용하도록 크기가 조정됩니다.

public class Utility {
    public static void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            // pre-condition
            return;
        }

        int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();

        for (int i = 0; i < listAdapter.getCount(); i++) {
            View listItem = listAdapter.getView(i, null, listView);
            if (listItem instanceof ViewGroup) {
                listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
             }

             listItem.measure(0, 0);
             totalHeight += listItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
    }
}

66
당신은 방금 매우 비싼 LinearLayout을 재현했습니다 :)
Romain Guy

82
를 제외하고 LinearLayout는 나름대로의 ListView장점 을 모두 가지고 있지 않습니다. 분배기, 머리글 / 바닥 글보기, 전화의 UI 테마 색상과 일치하는 목록 선택기 등이 없습니다. 솔직히 말해서 스크롤 할 수없는 이유는 없습니다. 내부 컨테이너가 끝에 도달 할 때까지 터치 한 다음 외부 컨테이너가 스크롤되도록 터치 이벤트 차단을 중지합니다. 스크롤 휠을 사용할 때 모든 데스크탑 브라우저가이를 수행합니다. 트랙볼을 통해 탐색하는 경우 Android 자체에서 중첩 스크롤을 처리 할 수 ​​있습니다.
닐 트래 프트

29
listItem.measure(0,0)listItem이 ViewGroup 인스턴스 인 경우 NPE가 발생합니다. 전에 다음을 추가했습니다 listItem.measure.if (listItem instanceof ViewGroup) listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
Siklab.ph

4
패딩이 0이 아닌 ListViews에 대한 수정 :int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
Paul

8
불행히도이 방법은 ListView다양한 높이의 아이템을 포함 할 수 있을 때 약간 결함이 있습니다. 호출 getMeasuredHeight()은 실제 높이와 반대로 목표 최소 높이 만 반환합니다. getHeight()대신 사용해 보았지만 0을 반환합니다. 누구에게 접근하는 방법에 대한 통찰력이 있습니까?
Matt Huggins 2016 년

89

이것은 확실히 작동합니다 ......... 레이아웃 XML 파일을 다음 과 같이
바꿔야합니다 <ScrollView ></ScrollView>.Custom ScrollView<com.tmd.utils.VerticalScrollview > </com.tmd.utils.VerticalScrollview >

package com.tmd.utils;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ScrollView;

public class VerticalScrollview extends ScrollView{

    public VerticalScrollview(Context context) {
        super(context);
    }

     public VerticalScrollview(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public VerticalScrollview(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        switch (action)
        {
            case MotionEvent.ACTION_DOWN:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: DOWN super false" );
                    super.onTouchEvent(ev);
                    break;

            case MotionEvent.ACTION_MOVE:
                    return false; // redirect MotionEvents to ourself

            case MotionEvent.ACTION_CANCEL:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: CANCEL super false" );
                    super.onTouchEvent(ev);
                    break;

            case MotionEvent.ACTION_UP:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: UP super false" );
                    return false;

            default: Log.i("VerticalScrollview", "onInterceptTouchEvent: " + action ); break;
        }

        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        super.onTouchEvent(ev);
        Log.i("VerticalScrollview", "onTouchEvent. action: " + ev.getAction() );
         return true;
    }
}

4
이것은 매우 좋습니다. 또한 Google Maps Fragment를 ScrollView 안에 넣을 수 있으며 확대 / 축소가 가능합니다. 감사.
Magnus Johansson

누구 든지이 접근법에 대한 단점을 발견 했습니까? 너무 간단합니다 : o
Marija Milosevic

1
좋은 해결책, 허용되는 대답 +1이어야합니다! 나는 (djunod의) 아래 답변과 혼합했으며 훌륭하게 작동합니다!
tanou

1
이것은 나를 위해 동시에 일하는 어린이를 스크롤하고 클릭하는 유일한 수정입니다. 큰 감사
pellyadolfo

25

퍼팅의 Insted ListView돌며 ScrollView, 우리가 사용할 수있는 ListViewA와 ScrollView. ListView안에 있어야 할 것들을 안에 넣을 수 있습니다 ListView. 의 ListView머리글과 바닥 글에 레이아웃을 추가하여 상단 및 하단의 다른 레이아웃을 넣을 수 있습니다 ListView. 따라서 전체 ListView가 스크롤의 경험을 제공합니다.


3
이것은 가장 우아하고 적절한 답변입니다. 이 접근 방식에 대한 자세한 내용은 다음 답변을 참조하십시오. stackoverflow.com/a/20475821/1221537
adamdport

21

ListView를 ScrollView에 포함시키는 것이 합리적 인 상황 이 많이 있습니다 .

다음은 DougW의 제안을 기반으로 한 코드입니다 ... 조각으로 작동하고 메모리를 덜 차지합니다.

public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
        return;
    }
    int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);
    int totalHeight = 0;
    View view = null;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        view = listAdapter.getView(i, view, listView);
        if (i == 0) {
            view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, LayoutParams.WRAP_CONTENT));
        }
        view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
        totalHeight += view.getMeasuredHeight();
    }
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}

포함 된 각 목록보기에서 setListViewHeightBasedOnChildren (listview)을 호출하십시오.


목록 항목의 크기가 가변적 일 때 (예 : 여러 줄로 확장되는 일부 텍스트보기) 작동하지 않습니다. 어떤 해결책?
Khobaib

여기에 내 의견을 확인 - 그것은 완벽한 솔루션을 제공합니다 - stackoverflow.com/a/23315696/1433187
Khobaib

API 19에서는 작동하지만 API 23에서는 작동하지 않습니다. 여기에 listView 아래에 많은 공백이 추가됩니다.
Lucker10

17

ListView는 실제로 모든 항목을 표시 할만큼 충분히 키가 큰 것으로 측정 할 수 있지만 wrap_content (MeasureSpec.UNSPECIFIED)를 지정하면이 작업을 수행하지 않습니다. MeasureSpec.AT_MOST로 높이가 주어지면이 작업을 수행합니다. 이 지식을 통해이 문제를 해결하기 위해 매우 간단한 서브 클래스를 생성 할 수 있습니다.이 문제는 위에 게시 된 솔루션보다 훨씬 효과적입니다. 이 서브 클래스에는 여전히 wrap_content를 사용해야합니다.

public class ListViewForEmbeddingInScrollView extends ListView {
    public ListViewForEmbeddingInScrollView(Context context) {
        super(context);
    }

    public ListViewForEmbeddingInScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ListViewForEmbeddingInScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 4, MeasureSpec.AT_MOST));
    }
}

heightMeasureSpec을 매우 큰 크기 (Integer.MAX_VALUE >> 4)의 AT_MOST로 조작하면 ListView가 모든 하위를 주어진 (매우 큰) 높이까지 측정하고 그에 따라 높이를 설정합니다.

몇 가지 이유로 다른 솔루션보다 더 효과적입니다.

  1. 모든 것을 정확하게 측정합니다 (패딩, 분배기)
  2. 측정 단계 동안 ListView를 측정합니다.
  3. # 2로 인해 추가 코드없이 너비 또는 항목 수의 변경을 올바르게 처리합니다.

단점은이 작업을 수행하는 것이 SDK에서 문서화되지 않은 동작에 의존하여 변경 될 수 있다고 주장 할 수 있습니다. 반면에 wrap_content가 실제로 ListView에서 작동하는 방식이며 현재 wrap_content 동작이 손상되었다고 주장 할 수 있습니다.

앞으로 동작이 변경 될까 걱정이되면 onMeasure 함수 및 관련 함수를 ListView.java에서 자신의 하위 클래스로 복사 한 다음 onMeasure를 통해 AT_MOST 경로를 UNSPECIFIED에 대해 실행해야합니다.

그건 그렇고, 나는 이것이 적은 수의 목록 항목으로 작업 할 때 완벽하게 유효한 접근법이라고 생각합니다. LinearLayout과 비교할 때 비효율적 일 수 있지만 항목 수가 적은 경우 LinearLayout을 사용하는 것은 불필요한 최적화이므로 불필요한 복잡성입니다.


또한 목록보기에서 가변 높이 항목과 함께 작동합니다!
데니

@Jason Y 이것이하는 일, Integer.MAX_VALUE >> 4는 ?? 4가 무엇입니까 ??
KJEjava48

@Jason Y 나를 위해 Integer.MAX_VALUE >> 2를 Integer.MAX_VALUE >> 21로 변경 한 후에 만 ​​작동했습니다. 왜 그럴까요? 또한 NestedScrollView가 아닌 ​​ScrollView와 함께 작동합니다.
KJEjava48

13

내장 설정이 있습니다. ScrollView에서 :

android:fillViewport="true"

자바에서는

mScrollView.setFillViewport(true);

Romain Guy는 여기에 자세히 설명되어 있습니다. http://www.curious-creature.org/2010/08/15/scrollviews-handy-trick/


2
그가 설명하는 것처럼 LinearLayout에서 작동합니다. ListView에서는 작동하지 않습니다. 그 게시물의 날짜를 기준으로, 나는 그가 대안의 예를 제공하기 위해 글을 썼다고 생각하지만 이것이 질문에 대한 답은 아닙니다.
DougW

이것은 빠른 해결책입니다

10

스크롤 할 수없는 사용자 정의 ListView를 작성합니다.

public class NonScrollListView extends ListView {

    public NonScrollListView(Context context) {
        super(context);
    }
    public NonScrollListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
                    Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = getMeasuredHeight();    
    }
}

레이아웃 리소스 파일에서

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <!-- com.Example Changed with your Package name -->

    <com.Example.NonScrollListView
        android:id="@+id/lv_nonscroll_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </com.Example.NonScrollListView>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/lv_nonscroll_list" >

        <!-- Your another layout in scroll view -->

    </RelativeLayout>
</RelativeLayout>

자바 파일에서

다음과 같이 ListView 대신 customListview 오브젝트를 작성하십시오. NonScrollListView non_scroll_list = (NonScrollListView) findViewById (R.id.lv_nonscroll_list);


8

우리는 두 개의 스크롤을 동시에 사용할 수 없었습니다. 우리는 ListView의 전체 길이를 가져 와서 전체 높이로 listview를 확장합니다. 그런 다음 ScrollView에 직접 하나의 자식이 있기 때문에 ListView를 직접 추가하거나 LinearLayout을 사용하여 ListView를 추가 할 수 있습니다. 코드에 setListViewHeightBasedOnChildren (lv) 메소드를 복사하고 listview를 확장하면 scrollview 내에서 listview를 사용할 수 있습니다. \ layout xml 파일

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
 <ScrollView

        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
         android:background="#1D1D1D"
        android:orientation="vertical"
        android:scrollbars="none" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#1D1D1D"
            android:orientation="vertical" >

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="40dip"
                android:background="#333"
                android:gravity="center_vertical"
                android:paddingLeft="8dip"
                android:text="First ListView"
                android:textColor="#C7C7C7"
                android:textSize="20sp" />

            <ListView
                android:id="@+id/first_listview"
                android:layout_width="260dp"
                android:layout_height="wrap_content"
                android:divider="#00000000"
               android:listSelector="#ff0000"
                android:scrollbars="none" />

               <TextView
                android:layout_width="fill_parent"
                android:layout_height="40dip"
                android:background="#333"
                android:gravity="center_vertical"
                android:paddingLeft="8dip"
                android:text="Second ListView"
                android:textColor="#C7C7C7"
                android:textSize="20sp" />

            <ListView
                android:id="@+id/secondList"
                android:layout_width="260dp"
                android:layout_height="wrap_content"
                android:divider="#00000000"
                android:listSelector="#ffcc00"
                android:scrollbars="none" />
  </LinearLayout>
  </ScrollView>

   </LinearLayout>

활동 클래스의 onCreate 메소드 :

 import java.util.ArrayList;
  import android.app.Activity;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.ListAdapter;
  import android.widget.ListView;

   public class MainActivity extends Activity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.listview_inside_scrollview);
    ListView list_first=(ListView) findViewById(R.id.first_listview);
    ListView list_second=(ListView) findViewById(R.id.secondList);
    ArrayList<String> list=new ArrayList<String>();
    for(int x=0;x<30;x++)
    {
        list.add("Item "+x);
    }

       ArrayAdapter<String> adapter=new ArrayAdapter<String>(getApplicationContext(), 
          android.R.layout.simple_list_item_1,list);               
      list_first.setAdapter(adapter);

     setListViewHeightBasedOnChildren(list_first);

      list_second.setAdapter(adapter);

    setListViewHeightBasedOnChildren(list_second);
   }



   public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
        // pre-condition
        return;
    }

    int totalHeight = 0;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();
    }

    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight
            + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
      }

좋은 수정! 필요에 따라 작동합니다. 감사. 내 경우에는 목록 앞에 많은 뷰가 있으며 완벽한 솔루션은 뷰에 하나의 세로 스크롤 만있는 것입니다.
Proverbio

4

이것은 DougW, Good Guy Greg 및 Paul의 답변을 조합 한 것입니다. 나는 이것을 사용자 정의 listview 어댑터 및 비표준 목록 항목과 함께 사용하려고 할 때 모두 필요하다는 것을 알았습니다. 그렇지 않으면 listview가 응용 프로그램과 충돌했습니다 (Nex의 답변과 충돌 함).

public void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            return;
        }

        int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
        for (int i = 0; i < listAdapter.getCount(); i++) {
            View listItem = listAdapter.getView(i, null, listView);
            if (listItem instanceof ViewGroup)
                listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            listItem.measure(0, 0);
            totalHeight += listItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
    }

이 메소드를 어디에서 호출합니까?
Dhrupal

목록보기를 작성하고 어댑터를 설정 한 후
버려진 카트

긴 목록에서는 너무 느립니다.
cakan

@cakan 또한 실제로 결합되지 않은 특정 UI 구성 요소를 함께 사용할 수있는 해결 방법으로 2 년 전에 작성되었습니다. 현대 Android에서 불필요한 양의 오버 헤드를 추가하고 있으므로 목록이 커지면 목록이 느려질 것으로 예상됩니다.
버려진 카트

3

ListView가 이미 있기 때문에 당신은있는 ScrollView에 ListView를 넣어 안 입니다 있는 ScrollView. 따라서 ScrollView에 ScrollView를 넣는 것과 같습니다.

무엇을 이루려고 노력하고 있습니까?


4
ScrollView 안에 ListView를 사용하려고합니다. ListView를 스크롤 할 수는 없지만 myListView.scrollEnabled = false를 설정하는 간단한 속성은 없습니다.
DougW

Romain Guy가 말했듯이 이것은 ListView의 목적이 아닙니다. ListView는 긴 항목 목록을 표시하는 데 최적화 된보기로,보기의 지연 로딩,보기 재사용 등을 수행하는 복잡한 논리가 많이 있습니다. ListView에 스크롤하지 말라고 지시하는 경우에는 아무런 의미가 없습니다. 세로 열에 많은 항목을 원하면 LinearLayout을 사용하십시오.
Cheryl Simon

9
문제는 언급 한 솔루션을 포함하여 ListView가 제공하는 많은 솔루션이 있다는 것입니다. 어댑터가 단순화하는 많은 기능은 UI 최적화가 아니라 데이터 관리 및 제어에 관한 것입니다. IMO에는 간단한 ListView와 모든 목록 항목 재사용 및 작업을 수행하는 더 복잡한 ListView가 있어야합니다.
DougW

2
절대적으로 동의했다. LinearLayout과 함께 기존 어댑터를 사용하는 것은 쉽지만 디바이더와 같이 ListView가 제공하는 모든 장점을 원하며 수동으로 구현하고 싶지 않다는 점에 유의하십시오.
Artem Russakovskii 2016 년

1
@ArtemRussakovskii ListView를 기존 어댑터로 LinearLayout으로 바꾸는 쉬운 방법을 설명해 주시겠습니까?
happyhardik

3

@DougW Utility를 C #으로 변환했습니다 (Xamarin에서 사용). 다음은 목록의 고정 높이 항목에 적합하며 일부 항목 만 표준 항목보다 약간 큰 경우에는 대부분 양호하거나 적어도 좋은 시작입니다.

// You will need to put this Utility class into a code file including various
// libraries, I found that I needed at least System, Linq, Android.Views and 
// Android.Widget.
using System;
using System.Linq;
using Android.Views;
using Android.Widget;

namespace UtilityNamespace  // whatever you like, obviously!
{
    public class Utility
    {
        public static void setListViewHeightBasedOnChildren (ListView listView)
        {
            if (listView.Adapter == null) {
                // pre-condition
                return;
            }

            int totalHeight = listView.PaddingTop + listView.PaddingBottom;
            for (int i = 0; i < listView.Count; i++) {
                View listItem = listView.Adapter.GetView (i, null, listView);
                if (listItem.GetType () == typeof(ViewGroup)) {
                    listItem.LayoutParameters = new LinearLayout.LayoutParams (ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
                }
                listItem.Measure (0, 0);
                totalHeight += listItem.MeasuredHeight;
            }

            listView.LayoutParameters.Height = totalHeight + (listView.DividerHeight * (listView.Count - 1));
        }
    }
}

@DougW 덕분에 OtherPeople'sCode로 작업해야 할 때 빡빡했습니다. :-)


한 가지 질문 : 언제 setListViewHeightBasedOnChildren()메소드 를 호출 했 습니까? 항상 작동하지 않기 때문입니다.
Alexandre D.

@AlexandreD 항목 목록을 만들 때 Utility.setListViewHeightBasedOnChildren (yourListView)을 호출합니다. 즉, 먼저 목록을 작성했는지 확인하십시오! 나는 목록을 만들고 있음을 알았고 심지어 Console.WriteLine에 항목을 표시하고 있었지만 어떻게 든 항목의 범위가 잘못되었습니다. 일단 내가 그것을 알아 내고 내 목록에 올바른 범위를 갖도록 보장하면 이것은 매력처럼 작동했습니다.
Phil Ryan

사실 내 목록은 내 ViewModel에서 생성됩니다. Utility.setListViewHeightBasedOnChildren (yourListView)을 호출하기 전에 내보기에서 내 목록이 작성 될 때까지 어떻게 대기합니까?
Alexandre D.

3

이것이 나를 위해 일한 유일한 것입니다.

롤리팝에서 당신은 사용할 수 있습니다

yourtListView.setNestedScrollingEnabled(true);

이전 버전의 OS와 하위 호환성이 필요한 경우 RecyclerView를 사용해야하는 경우이보기에 대해 중첩 스크롤을 사용하거나 사용하지 않습니다.


이것은 AppCompat 활동의 DialogFragment에서 API 22의 목록보기에 영향을 미치지 않았습니다. 그들은 여전히 ​​붕괴합니다. @ Phil Ryan의 대답 은 약간의 수정으로 작동했습니다.
Hakan Çelik MK1

3

불가능하기 전에. 그러나 새로운 Appcompat 라이브러리 및 디자인 라이브러리가 출시되면서이를 달성 할 수 있습니다.

NestedScrollView https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html 을 사용해야합니다.

Listview에서 작동하는지 여부는 알지 못하지만 RecyclerView에서는 작동합니다.

코드 스 니펫 :

<android.support.v4.widget.NestedScrollView 
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</android.support.v4.widget.NestedScrollView>

쿨, 나는 그것을 시도하지 않았으므로 내부 ListView가 붕괴되지는 않지만 그 의도가있는 것처럼 보일 수는 없습니다. Romain Guy를 비난 기록에서 보지 않는 것이 매우 슬퍼했다;)
DougW

1
이것은 recyclerView와 함께 작동하지만 listView에서는 작동하지 않습니다. 고맙습니다
IgniteCoders

2

이봐 비슷한 문제가 있었다. 스크롤하지 않은 목록보기를 표시하고 싶었지만 매개 변수 조작이 효과가 있지만 비효율적이며 다른 장치에서 다르게 작동한다는 것을 알았습니다. 결과적으로 이것은 실제로 매우 효율적으로 수행하는 일정 코드의 일부입니다 .

db = new dbhelper(this);

 cursor = db.dbCursor();
int count = cursor.getCount();
if (count > 0)
{    
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.layoutId);
startManagingCursor(YOUR_CURSOR);

YOUR_ADAPTER(**or SimpleCursorAdapter **) adapter = new YOUR_ADAPTER(this,
    R.layout.itemLayout, cursor, arrayOrWhatever, R.id.textViewId,
    this.getApplication());

int i;
for (i = 0; i < count; i++){
  View listItem = adapter.getView(i,null,null);
  linearLayout.addView(listItem);
   }
}

참고 : 이것을 사용 notifyDataSetChanged();하면보기가 다시 그려지지 않으므로 의도 한대로 작동하지 않습니다. 해결 방법이 필요한 경우이 작업을 수행하십시오.

adapter.registerDataSetObserver(new DataSetObserver() {

            @Override
            public void onChanged() {
                super.onChanged();
                removeAndRedrawViews();

            }

        });

2

ScrollView 내에서 ListView를 사용할 때 두 가지 문제가 있습니다.

1- ListView가 자식 높이까지 완전히 확장되어야합니다. 이 ListView는 이것을 해결합니다.

public class ListViewExpanded extends ListView
{
    public ListViewExpanded(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        setDividerHeight(0);
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST));
    }
}

분배기 높이는 0이어야합니다. 대신 패딩을 행으로 사용하십시오.

2- ListView는 터치 이벤트를 소비하므로 ScrollView를 평소와 같이 스크롤 할 수 없습니다. 이 ScrollView는이 문제를 해결합니다.

public class ScrollViewInterceptor extends ScrollView
{
    float startY;

    public ScrollViewInterceptor(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e)
    {
        onTouchEvent(e);
        if (e.getAction() == MotionEvent.ACTION_DOWN) startY = e.getY();
        return (e.getAction() == MotionEvent.ACTION_MOVE) && (Math.abs(startY - e.getY()) > 50);
    }
}

이것이 내가 트릭을 찾은 가장 좋은 방법입니다!


2

내가 사용하는 해결책은 ScrollView의 모든 내용 (목록보기 위와 아래에 있어야 함)을 ListView에서 headerView 및 footerView로 추가하는 것입니다.

그래서 그것은 작동합니다, 또한 convertview는 어떻게되어야하는지에 달려 있습니다.


1

Vinay의 코드 덕분에 스크롤 뷰 안에 목록보기를 가질 수 없지만 그와 같은 것이 필요할 때를위한 코드가 있습니다.

LayoutInflater li = LayoutInflater.from(this);

                RelativeLayout parent = (RelativeLayout) this.findViewById(R.id.relativeLayoutCliente);

                int recent = 0;

                for(Contatto contatto : contatti)
                {
                    View inflated_layout = li.inflate(R.layout.header_listview_contatti, layout, false);


                    inflated_layout.setId(contatto.getId());
                    ((TextView)inflated_layout.findViewById(R.id.textViewDescrizione)).setText(contatto.getDescrizione());
                    ((TextView)inflated_layout.findViewById(R.id.textViewIndirizzo)).setText(contatto.getIndirizzo());
                    ((TextView)inflated_layout.findViewById(R.id.textViewTelefono)).setText(contatto.getTelefono());
                    ((TextView)inflated_layout.findViewById(R.id.textViewMobile)).setText(contatto.getMobile());
                    ((TextView)inflated_layout.findViewById(R.id.textViewFax)).setText(contatto.getFax());
                    ((TextView)inflated_layout.findViewById(R.id.textViewEmail)).setText(contatto.getEmail());



                    RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);

                    if (recent == 0)
                    {
                        relativeParams.addRule(RelativeLayout.BELOW, R.id.headerListViewContatti);
                    }
                    else
                    {
                        relativeParams.addRule(RelativeLayout.BELOW, recent);
                    }
                    recent = inflated_layout.getId();

                    inflated_layout.setLayoutParams(relativeParams);
                    //inflated_layout.setLayoutParams( new RelativeLayout.LayoutParams(source));

                    parent.addView(inflated_layout);
                }

relativeLayout은 ScrollView 안에 유지되므로 모두 스크롤 가능하게됩니다. :)


1

다음은 @djunod대답 을 약간 수정하여 완벽하게 작동해야한다는 것입니다.

public static void setListViewHeightBasedOnChildren(ListView listView)
{
    ListAdapter listAdapter = listView.getAdapter();
    if(listAdapter == null) return;
    if(listAdapter.getCount() <= 1) return;

    int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);
    int totalHeight = 0;
    View view = null;
    for(int i = 0; i < listAdapter.getCount(); i++)
    {
        view = listAdapter.getView(i, view, listView);
        view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
        totalHeight += view.getMeasuredHeight();
    }
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}

: 안녕하세요, 귀하의 답변은 대부분 작동하지만 목록보기에 머리글보기 및 발보기가 있으면 작동하지 않습니다. 당신은 그것을 확인할 수 있습니까?
hguser

목록 항목의 크기가 가변적 인 경우 솔루션이 필요합니다. 예를 들어 여러 줄로 확장되는 일부 텍스트보기. 어떠한 제안?
Khobaib

1

이것을 시도해보십시오. 이것은 저에게 효과적이며, 스택 오버플로의 어딘가에서 그것을 발견 한 곳을 잊어 버렸습니다. 왜 작동하지 않는지 설명하지 않았습니다. 그러나 이것이 답입니다 :).

    final ListView AturIsiPulsaDataIsiPulsa = (ListView) findViewById(R.id.listAturIsiPulsaDataIsiPulsa);
    AturIsiPulsaDataIsiPulsa.setOnTouchListener(new ListView.OnTouchListener() 
    {
        @Override
        public boolean onTouch(View v, MotionEvent event) 
        {
            int action = event.getAction();
            switch (action) 
            {
                case MotionEvent.ACTION_DOWN:
                // Disallow ScrollView to intercept touch events.
                v.getParent().requestDisallowInterceptTouchEvent(true);
                break;

                case MotionEvent.ACTION_UP:
                // Allow ScrollView to intercept touch events.
                v.getParent().requestDisallowInterceptTouchEvent(false);
                break;
            }

            // Handle ListView touch events.
            v.onTouchEvent(event);
            return true;
        }
    });
    AturIsiPulsaDataIsiPulsa.setClickable(true);
    AturIsiPulsaDataIsiPulsa.setAdapter(AturIsiPulsaDataIsiPulsaAdapter);

편집!, 나는 코드를 어디서 얻었는지 마침내 알았습니다. 여기 ! : ScrollView 내의 ListView가 Android에서 스크롤되지 않습니다.


여기에는 스크롤보기 상호 작용을 잃지 않도록하는 방법이 포함되지만 문제는 목록보기 높이를 유지하는 것입니다.
버려진 카트

1

제안 된 setListViewHeightBasedOnChildren () 메소드는 대부분의 경우에 작동하지만 경우에 따라 특히 많은 항목이있는 경우에도 마지막 요소가 표시되지 않습니다. 그래서 어댑터 코드를 재사용하기 위해 간단한 버전의 ListView 동작 을 모방하기로 결정했습니다 . 여기에는 ListView 대안이 있습니다.

import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ListAdapter;

public class StretchedListView extends LinearLayout {

private final DataSetObserver dataSetObserver;
private ListAdapter adapter;
private OnItemClickListener onItemClickListener;

public StretchedListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setOrientation(LinearLayout.VERTICAL);
    this.dataSetObserver = new DataSetObserver() {
        @Override
        public void onChanged() {
            syncDataFromAdapter();
            super.onChanged();
        }

        @Override
        public void onInvalidated() {
            syncDataFromAdapter();
            super.onInvalidated();
        }
    };
}

public void setAdapter(ListAdapter adapter) {
    ensureDataSetObserverIsUnregistered();

    this.adapter = adapter;
    if (this.adapter != null) {
        this.adapter.registerDataSetObserver(dataSetObserver);
    }
    syncDataFromAdapter();
}

protected void ensureDataSetObserverIsUnregistered() {
    if (this.adapter != null) {
        this.adapter.unregisterDataSetObserver(dataSetObserver);
    }
}

public Object getItemAtPosition(int position) {
    return adapter != null ? adapter.getItem(position) : null;
}

public void setSelection(int i) {
    getChildAt(i).setSelected(true);
}

public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
    this.onItemClickListener = onItemClickListener;
}

public ListAdapter getAdapter() {
    return adapter;
}

public int getCount() {
    return adapter != null ? adapter.getCount() : 0;
}

private void syncDataFromAdapter() {
    removeAllViews();
    if (adapter != null) {
        int count = adapter.getCount();
        for (int i = 0; i < count; i++) {
            View view = adapter.getView(i, null, this);
            boolean enabled = adapter.isEnabled(i);
            if (enabled) {
                final int position = i;
                final long id = adapter.getItemId(position);
                view.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        if (onItemClickListener != null) {
                            onItemClickListener.onItemClick(null, v, position, id);
                        }
                    }
                });
            }
            addView(view);

        }
    }
}
}

1

이 모든 대답이 잘못되었습니다 !!! 목록보기를 스크롤보기에 넣으려는 경우 디자인을 다시 생각해야합니다. ScrollView에 ScrollView를 넣으려고합니다. 목록을 방해하면 목록 성능이 저하됩니다. 그것은 안드로이드에 의해 이와 같이 설계되었습니다.

목록이 다른 요소와 동일한 스크롤에있게하려면 어댑터의 간단한 switch 문을 사용하여 목록의 맨 위에 다른 항목을 추가하기 만하면됩니다.

class MyAdapter extends ArrayAdapter{

    public MyAdapter(Context context, int resource, List objects) {
        super(context, resource, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
         ViewItem viewType = getItem(position);

        switch(viewType.type){
            case TEXTVIEW:
                convertView = layouteInflater.inflate(R.layout.textView1, parent, false);

                break;
            case LISTITEM:
                convertView = layouteInflater.inflate(R.layout.listItem, parent, false);

                break;            }


        return convertView;
    }


}

목록 어댑터는 보이는 것만 렌더링하므로 모든 것을 처리 할 수 ​​있습니다.


0

LinearLayout에 setAdapter 메소드가 있으면이 모든 문제는 사라질 것입니다. 왜냐하면 누군가에게 사용하도록 지시했을 때 대안이 사소한 것이기 때문입니다.

실제로 다른 스크롤보기 내에서 스크롤 ListView를 원한다면 도움이되지 않지만 적어도 아이디어를 줄 것입니다.

스크롤하려는 모든 컨텐츠를 결합하고 ListView의 어댑터를 해당 어댑터로 설정하려면 사용자 정의 어댑터를 작성해야합니다.

편리한 샘플 코드는 없지만 원하는 경우 원합니다.

<ListView/>

(other content)

<ListView/>

그런 다음 해당 컨텐츠를 모두 나타내는 어댑터를 작성해야합니다. ListView / Adapters는 다양한 유형을 처리 할 수있을만큼 똑똑하지만 어댑터를 직접 작성해야합니다.

안드로이드 UI API는 다른 모든 것만 큼 성숙하지 않으므로 다른 플랫폼과 같은 장점이 없습니다. 또한 안드로이드에서 무언가를 할 때 당신은 아마 작은 부분의 기능을 조립하고 그것을 얻기 위해 많은 코드를 작성해야 할 것으로 예상되는 안드로이드 (유닉스) 사고 방식에 있어야합니다. 작업.


0

우리가 ListView안에두면 ScrollView두 가지 문제가 발생합니다. 하나는 지정되지 ScrollView않은 모드에서 자녀를 측정하므로 ListView하나의 항목 (왜 그런지 모르겠습니다)을 수용하도록 자체 높이를 설정하고 다른 하나는 ScrollView터치 이벤트를 차단합니다.ListView 스크롤하지 않습니다.

그러나 우리 몇 가지 해결 방법으로 ListView내부 ScrollView를 배치 할 수 있습니다 . 이 게시물 은 해결 방법을 설명합니다. 이 해결 방법으로 ListView의 재활용 기능 도 유지할 수 있습니다 .


0

목록보기를 Scrollview 안에 넣는 대신 목록보기와 Scrollview 열기 사이의 나머지 내용을 별도의보기로 놓고 해당보기를 목록보기의 헤더로 설정하십시오. 따라서 마지막으로 Scroll을 담당하는 목록보기 만 끝납니다.



-1

다음은 목록보기의 총 높이를 계산하는 코드 버전입니다. 이것은 나를 위해 작동합니다 :

   public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null || listAdapter.getCount() < 2) {
        // pre-condition
        return;
    }

    int totalHeight = 0;
    int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(BCTDApp.getDisplaySize().width, View.MeasureSpec.AT_MOST);
    int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        if (listItem instanceof ViewGroup) listItem.setLayoutParams(lp);
        listItem.measure(widthMeasureSpec, heightMeasureSpec);
        totalHeight += listItem.getMeasuredHeight();
    }

    totalHeight += listView.getPaddingTop() + listView.getPaddingBottom();
    totalHeight += (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight;
    listView.setLayoutParams(params);
    listView.requestLayout();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.