LoginSignup
17
15

More than 5 years have passed since last update.

【ListView Tips Vol.2】忙しい人のためのStickyListHeadersListViewの使い方 〜基本編〜

Last updated at Posted at 2015-08-15

AndroidのViewの中でも頻出かつクセのある ListView関連のTips集Vol.2

カテゴリ毎にHeaderが追従するタイプのListViewである、StickyListHeadersListViewの基本的な使い方と使用上の注意点。

はじめに

使い方については以下の本家GitHubを参考にしましょう。

emilsjolander/StickyListHeaders

とはいえ、

  • そんなの読んでる時間ないよ!
  • 英語きっついわー。。。
  • すぐにでもStickyListHeadersの世界を体感したい!

という忙しい人は以下をお読みください。

ライブラリの登録

build.gradle.xmlに以下を追加する。

build.gradle.xml
dependencies {
    compile 'se.emilsjolander:stickylistheaders:2.6.0'
}

これで準備は完了!

「あれっ?使えないぞ?」
という人は、落ち着きましょう。

ネットにつないで、一度BuildしてデータをDLしてくる必要があります。
(本家サイトでは、git cloneのやり方も書いてあります)

StickyListHeadersListViewの基本構造

StickyListHeadersListViewはその名の通りカスタムListViewです。
なので、ListViewの基本的な使い方に不安がある人は、まずそちらを復習しましょう!
(ここでは割愛します)

■ データBindはListViewと同じ

ListViewにデータを入れ込む代表的な方法としてAdapter経由で行うものがありますが、StickyListHeadersListViewでも同じ。
基本はStickyListHeadersAdapterをimplementsしたAdapterを使いましょう。

■ 何が違うか -> Headerがsticky

このViewを使いたい人には自明ですが、StickyListHeadersListViewはHeader部分がカテゴリ毎に追従してきます。
ListViewではgetViewに処理を書けば大体OKですが、StickyListHeadersListViewではHeaderの動きを制御する getHeaderId、HeaderのViewを扱う getHeaderViewにきちんと処理を書かないとうまく動きません。

■ Headerがstickyになる仕組み

詳細は後述しますが、getHeaderIdにて、アイテム行がどのHeaderグループに所属するのかを判定しています。

例えば、

リストPosition getHeaderIdの返り値(※1) Headerグループ Header生成(※2)
0 1 A
1 1 A -
2 1 A -
3 2 B
4 2 B -
5 3 C
6 4 D
7 4 D -
8 4 D -
9 4 D -
10 5 E
11 5 E -

のようになります。
getHeaderIdの返り値は連続していなくて良いですが、 longである必要があります。
キャストされるので、int型あたりが返っていれば大丈夫です。

※1: 返り値は適当です(long型であればなんでも)
※2: 「-」は生成されたものが生き残るので再生成はされない

仕組み自体は簡単ですね。


つまり、Stickyに動かしたい範囲(=所属させたいHeaderグループ)毎に、同じgetHeaderIdを返すようにすればよい!!

ということです!

実装方法

■ ファイルと役割

以下のファイルを参考にしてください。

ファイル名 役割
StickyActivity.java RootActivity
StickyAdapter.java データバインド用Adapter
main_sticky.xml StickyActivityのレイアウト
sticky_header_row.xml StickyListのHeaderレイアウト
sticky_item_row.xml StickyListのItemレイアウト

注意点はStickyAdapter.javaにおけるgetHeaderIdの扱いです。

■ ファイル詳細

まずはRootActivityです。

StickyActivkty.java
public class StickyActivity extends Activity {

    private StickyListHeadersListView mListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_sticky);

        mListView = (StickyListHeadersListView) findViewById(R.id.sticky_listview);
    }

    @Override
    protected void onResume() {
        super.onResume();

        StickyAdapter adapter = new StickyAdapter(this, android.R.layout.simple_list_item_1, createSampleArray());
        mListView.setAdapter(adapter);
    }

    // Adapterに渡すサンプルListを生成します
    private List<String> createSampleArray() {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add("position: " + i);
        }
        return list;
    }
}


次はデータを入れ込むAdapterを見てみましょう。
注意点はこれまでお話してきた通りです。

StickyAdapter.java
public class StickyAdapter extends ArrayAdapter<String> implements StickyListHeadersAdapter {

    private LayoutInflater mInflater;

    public StickyAdapter(Context context, int resource, List<String> itemList) {
        super(context, resource, itemList);

        mInflater = LayoutInflater.from(context);
    }

    // Headerのレイアウトを定義します
    @Override
    public View getHeaderView(int i, View view, ViewGroup viewGroup) {
        HeaderViewHolder holder;

        if (view == null) {
            view = mInflater.inflate(R.layout.sticky_header_row, null);
            holder = new HeaderViewHolder(view);
            view.setTag(holder);
        } else {
            holder = (HeaderViewHolder) view.getTag();
        }
        holder.textView.setText("Header: " +getHeaderItem(i));

        return view;
    }

    // 【重要】Headerグループ毎に同じ値を返すようにしましょう
    // ここではItemポジションを5で割った商が同じになるものをグループにします(5個/グループ)
    // getHeaderItemを参考にしましょう
    @Override
    public long getHeaderId(int i) {
        return getHeaderItem(i);
    }

    // getItemにならい、Header版も作る優しさが持てるといいですね
    // 各アイテムのHeaderはポジションを5で割った商です
    // getHeaderIdを参考にしましょう
    public int getHeaderItem(int position) {
        return position / 5;
    }

    // おなじみのgetViewです
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ItemViewHolder holder;

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.sticky_item_row, parent, false);
            holder = new ItemViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (ItemViewHolder) convertView.getTag();
        }

        // Adapterに渡されたテキストを入れます
        holder.textView.setText(getItem(position));

        return convertView;
    }

    public static class HeaderViewHolder {
        TextView textView;

        // コンストラクタ内でidバインドを行なうとスッキリします
        public HeaderViewHolder(View view) {
            textView = (TextView) view.findViewById(R.id.header_textview);
        }
    }

    public static class ItemViewHolder {
        TextView textView;

        // コンストラクタ内でidバインドを行なうとスッキリします
        public ItemViewHolder(View view) {
            textView = (TextView) view.findViewById(R.id.item_textview);
        }
    }
}



お疲れ様です。
あとはレイアウト関連をちゃちゃっと片付けましょう。

main_sticky.xml
<?xml version="1.0" encoding="utf-8"?>
<se.emilsjolander.stickylistheaders.StickyListHeadersListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sticky_listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" />


sticky_header_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_red_light"
    android:orientation="vertical">

    <TextView
        android:id="@+id/header_textview"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:gravity="center_vertical"
        android:text="test"
        android:textColor="@android:color/white"
        android:textSize="14sp" />

</LinearLayout>


sticky_item_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="56dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/item_textview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:text="item"
        android:textSize="16sp" />
</LinearLayout>

実装結果

以下の様な挙動ができたら成功です!
(marginなど各種数値は最低限しかセットしていません)


sticky_sample_app.gif


それではStickyな世界を楽しみましょう!

※注意
レイアウトはもっとカッコよくしましょう。
サンプルはダサくて泣けます。。笑

ListView Tips集

17
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
15