Android Advent Calendar 12/18 担当の @daichan4649 です。
豪華メンバーがものすごく有意義なネタを記載されている中で、
空気を無視して ListView について誰得なネタを書いてみるとします。
ListViewには項目が多い場合に 「高速スクローラ」 を表示する機能がありますよね。
このとき 「スクロール中に項目のどのあたりを表示しているか」 を示す、
ガイドみたいなヤツを表示することができたりもします。
高速スクローラ、ガイド表示の実装に必要なのはこの2つ。
- AbsListView#setFastScrollEnabled(true)
- 1のListViewに設定するAdapterに SectionIndexer をimplementsする
1で高速スクローラを有効にし、
2でガイド表示内容を実装する、と。
実装してみると実際のイメージはこんなかんじになります。
この例ではガイドに 「画面内先頭要素のindex値」 をそのまま表示してます。
右側に表示されているスクローラをドラッグすると、
リスト先頭項目に合わせてガイド表示内容も変わっていく、という感じです。
本題に入ります。
このガイド。
普通にいじろうとすると 指定した文字列を表示する、くらいしかできない。
濃いグレーの正方形 は編集できない。
編集できないと言われるといじりたいじゃないですか。
ということで、framework のソース読んで試してみました。
とりあえず今回はこのグレーの正方形を変えるところを実験してみるとします。
いつものドロイド君アイコン画像に差し替えてみたり。
では、どんなかんじで差し替えたかを書いてみます。
まず、基本から。
AbsListView#setFastScrollEnabled(true) を呼び出すと
「高速スクローラとガイド」 を管理している FastScrollerインスタンス が
AbsListView内で生成されます。
しかし、ガイドのインスタンスが FastScrollerクラス 内に隠蔽されていて、
外部から編集できないようになっていたりします。
↓の FastScrollerインスタンス の中をいじりたいのですが。。。
// AbsListView.java public void setFastScrollEnabled(boolean enabled) { mFastScrollEnabled = enabled; if (enabled) { if (mFastScroller == null) { mFastScroller = new FastScroller(getContext(), this); } } else { if (mFastScroller != null) { mFastScroller.stop(); mFastScroller = null; } } }
とまぁ、どうしようもなかったのでリフレクションでいじりましたw
リフレクションでいじるポイント、タイミングは以下。
- AbsListView#setFastScrollEnabled(true) を呼び出す
- その直後にリフレクションで内部のFastScrollerインスタンスを差し替える
実際のソースはこんな感じになります。
https://gist.github.com/1384757
// 今回のポイントになるリフレクション部分だけ貼りつけてみます @Override protected void onCreate(Bundle savedInstanceState) { (抜粋) final ListView listView = (ListView) findViewById(R.id.listview); // 高速スクローラ有効化設定 listView.setFastScrollEnabled(true); // AbsListView#setFastScrollEnabled 直後に実行 Drawable overlay = getResources().getDrawable(R.drawable.ic_launcher); customizeFastScroller(listView, overlay); } private void customizeFastScroller(AbsListView listView, Drawable overlay) { try { // 新FastScrollerインスタンス生成 Class<?> clazz = Class.forName("android.widget.FastScroller"); Constructor<?> constructor = clazz.getConstructor(Context.class, AbsListView.class); Object newFastScroller = constructor.newInstance(this, listView); // ガイド用drawable(グレーの四角)を上書き Field fieldOverlay = clazz.getDeclaredField("mOverlayDrawable"); fieldOverlay.setAccessible(true); fieldOverlay.set(newFastScroller, overlay); // FastScrollerインスタンス(オリジナル)を上書き Field orgFastScroller = AbsListView.class.getDeclaredField("mFastScroller"); orgFastScroller.setAccessible(true); orgFastScroller.set(listView, newFastScroller); } catch (Exception e) { e.printStackTrace(); } }
見てわかるとおり、強引な手法ですw
この方法を使えば 「スクローラ画像」 自体も差し替え可能ですね。
それについてはまた後日書こうと思います。(gistにはコード載せてます)
最後に。
このような素晴らしい企画に参加させていただきまして、
@youten_redo さん、本当にありがとうございました!
明日(12/19)の担当は @patorash さんです!