Her satır için farklı düzenlere sahip Android ListView


348

Her satır için farklı düzenleri içeren tek bir ListView için en iyi yolu belirlemeye çalışıyorum. Tüm liste görünümü için özel bir satırı desteklemek için özel bir satır + özel dizi bağdaştırıcısı oluşturmak nasıl biliyorum, ama nasıl ListView birçok farklı satır stilleri uygulayabilirsiniz?


1
güncelleme: Android'in RecyclerView kod2concept.blogspot.in/2015/10/…
nitesh

Yanıtlar:


413

Kaç tane düzen türünüz olacağını bildiğiniz için, bu yöntemleri kullanmak mümkündür.

getViewTypeCount() - bu yöntemler listenizde kaç satır türü olduğunuzu gösterir

getItemViewType(int position) - konuma göre hangi yerleşim türünü kullanmanız gerektiğine dair bilgileri döndürür

Ardından, düzeni yalnızca boşsa şişirir ve kullanarak türü belirlersiniz getItemViewType.

Bak Bu eğitimde daha fazla bilgi için.

Yorumda tanımladığınız yapıda bazı optimizasyonlar elde etmek için şunu öneririm:

  • Görüntülenen nesnede görünümleri saklama ViewHolder. Hızı artıracaktır çünkü yöntemde findViewById()her seferinde aramak zorunda kalmazsınız getView. API demolarında Liste 14'e bakın .
  • Tüm özellik kombinasyonlarına uyacak bir genel mizanpaj oluşturun ve geçerli konumda yoksa bazı öğeleri gizleyin.

Umarım bu size yardımcı olur. Veri yapınız ve satırla tam olarak nasıl eşlemek istediğinizle ilgili bazı XML saplamaları sağlayabilirseniz, size daha kesin tavsiye verebilirim. Piksel ile.


Teşekkürler blog çok güzeldi, ama onay kutusu ekledim. İlk öğeyi kontrol etmek ve Listeyi kaydırmak için bir sorun yaşadım. Garip anonim öğeler nerede kontrol. Bunun için çözüm sağlayabilir misiniz? Teşekkürler
Lalit Poptani

2
tekrar kazmak için özür dilerim, ama aslında getItemViewType kullanarak şişirilmiş ayrı düzen dosyaları yerine, tek bir büyük düzen dosyası olmasını ve parçalarının görünürlüğünü kontrol etmenizi öneririm?
Makibo

2
Siz de yapabilirsiniz. Yine de burada maruz kalmayı tercih etsem de. Neyi başarmak istediğinizi netleştirir.
Cristian

Ancak, çoklu düzen stratejisinde, setTag yalnızca bir görünüm tutucu içerebildiğinden ve satır düzeni her değiştiğinde findViewById () öğesini çağırmamız gerektiğinden kullanıcı görünümü tutucusunu düzgün bir şekilde kullanamayız. Böylece liste görünümü çok düşük performans gösterir. Kişisel olarak deneyimledim, öneriniz nedir?
pyus13

@ pyus13, tek bir görünüm tutucuda istediğiniz sayıda görünüm bildirebilirsiniz ve görünüm tutucuda bildirilen her görünümü kullanmak gerekmez. Örnek bir kod gerekiyorsa, lütfen bana bildirin, gönderirim.
Ahmad Ali Nasir

62

Tüm liste görünümü için özel bir satırı desteklemek için özel bir satır + özel dizi bağdaştırıcısı oluşturmayı biliyorum. Ancak bir liste görünümü birçok farklı satır stilini nasıl destekleyebilir?

Temel bilgileri zaten biliyorsunuz. Sağlanan satır / imleç bilgilerine göre farklı bir düzen / görünüm döndürmek için özel adaptörünüzü almanız yeterlidir.

A ListView, AdapterView öğesinden türetildiği için birden çok satır stilini destekleyebilir :

BağdaştırıcıGörünümü, altları Bağdaştırıcı tarafından belirlenen bir görünümdür .

Bağdaştırıcıya bakarsanız, satıra özgü görünümlerin kullanılmasını açıklayan yöntemler görürsünüz:

abstract int getViewTypeCount()
// Returns the number of types of Views that will be created ...

abstract int getItemViewType(int position)
// Get the type of View that will be created ...

abstract View getView(int position, View convertView, ViewGroup parent)
// Get a View that displays the data ...

Son iki yöntem konumu sağlar, böylece o satır için kullanmanız gereken görünüm türünü belirlemek için bunu kullanabilirsiniz .


Tabii ki, genellikle AdapterView ve Adapter'ı doğrudan kullanmazsınız, aksine alt sınıflarından birini kullanır veya bunlardan türetirsiniz. Bağdaştırıcının alt sınıfları, farklı satırlar için özel düzenlerin nasıl alınacağını değiştiren ek işlevler ekleyebilir. Belirli bir satır için kullanılan görünüm bağdaştırıcı tarafından yönlendirildiğinden, hile, bağdaştırıcının belirli bir satır için istenen görünümü döndürmesini sağlamaktır. Bunun nasıl yapılacağı özel adaptöre bağlı olarak değişir.

Örneğin, ArrayAdapter kullanmak için ,

  • getView()verilen konum için istenen görünümü şişirmek, doldurmak ve geri döndürmek için geçersiz kılın . getView()Yöntem ile görüş yeniden bir fırsat içermektedir convertViewparametresi.

Ancak CursorAdapter türevlerini kullanmak için ,

  • newView()geçerli imleç durumu (yani geçerli "satır") için istediğiniz görünümü şişirmek, doldurmak ve döndürmek için geçersiz kıl [ bindViewwidget’ın görünümleri yeniden kullanabilmesi için de geçersiz kılmanız gerekir ]

Ancak, SimpleCursorAdapter kullanmak için ,

  • belirli bir satır (geçerli imleç durumu) ve veri "sütunu" için istenen görünümü şişirmek, doldurmak ve döndürmek için SimpleCursorAdapter.ViewBinderbir setViewValue()yöntemle a tanımlayın . Yöntem sadece "özel" görünümleri tanımlayabilir ve SimpleCursorAdapter'ın "normal" bağlamalar için standart davranışına erteleyebilir.

Son kullandığınız adaptör türü için belirli örneklere / eğiticilere bakın.


Bağdaştırıcının esnek bir şekilde uygulanması için bu bağdaştırıcı türlerinden hangisinin en iyisi olduğuna dair düşünceler nelerdir? Bunun için tahtaya başka bir soru daha ekliyorum.
Androider

1
@Androider - "esnek için en iyisi" çok açık uçludur - her ihtiyacı karşılayacak hiçbir nihai, hepsi bir sınıf yoktur; bu zengin bir hiyerarşi - bir alt sınıfta amacınız için yararlı bir işlevsellik olup olmadığına gelir. Öyleyse, bu alt sınıfla başlayın; değilse, yukarı hareket edin BaseAdapter. BaseAdapter'tan türetmek en "esnek" olacaktır, ancak diğer bağdaştırıcılara zaten yerleştirilmiş olan bilgi ve olgunluktan faydalanmadığı için kodun yeniden kullanılması ve olgunluğun en kötüsü olacaktır. BaseAdapterdiğer adaptörlerin uymadığı standart dışı bağlamlar için vardır.
Bert F

3
CursorAdapterVe arasındaki ince ayrım için +1 SimpleCursorAdapter.
Giulio Piancastelli

1
Ayrıca ArrayAdapter, geçersiz kılarsanız , getView()doğru düzen türünü
şişirip

1
Her getViewTypeCount()aradığınızda ListView.setAdapter()değil, her aradığınızda tetiklendiğine dikkat edilmelidir Adapter.notifyDataSetChanged().
hidro

43

Aşağıdaki koda bir göz atın.

İlk olarak, özel düzenler oluştururuz. Bu durumda, dört tip.

even.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#ff500000"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:textColor="@android:color/white"
        android:layout_width="match_parent"
        android:layout_gravity="center"
        android:textSize="24sp"
        android:layout_height="wrap_content" />

 </LinearLayout>

odd.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#ff001f50"
    android:gravity="right"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:textSize="28sp"
        android:layout_height="wrap_content"  />

 </LinearLayout>

white.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#ffffffff"
    android:gravity="right"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:textColor="@android:color/black"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:textSize="28sp"
        android:layout_height="wrap_content"   />

 </LinearLayout>

black.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#ff000000"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:textSize="33sp"
        android:layout_height="wrap_content"   />

 </LinearLayout>

Sonra liste görünümü öğesini yaratırız. Bizim durumumuzda, bir dize ve bir tür ile.

public class ListViewItem {
        private String text;
        private int type;

        public ListViewItem(String text, int type) {
            this.text = text;
            this.type = type;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }

    }

Bundan sonra bir görünüm tutucu oluştururuz. Android işletim sistemi, öğenizi kaybolduğunda ve ekranda tekrar göründüğünde yeniden kullanmak için düzen referansını tuttuğu için şiddetle önerilir. Bu yaklaşımı kullanmazsanız, öğeniz ekranda her görüntülendiğinde Android OS yeni bir tane oluşturacak ve uygulamanızın bellek sızdırmasına neden olacaktır.

public class ViewHolder {
        TextView text;

        public ViewHolder(TextView text) {
            this.text = text;
        }

        public TextView getText() {
            return text;
        }

        public void setText(TextView text) {
            this.text = text;
        }

    }

Son olarak, getViewTypeCount () ve getItemViewType (int position) geçersiz kılan özel bağdaştırıcımızı oluşturuyoruz.

public class CustomAdapter extends ArrayAdapter {

        public static final int TYPE_ODD = 0;
        public static final int TYPE_EVEN = 1;
        public static final int TYPE_WHITE = 2;
        public static final int TYPE_BLACK = 3;

        private ListViewItem[] objects;

        @Override
        public int getViewTypeCount() {
            return 4;
        }

        @Override
        public int getItemViewType(int position) {
            return objects[position].getType();
        }

        public CustomAdapter(Context context, int resource, ListViewItem[] objects) {
            super(context, resource, objects);
            this.objects = objects;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            ViewHolder viewHolder = null;
            ListViewItem listViewItem = objects[position];
            int listViewItemType = getItemViewType(position);


            if (convertView == null) {

                if (listViewItemType == TYPE_EVEN) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_even, null);
                } else if (listViewItemType == TYPE_ODD) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_odd, null);
                } else if (listViewItemType == TYPE_WHITE) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_white, null);
                } else {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_black, null);
                }

                TextView textView = (TextView) convertView.findViewById(R.id.text);
                viewHolder = new ViewHolder(textView);

                convertView.setTag(viewHolder);

            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            viewHolder.getText().setText(listViewItem.getText());

            return convertView;
        }

    }

Ve faaliyetimiz şöyle bir şey:

private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main); // here, you can create a single layout with a listview

        listView = (ListView) findViewById(R.id.listview);

        final ListViewItem[] items = new ListViewItem[40];

        for (int i = 0; i < items.length; i++) {
            if (i == 4) {
                items[i] = new ListViewItem("White " + i, CustomAdapter.TYPE_WHITE);
            } else if (i == 9) {
                items[i] = new ListViewItem("Black " + i, CustomAdapter.TYPE_BLACK);
            } else if (i % 2 == 0) {
                items[i] = new ListViewItem("EVEN " + i, CustomAdapter.TYPE_EVEN);
            } else {
                items[i] = new ListViewItem("ODD " + i, CustomAdapter.TYPE_ODD);
            }
        }

        CustomAdapter customAdapter = new CustomAdapter(this, R.id.text, items);
        listView.setAdapter(customAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView adapterView, View view, int i, long l) {
                Toast.makeText(getBaseContext(), items[i].getText(), Toast.LENGTH_SHORT).show();
            }
        });

    }
}

Şimdi mainactivity.xml içinde böyle bir liste görünümü oluşturun

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.example.shivnandan.gygy.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <ListView
        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:id="@+id/listView"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"


        android:layout_marginTop="100dp" />

</android.support.design.widget.CoordinatorLayout>

<include layout = "@ layout / content_main" /> nereden geliyor
nyxee

Yalnızca (convertView == null) deyimine ihtiyacım vardı. 'İzleyiciye' gerek yoktu
Jomme

14

Özel dizi bağdaştırıcınızda, muhtemelen bildiğiniz gibi getView () yöntemini geçersiz kılarsınız. Sonra tek yapmanız gereken, getView yöntemine iletilen konum bağımsız değişkenine bağlı olarak belirli bir özel Görünümü döndürmek için bir switch deyimi veya bir if deyimi kullanmaktır. Android sadece size konum / satır için uygun türde bir convertView verecek zekidir; doğru tipte olup olmadığını kontrol etmenize gerek yoktur. GetItemViewType () ve getViewTypeCount () yöntemlerini uygun şekilde geçersiz kılarak Android'e bu konuda yardımcı olabilirsiniz.


4

Liste görünümünde farklı türden bir görünüm göstermemiz gerekiyorsa, bir görünümü değiştirmek yerine adaptörde getViewTypeCount () ve getItemViewType () yöntemini kullanmak iyi olur. liste kaydırma etkiler.

Lütfen Bağdaştırıcı'da getViewTypeCount () ve getItemViewType () kullanımı için bunu kontrol edin.

Bağlantı: getviewtypecount'un kullanımı


1

ListView, tüm satır öğeleri için aynı statik görünüm gibi basit kullanım durumları için tasarlanmıştır.
ViewHolders oluşturmanız ve getItemViewType()farklı satır öğesi düzeni xml'lerini önemli ölçüde kullanmanız ve dinamik olarak göstermeniz gerektiğinden , Android API 22'de bulunan RecyclerView'i kullanarak bunu denemelisiniz . Birden çok görünüm türü için daha iyi destek ve yapı sunar.

Aradığınızı yapmak için RecyclerView uygulamasını nasıl kullanacağınıza ilişkin bu eğiticiye göz atın .

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.