Bir ScrollView düzeni içinde bir LinearLayout doldurmak için ListAdapter kullanma


95

Çok yaygın bir sorunla karşı karşıyayım: Bir etkinlik düzenledim ve şimdi bunun içinde birkaç öğe göstermesi gerektiği ortaya çıktı ScrollView. Bunu yapmanın normal yolu, var olanı kullanmak, ListAdapteronu bir'e bağlamak ListViewve BOOM'a öğeler listeme sahip olmaktır .

ANCAK Sen iç içe geçmiş bir yer olmamalıdır ListViewbir de ScrollViewhatta Android Lint bu konuda şikayet - kaydırma yukarı o vida gibi.

İşte sorum şu:

ListAdapterA LinearLayoutveya benzeri bir şeye nasıl bağlanırım ?

Bu çözümün pek çok öğe için ölçeklenmeyeceğini biliyorum, ancak listelerim çok kısa (<10 öğe), bu nedenle görünümlerin yeniden kullanılması gerçekten gerekli değil. Performans açısından tüm görünümleri doğrudan LinearLayout.

Bulduğum çözümlerden biri, mevcut etkinlik düzenimi .NET Framework'ün headerView bölümüne yerleştirmek olabilir ListView. Ama bu, bu mekanizmayı kötüye kullanmak gibi geliyor, bu yüzden daha temiz bir çözüm arıyorum.

Fikirler?

GÜNCELLEME: Doğru yöne ilham vermek için sorunumu göstermek için örnek bir düzen ekliyorum:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/news_detail_layout"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical"
              android:visibility="visible">


    <ScrollView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#FFF"
            >

        <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:orientation="vertical"
                android:paddingLeft="@dimen/news_detail_layout_side_padding"
                android:paddingRight="@dimen/news_detail_layout_side_padding"
                android:paddingTop="@dimen/news_detail_layout_vertical_padding"
                android:paddingBottom="@dimen/news_detail_layout_vertical_padding"
                >

            <TextView
                    android:id="@+id/news_detail_date"
                    android:layout_height="wrap_content"
                    android:layout_width="fill_parent"
                    android:gravity="center_horizontal"
                    android:text="LALALA"
                    android:textSize="@dimen/news_detail_date_height"
                    android:textColor="@color/font_black"
                    />

            <Gallery
                    android:id="@+id/news_detail_image"
                    android:layout_height="wrap_content"
                    android:layout_width="fill_parent"
                    android:paddingTop="5dip"
                    android:paddingBottom="5dip"
                    />

            <TextView
                    android:id="@+id/news_detail_headline"
                    android:layout_height="wrap_content"
                    android:layout_width="fill_parent"
                    android:gravity="center_horizontal"
                    android:text="Some awesome headline"
                    android:textSize="@dimen/news_detail_headline_height"
                    android:textColor="@color/font_black"
                    android:paddingTop="@dimen/news_detail_headline_paddingTop"
                    android:paddingBottom="@dimen/news_detail_headline_paddingBottom"
                    />

            <TextView
                    android:id="@+id/news_detail_content"
                    android:layout_height="wrap_content"
                    android:layout_width="fill_parent"
                    android:text="Here comes a lot of text so the scrollview is really needed."
                    android:textSize="@dimen/news_detail_content_height"
                    android:textColor="@color/font_black"
                    />

            <!---
                HERE I NEED THE LIST OF ITEMS PROVIDED BY THE EXISTING ADAPTER. 
                They should be positioned at the end of the content, so making the scrollview smaller is not an option.
            ---->                        

        </LinearLayout>
    </ScrollView>
</LinearLayout>

GÜNCELLEME 2 Anlaşılmasını kolaylaştırmak için başlığı değiştirdim (olumsuz oy aldım, doh!).


1
Bu soruna hiç güzel ve temiz bir çözüm buldunuz mu? Google'ın bunu oyun mağazasında incelemeler için kullandığını görüyorum. Bunu nasıl yaptıklarını bilen var mı?
Zapnologica

Yanıtlar:


96

Muhtemelen öğelerinizi şuraya manuel olarak eklemelisiniz LinearLayout:

LinearLayout layout = ... // Your linear layout.
ListAdapter adapter = ... // Your adapter.

final int adapterCount = adapter.getCount();

for (int i = 0; i < adapterCount; i++) {
  View item = adapter.getView(i, null, null);
  layout.addView(item);
}

DÜZENLEME : Yaklaşık 200 önemsiz olmayan liste öğesini görüntülemem gerektiğinde bu yaklaşımı reddettim, çok yavaş - Nexus 4'ün "listemi" görüntülemek için yaklaşık 2 saniyeye ihtiyacı vardı, bu kabul edilemezdi. Bu yüzden Flo'nun başlıklarla yaklaşımına döndüm. Çok daha hızlı çalışır çünkü liste görünümleri, görünüm oluşturulduğunda değil, kullanıcı kaydırdığında istek üzerine oluşturulur.

Özgeçmiş: Görünümlerin düzene manuel olarak eklenmesinin kodlanması daha kolaydır (bu nedenle potansiyel olarak daha az hareketli parça ve hatalar), ancak performans sorunları yaşanır, bu nedenle 50 veya daha fazla görünümünüz varsa, başlık yaklaşımını kullanmanızı öneririm.

Misal. Temel olarak etkinlik (veya parça) düzeni böyle bir şeye dönüşür (artık ScrollView gerekmez):

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/my_top_layout"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"/>

Sonra onCreateView()(bir parçalı bir örnek kullanacağım) bölümünde bir başlık görünümü eklemeniz ve ardından bir bağdaştırıcı ayarlamanız gerekir (üstbilgi kaynağı kimliğinin şöyle olduğunu varsayıyorum header_layout):

ListView listView = (ListView) inflater.inflate(R.layout.my_top_layout, container, false);
View header = inflater.inflate(R.layout.header_layout, null);
// Initialize your header here.
listView.addHeaderView(header, null, false);

BaseAdapter adapter = // ... Initialize your adapter.
listView.setAdapter(adapter);

// Just as a bonus - if you want to do something with your list items:
view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    // You can just use listView instead of parent casted to ListView.
    if (position >= ((ListView) parent).getHeaderViewsCount()) {
      // Note the usage of getItemAtPosition() instead of adapter's getItem() because
      // the latter does not take into account the header (which has position 0).
      Object obj = parent.getItemAtPosition(position);
      // Do something with your object.
    }
  }
});

Evet, aklıma gelen ikinci fikir buydu ama bundan emin değildim ListAdapterve ListViewo zaman manuel olarak yapmadığım ekranların arkasında daha fazla sihir var.
Stefan Hoth

Elbette ListView biraz sihir yapar, en azından öğeler arasında bölücüler, onları manuel olarak eklemeniz gerekir, sanırım. ListAdapter (doğru bir şekilde uygulanırsa) artık sihir yapmamalıdır.
smok

2
Bu benim için olumsuz bir oy. Hafıza sorunu var mı? Adaptör kullanmamızın bir nedeni var ...
Kevin Parker

1
Bağdaştırıcının notifyDataSetChangedyöntemini kullanarak görünümleri güncellemede sorun yaşayan başka biri var mı ? Bu, a'ya bağladığım farklı bir adaptörde iyi çalışıyor ListView, ancak a'ya bağladığım ile çalışıyor gibi görünmüyor LinearLayout.
jokeefe

2
Android'den nefret etmemin nedenlerinden biri budur. Neden karmaşık çözümler uygulamak zorunda olduğunuzu veya performans sorunları yaşadığınızı anlamıyorum.
IARI

6

Başlık görünümü çözümüne bağlı kalırdım. Bunda yanlış bir şey yok. Şu anda aynı yaklaşımı kullanarak bir faaliyet uyguluyorum.

Açıkçası, "öğe bölümü" statikten daha dinamiktir (değişen öğe sayımına karşı sabit öğe sayısı vb.) Aksi takdirde bir adaptör kullanmayı hiç düşünmezsiniz. Yani bir adaptöre ihtiyacınız olduğunda ListView kullanın.

Bir adaptörden bir LinearLayout dolduran bir çözüm uygulamak, sonunda özel bir düzene sahip bir ListView oluşturmaktan başka bir şey değildir.

Sadece 2 sentim.


Bu yaklaşımın bir dezavantajı, neredeyse tüm etkinlik düzeninizi (yukarıya bakın) başka bir düzen parçasına taşımanız gerektiğidir, böylece onu bir ListHeaderView olarak referans alabilir ve yalnızca ListView. Bu yama çalışmasını sevdiğimden emin değilim.
Stefan Hoth

1
Evet, ne demek istediğinizi biliyorum, bu yaklaşımı kullanmaya başlamadan önce, etkinlik düzenimin birden çok dosyaya ayrılması fikrinden de hoşlanmadım. Ancak genel düzenin bakmayı beklediğim gibi göründüğünü gördüğümde, yalnızca iki dosyaya ayrıldığı için ayırmayı önemsemedim. Bilmeniz gereken bir şey var, ListView boş bir görünüme sahip olmamalıdır çünkü bu, liste öğesi olmadığında liste başlığınızı gizleyecektir.
Flo

3
Ayrıca, bir sayfada birden fazla listeye sahip olmak istiyorsanız, bu gerçekten işe yaramaz. Öğeleri "düz", yani kaydırılmayan bir listeye çıkaran özel bir adaptör görünümü örneği olan var mı?
murtuza

0

Görünümünüzü main.xml onCreate olarak ayarlayın, ardından row.xml'den şişirin

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="450dp" >

        <ListView
            android:id="@+id/mainListView"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_above="@+id/size"
            android:layout_below="@+id/editText1"
            android:gravity="fill_vertical|fill_horizontal"
            android:horizontalSpacing="15dp"
            android:isScrollContainer="true"
            android:numColumns="1"
            android:padding="5dp"
            android:scrollbars="vertical"
            android:smoothScrollbar="true"
            android:stretchMode="columnWidth" >

</ListView>

    <TextView
        android:id="@+id/size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:background="#ff444444"
        android:gravity="center"
        android:text="TextView"
        android:textColor="#D3D3D3"
        android:textStyle="italic" />

    </EditText>

</RelativeLayout> 

row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" 
  android:paddingTop="3dp">

<TextView
  android:id="@+id/rowTextView"
  android:layout_width="0dip"
  android:layout_height="41dp"
  android:layout_margin="4dp"
  android:layout_weight="2.83"
  android:ellipsize="end"
  android:gravity="center_vertical"
  android:lines="1"
  android:text="John Doe"
  android:textColor="@color/color_white"
  android:textSize="23dp" >
</TextView>

</LinearLayout>

Sanırım soruyu anlamışsın. Bir listeyi LinearLayouts ile doldurmak istemiyor.
Flo

"Bir ListAdapter'ı bir LinearLayout'a veya benzer bir şeye nasıl bağlarım?" Sadece OP'nin sorduğu
cevabı veriyorum

2
Hayır, ListView kullanmak istemiyor. Yalnızca bir adaptör kullanmak ve onu bir Doğrusal Düzeni girişlerle doldurmak için kullanmak istiyor.
Flo

Cevabınız için teşekkürler ama @Flo doğru. Ben edemez dış düzen zaten scrollview çünkü bir ListView kullanın. Lütfen metnimi ve örnek düzeni kontrol edin.
Stefan Hoth

neden bu scrollView gerekli?
fasheikh

0

Adaptör işlevselliğini ViewGroupve ile çoğaltan aşağıdaki kodu kullanıyorum TabLayout. Bununla ilgili iyi bir şey, listenizi değiştirip tekrar bağlarsanız, yalnızca değiştirilen öğeleri etkileyecektir:

Kullanım:

val list = mutableListOf<Person>()
layout.bindChildren(list, { it.personId }, { bindView(it) }, {d, t ->bindView(d, t)})
list.removeAt(0)
list+=newPerson
layout.bindChildren(list, { it.personId }, { bindView(it) }, {d, t ->bindView(d, t)})

Şunun için ViewGroups:

fun <Item, Key> ViewGroup.bindChildren(items: List<Item>, id: (Item) -> Key, view: (Item) -> View, bind: (Item, View) -> Unit) {
    val old = children.map { it.tag as Key }.toList().filter { it != null }
    val new = items.map(id)

    val add = new - old
    val remove = old - new
    val keep = new.intersect(old)

    val tagToChildren = children.associateBy { it.tag as Key }
    val idToItem = items.associateBy(id)

    remove.forEach { tagToChildren[it].let { removeView(it) } }
    keep.forEach { bind(idToItem[it]!!, tagToChildren[it]!!) }
    add.forEach { id -> view(idToItem[id]!!).also { it.tag = id }.also { addView(it, items.indexOf(idToItem[id])) } }
}

Bunun için TabLayoutbende:

fun <Item, Key> TabLayout.bindTabs(items: List<Item>, toKey: (Item) -> Key, tab: (Item) -> TabLayout.Tab, bind: (Item, TabLayout.Tab) -> Unit) {
    val old = (0 until tabCount).map { getTabAt(it)?.tag as Key }
    val new = items.map(toKey)

    val add = new - old
    val remove = old - new
    val keep = new.intersect(old)

    val tagToChildren = (0 until tabCount).map { getTabAt(it) }.associateBy { it?.tag as Key }
    val idToItem = items.associateBy(toKey)

    remove.forEach { tagToChildren[it].let { removeTab(it) } }
    keep.forEach { bind(idToItem[it]!!, tagToChildren[it]!!) }
    add.forEach { key -> tab(idToItem[key]!!).also { it.tag = key }.also { addTab(it, items.indexOf(idToItem[key])) } }
}
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.