参考网上代码自己做了一个客户定制 Preference,没有使用 preference的 android:layout 属性,因为总觉得在不同版本系统上难以和其他preference的控件对齐,所以采用仅设置 android:widget_layout 的方式。
先创建自己要定制的 widget_layout 文件“preference_widget_seekbar.xml”:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical" android:minHeight="?android:attr/listPreferredItemHeight" android:paddingRight="?android:attr/scrollbarSize" > <SeekBar android:id="@+id/seekBar1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:max="60" android:minWidth="240dip" android:paddingRight="5dp" android:progress="20" android:textIsSelectable="true" > </SeekBar> <TextView android:id="@+id/seekBarPrefValue" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/seekBar1" android:layout_centerHorizontal="true" android:textIsSelectable="true" > </TextView> <TextView android:id="@+id/seekBarPrefUnitsLeft" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/seekBar1" android:layout_toLeftOf="@id/seekBarPrefValue" android:textIsSelectable="true" > </TextView> <TextView android:id="@+id/seekBarPrefUnitsRight" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textIsSelectable="true" android:layout_below="@id/seekBar1" android:layout_toRightOf="@id/seekBarPrefValue" > </TextView> </RelativeLayout>
建立 com.sample.preference.SeekBarPreference 类,用来初始化界面,同步音量滑块表示的preference数据
package com.sample.preference; import com.sample.R;
import android.content.Context; import android.content.res.TypedArray; import android.preference.Preference; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; public class SeekBarPreference extends Preference implements OnSeekBarChangeListener { private final String TAG = "SeekBarPreference"; //private static final String ANDROIDNS="http://schemas.android.com/apk/res/android"; private static final String SEEKBAR_PREFS="SeekBarPreference"; private static final int DEFAULT_VALUE = 50; Context mContext; private int mMaxValue = 100; private int mMinValue = 0; private int mInterval = 1; private int mCurrentValue = 50; private String mUnitsLeft = ""; private String mUnitsRight = ""; private SeekBar mSeekBar; private TextView mCurrentValueText; public SeekBarPreference(Context context, AttributeSet attrs) { super(context, attrs); initPreference(context, attrs); } public SeekBarPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initPreference(context, attrs); } private void initPreference(Context context, AttributeSet attrs) { Log.v(TAG,"initPreference"); mContext = context; updateValuesFromXml(attrs); this.setWidgetLayoutResource(R.layout.preference_widget_seekbar); } private void updateValuesFromXml(AttributeSet attrs) { mMaxValue = attrs.getAttributeIntValue(SEEKBAR_PREFS, "max", 100); mMinValue = attrs.getAttributeIntValue(SEEKBAR_PREFS, "min", 0); mUnitsLeft = getAttributeStringValue(attrs, SEEKBAR_PREFS, "unitsLeft", ""); String value = getAttributeStringValue(attrs, SEEKBAR_PREFS, "value", ""); mUnitsRight = getAttributeStringValue(attrs, SEEKBAR_PREFS, "unitsRight", ""); try { mInterval = attrs.getAttributeIntValue(SEEKBAR_PREFS, "interval", 1); if( mInterval <= 0 ) { mInterval = 1; } } catch(Exception e) { Log.e(TAG, "Invalid interval value", e); } } private String getAttributeStringValue(AttributeSet attrs, String namespace, String name, String defaultValue) { String value = attrs.getAttributeValue(namespace, name); if(value == null) value = defaultValue; return value; } @Override protected View onCreateView(ViewGroup parent){ Log.v(TAG, "onCreateView()"); return super.onCreateView(parent); } @Override public void onBindView(View view) { super.onBindView(view); View layout = view; Log.v(TAG, "onBindView()"); mSeekBar = (SeekBar)view.findViewById(R.id.seekBar1); mCurrentValueText = (TextView)view.findViewById(R.id.seekBarPrefValue); if( null != mCurrentValueText ){ mCurrentValueText.setText(String.valueOf(mCurrentValue)); mCurrentValueText.setMinimumWidth(30); } if( null != mSeekBar ) { mSeekBar.setMax(mMaxValue - mMinValue); mSeekBar.setProgress(mCurrentValue - mMinValue); mSeekBar.setOnSeekBarChangeListener(this); } TextView unitsRight = (TextView)view.findViewById(R.id.seekBarPrefUnitsRight); if( null != unitsRight ){ unitsRight.setText(mUnitsRight); } TextView unitsLeft = (TextView)view.findViewById(R.id.seekBarPrefUnitsLeft); if( null != unitsLeft ){ unitsLeft.setText(mUnitsLeft); } } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { int newValue = progress + mMinValue; if(newValue > mMaxValue) newValue = mMaxValue; else if(newValue < mMinValue) newValue = mMinValue; else if(mInterval != 1 && newValue % mInterval != 0) newValue = Math.round(((float)newValue)/mInterval)*mInterval; // change rejected, revert to the previous value if(!callChangeListener(newValue)){ seekBar.setProgress(mCurrentValue - mMinValue); return; } // change accepted, store it mCurrentValue = newValue; if( null != mCurrentValueText ){ mCurrentValueText.setText(String.valueOf(newValue)); } persistInt(newValue); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) { notifyChanged(); } @Override protected Object onGetDefaultValue(TypedArray ta, int index){ int defaultValue = ta.getInt(index, DEFAULT_VALUE); return defaultValue; } @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { if(restoreValue) { mCurrentValue = getPersistedInt(mCurrentValue); } else { int temp = 0; try { temp = (Integer)defaultValue; } catch(Exception ex) { Log.e(TAG, "Invalid default value: " + defaultValue.toString()); } persistInt(temp); mCurrentValue = temp; } } }
最后,在preference xml中加入对应项
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="audioSetting"> <!-- ... 其他 preference 项 --> <com.sample.preference.SeekBarPreference android:key="key_nes_volume_option" android:title="volumeSetting" android:summary="volumeSettingSummary" />
<!-- ... 其他 preference 项 -->
</PreferenceScreen>