Bagaimana saya bisa memasukkan ListView ke dalam ScrollView tanpa runtuh?

351

Saya telah mencari-cari solusi untuk masalah ini, dan satu-satunya jawaban yang dapat saya temukan adalah " jangan masukkan ListView ke dalam ScrollView ". Saya belum melihat penjelasan nyata mengapa . Satu-satunya alasan yang dapat saya temukan adalah bahwa Google tidak berpikir Anda harus melakukan itu. Ya saya lakukan, jadi saya lakukan.

Jadi pertanyaannya adalah, bagaimana Anda bisa menempatkan ListView ke dalam sebuah ScrollView tanpa runtuh ke ketinggian minimum?

DougW
sumber
11
Karena ketika sebuah perangkat menggunakan layar sentuh, tidak ada cara yang baik untuk membedakan antara dua wadah yang dapat digulir. Ini bekerja pada desktop tradisional di mana Anda menggunakan scrollbar untuk menggulir wadah.
Romain Guy
57
Tentu ada. Jika mereka menyentuh bagian dalam, gulir ke sana. Jika bagian dalam terlalu besar, itu desain UI yang buruk. Itu tidak berarti itu ide yang buruk secara umum.
DougW
5
Baru saja menemukan ini untuk aplikasi Tablet saya. Meskipun tampaknya tidak terlalu buruk pada Smartphone, ini mengerikan pada tablet di mana Anda dapat dengan mudah memutuskan apakah pengguna ingin menggulir ScrollView luar atau batin. @ DougW: Desain yang mengerikan, saya setuju.
Ubur
4
@ Domain - ini adalah posting yang cukup lama, jadi saya ingin tahu apakah masalah di sini sudah diatasi, atau apakah masih ada cara yang baik untuk menggunakan ListView di dalam ScrollView (atau alternatif yang tidak melibatkan pengkodean secara manual semua kebaikan ListView menjadi LinearLayout)?
Jim
3
@ Jim: "atau alternatif yang tidak melibatkan pengodean secara manual semua kelebihan ListView ke dalam LinearLayout" - masukkan semua yang ada di dalam ListView. ListViewdapat memiliki beberapa gaya baris, ditambah baris yang tidak dapat dipilih.
CommonsWare

Jawaban:

196

Menggunakan ListViewuntuk membuatnya tidak gulir sangat mahal dan bertentangan dengan seluruh tujuan ListView. Anda TIDAK boleh melakukan ini. Cukup gunakan LinearLayoutsaja.

Guy Romain
sumber
69
Sumbernya adalah saya sejak saya bertanggung jawab atas ListView selama 2 atau 3 tahun terakhir :) ListView melakukan banyak pekerjaan ekstra untuk mengoptimalkan penggunaan adaptor. Mencoba mengatasinya masih akan menyebabkan ListView melakukan banyak pekerjaan yang tidak harus dilakukan oleh LinearLayout. Saya tidak akan masuk ke detail karena tidak ada cukup ruang di sini untuk menjelaskan implementasi ListView. Ini juga tidak akan menyelesaikan cara ListView berperilaku sehubungan dengan acara sentuh. Juga tidak ada jaminan bahwa jika peretasan Anda berhasil hari ini, peretasan Anda masih akan berfungsi di masa mendatang. Menggunakan LinearLayout tidak akan banyak pekerjaan sebenarnya.
Romain Guy
7
Yah itu respons yang masuk akal. Jika saya dapat membagikan umpan balik, saya memiliki pengalaman iPhone yang jauh lebih signifikan, dan saya dapat memberi tahu Anda bahwa dokumentasi Apple ditulis jauh lebih baik berkenaan dengan karakteristik kinerja dan kasus penggunaan (atau anti-pola) seperti ini. Secara keseluruhan, dokumentasi Android jauh lebih terdistribusi dan kurang fokus. Saya mengerti ada beberapa alasan di balik itu, tapi itu diskusi panjang, jadi jika Anda merasa terdorong untuk mengobrol tentang hal itu, beri tahu saya.
DougW
6
Anda dapat dengan mudah menulis metode statis yang membuat LinearLayout dari Adaptor.
Romain Guy
125
Ini BUKAN jawaban untuk How can I put a ListView into a ScrollView without it collapsing?... Anda bisa mengatakan bahwa Anda seharusnya tidak melakukannya, tetapi juga memberikan jawaban tentang bagaimana melakukannya, itu jawaban yang bagus. Anda tahu bahwa ListView memiliki beberapa fitur keren lain selain pengguliran, fitur yang tidak dimiliki LinearLayout.
Jorge Fuentes González
44
Bagaimana jika Anda memiliki wilayah yang mengandung ListView + Tampilan lain, dan Anda ingin semuanya menggulir sebagai satu? Itu tampak seperti kasus penggunaan yang wajar untuk menempatkan ListView di dalam ScrollView. Mengganti ListView dengan LinearLayout bukanlah solusi karena Anda tidak dapat menggunakan Adaptor.
Barry Fruitman
262

Inilah solusi saya. Saya cukup baru di platform Android, dan saya yakin ini agak meretas, terutama di bagian tentang memanggil pengukuran langsung, dan mengatur LayoutParams.heightproperti secara langsung, tetapi berfungsi.

Yang harus Anda lakukan adalah menelepon Utility.setListViewHeightBasedOnChildren(yourListView)dan akan diubah ukurannya untuk mengakomodasi ketinggian barang-barangnya.

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);
    }
}
DougW
sumber
66
Anda baru saja membuat ulang LinearLayout yang sangat mahal :)
Romain Guy
82
Kecuali a LinearLayouttidak memiliki semua sifat bawaan dari a ListView- ia tidak memiliki pembagi, tampilan header / footer, pemilih daftar yang cocok dengan warna tema UI ponsel, dll. Jujur, tidak ada alasan mengapa Anda tidak dapat menggulir wadah bagian dalam sampai mencapai ujung dan kemudian berhenti mencegat acara sentuh sehingga wadah luar bergulir. Setiap browser desktop melakukan ini saat Anda menggunakan roda gulir Anda. Android sendiri dapat menangani penggulungan bersarang jika Anda menavigasi melalui trackball, jadi mengapa tidak melalui sentuhan?
Neil Traft
29
listItem.measure(0,0)akan melempar NPE jika listItem adalah instance ViewGroup. Saya menambahkan yang berikut sebelumnya listItem.measure:if (listItem instanceof ViewGroup) listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
Siklab.ph
4
Perbaiki untuk ListViews dengan bantalan selain 0:int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
Paul
8
Sayangnya metode ini sedikit cacat ketika ListViewdapat berisi item dengan tinggi variabel. Panggilan untuk getMeasuredHeight()hanya mengembalikan tinggi minimum yang ditargetkan sebagai lawan dari tinggi aktual. Saya mencoba menggunakan getHeight()sebagai gantinya, tetapi ini mengembalikan 0. Apakah ada yang punya wawasan tentang cara mendekati ini?
Matt Huggins
89

Ini pasti akan bekerja ............
Anda harus hanya mengganti Anda <ScrollView ></ScrollView>dalam tata letak file XML dengan ini Custom ScrollViewseperti <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;
    }
}
Atul Bhardwaj
sumber
4
Ini sangat bagus. Ini juga memungkinkan untuk menempatkan Google Maps Fragment di dalam ScrollView, dan masih berfungsi memperbesar / memperkecil. Terima kasih.
Magnus Johansson
Adakah yang memperhatikan adanya kontra dengan pendekatan ini? Sederhana sekali, aku takut: o
Marija Milosevic
1
Solusi yang bagus, seharusnya jawaban yang diterima +1! Saya sudah mencampurnya dengan jawaban di bawah (dari Djunod), dan itu berhasil!
tanou
1
Ini adalah satu-satunya perbaikan yang membuat bergulir DAN KLIK pada anak-anak yang bekerja pada saat yang sama untuk saya. Terima kasih besar
pellyadolfo
25

Insted menempatkan ListViewdi dalam ScrollView, kita dapat menggunakan ListViewsebagai ScrollView. Hal-hal yang harus di ListViewdapat dimasukkan ke dalam ListView. Layout lain di atas dan bawah ListViewdapat dimasukkan dengan menambahkan layout ke header dan footer ListView. Jadi keseluruhannya ListViewakan memberi Anda pengalaman menggulir.

Arun
sumber
3
Ini adalah jawaban imho yang paling elegan dan tepat. Untuk detail lebih lanjut tentang pendekatan ini, lihat jawaban ini: stackoverflow.com/a/20475821/1221537
adamdport
21

Ada banyak situasi di mana masuk akal untuk memiliki ListView di ScrollView.

Berikut kode berdasarkan saran DougW ... bekerja dalam sebuah fragmen, membutuhkan lebih sedikit memori.

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();
}

panggil setListViewHeightBasedOnChildren (listview) pada setiap tampilan daftar yang disematkan.

Djunod
sumber
Ini tidak akan berfungsi ketika item daftar memiliki ukuran variabel, yaitu beberapa tampilan teks yang meluas ke beberapa baris. Ada solusi?
Khobaib
Periksa komentar saya di sini - ini memberikan solusi sempurna - stackoverflow.com/a/23315696/1433187
Khobaib
bekerja untuk saya dengan API 19, tetapi tidak dengan API 23: Ini menambah banyak ruang putih di bawah listView.
Keberuntungan
17

ListView sebenarnya sudah mampu mengukur dirinya cukup tinggi untuk menampilkan semua item, tetapi tidak melakukan ini ketika Anda cukup menentukan wrap_content (MeasureSpec.UNSPECIFIED). Ini akan melakukan ini ketika diberi ketinggian dengan MeasureSpec.AT_MOST. Dengan pengetahuan ini, Anda dapat membuat subclass yang sangat sederhana untuk menyelesaikan masalah ini yang bekerja jauh lebih baik daripada solusi yang diposting di atas. Anda harus tetap menggunakan wrap_content dengan subclass ini.

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));
    }
}

Memanipulasi heightMeasureSpec menjadi AT_MOST dengan ukuran yang sangat besar (Integer.MAX_VALUE >> 4) menyebabkan ListView mengukur semua anak-anaknya hingga ketinggian (sangat besar) yang diberikan dan mengatur tingginya.

Ini berfungsi lebih baik daripada solusi lain karena beberapa alasan:

  1. Ini mengukur semuanya dengan benar (padding, pembagi)
  2. Ini mengukur ListView selama pass pengukuran
  3. Karena # 2, ia menangani perubahan lebar atau jumlah item dengan benar tanpa kode tambahan

Pada sisi negatifnya, Anda bisa berpendapat bahwa melakukan ini bergantung pada perilaku tidak berdokumen di SDK yang dapat berubah. Di sisi lain, Anda bisa berpendapat bahwa ini adalah bagaimana wrap_content harus benar-benar berfungsi dengan ListView dan bahwa perilaku wrap_content saat ini baru saja rusak.

Jika Anda khawatir bahwa perilaku tersebut dapat berubah di masa mendatang, Anda cukup menyalin fungsi onMeasure dan fungsi terkait dari ListView.java dan ke dalam subkelas Anda sendiri, lalu buat jalur AT_MOST melalui onMeasure yang dijalankan untuk UNSPECIFIED juga.

Omong-omong, saya percaya bahwa ini adalah pendekatan yang benar-benar valid ketika Anda bekerja dengan sejumlah kecil item daftar. Mungkin tidak efisien jika dibandingkan dengan LinearLayout, tetapi ketika jumlah item kecil, menggunakan LinearLayout adalah optimasi yang tidak perlu dan karenanya kompleksitas yang tidak perlu.

Jason Y
sumber
Juga berfungsi dengan item ketinggian variabel dalam tampilan daftar!
Denny
@Jason Y apa artinya ini, Integer.MAX_VALUE >> 4 artinya ?? ada 4 apa ??
KJEjava48
@Jason Y bagi saya itu bekerja hanya setelah saya mengubah Integer.MAX_VALUE >> 2 menjadi Integer.MAX_VALUE >> 21. Kenapa begitu? Juga berfungsi dengan ScrollView bukan dengan NestedScrollView.
KJEjava48
13

Ada pengaturan bawaan untuk itu. Di ScrollView:

android:fillViewport="true"

Di jawa

mScrollView.setFillViewport(true);

Romain Guy menjelaskannya secara mendalam di sini: http://www.curious-creature.org/2010/08/15/scrollviews-handy-trick/

TalkLittle
sumber
2
Itu bekerja untuk LinearLayout saat ia menunjukkan. Ini tidak berfungsi untuk ListView. Berdasarkan tanggal posting itu, saya membayangkan dia menulisnya untuk memberikan contoh dari alternatifnya, tetapi ini tidak menjawab pertanyaan.
DougW
ini adalah solusi cepat
10

Anda Membuat Custom ListView yang bukan dapat digulir

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();    
    }
}

Di File Sumber Daya Tata Letak Anda

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

Dalam Java File

Buat objek customListview Anda, bukan ListView seperti: NonScrollListView non_scroll_list = (NonScrollListView) findViewById (R.id.lv_nonscroll_list);

Dedaniya HirenKumar
sumber
8

Kami tidak dapat menggunakan dua pengguliran secara simulteniuosly. Kami akan mendapatkan total panjang ListView dan memperluas listview dengan tinggi total. Kemudian kami dapat menambahkan ListView di ScrollView secara langsung atau menggunakan LinearLayout karena ScrollView memiliki satu anak secara langsung. salin metode setListViewHeightBasedOnChildren (lv) dalam kode Anda dan perluas listview maka Anda bisa menggunakan listview di dalam scrollview. \ tata letak file 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>

Metode onCreate di kelas aktivitas:

 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);
      }
Ashish Saini
sumber
Perbaikan yang bagus! Ini berfungsi sesuai kebutuhan. Terima kasih. Dalam kasus saya, saya memiliki banyak pandangan sebelum daftar dan solusi yang sempurna adalah dengan hanya memiliki satu gulir vertikal pada tampilan.
Pepatah
4

Ini adalah kombinasi dari jawaban oleh DougW, Good Guy Greg, dan Paul. Saya menemukan itu semua diperlukan ketika mencoba menggunakan ini dengan adaptor listview khusus dan item daftar non-standar jika tidak, listview merusak aplikasi (juga macet dengan jawaban oleh 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);
    }
Keranjang yang ditinggalkan
sumber
Di mana harus memanggil metode ini?
Dhrupal
Setelah membuat tampilan daftar dan mengatur adaptor.
Abandoned Cart
Ini terlalu lambat pada daftar panjang.
cakan
@cakan Itu juga ditulis 2 tahun sebelumnya sebagai solusi untuk memungkinkan menggunakan komponen UI tertentu bersama-sama yang tidak benar-benar dimaksudkan untuk digabungkan. Anda menambahkan jumlah overhead yang tidak perlu di Android modern, jadi diharapkan daftar akan melambat ketika menjadi besar.
Abandoned Cart
3

Anda tidak boleh meletakkan ListView di ScrollView karena ListView sudah merupakan ScrollView. Jadi itu akan seperti meletakkan ScrollView di ScrollView.

Apa yang ingin Anda capai?

Cheryl Simon
sumber
4
Saya mencoba menyelesaikan memiliki ListView di dalam ScrollView. Saya tidak ingin ListView saya dapat digulir, tetapi tidak ada properti sederhana untuk mengatur myListView.scrollEnabled = false;
DougW
Seperti yang dikatakan Romain Guy, ini bukan tujuan ListView. ListView adalah tampilan yang dioptimalkan untuk menampilkan daftar panjang item, dengan banyak logika rumit untuk melakukan pemuatan malas tampilan, menggunakan kembali tampilan, dll. Semua ini tidak masuk akal jika Anda mengatakan pada ListView untuk tidak menggulir. Jika Anda hanya ingin banyak item dalam kolom vertikal, gunakan LinearLayout.
Cheryl Simon
9
Masalahnya adalah bahwa ada sejumlah solusi yang disediakan ListView, termasuk yang Anda sebutkan. Sebagian besar fungsi yang disederhanakan Adapters adalah tentang manajemen dan kontrol data, bukan optimasi UI. IMO seharusnya ada ListView sederhana, dan yang lebih kompleks yang melakukan semua item daftar menggunakan kembali dan barang-barang.
DougW
2
Sepenuhnya disetujui. Perhatikan bahwa mudah menggunakan Adaptor yang ada dengan LinearLayout, tapi saya ingin semua hal-hal bagus yang disediakan ListView, seperti pembagi, dan saya tidak merasa ingin menerapkan secara manual.
Artem Russakovskii
1
@ArtemRussakovskii Bisakah Anda jelaskan cara mudah Anda untuk mengganti ListView ke LinearLayout dengan Adaptor yang ada?
happyhardik
3

Saya mengkonversi @ DougW Utilitymenjadi C # (digunakan di Xamarin). Berikut ini berfungsi dengan baik untuk item ketinggian tetap dalam daftar, dan sebagian besar akan baik-baik saja, atau setidaknya awal yang baik, jika hanya beberapa item yang sedikit lebih besar dari item standar.

// 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));
        }
    }
}

Terima kasih @DougW, ini membuat saya keluar dari tempat yang sempit ketika saya harus bekerja dengan OtherPeople'sCode. :-)

Phil Ryan
sumber
Satu pertanyaan: Kapan Anda memanggil setListViewHeightBasedOnChildren()metode ini? Karena itu tidak berfungsi sepanjang waktu untuk saya.
Alexandre D.
@AlexandreD Anda memanggil Utility.setListViewHeightBasedOnChildren (yourListView) ketika Anda telah membuat daftar item. yaitu pastikan Anda telah membuat daftar Anda terlebih dahulu! Saya telah menemukan bahwa saya membuat daftar, dan saya bahkan menampilkan barang-barang saya di Console.WriteLine ... tapi entah bagaimana barang-barang itu berada dalam ruang lingkup yang salah. Setelah saya menemukan jawabannya, dan memastikan bahwa saya memiliki ruang lingkup yang tepat untuk daftar saya, ini bekerja seperti pesona.
Phil Ryan
Faktanya adalah daftar saya dibuat di ViewModel saya. Bagaimana saya bisa menunggu bahwa daftar saya dibuat dalam tampilan saya sebelum panggilan ke Utility.setListViewHeightBasedOnChildren (yourListView)?
Alexandre D.
3

Ini adalah satu-satunya hal yang berhasil untuk saya:

pada Lollipop dan seterusnya, Anda dapat menggunakan

yourtListView.setNestedScrollingEnabled(true);

Ini mengaktifkan atau menonaktifkan pengguliran bersarang untuk tampilan ini jika Anda memerlukan kompatibilitas mundur dengan versi OS yang lebih lama Anda harus menggunakan RecyclerView.

JackA
sumber
Ini tidak berpengaruh pada tampilan daftar saya di API 22 di DialogFragment dalam aktivitas AppCompat. Mereka masih runtuh. @ Phil Ryan 's jawabannya bekerja dengan sedikit modifikasi.
Hakan Çelik MK1
3

Sebelumnya itu tidak mungkin. Tetapi dengan dirilisnya pustaka Appcompat baru dan pustaka Desain, ini dapat dicapai.

Anda hanya perlu menggunakan NestedScrollView https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

Saya tidak tahu ini akan bekerja dengan Listview atau tidak tetapi berfungsi dengan RecyclerView.

Cuplikan Kode:

<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>
Shashank Kapsime
sumber
Keren, saya belum mencobanya jadi saya tidak bisa mengkonfirmasi bahwa ListView internal tidak runtuh, tetapi sepertinya itu maksudnya. Sangat sedih tidak melihat Romain Guy di log kesalahan;)
DougW
1
Ini berfungsi dengan recyclerView tetapi tidak listView. Thanx
IgniteCoders
2

hei saya punya masalah serupa. Saya ingin menampilkan tampilan daftar yang tidak menggulir dan saya menemukan bahwa memanipulasi parameter berfungsi tetapi tidak efisien dan akan berperilaku berbeda pada perangkat yang berbeda .. sebagai hasilnya, ini adalah bagian dari kode jadwal saya yang sebenarnya melakukan ini dengan sangat efisien .

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);
   }
}

Catatan: jika Anda menggunakan ini, notifyDataSetChanged();tidak akan berfungsi sebagaimana dimaksud karena tampilan tidak akan digambar ulang. Lakukan ini jika Anda perlu bekerja

adapter.registerDataSetObserver(new DataSetObserver() {

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

            }

        });
PSchuette
sumber
2

Ada dua masalah saat menggunakan ListView di dalam ScrollView.

1- ListView harus sepenuhnya diperluas hingga ketinggian anak-anaknya. ListView ini menyelesaikan ini:

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));
    }
}

Tinggi pembagi harus 0, gunakan bantalan dalam baris sebagai gantinya.

2- ListView mengkonsumsi acara sentuh sehingga ScrollView tidak dapat digulir seperti biasa. ScrollView ini menyelesaikan masalah ini:

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);
    }
}

Ini adalah cara terbaik yang saya temukan untuk melakukan trik!

Ali
sumber
2

Solusi yang saya gunakan adalah, untuk menambahkan semua Konten ScrollView (apa yang harus di atas dan di bawah listView) sebagai headerView dan footerView di ListView.

Jadi berfungsi seperti itu, juga konversi diatur ulang bagaimana seharusnya.

brokedid
sumber
1

terima kasih untuk kode Vinay di sini adalah kode saya ketika Anda tidak dapat memiliki tampilan daftar di dalam scrollview namun Anda memerlukan sesuatu seperti itu

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 tetap berada di dalam ScrollView sehingga semuanya menjadi scrollable :)

maksimal4ever
sumber
1

Berikut ini adalah modifikasi kecil pada @djunod 's jawaban yang saya butuhkan untuk membuatnya bekerja dengan sempurna:

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();
}
Eng.Fouad
sumber
: Hai, jawaban Anda berfungsi sebagian besar waktu, tetapi tidak berfungsi jika tampilan daftar memiliki tampilan tajuk dan tampilan kaki. Bisakah Anda memeriksanya?
hguser
Saya butuh solusi ketika daftar-item berukuran variabel, yaitu beberapa tampilan teks yang diperluas beberapa baris. Ada saran?
Khobaib
1

coba ini, ini bekerja untuk saya, saya lupa di mana saya menemukannya, di suatu tempat di stack overflow, saya di sini bukan untuk menjelaskan mengapa itu tidak berhasil, tapi ini jawabannya :).

    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);

EDIT !, Saya akhirnya menemukan di mana saya mendapatkan kode. di sini! : ListView di dalam ScrollView tidak menggulir di Android

Bhimbim
sumber
Ini mencakup bagaimana mencegah kehilangan interaksi scrollview, tetapi pertanyaannya adalah tentang menjaga ketinggian tampilan daftar.
Abandoned Cart
1

Meskipun metode setListViewHeightBasedOnChildren () yang disarankan bekerja di sebagian besar kasus, dalam beberapa kasus, khususnya dengan banyak item, saya perhatikan bahwa elemen terakhir tidak ditampilkan. Jadi saya memutuskan untuk meniru versi sederhana dari perilaku ListView untuk menggunakan kembali kode Adaptor, ini dia alternatif 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);

        }
    }
}
}
Alécio Carvalho
sumber
1

Semua jawaban ini salah !!! Jika Anda mencoba untuk menempatkan tampilan daftar dalam tampilan gulir, Anda harus memikirkan kembali desain Anda. Anda mencoba untuk meletakkan ScrollView di dalam ScrollView. Mengganggu daftar akan merusak kinerja daftar. Itu dirancang untuk menjadi seperti ini oleh Android.

Jika Anda benar-benar ingin daftar berada dalam gulir yang sama dengan elemen lainnya, yang harus Anda lakukan adalah menambahkan item lain ke bagian atas daftar menggunakan pernyataan sakelar sederhana di adaptor Anda:

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;
    }


}

Adaptor daftar dapat menangani semuanya karena hanya membuat apa yang terlihat.

TacoEater
sumber
0

Seluruh masalah ini hanya akan hilang jika LinearLayout memiliki metode setAdapter, karena saat itu ketika Anda memberi tahu seseorang untuk menggunakannya, alternatifnya adalah sepele.

Jika Anda benar-benar menginginkan scroll ListView di dalam tampilan scroll lain, ini tidak akan membantu, tetapi jika tidak, ini setidaknya akan memberi Anda ide.

Anda perlu membuat adaptor khusus untuk menggabungkan semua konten yang ingin Anda gulir dan mengatur adaptor ListView itu.

Saya tidak memiliki kode sampel yang berguna, tetapi jika Anda menginginkan sesuatu seperti.

<ListView/>

(other content)

<ListView/>

Maka Anda perlu membuat adaptor yang mewakili semua konten itu. ListView / Adapters cukup pintar untuk menangani berbagai jenis juga, tetapi Anda harus menulis sendiri adaptor.

Android UI API tidak setua apa pun yang ada di luar sana, sehingga tidak memiliki kualitas yang sama dengan platform lainnya. Juga, ketika melakukan sesuatu di android Anda harus berada dalam pola pikir android (unix) di mana Anda berharap untuk melakukan apa pun yang Anda mungkin harus merakit fungsi bagian-bagian yang lebih kecil dan menulis banyak kode Anda sendiri untuk mendapatkannya kerja.

majinnaibu
sumber
0

Ketika kita menempatkan ListViewdi dalam ScrollViewdua masalah muncul. Salah satunya adalah ScrollViewmengukur anak-anaknya dalam mode UNSPECIFIED, jadi ListViewatur sendiri tingginya untuk mengakomodasi hanya satu item (saya tidak tahu mengapa), yang lain ScrollViewmencegat acara sentuh sehinggaListView tidak menggulir.

Tetapi kita dapat menempatkan ListViewdi dalam ScrollViewdengan beberapa solusi. Posting ini , oleh saya, menjelaskan solusinya. Dengan solusi ini, kami juga dapat mempertahankan ListViewfitur daur ulang.

Durgadass S
sumber
0

Alih-alih menempatkan tampilan daftar di dalam Scrollview, taruh sisa konten antara tampilan daftar dan pembukaan Scrollview sebagai tampilan terpisah dan mengatur tampilan itu sebagai header dari tampilan daftar. Jadi akhirnya Anda hanya akan berakhir dengan tampilan daftar yang mengambil alih Scroll.

Saksham
sumber
0

Perpustakaan ini adalah solusi termudah dan tercepat untuk masalah ini.

Kashif Nazar
sumber
-1

Ini adalah versi kode saya yang menghitung tinggi total tampilan daftar. Yang ini bekerja untuk saya:

   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();
}
forforum saya
sumber