実は style の設定で合うようにすることができます。
以下では、つまみ部分に星型の画像を、トラック部分に 9patch の画像を用意しています。
つまみ
プログレス
トラックベース
res/values/style.xml で次のように定義し
<resources>
<style name="AppTheme" parent="android:Theme.Light">
<item name="android:seekBarStyle">@style/MySeekBar</item>
</style>
<style name="MySeekBar" parent="@android:style/Widget.SeekBar">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">@drawable/seekbar</item>
<item name="android:indeterminateDrawable">@drawable/seekbar</item>
<item name="android:thumb">@drawable/seekbar_thumb</item>
</style>
</resources>
Android 2.3.3 をターゲットとしてビルドし、実行すると次のようになります。
確かにバーの進み具体がつまみの中心になっていません。
左端
少し進んだところ
右端
どうしてこうなるかを理解するには、実際のプログラムでどう描画されるのかを知る必要があります。
1. 初期値を知る
上記では parent="@android:style/Widget.SeekBar" しているので、このスタイルで定義されている値をみてみましょう。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#397
397 <style name="Widget.SeekBar">
398 <item name="android:indeterminateOnly">false</item>
399 <item name="android:progressDrawable">@android:drawable/progress_horizontal</item>
400 <item name="android:indeterminateDrawable">@android:drawable/progress_horizontal</item>
401 <item name="android:minHeight">20dip</item>
402 <item name="android:maxHeight">20dip</item>
403 <item name="android:thumb">@android:drawable/seek_thumb</item>
404 <item name="android:thumbOffset">8dip</item>
405 <item name="android:focusable">true</item>
406 </style>
レイアウトに関連する値として minHeight と maxHeight が 20dip、thumbOffset が 8dip に設定されています。
2. コードの初期値を知る
parent="@android:style/Widget.SeekBar" を外してみましょう。そうすると、バーの進み具合がつまみの中心になります。
このときは minHeight, maxHeight, thumbOffset は設定されていないわけですから、コード内で設定されていないときの初期値が割り当てられています。
SeekBar の親クラスの AbsSeekBar でその処理が実装されています。
tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/widget/AbsSeekBar.java
65 public AbsSeekBar(Context context, AttributeSet attrs, int defStyle) {
66 super(context, attrs, defStyle);
67
68 TypedArray a = context.obtainStyledAttributes(attrs,
69 com.android.internal.R.styleable.SeekBar, defStyle, 0);
70 Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
71 setThumb(thumb); // will guess mThumbOffset if thumb != null...
72 // ...but allow layout to override this
73 int thumbOffset = a.getDimensionPixelOffset(
74 com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
75 setThumbOffset(thumbOffset);
76 a.recycle();
77
78 a = context.obtainStyledAttributes(attrs,
79 com.android.internal.R.styleable.Theme, 0, 0);
80 mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.Theme_disabledAlpha, 0.5f);
81 a.recycle();
82
83 mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
84 }
94 public void setThumb(Drawable thumb) {
95 boolean needUpdate;
96 // This way, calling setThumb again with the same bitmap will result in
97 // it recalcuating mThumbOffset (if for example it the bounds of the
98 // drawable changed)
99 if (mThumb != null && thumb != mThumb) {
100 mThumb.setCallback(null);
101 needUpdate = true;
102 } else {
103 needUpdate = false;
104 }
105 if (thumb != null) {
106 thumb.setCallback(this);
107
108 // Assuming the thumb drawable is symmetric, set the thumb offset
109 // such that the thumb will hang halfway off either edge of the
110 // progress bar.
111 mThumbOffset = thumb.getIntrinsicWidth() / 2;
112
113 // If we're updating get the new states
114 if (needUpdate &&
115 (thumb.getIntrinsicWidth() != mThumb.getIntrinsicWidth()
116 || thumb.getIntrinsicHeight() != mThumb.getIntrinsicHeight())) {
117 requestLayout();
118 }
119 }
120 mThumb = thumb;
121 invalidate();
122 if (needUpdate) {
123 updateThumbPos(getWidth(), getHeight());
124 if (thumb.isStateful()) {
125 // Note that if the states are different this won't work.
126 // For now, let's consider that an app bug.
127 int[] state = getDrawableState();
128 thumb.setState(state);
129 }
130 }
131 }
132
133 /**
134 * @see #setThumbOffset(int)
135 */
136 public int getThumbOffset() {
137 return mThumbOffset;
138 }
一言でいうと、thumbOffset が明示的に設定されていない場合、つまみ画像の横幅の半分が thumbOffset になります。111行目の処理です。
つまり、parent="@android:style/Widget.SeekBar" を入れていると、thumbOffset が 8dip なので、横幅が 16dip ではない画像をつまみとして使うとずれてしまうという事です。
3. padding をセットする
このままだと端にいったときにつまみが切れてしまいます。実は Holo テーマの SeekBar はその辺りの設定が正しくされています。
Holo テーマでの SeekBar の設定をみてみましょう。
1739 <style name="Widget.Holo.SeekBar">
1740 <item name="android:indeterminateOnly">false</item>
1741 <item name="android:progressDrawable">@android:drawable/scrubber_progress_horizontal_holo_dark</item>
1742 <item name="android:indeterminateDrawable">@android:drawable/scrubber_progress_horizontal_holo_dark</item>
1743 <item name="android:minHeight">13dip</item>
1744 <item name="android:maxHeight">13dip</item>
1745 <item name="android:thumb">@android:drawable/scrubber_control_selector_holo</item>
1746 <item name="android:thumbOffset">16dip</item>
1747 <item name="android:focusable">true</item>
1748 <item name="android:paddingLeft">16dip</item>
1749 <item name="android:paddingRight">16dip</item>
1750 </style>
minHeight, maxHeight, thumbOffset は値が少し変わっています。そのほか、paddingLeft と paddingRight が新しく追加されています。
この設定によって、つまみが内側に収まるようにしているのです。
この padding に設定する値はつまみ画像の横幅の半分にします。
例えば、この星の画像は横幅が 68px で、xhdpi 用としているので、dip に直すと 68 / 2 = 34dip です。 横幅の半分を padding にするので 17dip です。
よって
<style name="MySeekBar">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">@drawable/seekbar</item>
<item name="android:indeterminateDrawable">@drawable/seekbar</item>
<item name="android:thumb">@drawable/seekbar_thumb</item>
<item name="android:paddingLeft">17dip</item>
<item name="android:paddingRight">17dip</item>
</style>
とすれば OK です。
こうするとわかりますが、つまみの画像の横幅は 32dip (xhdpi 用だと 64px)がいいんでしょうね。
0 件のコメント:
コメントを投稿