1. ホーム
  2. android

[解決済み] Recyclerviewと異なるタイプの行のインフレーションの処理

2022-07-30 19:03:18

質問

私は、新しい RecyclerView の例を見つけられませんでした。 RecyclerView で、異なるタイプの行/カードビューが膨張している例は見つかりませんでした。

とは ListView をオーバーライドして getViewTypeCountgetItemViewType で、異なるタイプの行を処理するためのものです。

私はそれを"old"のように行うことになっていますか、私は何かで行う必要があります LayoutManager ? どなたか正しい方向を教えていただけませんか?なぜなら、私は1つのタイプの例しか見つけることができないからです。

私は少し異なるカードのリストを持ちたいのです。または、私はちょうど scrollViewcardViews の中にある...アダプターなしで作って recyclerView ?

どのように解決するのですか?

iOSのUITableViewのような行/項ロジックをAndroidで扱うのは、iOSほど簡単ではありませんが、RecyclerViewを使うと、できることの柔軟性ははるかに高くなります。

結局のところ、Adapterでどのようなビューを表示しているのかを把握することが重要です。

結局のところ、Adapter でどのようなビューを表示しているのかを把握する方法がすべてです。

アダプタはふたつのメソッドを公開しており、これらはオーバーライドする必要があります。

getItemViewType(int position)

このメソッドのデフォルトの実装は常に0を返し、ビューの種類が1つしかないことを示します。あなたのケースでは、そうではありません。したがって、どの行がどのビュータイプに対応するかを表明する方法を見つける必要があります。行とセクションでこれを管理する iOS とは異なり、ここでは依存するインデックスは 1 つだけで、位置がセクションヘッダーに関連するときと、通常の行に関連するときを知るために、開発者のスキルを使用する必要があります。

createViewHolder(ViewGroup parent, int viewType)

いずれにせよこのメソッドをオーバーライドする必要がありますが、通常、人々は viewType パラメータを無視するだけです。ビュータイプに応じて、正しいレイアウトリソースをインフレートし、それに応じてビューホルダーを作成する必要があります。RecyclerViewは、異なるビュータイプの衝突を回避する方法で、異なるビュータイプのリサイクルを処理します。

のようなデフォルトのLayoutManagerを使おうと思っているならば、そのようなリソースは必要ありません。 LinearLayoutManager のようなデフォルトの LayoutManager を使うつもりなら、問題ないでしょう。もし、あなた自身の LayoutManager の実装を作ろうと思っているなら、もう少し頑張る必要があるでしょう。あなたが実際に扱わなければならない唯一の API は findViewByPosition(int position) で、これは特定の位置で与えられたビューを提供します。おそらく、あなたはどのような タイプ に応じて異なるレイアウトをしたいと思うでしょうから、いくつかのオプションがあります。

  1. 通常、ViewHolderパターンを使用する場合、ビューホルダーでビューのタグを設定します。これを表現するフィールドをビューホルダーに追加することで、レイアウトマネージャーの実行時にこれを使用して、ビューがどのようなタイプであるかを見つけることができます。

  2. どの位置がどのビュータイプに相関するかを決定する関数が必要になるので、このメソッドを何らかの方法でグローバルにアクセスできるようにするとよいでしょう(データを管理するシングルトンクラスとか?)、そして、位置に従って同じメソッドを単にクエリーすることができます。

以下はコードサンプルです。

// in this sample, I use an object array to simulate the data of the list. 
// I assume that if the object is a String, it means I should display a header with a basic title.
// If not, I assume it's a custom model object I created which I will use to bind my normal rows.
private Object[] myData;

public static final int ITEM_TYPE_NORMAL = 0;
public static final int ITEM_TYPE_HEADER = 1;

public class MyAdapter extends Adapter<ViewHolder> {

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        if (viewType == ITEM_TYPE_NORMAL) {
            View normalView = LayoutInflater.from(getContext()).inflate(R.layout.my_normal_row, null);
            return new MyNormalViewHolder(normalView); // view holder for normal items
        } else if (viewType == ITEM_TYPE_HEADER) {
            View headerRow = LayoutInflater.from(getContext()).inflate(R.layout.my_header_row, null);
            return new MyHeaderViewHolder(headerRow); // view holder for header items
        }
    }


    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        final int itemType = getItemViewType(position);

        if (itemType == ITEM_TYPE_NORMAL) {
            ((MyNormalViewHolder)holder).bindData((MyModel)myData[position]);
        } else if (itemType == ITEM_TYPE_HEADER) {
            ((MyHeaderViewHolder)holder).setHeaderText((String)myData[position]);
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (myData[position] instanceof String) {
            return ITEM_TYPE_HEADER;
        } else {
            return ITEM_TYPE_NORMAL;
        }
    }

    @Override
    public int getItemCount() {
        return myData.length;
    }
}

これらのビューホルダーがどのように見えるかのサンプルは以下のとおりです。

public MyHeaderViewHolder extends ViewHolder {

    private TextView headerLabel;    

    public MyHeaderViewHolder(View view) {
        super(view);

        headerLabel = (TextView)view.findViewById(R.id.headerLabel);
    }

    public void setHeaderText(String text) {
        headerLabel.setText(text);
    }    
}


public MyNormalViewHolder extends ViewHolder {

    private TextView titleLabel;
    private TextView descriptionLabel;    

    public MyNormalViewHolder(View view) {
        super(view);

        titleLabel = (TextView)view.findViewById(R.id.titleLabel);
        descriptionLabel = (TextView)view.findViewById(R.id.descriptionLabel);
    }

    public void bindData(MyModel model) {
        titleLabel.setText(model.getTitle());
        descriptionLabel.setText(model.getDescription());
    }    
}

もちろん、このサンプルはデータソース (myData) がこのように簡単にアダプタを実装できるように構築されていることを前提にしています。例として、名前のリストを表示し、名前の一文字目が変わるたびにヘッダを表示する (リストはアルファベット順とする) データソースを構築する方法を紹介します。

// Assume names & descriptions are non-null and have the same length.
// Assume names are alphabetized
private void processDataSource(String[] names, String[] descriptions) {
    String nextFirstLetter = "";
    String currentFirstLetter;

    List<Object> data = new ArrayList<Object>();

    for (int i = 0; i < names.length; i++) {
        currentFirstLetter = names[i].substring(0, 1); // get the 1st letter of the name

        // if the first letter of this name is different from the last one, add a header row
        if (!currentFirstLetter.equals(nextFirstLetter)) {
            nextFirstLetter = currentFirstLetter;
            data.add(nextFirstLetter);
        }

        data.add(new MyModel(names[i], descriptions[i]));
    }

    myData = data.toArray();
}

この例はかなり特殊な問題を解決するためのものですが、リサイクラーでさまざまな種類の行を処理する方法の概要を理解し、自分のコードに必要な修正を加えて、自分のニーズに合うようにできるようになるとよいでしょう。