[Android] ListFragment の View をカスタマイズする方法 - adakoda - http://goo.gl/UBgyu
で、onCreateView() を override してカスタムレイアウトを指定すると setEmptyText() で落ちるという話があって、正しい ID を指定してないからでは〜?的な流れになったのですが、どうもそうではないようなので調べてみました。
まず、setEmptyText(CharSequence text) は API Level 11 (Android 3.0) で追加された API です。
結果をまとめると
・ListActivity
・setEmptyView() : 使える
・getEmptyView() : 使える
・@android:id/list, @android:id/empty を使った
カスタムレイアウト : 使える
・ListFragment
・@android:id/list, @android:id/empty を使った
カスタムレイアウト : 使える
・カスタムレイアウトを使わない場合
・getListView().setEmptyView() : なにもおこらない
・getListView().getEmptyView() : 使える
・setEmptyText() : 使える
・カスタムレイアウトを使った場合
・getListView().setEmptyView() : なにもおこらない
(@android:id/empty がそのまま表示される)
・getListView().getEmptyView() : 使える
・setEmptyText() : 落ちる
ListFragment で、カスタムレイアウトで setEmptyText() を使いたい場合は、次のように override しておくこと
- @Override
- public void setEmptyText(CharSequence text) {
- TextView tv = (TextView)getListView().getEmptyView();
- tv.setText(text);
- }
ちなみに、このときのカスタムレイアウトはこんな感じ
- <?xml version="1.0" encoding="utf-8"?>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:drawSelectorOnTop="false" />
- <TextView
- android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="No data"
- android:textSize="30sp"/>
- </FrameLayout>
さて、結論をいったので、それまでの過程もちょっと紹介。
まずは、ListFragment の onCreateView() で生成される View のなかから empty 用っぽい View を見つけて、それの ID から resourceName とればわかるんじゃね?と思ってやってみました。
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View v = super.onCreateView(inflater, container, savedInstanceState);
- Log.d("NoteListFragment", v.getClass().getName() + ", " + v.getId());
- if (v instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) v;
- for (int i = 0; i < vg.getChildCount(); i++) {
- View child = vg.getChildAt(i);
- String resourcename = getResourceNameByID(android.R.id.class, child.getId());
- Log.d("NoteListFragment", child.getClass().getName() + ", "
- + child.getId() + ", " + resourcename);
- if(child instanceof ViewGroup) {
- ViewGroup vg2 = (ViewGroup) child;
- for (int j = 0; j < vg2.getChildCount(); j++) {
- View child2 = vg2.getChildAt(j);
- String resourcename2 = getResourceNameByID(android.R.id.class, child2.getId());
- Log.d("NoteListFragment", child2.getClass().getName() + ", "
- + child2.getId() + ", " + resourcename2);
- }
- }
- }
- }
- return v;
- }
- public String getResourceNameByID(Class<?> clazz, int resourceId)
- throws IllegalArgumentException {
- Field[] fields = clazz.getFields();
- for (Field f : fields) {
- try {
- if (resourceId == f.getInt(null)) {
- return f.getName();
- }
- } catch (Exception e) {
- e.printStackTrace();
- throw new IllegalArgumentException();
- }
- }
- return null;
- }
リフレクション。リフレクション。
結果はこんな感じ
07-05 13:46:40.090: DEBUG/NoteListFragment(30597): android.widget.FrameLayout, -1
07-05 13:46:40.090: DEBUG/NoteListFragment(30597): android.widget.LinearLayout, 16908904, null
07-05 13:46:40.090: DEBUG/NoteListFragment(30597): android.widget.ProgressBar, -1, null
07-05 13:46:40.090: DEBUG/NoteListFragment(30597): android.widget.TextView, -1, null
07-05 13:46:40.090: DEBUG/NoteListFragment(30597): android.widget.FrameLayout, 16908905, null
07-05 13:46:40.090: DEBUG/NoteListFragment(30597): android.widget.ListView, 16908298, list
07-05 13:46:40.090: DEBUG/NoteListFragment(30597): android.widget.TextView, 16908906, null
ListView には list (= android.R.id.list) が割当てられているのに、その下の TextView (EmptyView だと思われる) には null が入っている。つまり、android.R.id.** に対象の ID がなかったということです。コードで動的に生成してるのかな?
念のためこの TextView が EmptyView なのかチェック
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- ...
- setEmptyText(getActivity().getString(R.string.no_notes));
- Log.d("NoteListFragment", getListView().getEmptyView().getId() + "");
- }
結果は
07-05 13:56:22.510: DEBUG/NoteListFragment(30953): 16908906
ビンゴ。
ちなみに setEmptyText() する前に getListView().getEmptyView().getId() するとヌルポになります。
EmptyView が生成されてないんですね。このことからも EmptyView が動的に生成されてるのがわかります。
なので、レイアウトをカスタムしないで setEmptyText を override するときは super.setEmpty() を忘れずに最初にしてください。そうしないとその後の getListView().getEmptyView() には null が返ってきてしまいます。
- @Override
- public void setEmptyText(CharSequence text) {
- super.setEmptyText(text); // don't forget
- TextView tv = (TextView)getListView().getEmptyView();
- tv.setText(text);
- }
カスタムを使っていてもいなくても、カスタムレイアウトに android:empty が入っていてもいなくても動くようにするには、こんな感じかなぁ。。。
- @Override
- public void setEmptyText(CharSequence text) {
- try {
- super.setEmptyText(text);
- }
- catch (IllegalStateException e1){
- e1.printStackTrace();
- Log.d("NoteListFragment", e1.getClass().getName() + " : " + e1.getMessage());
- try {
- TextView tv = (TextView)getListView().getEmptyView();
- tv.setText(text);
- }
- catch(NullPointerException e2) {
- e2.printStackTrace();
- Log.d("NoteListFragment", e2.getClass().getName() + " : " + e2.getMessage());
- TextView tv = new TextView(getActivity());
- tv.setText(text);
- tv.setId(android.R.id.empty);
- ViewParent v = getListView().getParent();
- if(v instanceof ViewGroup) {
- ((ViewGroup)v).addView(tv);
- }
- getListView().setEmptyView(tv);
- }
- }
- }
setListShownも
返信削除mProgressContainer = root.findViewById(com.android.internal.R.id.progressContainer);
で適当なのが取得できなくて死ぬ罠…(´・ω・`) ListFragmentはデフォView以外使うべきではないってことなのかなぁ