行の最後にフッターの行を追加する理由としては、現在のandroidのUIデザインの定番となっているFloatingActionButtonを実装した場合に、一番最後の行の内容が被って見えないという事態が起きるためです。

フッターを追加した場合の表示イメージは以下です。

 

一番下までスクロールすると・・・

 → 

上記のように最後にフッターとなる空白行が追加されている状態です。

この実装について記載していきたいと思います。

 

ListViewを利用していた場合であれば、addFooterViewというIFを利用すればフッターが追加されるようですが、RecyclerViewを利用している場合には、自前で用意する必要があります。

RecyclerViewを利用したリスト表示の実装については、以前のブログ「[Java]ListViewからRecyclerViewに変更して並べ替えを実装」を参考にしてください。

 

1.フッター用セルのxmlを用意する

データを表示する行とは別のレイアウトとなるフッター用のxmlファイルを用意します。
今回の例では何も表示しない空白xmlとなるので、layout/row_footer.xmlを用意します。

<!--?xml version="1.0" encoding="utf-8"?-->
<linearlayout android:id="@+id/row_footer" android:layout_height="80dp" android:layout_width="match_parent" android:orientation="horizontal" xmlns:android="http://schemas.android.com/apk/res/android">

</linearlayout>

2.RecyclerView.Adapterを継承したクラスに処理を追加していく

2.1.通常セルとフッターセルの種別を追加する

クラスのメンバ変数に以下のような定数を定義します。値は何でもOKです。

private static final int TYPE_ITEM = 1;
private static final int TYPE_FOOTER = 2;

2.2.getItemCountメソッドをOverrideする

通常であれば、表示するデータ数をreturn値として返却するのですが、フッター行を1行追加するため、+1して返却するように以下のような処理で実装します。

    @Override
    public int getItemCount() {
        if (listData != null) {
            return listData.size() + 1;
        } else {
            return 0;
        }
    }

2.3.getItemViewTypeメソッドをOverrideする

通常のデータを表示するxmlとフッターを表示するxmlを区別するために、最後のデータ+1以上の時はフッターのタイプを返却するように以下のような実装をします。

    @Override
    public int getItemViewType(int position) {
        if (position >= listData.size()) {
            return TYPE_FOOTER;
        }
        return TYPE_ITEM;
    }

2.4.onCreateViewHolderをOverrideする

View Holderを生成してreturnするメソッドですが、適用するxmlを通常の場合とフッターの場合で区別して適用する実装をします。

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_ITEM) {
            return new ViewHolder(inflater.inflate(R.layout.row, parent, false));
        } else if (viewType == TYPE_FOOTER) {
            return new ViewHolder(inflater.inflate(R.layout.row_footer, parent, false));
        } else {
            return  null;
        }
    }

2.5.onBindViewHolderをOverrideする

RecyclerViewを利用してデータを表示する場合には、既にOverrideしていると思いますが、フッター行の場合には、バインドするデータが存在しないため、何もせずにreturnするように条件を追加します。

    public void onBindViewHolder(ViewHolder holder, final int position) {
        // フッターの場合にはデータをバインドしない
        if (position >= listData.size()) return;

        String id = ((HashMapHashMap<?, ?>) listData.get(position)).get("id").toString();
        …

ポイントとなる部分は以上となります。
最後にコードの全体を以下に記載します。

public class ListViewAdapter extends RecyclerView.Adapter<ListViewAdapter.ViewHolder> implements Filterable {

    // private Context context;
    private static final int TYPE_ITEM = 1;
    private static final int TYPE_FOOTER = 2;

    private LayoutInflater inflater;
    private List<? extends Map<String, ?>> listData;

    public class ViewHolder extends RecyclerView.ViewHolder {
        Long  id;
        TextView title;

        public ViewHolder(final View itemView) {
            super(itemView);
            // フッターの場合には何も設定しない
            if (itemView.getId() == R.id.row_footer) {
                return;
            }
            title = (TextView) itemView.findViewById(R.id.title);
        }
    }

    public ListViewAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to, AdapterListener listener) {
        this.inflater = LayoutInflater.from(context);
        this.listData = data;
    }

    @Override
    public int getItemCount() {
        if (listData != null) {
            return listData.size() + 1;
        } else {
            return 0;
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (position >= listData.size()) {
            return TYPE_FOOTER;
        }
        return TYPE_ITEM;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_ITEM) {
            return new ViewHolder(inflater.inflate(R.layout.row, parent, false));
        } else if (viewType == TYPE_FOOTER) {
            return new ViewHolder(inflater.inflate(R.layout.row_footer, parent, false));
        } else {
            return  null;
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        // フッターの場合にはデータをバインドしない
        if (position >= listData.size()) return;

        String id = ((HashMap<?, ?>) listData.get(position)).get("id").toString();
        String title = ((HashMap<?, ??>) listData.get(position)).get("title").toString();
        holder.id = new Long(id);
        holder.title.setText(title);
        holder.itemView.setTag(holder);
    }
}

動作を見てみたい場合には、シンプルな操作感のパスワード管理アプリ「パスワードメモ」のアプリをGoogle Playで公開していますので確認してみてください。