प्रत्येक पंक्ति के लिए अलग-अलग लेआउट वाले Android ListView


348

मैं एक एकल सूची दृश्य का सबसे अच्छा तरीका निर्धारित करने का प्रयास कर रहा हूं जिसमें प्रत्येक पंक्ति के लिए अलग-अलग लेआउट हैं। मुझे पता है कि पूरी सूची दृश्य के लिए एक कस्टम पंक्ति का समर्थन करने के लिए एक कस्टम पंक्ति + कस्टम सरणी एडाप्टर कैसे बनाया जाए, लेकिन मैं सूची दृश्य में कई अलग-अलग पंक्ति शैलियों को कैसे लागू कर सकता हूं?


1
अपडेट: Android के RecyclerView code2concept.blogspot.in/2015/10/…
nitesh

जवाबों:


413

चूंकि आप जानते हैं कि आपके पास कितने प्रकार के लेआउट होंगे - उन तरीकों का उपयोग करना संभव है।

getViewTypeCount() - यह विधियां आपकी सूची में आपके पास कितनी प्रकार की पंक्तियाँ हैं, इसकी जानकारी देती है

getItemViewType(int position) - स्थिति के आधार पर आपको कौन से लेआउट प्रकार का उपयोग करना चाहिए, इसकी जानकारी देता है

तब आप लेआउट को केवल तभी फुलाते हैं जब वह अशक्त और निर्धारित प्रकार का उपयोग कर रहा हो getItemViewType

देखो इस ट्यूटोरियल अधिक जानकारी के लिए।

टिप्पणी में वर्णित संरचना में कुछ अनुकूलन प्राप्त करने के लिए मैं सुझाव दूंगा:

  • वस्तु में संग्रहीत विचार ViewHolder। यह गति में वृद्धि करेगा क्योंकि आपको findViewById()हर बार getViewविधि में कॉल नहीं करना पड़ेगा । API डेमो में List14 देखें ।
  • एक जेनेरिक लेआउट बनाएँ जो संपत्तियों के सभी संयोजनों के अनुरूप होगा और कुछ तत्वों को छिपाएगा यदि वर्तमान स्थिति नहीं है।

मुझे उम्मीद है कि इससे आपको मदद मिलेगी। यदि आप अपनी डेटा संरचना और जानकारी के साथ कुछ XML स्टब प्रदान कर सकते हैं तो आप इसे पंक्ति में कैसे मैप करना चाहते हैं, मैं आपको अधिक सटीक सलाह दे सकता हूं। पिक्सेल द्वारा।


धन्यवाद ब्लॉग बहुत अच्छा था, लेकिन मैंने चेकबॉक्स जोड़ा। मुझे इसमें एक समस्या थी कि पहले आइटम की जांच करेंगे और सूची को स्क्रॉल करेंगे। अजीब तरह से अनाम आइटम जहाँ जाँच हो। क्या आप उसके लिए समाधान प्रदान कर सकते हैं। धन्यवाद
ललित Poptani

2
इसे फिर से खोदने के लिए खेद है, लेकिन आप वास्तव में एक बड़ी लेआउट फ़ाइल रखने और इसके कुछ हिस्सों की दृश्यता को नियंत्रित करने की अनुशंसा करेंगे, इसके बजाय अलग लेआउट फाइलें हैं, जो क्रमशः getItemViewType का उपयोग करके फुलाया जाता है?
मकीबो 9'13

2
आप भी ऐसा कर सकते हैं। हालांकि मैं अभी भी यहां उजागर तरीके को पसंद करता हूं। यह स्पष्ट करता है कि आप क्या हासिल करना चाहते हैं।
क्रिस्टियान

लेकिन कई लेआउट रणनीति में हम उपयोगकर्ता धारक को ठीक से नहीं देख सकते हैं क्योंकि सेटटैग में केवल एक दृश्य धारक हो सकता है और जब भी पंक्ति लेआउट फिर से होता है तो हमें findViewById () कॉल करना होगा। जो सूची को बहुत कम प्रदर्शन करता है। मैंने व्यक्तिगत अनुभव किया कि इस पर आपका क्या सुझाव है?
pyus13

@ pyus13 आप एक दृश्य धारक में जितने चाहें उतने दृश्य घोषित कर सकते हैं और दृश्य धारक में घोषित प्रत्येक दृश्य का उपयोग करना आवश्यक नहीं है। यदि एक नमूना कोड की आवश्यकता है, तो कृपया मुझे बताएं, मैं इसे पोस्ट करूंगा।
अहमद अली नासिर

62

मुझे पता है कि पूरी सूची दृश्य के लिए कस्टम पंक्ति का समर्थन करने के लिए कस्टम पंक्ति + कस्टम सरणी एडाप्टर कैसे बनाया जाता है। लेकिन एक श्रवण कई अलग-अलग पंक्ति शैलियों का समर्थन कैसे कर सकता है?

आप पहले से ही मूल बातें जानते हैं। आपको बस प्रदान की जाने वाली पंक्ति / कर्सर जानकारी के आधार पर एक अलग लेआउट / दृश्य वापस करने के लिए अपने कस्टम एडाप्टर को प्राप्त करने की आवश्यकता है।

A ListViewकई पंक्ति शैलियों का समर्थन कर सकता है क्योंकि यह AdapterView से प्राप्त होता है :

एक एडॉप्टर व्यू एक ऐसा दृश्य है जिसके बच्चे एक एडेप्टर द्वारा निर्धारित किए जाते हैं।

यदि आप एडॉप्टर को देखते हैं, तो आपको रो-विशिष्ट विचारों का उपयोग करने के लिए तरीके दिखाई देंगे:

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

बाद की दो विधियाँ स्थिति प्रदान करती हैं ताकि आप उस पंक्ति के लिए उपयोग किए जाने वाले दृश्य के प्रकार को निर्धारित करने के लिए उपयोग कर सकें ।


बेशक, आप आमतौर पर एडेप्टर व्यू और एडेप्टर का उपयोग सीधे नहीं करते हैं, बल्कि उनके किसी उपवर्ग से उपयोग या प्राप्त करते हैं। एडेप्टर के उपवर्ग अतिरिक्त कार्यक्षमता जोड़ सकते हैं जो विभिन्न पंक्तियों के लिए कस्टम लेआउट प्राप्त करने के तरीके को बदलते हैं। चूंकि किसी दिए गए पंक्ति के लिए उपयोग किया गया दृश्य एडेप्टर द्वारा संचालित होता है, इसलिए ट्रिक को दिए गए पंक्ति के लिए वांछित दृश्य वापस करने के लिए एडेप्टर प्राप्त करना होता है। यह कैसे करना है यह विशिष्ट एडाप्टर के आधार पर भिन्न होता है।

उदाहरण के लिए, ArrayAdapter का उपयोग करने के लिए ,

  • getView()दी गई स्थिति के लिए वांछित दृश्य को बढ़ाने, आबाद करने और वापस करने के लिए ओवरराइड करें। getView()विधि के माध्यम से विचारों का पुन: उपयोग का अवसर भी शामिल है convertViewपैरामीटर।

लेकिन CursorAdapter के डेरिवेटिव का उपयोग करने के लिए ,

  • ओवरराइड newView(), बढ़ रूप से भरें, और वर्तमान कर्सर राज्य के लिए वांछित दृश्य (यानी वर्तमान "पंक्ति") वापस जाने के लिए [आप भी ओवरराइड करने के लिए की जरूरत है bindViewताकि विजेट विचारों का पुन: उपयोग कर सकते हैं]

हालाँकि, SimpleCursorAdapter का उपयोग करने के लिए ,

  • किसी दिए गए पंक्ति (वर्तमान कर्सर स्थिति) और डेटा "कॉलम" के लिए वांछित दृश्य को बढ़ाने, आबाद करने और वापस करने के लिए SimpleCursorAdapter.ViewBinderएक setViewValue()विधि के साथ परिभाषित करें। विधि केवल "विशेष" विचारों को परिभाषित कर सकती है और "सामान्य" बाइंडिंग के लिए SimpleCursorAdapter के मानक व्यवहार को स्थगित कर सकती है।

आपके द्वारा उपयोग किए जाने वाले एडाप्टर के प्रकार के लिए विशिष्ट उदाहरण / ट्यूटोरियल देखें।


एडॉप्टर के लचीले कार्यान्वयन के लिए इनमें से कौन सा एडॉप्टर प्रकार सबसे अच्छा है? मैं इसके लिए बोर्ड पर एक और सवाल जोड़ रहा हूं।
एंड्रॉइड

1
@Androider - "लचीलेपन के लिए सबसे अच्छा" बहुत खुला-अंत है - कोई अंत-सभी नहीं है, सभी वर्ग हैं जो हर ज़रूरत को पूरा करेंगे; यह एक समृद्ध पदानुक्रम है - यह नीचे आता है कि क्या उपवर्ग में कार्यक्षमता है जो आपके उद्देश्य के लिए उपयोगी है। यदि हां, तो उस उपवर्ग के साथ शुरू करें; यदि नहीं, तो ऊपर जाएँ BaseAdapter। बेस एडेप्टर से वितरण सबसे "लचीला" होगा, लेकिन कोड के पुन: उपयोग और परिपक्वता पर सबसे खराब होगा क्योंकि यह ज्ञान का लाभ नहीं उठाता है और परिपक्वता पहले से ही दूसरे एडेप्टर में डाल दी गई है। BaseAdapterगैर-मानक संदर्भों के लिए वहाँ है जहाँ अन्य एडेप्टर फिट नहीं होते हैं।
बर्ट एफ

3
CursorAdapterऔर के बीच ठीक अंतर के लिए +1 SimpleCursorAdapter
Giulio Piancastelli

1
यह भी ध्यान दें कि यदि आप ओवरराइड करते हैं ArrayAdapter, तो इससे कोई फर्क नहीं पड़ता कि आप कंस्ट्रक्टर को कौन सा लेआउट देते हैं, जब तक कि getView()वह
फुलाए

1
यह ध्यान दिया जाना चाहिए कि getViewTypeCount()केवल एक बार ट्रिगर किया जाता है जब आप कॉल करते हैं ListView.setAdapter(), प्रत्येक के लिए नहीं Adapter.notifyDataSetChanged()
हिद्रो

43

नीचे दिए गए कोड में एक नज़र डालें।

सबसे पहले, हम कस्टम लेआउट बनाते हैं। इस मामले में, चार प्रकार।

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>

फिर, हम सूची आइटम बनाते हैं। हमारे मामले में, एक स्ट्रिंग और एक प्रकार के साथ।

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

    }

उसके बाद, हम एक व्यू होल्डर बनाते हैं। यह दृढ़ता से अनुशंसित है क्योंकि एंड्रॉइड ओएस आपके आइटम का पुन: उपयोग करने के लिए लेआउट संदर्भ रखता है जब वह गायब हो जाता है और स्क्रीन पर वापस दिखाई देता है। यदि आप इस दृष्टिकोण का उपयोग नहीं करते हैं, तो हर बार जब आपका आइटम स्क्रीन पर दिखाई देता है तो एंड्रॉइड ओएस एक नया बना देगा और आपके ऐप को मेमोरी को लीक करने का कारण होगा।

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

    }

अंत में, हम getViewTypeCount () और getItemViewType (int स्थिति) को ओवरराइड करते हुए अपना कस्टम एडेप्टर बनाते हैं।

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

    }

और हमारी गतिविधि कुछ इस तरह है:

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

    }
}

अब इस तरह mainactivity.xml के अंदर एक सूची बनाएँ

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

<लेआउट में शामिल हैं = "@ लेआउट / content_main" /> वह कहां से आ रहा है
nyxee

मुझे केवल (ConvertView == null) क्लॉज की जरूरत थी। एक 'देखने वाले' की ज़रूरत नहीं थी
जोमे

14

अपने कस्टम ऐरे एडॉप्टर में, आप getView () विधि को ओवरराइड करते हैं, जैसा कि आप संभवतः परिचित हैं। फिर आपको बस इतना करना है कि स्विच व्यू का उपयोग करें या यदि एक निश्चित रिवाज को वापस करने के लिए स्टेटमेंट प्राप्त करें तो गेटवे विधि में दिए गए स्थिति तर्क के आधार पर देखें। एंड्रॉइड इस मामले में चतुर है कि यह आपको केवल आपकी स्थिति / पंक्ति के लिए उपयुक्त प्रकार का एक कन्वर्ट व्यू देगा; आपको यह जाँचने की आवश्यकता नहीं है कि यह सही प्रकार का है। आप getItemViewType () और getViewTypeCount () विधियों को उचित तरीके से ओवरराइड करके इसके साथ Android की मदद कर सकते हैं।


4

यदि हमें सूची-दृश्य में विभिन्न प्रकार के दृश्य दिखाने की आवश्यकता है, तो एक दृश्य VIEW.GONE और VIEW.VISIBLE के बजाय getViewTypeCount () और getItemViewType () एडॉप्टर का उपयोग करने के लिए इसका अच्छा उपयोग getView () के अंदर बहुत महंगा कार्य हो सकता है। सूची स्क्रॉल को प्रभावित करें।

कृपया एडाप्टर में getViewTypeCount () और getItemViewType () के उपयोग के लिए इसे जांचें।

लिंक: उपयोग-का-प्राप्त- getviewtypecount


1

सूची दृश्य का उपयोग सभी पंक्ति वस्तुओं के लिए एक ही स्थिर दृश्य जैसे सरल उपयोग के मामलों के लिए किया गया था।
चूँकि आपको ViewHolders बनाना है और इसका महत्वपूर्ण उपयोग करना है getItemViewType(), और गतिशील रूप से अलग-अलग पंक्ति वस्तु लेआउट xml's को दिखाना है, तो आपको यह करने की कोशिश करनी चाहिए कि RecyclerView , जो Android API 22 में उपलब्ध है, का उपयोग करके । यह कई दृश्य प्रकारों के लिए बेहतर समर्थन और संरचना प्रदान करता है।

इस ट्यूटोरियल की जाँच करें कि आप क्या देख रहे हैं, यह करने के लिए RecyclerView का उपयोग कैसे करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.