Android ListView với các bố cục khác nhau cho mỗi hàng


348

Tôi đang cố gắng xác định cách tốt nhất để có một ListView chứa các bố cục khác nhau cho mỗi hàng. Tôi biết cách tạo một hàng tùy chỉnh + bộ điều hợp mảng tùy chỉnh để hỗ trợ một hàng tùy chỉnh cho toàn bộ chế độ xem danh sách, nhưng làm cách nào tôi có thể triển khai nhiều kiểu hàng khác nhau trong ListView?


1
cập nhật: Bản trình diễn cho bố cục nhiều hàng bằng cách sử dụng mã RecyclerView của android2concept.blogspot.in/2015/10/ trên
nitesh

Câu trả lời:


413

Vì bạn biết bạn sẽ có bao nhiêu loại bố cục - có thể sử dụng các phương thức đó.

getViewTypeCount() - phương thức này trả về thông tin bạn có bao nhiêu loại hàng trong danh sách của mình

getItemViewType(int position) - trả về thông tin loại bố cục bạn nên sử dụng dựa trên vị trí

Sau đó, bạn chỉ tăng bố cục nếu nó không và xác định loại bằng cách sử dụng getItemViewType.

Nhìn vào hướng dẫn này để biết thêm thông tin.

Để đạt được một số tối ưu hóa trong cấu trúc mà bạn đã mô tả trong nhận xét, tôi sẽ đề xuất:

  • Lưu trữ quan điểm trong đối tượng được gọi ViewHolder. Nó sẽ tăng tốc độ vì bạn sẽ không phải gọi findViewById()mỗi lần trong getViewphương thức. Xem List14 trong các bản trình diễn API .
  • Tạo một bố cục chung sẽ phù hợp với tất cả các kết hợp thuộc tính và ẩn một số thành phần nếu vị trí hiện tại không có nó.

Tôi hy vọng điều đó sẽ giúp bạn. Nếu bạn có thể cung cấp một số sơ khai XML với cấu trúc dữ liệu và thông tin chính xác bạn muốn ánh xạ nó thành hàng như thế nào, tôi có thể cung cấp cho bạn lời khuyên chính xác hơn. Theo pixel.


Cảm ơn blog rất đẹp, nhưng tôi đã thêm hộp kiểm. Tôi gặp vấn đề trong đó sẽ kiểm tra mục đầu tiên và cuộn Danh sách. Các mặt hàng vô danh kỳ lạ nơi được kiểm tra. Bạn có thể cung cấp giải pháp cho điều đó. Cảm ơn
Lalit Poptani

2
xin lỗi vì đã đào lại cái này một lần nữa, nhưng bạn thực sự khuyên bạn nên có một tệp bố cục lớn duy nhất và kiểm soát mức độ hiển thị của các phần của nó, thay vì có các tệp bố cục riêng biệt, bị thổi phồng tương ứng khi sử dụng getItemViewType?
Makibo

2
Bạn cũng có thể làm điều đó mà. Mặc dù tôi vẫn thích cách tiếp xúc ở đây. Nó làm cho rõ ràng hơn những gì bạn muốn đạt được.
Cristian

Nhưng trong chiến lược nhiều bố cục, chúng tôi không thể sử dụng đúng chế độ xem chủ sở hữu vì setTag chỉ có thể chứa một chủ sở hữu chế độ xem và bất cứ khi nào bố trí hàng chuyển đổi một lần nữa, chúng tôi cần gọi findViewById (). Mà làm cho listview hiệu suất rất thấp. Tôi cá nhân có kinh nghiệm nó đề nghị của bạn về nó là gì?
pyus13

@ pyus13 bạn có thể khai báo bao nhiêu lượt xem như bạn muốn trong một chủ sở hữu chế độ xem duy nhất và không cần thiết phải sử dụng mọi chế độ xem được khai báo trong chế độ xem. Nếu cần một mã mẫu, xin vui lòng cho tôi biết, tôi sẽ đăng nó.
Ahmad Ali Nasir

62

Tôi biết cách tạo một hàng tùy chỉnh + bộ điều hợp mảng tùy chỉnh để hỗ trợ một hàng tùy chỉnh cho toàn bộ chế độ xem danh sách. Nhưng làm thế nào một listview có thể hỗ trợ nhiều kiểu hàng khác nhau?

Bạn đã biết những điều cơ bản. Bạn chỉ cần nhận bộ điều hợp tùy chỉnh của mình để trả về bố cục / chế độ xem khác nhau dựa trên thông tin hàng / con trỏ được cung cấp.

A ListViewcó thể hỗ trợ nhiều kiểu hàng vì nó xuất phát từ AdapterView :

AdaptorView là chế độ xem có con được xác định bởi Adaptor.

Nếu bạn nhìn vào Bộ điều hợp , bạn sẽ thấy các phương pháp tính đến việc sử dụng các chế độ xem theo hàng cụ thể:

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

Hai phương thức sau cung cấp vị trí để bạn có thể sử dụng vị trí đó để xác định loại chế độ xem bạn nên sử dụng cho hàng đó .


Tất nhiên, bạn thường không sử dụng Adaptor và Adaptor trực tiếp mà nên sử dụng hoặc lấy từ một trong các lớp con của chúng. Các lớp con của Adaptor có thể thêm chức năng bổ sung thay đổi cách nhận bố cục tùy chỉnh cho các hàng khác nhau. Vì chế độ xem được sử dụng cho một hàng nhất định được điều khiển bởi bộ điều hợp, nên mẹo là để bộ điều hợp trả về chế độ xem mong muốn cho một hàng nhất định. Làm thế nào để làm điều này khác nhau tùy thuộc vào bộ chuyển đổi cụ thể.

Ví dụ: để sử dụng ArrayAd CHƯƠNG ,

  • ghi đè getView()để tăng, điền và trả về chế độ xem mong muốn cho vị trí đã cho. Các getView()phương pháp bao gồm một cơ hội tái sử dụng quan điểm thông qua các convertViewtham số.

Nhưng để sử dụng các dẫn xuất của CthonAd CHƯƠNG ,

  • ghi đè newView()để tăng, điền và trả về chế độ xem mong muốn cho trạng thái con trỏ hiện tại (tức là "hàng" hiện tại) [bạn cũng cần ghi đè bindViewđể tiện ích có thể sử dụng lại chế độ xem]

Tuy nhiên, để sử dụng SimpleC bổngAd CHƯƠNG ,

  • xác định a SimpleCursorAdapter.ViewBindervới một setViewValue()phương thức để tăng, điền và trả về dạng xem mong muốn cho một hàng nhất định (trạng thái con trỏ hiện tại) và "cột" dữ liệu. Phương thức này có thể chỉ định nghĩa các khung nhìn "đặc biệt" và trì hoãn đối với hành vi tiêu chuẩn của SimpleCoderAd CHƯƠNG đối với các ràng buộc "thông thường".

Tra cứu các ví dụ / hướng dẫn cụ thể cho loại bộ điều hợp bạn sử dụng.


Bạn có suy nghĩ về loại bộ điều hợp nào là tốt nhất để thực hiện linh hoạt bộ điều hợp không? Tôi đang thêm một câu hỏi trên diễn đàn cho việc này.
Androider

1
@Androider - "tốt nhất cho sự linh hoạt" là kết thúc rất mở - không có lớp cuối cùng, tất cả sẽ đáp ứng mọi nhu cầu; đó là một hệ thống phân cấp phong phú - tùy thuộc vào việc liệu chức năng trong một lớp con có hữu ích cho mục đích của bạn hay không. Nếu vậy, bắt đầu với lớp con đó; nếu không, di chuyển lên BaseAdapter. Xuất phát từ BaseAd CHƯƠNG sẽ là "linh hoạt" nhất, nhưng sẽ là điều tồi tệ nhất trong việc tái sử dụng và trưởng thành mã vì nó không tận dụng kiến ​​thức và sự trưởng thành đã được đưa vào các bộ điều hợp khác. BaseAdaptercó bối cảnh không chuẩn mà các bộ điều hợp khác không phù hợp.
Bert F

3
+1 cho sự phân biệt tốt giữa CursorAdapterSimpleCursorAdapter.
Giulio Piancastelli

1
cũng lưu ý rằng nếu bạn ghi đè ArrayAdapter, bạn không tạo ra bố cục nào cho nhà xây dựng, miễn là getView()lạm phát và trả về đúng kiểu bố trí
woojoo666

1
Cần lưu ý rằng getViewTypeCount()chỉ được kích hoạt một lần mỗi khi bạn gọi ListView.setAdapter(), không phải cho mọi Adapter.notifyDataSetChanged().
hidro

43

Hãy xem mã dưới đây.

Đầu tiên, chúng tôi tạo bố cục tùy chỉnh. Trong trường hợp này, bốn loại.

thậm chí

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

lẻ

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

tệp trắng

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

hồ sơ đen

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

Sau đó, chúng tôi tạo ra mục listview. Trong trường hợp của chúng tôi, với một chuỗi và một loại.

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

    }

Sau đó, chúng tôi tạo ra một chủ sở hữu xem. Chúng tôi khuyên bạn nên vì HĐH Android giữ tham chiếu bố cục để sử dụng lại mục của bạn khi nó biến mất và xuất hiện lại trên màn hình. Nếu bạn không sử dụng phương pháp này, mỗi lần mục của bạn xuất hiện trên màn hình HĐH Android sẽ tạo một phương thức mới và khiến ứng dụng của bạn bị rò rỉ bộ nhớ.

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

    }

Cuối cùng, chúng tôi tạo bộ điều hợp tùy chỉnh ghi đè getViewTypeCount () và getItemViewType (vị trí 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;
        }

    }

Và hoạt động của chúng tôi là như thế này:

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

    }
}

bây giờ hãy tạo một listview bên trong mainactivity.xml như thế này

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

<bao gồm layout = "@ layout / content_main" /> nơi đến từ
nyxee

Tôi chỉ cần mệnh đề (convertView == null). Không cần 'người xem'
Jomme

14

Trong bộ điều hợp mảng tùy chỉnh của bạn, bạn ghi đè phương thức getView (), như bạn có lẽ đã quen thuộc. Sau đó, tất cả những gì bạn phải làm là sử dụng câu lệnh switch hoặc câu lệnh if để trả về một Dạng xem tùy chỉnh nhất định tùy thuộc vào đối số vị trí được truyền cho phương thức getView. Android thông minh ở chỗ nó sẽ chỉ cung cấp cho bạn một convertView loại phù hợp cho vị trí / hàng của bạn; bạn không cần phải kiểm tra nó có đúng loại không. Bạn có thể giúp Android với điều này bằng cách ghi đè các phương thức getItemViewType () và getViewTypeCount () một cách thích hợp.


4

Nếu chúng ta cần hiển thị loại chế độ xem khác nhau trong chế độ xem danh sách thì việc sử dụng getViewTypeCount () và getItemViewType () trong bộ điều hợp thay vì chuyển đổi chế độ xem VIEW.GONE và VIEW.VISIBLE có thể rất tốn kém trong getView () ảnh hưởng đến cuộn danh sách.

Vui lòng kiểm tra cái này để sử dụng getViewTypeCount () và getItemViewType () trong Adaptor.

Liên kết: việc sử dụng của getviewtypecount


1

ListView được dành cho các trường hợp sử dụng đơn giản như cùng một chế độ xem tĩnh cho tất cả các mục hàng.
Vì bạn phải tạo ViewHolders và sử dụng đáng kể getItemViewType()và hiển thị động các bố cục mục hàng khác nhau của xml, nên bạn nên thử làm điều đó bằng cách sử dụng RecyclerView , có sẵn trong API API 22. Nó cung cấp hỗ trợ và cấu trúc tốt hơn cho nhiều loại chế độ xem.

Hãy xem hướng dẫn này về cách sử dụng RecyclerView để làm những gì bạn đang tìm kiếm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.