読者です 読者をやめる 読者になる 読者になる

アナログ金木犀

つれづれなるまままにつれづれする

RecyclerView.ViewHolderの細かい話

受肉したノロいと化し(ノロになりました)、療養 & パンデミックを防ぐために今週はずっと家に引きこもってます、釘宮です。

おかげで今年一楽しみにしていた柗亭新年会にも行けず、ATI発揮してconnpassページ作ったりもろもろ頑張ってたkyobashi.dexにも参加できずで、なるほどこれが2017年かと噛みしめております。いっそ1月中に今年の不運全てを出しきりたい。

体調の方は、だいぶ動けるようになりちょっと買い物でもと外に出てみたら「うん、死にそう」と思うくらいには回復しております。 家でずっと座ってプログラミングする分には特に支障はありません。

ということで、動けない腹いせに今更RecyclerViewについての細かい記事を。

「 Activityでは createIntent を」だったり、「FragmentではnewInstanceを」みたいな話に近いです。

よく見かけるコード

RecyclerView.AdapteronCreateViewHolderでよく以下のコードを見かけます。

public class MyAdapter extends RecyclerView.Adapter<HogeViewHolder> {
    private LayoutInfalter infalater;
    :
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         return new HogeViewHolder(inflater.inflate(R.layout.hoge, parent, false)); // ★
    }
    :
}

viewtypeが複数ある場合はこんな感じですかね。

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private LayoutInfalter infalater;
    :
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case VIEW_TYPE_HOGE: 
                 return new HogeViewHolder(inflater.inflate(R.layout.hoge, parent, false));// ★
             case VIEW_TYPE_FUGA: 
                 return new FugaViewHolder(inflater.inflate(R.layout.fuga, parent, false));// ★
             :
         }
    }
    :
}

何が良くないと思ったか

別段悪いコードじゃないと思います。

が、ただ大抵の場合において HogeViewHolderに対応するlayoutは R.layout.hoge と決まっています。そしてどのlayoutを使うかを知るべきなのは HogeViewHolder のみでいいはず 。上のコードではそのことを Adapter が知ってしまっています。

この一点においてだけちょっといただけないなと思うんです。

改善

下記のようにstatic ファクトリメソッドからのみ作るようにして、その中でレイアウトの指定をしてあげれば、すっきりします。

public class HogeViewHolder extends RecyclerView.ViewHolder {
    public static HogeViewHolder create(
            LayoutInflater inflater,
            ViewGroup parent,
            boolean attachToRoot
    ) {
        return new HogeViewHolder(inflater.inflate(R.layout.hoge, parent, attachToRoot));
    }

    private HogeViewHolder(View itemView) {
        super(itemView);
    }
}
public class MyAdapter extends RecyclerView.Adapter<HogeViewHolder> {
    private LayoutInfalter infalater;
    :
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         return HogeViewHolder.create(inflater, parent, false); // ★
    }
    :
}

viewtypeが複数ある場合。

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private LayoutInfalter infalater;
    :
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case VIEW_TYPE_HOGE: 
                 return HogeViewHolder.create(inflater, parent, false); // ★
             case VIEW_TYPE_FUGA: 
                 return FugaViewHolder.create(inflater, parent, false); // ★
             :
         }
    }
    :
}

もちろん、同じ ViewHolder で複数のレイアウトを使うという場合や (自分はあまりそういう使い方をしたことないですが)、DataBinding + generics で解決したりするような場合はこの限りじゃないです。

以上です。些細な話で申し訳ないです。上記のように変えたところでビジネス的価値がどれだけ上がるかどうかはわかりませんが、 new HogeViewHolder(inflater.inflate(R.layout.fuga, parent, false)); みたいなレイアウトの指定ミスはなくなりますね。

(ちなみに、 kyobashi.dexで話そうと思ってた内容はこれじゃないです。それはまた別の機会に...)