0.
Preference の
android:fragment 属性で Fragment を指定すると、タップしたときにそのFragmentに遷移する
1.
PreferenceActivity のレイアウトは com.android.internal.R.layout.preference_list_content である。
http://tools.oesf.biz/android-4.4.2_r1.0/xref/frameworks/base/core/res/res/layout/preference_list_content.xml
- ...
- <android.preference.PreferenceFrameLayout android:id="@+id/prefs"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- />
- ...
...
<android.preference.PreferenceFrameLayout android:id="@+id/prefs"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
/>
...
2. PreferenceActivity では、
EXTRA_SHOW_FRAGMENT というキーに Fragment の完全修飾名を入れることで、起動時に表示する Fragment を指定できる。
http://tools.oesf.biz/android-4.4.2_r1.0/xref/frameworks/base/core/java/android/preference/PreferenceActivity.java#512
- 512 @Override
- 513 protected void onCreate(Bundle savedInstanceState) {
- 514 super.onCreate(savedInstanceState);
- 515
- 516 setContentView(com.android.internal.R.layout.preference_list_content);
- 517
- ...
- 522 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
- ...
- 526
- 527 if (savedInstanceState != null) {
- ...
- 540 } else {
- 541 if (initialFragment != null && mSinglePane) {
- 542
- 543
- 544
- 545 switchToHeader(initialFragment, initialArguments);
- ...
- 656 }
512 @Override
513 protected void onCreate(Bundle savedInstanceState) {
514 super.onCreate(savedInstanceState);
515
516 setContentView(com.android.internal.R.layout.preference_list_content);
517
...
522 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
...
526
527 if (savedInstanceState != null) {
...
540 } else {
541 if (initialFragment != null && mSinglePane) {
542 // If we are just showing a fragment, we want to run in
543 // new fragment mode, but don't need to compute and show
544 // the headers.
545 switchToHeader(initialFragment, initialArguments);
...
656 }
3.
EXTRA_SHOW_FRAGMENT で指定された Fragment は com.android.internal.R.id.prefs という id のコンテナに replace される
http://tools.oesf.biz/android-4.4.2_r1.0/xref/frameworks/base/core/java/android/preference/PreferenceActivity.java#1176
- 1176 private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
- 1177 getFragmentManager().popBackStack(BACK_STACK_PREFS,
- 1178 FragmentManager.POP_BACK_STACK_INCLUSIVE);
- 1179 if (!isValidFragment(fragmentName)) {
- 1180 throw new IllegalArgumentException("Invalid fragment for this activity: "
- 1181 + fragmentName);
- 1182 }
- 1183 Fragment f = Fragment.instantiate(this, fragmentName, args);
- 1184 FragmentTransaction transaction = getFragmentManager().beginTransaction();
- 1185 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
- 1186 transaction.replace(com.android.internal.R.id.prefs, f);
- 1187 transaction.commitAllowingStateLoss();
- 1188 }
- 1189
- 1190
-
-
-
-
-
-
- 1197 public void switchToHeader(String fragmentName, Bundle args) {
- 1198 setSelectedHeader(null);
- 1199 switchToHeaderInner(fragmentName, args, 0);
- 1200 }
1176 private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
1177 getFragmentManager().popBackStack(BACK_STACK_PREFS,
1178 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1179 if (!isValidFragment(fragmentName)) {
1180 throw new IllegalArgumentException("Invalid fragment for this activity: "
1181 + fragmentName);
1182 }
1183 Fragment f = Fragment.instantiate(this, fragmentName, args);
1184 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1185 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1186 transaction.replace(com.android.internal.R.id.prefs, f);
1187 transaction.commitAllowingStateLoss();
1188 }
1189
1190 /**
1191 * When in two-pane mode, switch the fragment pane to show the given
1192 * preference fragment.
1193 *
1194 * @param fragmentName The name of the fragment to display.
1195 * @param args Optional arguments to supply to the fragment.
1196 */
1197 public void switchToHeader(String fragmentName, Bundle args) {
1198 setSelectedHeader(null);
1199 switchToHeaderInner(fragmentName, args, 0);
1200 }
1.で見たように、com.android.internal.R.id.prefs は PreferenceFrameLayout である。
4. PreferenceFrameLayout は子ビューの追加時にスタイル属性で指定された値分だけパディングを追加する
http://tools.oesf.biz/android-4.4.2_r1.0/xref/frameworks/base/core/java/android/preference/PreferenceFrameLayout.java
- 44 public PreferenceFrameLayout(Context context, AttributeSet attrs) {
- 45 this(context, attrs, com.android.internal.R.attr.preferenceFrameLayoutStyle);
- 46 }
- 47
- 48 public PreferenceFrameLayout(Context context, AttributeSet attrs, int defStyle) {
- 49 super(context, attrs, defStyle);
- 50 TypedArray a = context.obtainStyledAttributes(attrs,
- 51 com.android.internal.R.styleable.PreferenceFrameLayout, defStyle, 0);
- 52
- 53 float density = context.getResources().getDisplayMetrics().density;
- 54 int defaultBorderTop = (int) (density * DEFAULT_BORDER_TOP + 0.5f);
- 55 int defaultBottomPadding = (int) (density * DEFAULT_BORDER_BOTTOM + 0.5f);
- 56 int defaultLeftPadding = (int) (density * DEFAULT_BORDER_LEFT + 0.5f);
- 57 int defaultRightPadding = (int) (density * DEFAULT_BORDER_RIGHT + 0.5f);
- 58
- 59 mBorderTop = a.getDimensionPixelSize(
- 60 com.android.internal.R.styleable.PreferenceFrameLayout_borderTop,
- 61 defaultBorderTop);
- 62 mBorderBottom = a.getDimensionPixelSize(
- 63 com.android.internal.R.styleable.PreferenceFrameLayout_borderBottom,
- 64 defaultBottomPadding);
- 65 mBorderLeft = a.getDimensionPixelSize(
- 66 com.android.internal.R.styleable.PreferenceFrameLayout_borderLeft,
- 67 defaultLeftPadding);
- 68 mBorderRight = a.getDimensionPixelSize(
- 69 com.android.internal.R.styleable.PreferenceFrameLayout_borderRight,
- 70 defaultRightPadding);
- 71
- 72 a.recycle();
- 73 }
- ...
- 83 @Override
- 84 public void addView(View child) {
- 85 int borderTop = getPaddingTop();
- 86 int borderBottom = getPaddingBottom();
- 87 int borderLeft = getPaddingLeft();
- 88 int borderRight = getPaddingRight();
- 89
- 90 android.view.ViewGroup.LayoutParams params = child.getLayoutParams();
- 91 LayoutParams layoutParams = params instanceof PreferenceFrameLayout.LayoutParams
- 92 ? (PreferenceFrameLayout.LayoutParams) child.getLayoutParams() : null;
- 93
- 94 if (layoutParams != null && layoutParams.removeBorders) {
- 95 if (mPaddingApplied) {
- 96 borderTop -= mBorderTop;
- 97 borderBottom -= mBorderBottom;
- 98 borderLeft -= mBorderLeft;
- 99 borderRight -= mBorderRight;
- 100 mPaddingApplied = false;
- 101 }
- 102 } else {
- 103
- 104
- 105 if (!mPaddingApplied) {
- 106 borderTop += mBorderTop;
- 107 borderBottom += mBorderBottom;
- 108 borderLeft += mBorderLeft;
- 109 borderRight += mBorderRight;
- 110 mPaddingApplied = true;
- 111 }
- 112 }
- 113
- 114 int previousTop = getPaddingTop();
- 115 int previousBottom = getPaddingBottom();
- 116 int previousLeft = getPaddingLeft();
- 117 int previousRight = getPaddingRight();
- 118 if (previousTop != borderTop || previousBottom != borderBottom
- 119 || previousLeft != borderLeft || previousRight != borderRight) {
- 120 setPadding(borderLeft, borderTop, borderRight, borderBottom);
- 121 }
- 122
- 123 super.addView(child);
- 124 }
44 public PreferenceFrameLayout(Context context, AttributeSet attrs) {
45 this(context, attrs, com.android.internal.R.attr.preferenceFrameLayoutStyle);
46 }
47
48 public PreferenceFrameLayout(Context context, AttributeSet attrs, int defStyle) {
49 super(context, attrs, defStyle);
50 TypedArray a = context.obtainStyledAttributes(attrs,
51 com.android.internal.R.styleable.PreferenceFrameLayout, defStyle, 0);
52
53 float density = context.getResources().getDisplayMetrics().density;
54 int defaultBorderTop = (int) (density * DEFAULT_BORDER_TOP + 0.5f);
55 int defaultBottomPadding = (int) (density * DEFAULT_BORDER_BOTTOM + 0.5f);
56 int defaultLeftPadding = (int) (density * DEFAULT_BORDER_LEFT + 0.5f);
57 int defaultRightPadding = (int) (density * DEFAULT_BORDER_RIGHT + 0.5f);
58
59 mBorderTop = a.getDimensionPixelSize(
60 com.android.internal.R.styleable.PreferenceFrameLayout_borderTop,
61 defaultBorderTop);
62 mBorderBottom = a.getDimensionPixelSize(
63 com.android.internal.R.styleable.PreferenceFrameLayout_borderBottom,
64 defaultBottomPadding);
65 mBorderLeft = a.getDimensionPixelSize(
66 com.android.internal.R.styleable.PreferenceFrameLayout_borderLeft,
67 defaultLeftPadding);
68 mBorderRight = a.getDimensionPixelSize(
69 com.android.internal.R.styleable.PreferenceFrameLayout_borderRight,
70 defaultRightPadding);
71
72 a.recycle();
73 }
...
83 @Override
84 public void addView(View child) {
85 int borderTop = getPaddingTop();
86 int borderBottom = getPaddingBottom();
87 int borderLeft = getPaddingLeft();
88 int borderRight = getPaddingRight();
89
90 android.view.ViewGroup.LayoutParams params = child.getLayoutParams();
91 LayoutParams layoutParams = params instanceof PreferenceFrameLayout.LayoutParams
92 ? (PreferenceFrameLayout.LayoutParams) child.getLayoutParams() : null;
93 // Check on the id of the child before adding it.
94 if (layoutParams != null && layoutParams.removeBorders) {
95 if (mPaddingApplied) {
96 borderTop -= mBorderTop;
97 borderBottom -= mBorderBottom;
98 borderLeft -= mBorderLeft;
99 borderRight -= mBorderRight;
100 mPaddingApplied = false;
101 }
102 } else {
103 // Add the padding to the view group after determining if the
104 // padding already exists.
105 if (!mPaddingApplied) {
106 borderTop += mBorderTop;
107 borderBottom += mBorderBottom;
108 borderLeft += mBorderLeft;
109 borderRight += mBorderRight;
110 mPaddingApplied = true;
111 }
112 }
113
114 int previousTop = getPaddingTop();
115 int previousBottom = getPaddingBottom();
116 int previousLeft = getPaddingLeft();
117 int previousRight = getPaddingRight();
118 if (previousTop != borderTop || previousBottom != borderBottom
119 || previousLeft != borderLeft || previousRight != borderRight) {
120 setPadding(borderLeft, borderTop, borderRight, borderBottom);
121 }
122
123 super.addView(child);
124 }
PreferenceFrameLayout のデフォルトのスタイルは preferenceFrameLayoutStyle で指定されている
■ 4.4 以降について
http://tools.oesf.biz/android-4.4.2_r1.0/xref/frameworks/base/core/res/res/values/themes.xml
- 43 <style name="Theme">
- ...
- 319 <item name="preferenceFragmentPaddingSide">@dimen/preference_fragment_padding_side</item>
- ...
- 400 </style>
- ...
- 906 <style name="Theme.Holo">
- ...
- 1193
- 1194 <item name="preferenceFrameLayoutStyle">@android:style/Widget.Holo.PreferenceFrameLayout</item>
- ...
- 1214 </style>
-
- 1221 <style name="Theme.Holo.Light" parent="Theme.Light">
- ...
- 1469
- 1470 <item name="preferenceFrameLayoutStyle">@android:style/Widget.Holo.PreferenceFrameLayout</item>
- ...
- 1563 </style>
43
...
906
1221
http://tools.oesf.biz/android-4.4.2_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#2441
- 2441 <style name="Widget.Holo.PreferenceFrameLayout">
- 2442 <item name="android:borderTop">0dip</item>
- 2443 <item name="android:borderBottom">@dimen/preference_fragment_padding_bottom</item>
- 2444 <item name="android:borderLeft">?attr/preferenceFragmentPaddingSide</item>
- 2445 <item name="android:borderRight">?attr/preferenceFragmentPaddingSide</item>
- 2446 </style>
2441
http://tools.oesf.biz/android-4.4.2_r1.0/xref/frameworks/base/core/res/res/values/dimens.xml#104
- 104
- 105 <dimen name="preference_fragment_padding_side">16dp</dimen>
104
105 16dp
Holoテーマの preferenceFrameLayoutStyle には Widget.Holo.PreferenceFrameLayout が指定されています。
Widget.Holo.PreferenceFrameLayout では、左右のパディング用に
?attr/preferenceFragmentPaddingSide を参照しており、この値には @dimen/preference_fragment_padding_side がセットされています。
@dimen/preference_fragment_padding_side は、values/dimens.xml では 16dp が、values-600dp/dimens.xml では 24dp が、values-720dp/dimens.xml では 32dp が指定されています。
このため、Preferenceに指定されているFragmentに遷移した場合、左右に余白が入ります。
わかりやすいように、Fragmentの背景を赤にしています。
android:preferenceFragmentPaddingSide を指定すれば左右の余白値を変えられそうですが、public なテーマ属性ではないため、
- <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
- <item name="android:preferenceFragmentPaddingSide">0dp</item>
- </style>
のように指定してもコンパイルエラーになります。
次のように * をつけてテーマ属性が存在しているかどうかを無視するようにするとコンパイルできます。
- <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
- <item name="*android:preferenceFragmentPaddingSide">0dp</item>
- </style>
■ 4.4 未満について
4.4 未満ではテーマ属性に preferenceFragmentPaddingSide がありません。
http://tools.oesf.biz/android-4.3.1_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#2420
- 2420 <style name="Widget.Holo.PreferenceFrameLayout">
- 2421 <item name="android:borderTop">0dip</item>
- 2422 <item name="android:borderBottom">@dimen/preference_fragment_padding_bottom</item>
- 2423 <item name="android:borderLeft">@dimen/preference_fragment_padding_side</item>
- 2424 <item name="android:borderRight">@dimen/preference_fragment_padding_side</item>
- 2425 </style>
2420
左右のパディング用には直接 @dimen/preference_fragment_padding_side が指定されています。
preferenceFragmentPaddingSide が使えないため、左右の余白を変えるには preferenceFrameLayoutStyle を上書きするしかなさそうです。
preferenceFrameLayoutStyle も public なテーマではないため、上書きするには * をつける必要があります。
ただし、4.4以降では上書きした preferenceFrameLayoutStyle の値が利用されますが、4.4 未満では指定しても挙動が変わりませんでした。残念。
- <style name="AppTheme" parent="android:Theme.Holo">
- <item name="*android:preferenceFrameLayoutStyle">@style/PreferenceFrameLayout</item>
- </style>
-
- <style name="PreferenceFrameLayout">
- <item name="*android:borderTop">0dip</item>
- <item name="*android:borderBottom">0dip</item>
- <item name="*android:borderLeft">0dip</item>
- <item name="*android:borderRight">0dip</item>
- </style>
■ 4.1.2 未満について
4.1.2 未満では Theme.Holo.Light に preferenceFrameLayoutStyle の指定がありません。それによって困った挙動になっています。
つまり、Theme.Holo では余白が入るのですが、Theme.Holo.Light や Theme.Holo.Light.DarkActionBar では余白が入りません。
http://tools.oesf.biz/android-4.1.1_r1.0/xref/frameworks/base/core/res/res/values/themes.xml
4.1.1 with Theme.Holo
4.1.1 with Theme.Holo.Light.DarkActionBar
「Theme.Light は parent が指定されてないので Theme を継承してるが、Theme.Holo.Light は parent に Theme.Light が指定されているので、Theme.Holo は継承されない」
ということを忘れていたのでしょうか、気をつけましょう。
■ 4.4 未満で余白をなくすには
これまで見てきたように、4.4未満ではテーマで余白サイズを変えることができません。
また、4.1.2未満では Theme.Holo.Light で余白が入らないという問題もあります。
そのため、どのバージョンのデバイスであっても余白が入らないようにしてしまうのが、この問題の現実的な解決方法でしょう。
PreferenceFrameLayoutはFragmentのコンテナになるため、親のViewGroupのpaddingを0にすることで余白を削除できます。
- public class SettingsBaseFragment extends Fragment {
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- ViewParent parent = view.getParent();
- ViewGroup container = (ViewGroup) parent;
- if (container != null) {
- container.setPadding(0, 0, 0, 0);
- }
- }
- }
public class SettingsBaseFragment extends Fragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ViewParent parent = view.getParent();
ViewGroup container = (ViewGroup) parent;
if (container != null) {
container.setPadding(0, 0, 0, 0);
}
}
}