行ごとに異なるレイアウトのAndroid ListView


348

行ごとに異なるレイアウトを含む単一のListViewを作成する最良の方法を決定しようとしています。カスタムビュー+カスタム配列アダプターを作成してリストビュー全体のカスタム行をサポートする方法を知っていますが、ListViewにさまざまな行スタイルを実装するにはどうすればよいですか?


1
更新:AndroidのRecyclerViewを使用して複数の行レイアウトのデモ code2concept.blogspot.in/2015/10/...を
nitesh

回答:


413

レイアウトの種類がいくつあるかわかっているので、これらのメソッドを使用することができます。

getViewTypeCount() -このメソッドは、リストにある行のタイプの数に関する情報を返します

getItemViewType(int position) -位置に基づいて使用するレイアウトタイプの情報を返します

次に、レイアウトがnullの場合にのみレイアウトを拡張し、を使用してタイプを決定しgetItemViewTypeます。

詳細については、このチュートリアルをご覧ください。

あなたがコメントで説明した構造のいくつかの最適化を達成するために、私はお勧めします:

  • と呼ばれるオブジェクトにビューを保存していますViewHolder。メソッドでfindViewById()毎回呼び出す必要がないため、速度が向上しますgetViewAPIデモのList14を参照してください。
  • プロパティのすべての組み合わせに準拠する1つの汎用レイアウトを作成し、現在の位置にない場合は一部の要素を非表示にします。

お役に立てば幸いです。XMLスタブにデータ構造とそれを行にマップする正確な情報を提供できれば、より正確なアドバイスを提供できます。ピクセル単位。


ブログありがとうございましたが、チェックボックスを追加しました。最初のアイテムをチェックしてリストをスクロールするという問題がありました。チェックされる奇妙な匿名のアイテム。そのためのソリューションを提供できますか。ありがとう
Lalit Poptani、2011年

2
これをもう一度掘り下げて申し訳ありませんが、実際には、getItemViewTypeを使用してそれぞれ膨張する個別のレイアウトファイルではなく、1つの大きなレイアウトファイルを用意して、その一部の表示を制御することをお勧めします。
マキボ2013年

2
あなたもそれを行うことができます。私はまだここで公開されている方法を好みますが。達成したいことがより明確になります。
クリスティアン2013年

ただし、複数のレイアウト戦略では、setTagにはビューホルダーを1つしか含めることができないため、ビューホルダーを適切に使用できません。行レイアウトが再び切り替わるたびに、findViewById()を呼び出す必要があります。これにより、リストビューのパフォーマンスが非常に低くなります。私は個人的にそれを経験しましたか?
pyus13 14

@ pyus13では、1つのビューホルダーで必要なだけビューを宣言できます。ビューホルダーで宣言されたすべてのビューを使用する必要はありません。サンプルコードが必要な場合はお知らせください。投稿します。
Ahmad Ali Nasir 2014年

62

カスタムビュー+カスタム配列アダプターを作成して、リストビュー全体のカスタム行をサポートする方法を知っています。しかし、1つのリストビューでさまざまな行スタイルをどのようにサポートできますか?

あなたはすでに基本を知っています。提供されている行/カーソル情報に基づいて、異なるレイアウト/ビューを返すようにカスタムアダプターを取得する必要があるだけです。

A ListViewAdapterViewから派生しているため、複数の行スタイルをサポートできます。

AdapterViewは、子がアダプタによって決定されるビューです。

Adapterを見ると、行固有のビューの使用を説明するメソッドが表示されます。

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

後者の2つの方法は位置を提供するので、その位置を使用して、その行に使用するビューのタイプ決定できます。


もちろん、通常は、AdapterViewとAdapterを直接使用するのではなく、サブクラスの1つを使用または派生させます。Adapterのサブクラスは、異なる行のカスタムレイアウトを取得する方法を変更する追加機能を追加する場合があります。 特定の行に使用されるビューはアダプターによって駆動されるため、秘訣はアダプターに特定の行に必要なビューを返させることです。これを行う方法は、特定のアダプターによって異なります。

たとえば、ArrayAdapterを使用するには

  • オーバーライドgetView()して、特定の位置に必要なビューを膨らませ、入力し、返します。このgetView()メソッドには、convertViewパラメータを。

しかし、CursorAdapterの派生物を使用するには

  • オーバーライドnewView()して、現在のカーソル状態(つまり、現在の「行」)に必要なビューを膨らませ、入力し、返します[ bindViewウィジェットがビューを再利用できるように、オーバーライドする必要もあります]

ただし、SimpleCursorAdapterを使用するには、

  • 特定の行(現在のカーソルの状態)とデータの「列」に必要なビューを拡張、入力、および返すメソッドを使用してSimpleCursorAdapter.ViewBinderを定義しますsetViewValue()。このメソッドは、「特別な」ビューのみを定義し、「通常の」バインディングに対するSimpleCursorAdapterの標準的な動作を遅延させることができます。

最終的に使用するアダプターの種類について、具体的な例/チュートリアルを調べます。


これらのアダプタータイプのうち、アダプターの柔軟な実装に最適なものは何ですか?これについて、ボードに別の質問を追加します。
Androider、2011

1
@Androider-「最高の柔軟性」は非常に制限がありません-すべてのニーズを満たすすべての、すべてのクラスはありません。それは豊富な階層です-それはあなたの目的に役立つサブクラスに機能があるかどうかにかかっています。その場合は、そのサブクラスから始めます。そうでない場合は、上に移動しBaseAdapterます。BaseAdapterから派生するのが最も「柔軟」ですが、他のアダプターにすでに組み込まれている知識と成熟度を利用しないため、コードの再利用と成熟度が最悪になります。 BaseAdapter他のアダプタが適合しない非標準のコンテキストのためにあります。
バートF

3
間の微妙な区別のための+1 CursorAdapterSimpleCursorAdapter
Giulio Piancastelli

1
また、をオーバーライドする場合、適切なタイプのレイアウトArrayAdaptergetView()膨らませて返す限り、コンストラクターにどのレイアウトを指定してもかまいません
woojoo666

1
getViewTypeCount()が呼び出さListView.setAdapter()れるたびに1回だけトリガーされ、毎回トリガーされるわけではないことに注意してくださいAdapter.notifyDataSetChanged()
hidro

43

以下のコードを見てください。

まず、カスタムレイアウトを作成します。この場合、4種類。

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

    }

その後、ビューホルダーを作成します。Android OSでは、アイテムが消えて画面に表示されたときにアイテムを再利用するために、レイアウト参照を保持することを強くお勧めします。このアプローチを使用しない場合、アイテムが画面に表示されるたびにAndroid OSが新しいアイテムを作成し、アプリがメモリリークを引き起こします。

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 position)をオーバーライドするカスタムアダプターを作成します。

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>

<include layout = "@ layout / content_main" />どこから来るか
nyxee

(convertView == null)句だけが必要でした。'viewholder'は必要ありませんでした
Jomme

14

カスタム配列アダプターでは、おそらく慣れているように、getView()メソッドをオーバーライドします。次に、switchステートメントまたはifステートメントを使用して、getViewメソッドに渡された位置引数に応じて特定のカスタムビューを返すだけです。Androidは、自分の位置/行に適切なタイプのconvertViewしか提供しないという点で賢いです。正しいタイプであることを確認する必要はありません。getItemViewType()およびgetViewTypeCount()メソッドを適切にオーバーライドすることで、Androidをこれで支援できます。


4

リストビューで異なるタイプのビューを表示する必要がある場合は、ビューを切り替える代わりにアダプタでgetViewTypeCount()とgetItemViewType()を使用すると便利です。VIEW.GONEとVIEW.VISIBLEは、getView()内で非常に負荷の高いタスクになる可能性があります。リストのスクロールに影響します。

アダプタでのgetViewTypeCount()およびgetItemViewType()の使用については、こちらを確認してください。

リンク:the-use-of-getviewtypecount


1

ListViewは、すべての行アイテムの同じ静的ビューのような単純なユースケースを対象としています。
ViewHoldersを作成してを大幅に使用しgetItemViewType()、さまざまな行アイテムレイアウトxmlを動的に表示する必要があるため、Android API 22で利用可能なRecyclerViewを使用してそれを試す必要があります。これにより、複数のビュータイプのサポートと構造が改善されます。

RecyclerViewを使用して探していることを行う方法について、このチュートリアルを確認してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.