ListView daraltılmadan bir ScrollView içine nasıl koyabilirim?


351

Bu soruna çözümler aradım ve bulabildiğim tek cevap " ScrollView içine bir ListView koymayın " gibi görünüyor . Neden olsa bunun için gerçek bir açıklama görmedim. Bulmamın tek nedeni, Google'ın bunu yapmak istediğinizi düşünmemesi. Ben de öyle yaptım.

Yani soru şu, bir ListView bir ScrollView içine minimum yüksekliğine daraltmadan nasıl yerleştirebilirsiniz?


11
Çünkü bir cihaz dokunmatik ekran kullandığında, iki iç içe kaydırılabilir kap arasında ayrım yapmanın iyi bir yolu yoktur. Bir kapsayıcıyı kaydırmak için kaydırma çubuğunu kullandığınız geleneksel masaüstlerinde çalışır.
Romain Guy

57
Elbette var. İç kısmın içine dokunurlarsa, kaydırın. İç kısım çok büyükse, bu kötü UI tasarımıdır. Bu, genel olarak kötü bir fikir olduğu anlamına gelmez.
DougW

5
Sadece benim Tablet uygulaması için tökezledi. Smartphone'da çok daha kötü görünmese de, bu, kullanıcının dış veya iç ScrollView'u kaydırmak isteyip istemediğine kolayca karar verebileceğiniz bir tablette korkunç. @DougW: Korkunç tasarım, katılıyorum.
denizanası

4
@Romain - bu oldukça eski bir yazı, bu yüzden buradaki endişelerin zaten ele alınıp alınmadığını veya ScrollView'ın (veya manuel olarak kodlamayı içermeyen bir alternatifin) bir ListView kullanmanın hala iyi bir yolu olmadığını merak ediyorum ListView tüm nitelikleri LinearLayout içine)?
Jim

3
@Jim: "veya bir ListView öğesinin tüm niteliklerini bir LinearLayout'a elle kodlamayı içermeyen bir alternatif" - her şeyi içine yerleştirin ListView. ListViewbirden çok satır stiline ek olarak seçilemeyen satırlara sahip olabilir.
CommonsWare

Yanıtlar:


196

ListViewKaydırma yapmamak için a kullanmak son derece pahalıdır ve tüm amacına aykırıdır ListView. Bunu YAPMAMALISINIZ . Bunun LinearLayoutyerine sadece bir kullanın.


69
Son 2 ya da 3 yıldır ListView'den sorumlu olduğumdan beri kaynak ben olurdu :) ListView adaptör kullanımını optimize etmek için çok fazla iş yapar. Bu sorunu gidermeye çalışmak yine de ListView'in bir LinearLayout'un yapması gerekmeyen çok fazla iş yapmasına neden olacaktır. Detaylara girmeyeceğim, çünkü burada ListView uygulamasını açıklamak için yeterli alan yok. Bu aynı zamanda ListView'in dokunma olaylarıyla ilgili davranış biçimini de çözmez. Ayrıca, hack'iniz bugün çalışıyorsa, gelecekteki bir sürümde çalışmaya devam edeceğine dair bir garanti yoktur. Bir LinearLayout kullanmak pek işe yaramaz.
Romain Guy

7
Bu makul bir yanıt. Bazı geri bildirimleri paylaşabilirsem, çok daha önemli bir iPhone deneyimim var ve Apple'ın belgelerinin performans özellikleri ve bunun gibi kullanım durumları (veya anti-desenler) ile ilgili çok daha iyi yazıldığını söyleyebilirim. Genel olarak, Android belgeleri çok daha dağılmış ve daha az odaklanmıştır. Bunun arkasında bazı nedenler olduğunu anlıyorum, ama bu uzun bir tartışma, bu yüzden sohbet etmeye mecbur hissediyorsanız bana bildirin.
DougW

6
Bir Bağdaştırıcıdan bir LinearLayout oluşturan statik bir yöntemi kolayca yazabilirsiniz.
Romain Guy

125
Bu bir cevap DEĞİLDİR How can I put a ListView into a ScrollView without it collapsing?... Bunu yapmamanız gerektiğini söyleyebilirsiniz, ancak nasıl yapılacağına da bir cevap verin, bu iyi bir cevap. Bir ListView'ın kaydırma dışında bazı harika özelliklere sahip olduğunu biliyorsunuz, LinearLayout'un sahip olmadığı özellikler.
Jorge Fuentes González

44
ListView + diğer Görünümler içeren bir bölgeniz varsa ve her şeyin bir bölge olarak kaydırılmasını istiyorsanız ne olur? Bu, bir ScrollView içine bir ListView yerleştirmek için makul bir kullanım örneği gibi görünüyor. ListView bir LinearLayout ile değiştirmek bir çözüm değildir, çünkü o zaman Adapters kullanamazsınız.
Barry Fruitman

262

İşte benim çözümüm. Android platformunda oldukça yeniyim ve özellikle .measure'u doğrudan çağırmak ve LayoutParams.heightmülkü doğrudan ayarlamakla ilgili kısımda bunun biraz hack olduğundan eminim , ama işe yarıyor.

Tek yapmanız gereken aramak Utility.setListViewHeightBasedOnChildren(yourListView)ve öğelerinin yüksekliğini tam olarak karşılamak için yeniden boyutlandırılacak.

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
Çok pahalı bir LinearLayout'u yeniden yarattınız :)
Romain Guy

82
Bir LinearLayouta'nın tüm doğal inceliklerine sahip olmadıkça ListView- bölücüler, üstbilgi / altbilgi görünümleri, telefonun kullanıcı arayüzü tema renkleriyle eşleşen bir liste seçici vb. iç kabın sonuna kadar ve sonra dış kabı kaydırmak için dokunma olaylarını durdurmayı bırakın. Kaydırma tekerleğinizi kullanırken her masaüstü tarayıcısı bunu yapar. Eğer topunu kullanarak navigasyon yapıyorsanız Android'in kendisi iç içe kaydırmayı kaldırabilir, o zaman neden dokunmuyorsunuz?
Neil Traft

29
listItem.measure(0,0)listItem bir ViewGroup örneğiyse bir NPE atar. Daha önce aşağıdakileri ekledim listItem.measure:if (listItem instanceof ViewGroup) listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
Siklab.ph

4
0'dan başka dolgu ile ListViews için düzeltme:int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
Paul

8
Ne yazık ki bu yöntem ListViewdeğişken yükseklikte öğeler içerebildiğinde biraz kusurludur . Çağrısı, getMeasuredHeight()yalnızca gerçek yüksekliğin aksine hedeflenen minimum yüksekliği döndürür. Onun getHeight()yerine kullanmayı denedim , ama bu 0 döndürür. Herkes bu yaklaşmak nasıl bir fikir var mı?
Matt Huggins,

89

Bu kesinlikle işe yarayacaktır ............
Sadece <ScrollView ></ScrollView>düzen XML dosyanızı bu Custom ScrollViewgibi ile değiştirmeniz gerekir<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
Bu çok iyi. Ayrıca, ScrollView içine bir Google Haritalar Parçası yerleştirmeyi mümkün kılar ve yine de yakınlaştırma / uzaklaştırma işlevi görür. Teşekkürler.
Magnus Johansson

Bu yaklaşımla ilgili herhangi bir eksileri fark eden var mı? Korkarım çok basit: o
Marija Milosevic

1
Güzel bir çözüm, kabul edilen cevap +1 olmalı! Aşağıdaki cevapla (djunod'dan) karıştırdım ve bu harika çalışıyor!
tanou

1
Bu benim için aynı anda çalışan çocuklar üzerinde kaydırma VE TIKLA yapan tek düzeltme. Büyük teşekkürler
pellyadolfo

25

Koyarak Insted ListViewbir iç ScrollViewkullanabileceğimiz ListViewbir şekilde ScrollView. ListViewİçinde olması gereken şeyler içine konabilir ListView. Üstündeki ve altındaki diğer düzenlerListView üstbilgi ve altbilgisine mizanpajlar eklenerek konabilir ListView. Böylece tüm ListViewsize kaydırma deneyimi verecektir.


3
Bu en zarif ve uygun cevap imho. Bu yaklaşımla ilgili daha fazla ayrıntı için şu yanıta bakın: stackoverflow.com/a/20475821/1221537
adamdport

21

Orada bol o ListView en bir scrollview olmasını anlamda bir çok yapar durumların.

İşte DougW'un önerisine dayanan kod ... bir parça halinde çalışıyor, daha az bellek alıyor.

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

Her gömülü liste görünümünde setListViewHeightBasedOnChildren (listview) öğesini çağırın.


Liste öğeleri değişken boyutlarda olduğunda, yani birden fazla satırda genişleyen bazı metin görünümlerinde çalışmaz. Herhangi bir çözüm?
Khobaib


benim için API 19 ile çalıştı, ancak API 23 ile değil: Burada listView altına çok fazla boşluk ekliyor.
Lucker10

17

ListView aslında kendisini tüm öğeleri görüntüleyecek kadar uzun olacak şekilde ölçebiliyor, ancak wrap_content'i (MeasureSpec.UNSPECIFIED) belirttiğinizde bunu yapmıyor. MeasureSpec.AT_MOST ile bir yükseklik verildiğinde bunu yapar. Bu bilgi ile, yukarıda yayınlanan çözümlerden çok daha iyi çalışan bu sorunu çözmek için çok basit bir alt sınıf oluşturabilirsiniz. Yine de bu alt sınıfla wrap_content kullanmalısınız.

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 öğesinin çok büyük boyutlu (Integer.MAX_VALUE >> 4) AT_MOST olması, ListView öğesinin tüm çocuklarını belirli (çok büyük) yüksekliğe kadar ölçmesine ve yüksekliğini buna göre ayarlamasına neden olur.

Bu, birkaç nedenden ötürü diğer çözümlerden daha iyi çalışır:

  1. Her şeyi doğru ölçer (dolgu, bölücüler)
  2. Ölçüm geçişi sırasında ListView'i ölçer
  3. # 2 nedeniyle, herhangi bir ek kod olmadan genişlik veya öğe sayısındaki değişiklikleri doğru şekilde işler

Dezavantajlı olarak, bunu yapmanın SDK'daki değişebilecek davranışsız davranışlara dayandığını iddia edebilirsiniz. Öte yandan, wrap_content'ın ListView ile gerçekten nasıl çalışması gerektiğini ve mevcut wrap_content davranışının sadece bozulduğunu iddia edebilirsiniz.

Davranışın ileride değişebileceğinden endişe ediyorsanız, onMeasure işlevini ve ilgili işlevleri ListView.java'dan ve kendi alt sınıfınıza kopyalamanız, ardından ONMMOST yolundaki AT_MOST yolunu UNSPECIFIED için de çalıştırmanız gerekir.

Bu arada, az sayıda liste öğesiyle çalışırken bunun tamamen geçerli bir yaklaşım olduğuna inanıyorum. LinearLayout ile karşılaştırıldığında verimsiz olabilir, ancak öğe sayısı az olduğunda, LinearLayout'u kullanmak gereksiz optimizasyon ve dolayısıyla gereksiz karmaşıklıktır.


Ayrıca liste görünümünde değişken yükseklik öğeleri ile çalışır!
Denny

@ Jason Y bu ne demek, Integer.MAX_VALUE >> 4 ne demek? orada 4 nedir ??
KJEjava48

@Jason Y benim için sadece Integer.MAX_VALUE >> 2'yi Integer.MAX_VALUE >> 21 olarak değiştirdikten sonra çalıştı. Neden böyle? Ayrıca ScrollView ile çalışır NestedScrollView ile değil.
KJEjava48

13

Bunun için yerleşik bir ayar var. ScrollView'da:

android:fillViewport="true"

Java'da,

mScrollView.setFillViewport(true);

Romain Guy burada derinlemesine açıklıyor: http://www.curious-creature.org/2010/08/15/scrollviews-handy-trick/


2
Bu gösterdiği gibi bir LinearLayout için işe yarıyor. ListView için çalışmaz. Bu gönderinin tarihine dayanarak, alternatifine bir örnek sunmak için yazdığını hayal ediyorum, ancak bu soruya cevap vermiyor.
DougW

Bu hızlı bir çözümdür

10

Kaydırılamayan Özel ListView Oluşturdunuz

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

Mizanpaj Kaynakları Dosyanızda

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

Java Dosyasında

ListView yerine customListview öğenizin bir nesnesini oluşturun: NonScrollListView non_scroll_list = (NonScrollListView) findViewById (R.id.lv_nonscroll_list);


8

İki kaydırma simulteniuosly kullanamadık. ListView toplam uzunluğunu elde edeceğiz ve listview'i toplam yüksekliğe genişleteceğiz.O zamandan sonra ScrollView'a doğrudan veya LinearLayout kullanarak ListView ekleyebiliriz çünkü ScrollView doğrudan bir çocuğa sahiptir. setListViewHeightBasedOnChildren (lv) yöntemini kodunuzda kopyalayın ve listview'i genişletin, sonra scrollview içinde listview kullanabilirsiniz. \ layout xml dosyası

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

Activity sınıfında onCreate yöntemi:

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

İyi düzeltme! Gerektiği gibi çalışır. Teşekkürler. Benim durumumda listeden önce bir sürü görüşüm var ve mükemmel çözüm görünümde sadece bir dikey kaydırma yapmaktır.
Proverbio

4

Bu DougW, Good Guy Greg ve Paul'un cevaplarının bir kombinasyonudur. Tüm bu özel bir listview adaptörü ve standart dışı liste öğeleri ile kullanmaya çalışırken gerekli olduğunu bulundu aksi takdirde listview (aynı zamanda Nex cevap ile çöktü) uygulama çöktü:

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

Bu yöntemi nereye çağırmalı?
Dhrupal

Liste görünümünü oluşturduktan ve adaptörü ayarladıktan sonra.
Terk Edilmiş Sepet

Bu uzun listelerde çok yavaşlıyor.
cakan

@cakan Ayrıca, gerçekten birleştirilmesi gerekmeyen belirli UI bileşenlerinin birlikte kullanılmasına izin vermek için 2 yıl önce bir geçici çözüm olarak yazılmıştır. Modern Android'de gereksiz miktarda ek yük ekliyorsunuz, bu nedenle listenin büyüdüğünde yavaşlaması bekleniyor.
Terk Edilmiş Sepet

3

Bir liste görünümü zaten çünkü bir scrollview bir ListView koymamaliydiniz olan bir scrollview. Bu bir ScrollView bir ScrollView koymak gibi olurdu.

Ne yapmaya çalışıyorsun?


4
Bir ScrollView içinde bir ListView sahip başarmak çalışıyorum. ListView kaydırılabilir olmasını istemiyorum, ancak myListView.scrollEnabled = false ayarlamak için basit bir özellik yoktur;
DougW

Romain Guy'ın dediği gibi, bu bir ListView'in amacı değildir. ListView, görünümlerin tembel olarak yüklenmesi, görünümlerin yeniden kullanılması vb. İçin çok sayıda karmaşık mantığa sahip, uzun bir öğe listesi görüntülemek için optimize edilmiş bir görünümdür. ListView'a kaydırmamasını söylüyorsanız bunların hiçbiri anlamlı değildir. Dikey bir sütunda yalnızca bir grup öğe istiyorsanız, LinearLayout kullanın.
Cheryl Simon

9
Sorun, bir ListView, sizin de dahil olmak üzere sunduğu bir dizi çözüm olmasıdır. Adaptörlerin basitleştirdiği işlevselliklerin çoğu, UI optimizasyonu değil, veri yönetimi ve kontrolü ile ilgilidir. IMO basit bir ListView ve tüm liste öğesinin yeniden kullanımı ve şeyler yapan daha karmaşık bir olması gerekirdi.
DougW

2
Kesinlikle katılıyorum. Varolan bir bağdaştırıcıyı LinearLayout ile kullanmanın kolay olduğunu, ancak ListView'ın bölücüler gibi sağladığı tüm güzel şeyleri istediğimi ve manuel olarak uygulamak istemediğimi unutmayın.
Artem Russakovskii

1
@ArtemRussakovskii ListView'i LinearLayout'a mevcut bir Adaptörle değiştirmenin kolay yolunu açıklayabilir misiniz?
happyhardik

3

@ DougW en Camar Utility(Xamarin kullanılan) dönüştürdüm. Aşağıdaki, listedeki sabit yükseklikte öğeler için iyi çalışır ve yalnızca bazı öğeler standart öğeden biraz daha büyükse, çoğunlukla iyi veya en azından iyi bir başlangıç ​​olacaktır.

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

Teşekkürler @ DougW, OtherPeople'sCode ile çalışmak zorunda kaldığımda bu beni dar bir noktadan kurtardı. :-)


Bir soru: setListViewHeightBasedOnChildren()Yöntemi ne zaman çağırdınız ? Çünkü benim için her zaman işe yaramıyor.
Alexandre

@AlexandreD, öğe listesini oluşturduğunuzda Utility.setListViewHeightBasedOnChildren'i (yourListView) çağırırsınız. yani önce listenizi oluşturduğunuzdan emin olun! Bir liste yaptığımı fark etmiştim ve eşyalarımı bile Console.WriteLine'da gösteriyordum ... ama bir şekilde öğeler yanlış kapsamdaydı. Bir kez anladım ve listem için doğru kapsama sahip olduğumdan emin olduktan sonra, bu bir cazibe gibi çalıştı.
Phil Ryan

Gerçek şu ki, listelerim ViewModel'de oluşturuldu. Utility.setListViewHeightBasedOnChildren'e (yourListView) çağrı yapmadan önce listelerimin görünümümde oluşturulmasını nasıl bekleyebilirim?
Alexandre

3

Benim için işe yarayan tek şey bu:

Lollipop ve sonrasında

yourtListView.setNestedScrollingEnabled(true);

Bu işletim sisteminin eski sürümüyle geriye dönük uyumluluğa ihtiyacınız varsa, bu görünüm için iç içe kaydırmayı etkinleştirin veya devre dışı bırakın RecyclerView kullanmanız gerekir.


Bunun, bir AppCompat etkinliğindeki DialogFragment içindeki API 22 üzerindeki liste görünümlerim üzerinde hiçbir etkisi olmadı. Hala çöküyorlar. @Phil Ryan'ın cevabı biraz değişiklikle çalıştı.
Hakan Çelik MK1

3

Daha önce mümkün değildi. Ancak yeni Appcompat kütüphaneleri ve Design kütüphaneleri piyasaya sürüldüğünde, bu başarılabilir.

Sadece NestedScrollView kullanmanız gerekir https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

Listview ile çalışacağının farkında değilim ama RecyclerView ile çalışıyor.

Kod Parçacığı:

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

Güzel, ben denemedim, bu yüzden bir iç ListView daralmadığını teyit edemez, ama niyet olabilir gibi görünüyor. Romain Guy'ı suçlama günlüğünde görmekten çok üzüldü;)
DougW

1
Bu, recyclerView ile çalışır ancak listView ile çalışmaz. Thanx
IgniteCoders

2

hey benzer bir sorunum vardı. Kaydırma yapmayan bir liste görünümü görüntülemek istedim ve parametrelerin değiştirilmesinin işe yaradığını, ancak verimsiz olduğunu ve farklı cihazlarda farklı davranacağını buldum. Sonuç olarak, bu benim program kodumun bir parçası. .

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

Not: Bunu kullanırsanız notifyDataSetChanged();, görünümler yeniden çizilmeyeceği için amaçlandığı gibi çalışmaz. Etrafında bir çalışmaya ihtiyacınız varsa bunu yapın

adapter.registerDataSetObserver(new DataSetObserver() {

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

            }

        });

2

Bir ScrollView içinde bir ListView kullanırken iki sorun vardır.

1- ListView çocuklarının boyuna tam olarak genişlemelidir. Bu ListView bunu giderir:

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

Bölücü yüksekliği 0 olmalıdır, bunun yerine satırlar halinde dolgu kullanın.

2- ListView dokunma olaylarını tüketir, böylece ScrollView her zamanki gibi kaydırılamaz. Bu ScrollView bu sorunu giderir:

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

Hile yapmanın en iyi yolu bu!


2

Kullandığım bir çözüm, ScrollView öğesinin tüm İçeriğini (listView'in üstünde ve altında olması gerekenler) listView içinde headerView ve footerView olarak eklemektir.

Bu yüzden çalışır, ayrıca convertview nasıl olması gerektiğine de karar verilir.


1

Vinay'ın kodu sayesinde, bir kaydırma görünümünde bir liste görünümüne sahip olamadığın için benim kodum var, ancak böyle bir şeye ihtiyacın var

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 bir ScrollView içinde kalır, böylece her şey kaydırılabilir hale gelir :)


1

İşte üzerinde küçük bir değişiklik olduğunu @djunod 'ın cevabı ben mükemmel çalışması gerektiğini:

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

: Merhaba, cevabınız çoğu zaman çalışır, ancak liste görünümünün başlık görünümü ve ayak görünümü varsa işe yaramaz. Kontrol edebilir misin?
hguser

Liste öğeleri değişken boyutları, yani birden çok satır üzerinde genişleyen bazı textview olduğunda bir çözüm gerekir. Herhangi bir öneri?
Khobaib

1

bunu deneyin, bu benim için çalışıyor, nerede bulduğumu unuttum, yığın taşması bir yerde, ben neden çalışmıyor açıklamak için burada değilim, ama bu cevap :).

    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 !, sonunda kodu nereden buldum. buraya ! : ScrollView içindeki ListView Android'de kaydırma yapmıyor


Bu, kaydırma görünümü etkileşiminin kaybedilmesinin nasıl önleneceğini kapsar, ancak soru liste görünümü yüksekliğini korumakla ilgilidir.
Terk Edilmiş Sepet

1

Önerilen setListViewHeightBasedOnChildren () yöntemleri çoğu durumda çalışsa da , bazı durumlarda, özellikle çok sayıda öğe ile, son öğelerin görüntülenmediğini fark ettim. Bu nedenle , herhangi bir Bağdaştırıcı kodunu yeniden kullanmak için ListView davranışının basit bir sürümünü taklit etmeye karar verdim , işte ListView alternatifi:

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

Tüm bu cevaplar yanlış !!! Kaydırma görünümüne bir liste görünümü koymaya çalışıyorsanız, tasarımınızı yeniden düşünmelisiniz. Bir ScrollView içine bir ScrollView koymaya çalışıyorsunuz. Listeye müdahale etmek liste performansına zarar verecektir. Android tarafından böyle olacak şekilde tasarlanmıştır.

Listenin diğer öğelerle aynı kaydırmada olmasını istiyorsanız, tek yapmanız gereken bağdaştırıcınızdaki basit bir anahtar deyimini kullanarak diğer öğeleri listenin en üstüne eklemektir:

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


}

Liste bağdaştırıcısı her şeyi işleyebilir, çünkü yalnızca görünür olanı işler.


0

LinearLayout bir setAdapter yöntemine sahip olsaydı bu sorun ortadan kalkacaktı, çünkü birisine bunu kullanmasını söylediğinizde alternatif önemsiz olurdu.

Aslında başka bir kaydırma görünümünde kayan bir ListView istiyorsanız, bu yardımcı olmaz, ancak aksi takdirde bu size en azından bir fikir verecektir.

Kaydırmak istediğiniz tüm içeriği birleştirmek ve ListView'ın adaptörünü buna ayarlamak için özel bir adaptör oluşturmanız gerekir.

Örnek kod kullanışlı değil, ama bir şey istiyorsanız.

<ListView/>

(other content)

<ListView/>

Ardından, tüm içeriği temsil eden bir bağdaştırıcı oluşturmanız gerekir. ListView / Bağdaştırıcılar farklı türleri işlemek için yeterince akıllıdır, ancak bağdaştırıcıyı kendiniz yazmanız gerekir.

Android UI API sadece diğer her şey kadar olgun değil, bu yüzden diğer platformlarla aynı inceliklere sahip değil. Ayrıca, android üzerinde bir şey yaparken, muhtemelen daha küçük parçaların işlevselliğini bir araya getirmek ve almak için kendi kodunuzun bir demet yazmak zorunda kalacak bir şey yapmak için bekliyoruz bir android (unix) zihniyet içinde olması gerekir. iş.


0

Biz yerleştirdiğinizde ListViewiçeride ScrollViewiki sorun ortaya çıkar. Biri ScrollViewçocuklarını BELİRTİLMEYEN modda ölçer, bu yüzden ListViewkendi yüksekliğini sadece bir öğeyi ScrollViewalacak şekilde ayarlar (nedenini bilmiyorum), diğeri dokunmatik olayı engelliyorListView kaydırmaz.

Ama biz olabilir yerleştirmek ListViewiçini ScrollViewbazı geçici çözüm. Bu yazı benim tarafımdan geçici çözümü açıklıyor. Bu geçici çözüm ListViewsayesinde, geri dönüşüm özelliğini de koruyabiliriz .


0

Liste görünümünü Scrollview içine koymak yerine, içeriğin geri kalanını liste görünümü ile Scrollview'in açılması arasında ayrı bir görünüm olarak yerleştirin ve bu görünümü liste görünümünün başlığı olarak ayarlayın. Böylece, sonunda Scroll'un sorumluluğunu alarak yalnızca liste görünümü elde edersiniz.



-1

İşte liste görünümünün toplam yüksekliğini hesaplayan kodun sürümü. Bu benim için çalışıyor:

   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();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.