2012-03-16 62 views
11

我想要實現的是基本上和下面的圖像(我平方的偏好)的確切副本。按首選項左側的任何內容都會打開一個對話框。按下切換按鈕將禁用/啓用我在此首選項中設置的任何內容。如何使用EditTextPreference和Togglebutton創建一個首選項?

我一直在嘗試幾個小時,我已經空手而歸了。我如何在PreferenceActivity中實現這一點?

Preference

編輯:看來人都誤解我的問題。使用PreferenceActivity來弄清楚如何解決我的問題是非常重要的。不是活動。我不在乎是否需要用XML或編程方式來完成它。只是請不要向我提供我無法在某個類似內容中使用的答案。

編輯2:添加了賞金 - 我真的需要一個答案

+0

很高興你會發起的主題! :) – Roylee 2013-05-12 09:27:00

回答

17

地獄的人,我喜歡你的想法:-)

這只是一樣@ MH的答案,但更簡潔。

我用ToggleButton而不是Switch進行了測試。

package android.dumdum; 

import android.content.Context; 
import android.preference.Preference; 
import android.util.AttributeSet; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.LinearLayout; 
import android.widget.TextView; 
import android.widget.ToggleButton; 

public class TogglePreference extends Preference { 

    public TogglePreference(Context context) { 
     super(context); 
    } 

    public TogglePreference(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public TogglePreference(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    public View getView(View convertView, ViewGroup parent) { 
     if (convertView == null) { 
      convertView = new LinearLayout(getContext()); 
      ((LinearLayout) convertView) 
        .setOrientation(LinearLayout.HORIZONTAL); 

      TextView txtInfo = new TextView(getContext()); 

      txtInfo.setText("Test"); 
      ((LinearLayout) convertView).addView(txtInfo, 
        new LinearLayout.LayoutParams(
          LinearLayout.LayoutParams.MATCH_PARENT, 
          LinearLayout.LayoutParams.WRAP_CONTENT, 1)); 

      ToggleButton btn = new ToggleButton(getContext()); 
      ((LinearLayout) convertView).addView(btn); 
     } 

     return convertView; 
    } 
} 

而且preferences.xml

<?xml version="1.0" encoding="utf-8"?> 
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > 

    <PreferenceCategory android:title="Test custom preferences" > 
     <android.dumdum.EncryptorEditTextPreference /> 
     <android.dumdum.TogglePreference /> 
    </PreferenceCategory> 

</PreferenceScreen> 

EncryptorEditTextPreference不涉及您的問題,但它使用(延伸EditTextPreference)相同的技術。

0

不能肯定,如果這是一個切換按鈕,但如果它是你可以說安卓紋元或Android:上textoff。 XML。如果它在Java部件上,它可能只是像setTextOn那樣確定如果這是一個切換按鈕,但如果它是你可以在.xml上說android:textOn或android:textoff。如果它在java部分,它可能只是像toggleButton.setChecked。

0

我不知道你碰到了,我剛剛創建像你指的是什麼的虛擬視圖什麼問題,我看不出有任何問題

<TableRow 
    android:id="@+id/tableRow1" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" > 

    <TextView 
     android:id="@+id/textView1" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="Large Text" 
     android:textAppearance="?android:attr/textAppearanceLarge" /> 


    <Switch 
     android:id="@+id/switch1" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_gravity="right" 
     android:text="Switch" /> 

</TableRow> 
+0

我遇到的問題是我在PreferenceActivity中工作的事實。不是活動。 PreferenceActivity的工作方式不同,每個「行」在XML中由Preferencetag定義,如:。我的問題在於結合這兩種偏好。不只是在表格中創建一個開關和一個textview。 – CodePrimate 2012-03-19 07:58:56

0

您可以在PREF使用XML代碼xml文件

<PreferenceCategory> 
     <EditTextPreference 
      android:key="myEditText" 
      android:title="Hi" 
      android:inputType="Mine"></EditTextPreference> 
    </PreferenceCategory> 

,你可以使用複選框切換按鈕,而不是與此代碼:

<CheckBoxPreference 
     android:key="testmode" 
     android:title="@string/test_mode"></CheckBoxPreference> 

如果你不希望使用複選框,您可以使用此代碼:

+1

謝謝,你的代碼相當不錯:-) – 2012-03-24 09:57:17

8

剛一說明前期:這將是一個有點長的答案,但我的本意是提供你有一個很好的答案,你可以從字面上複製和粘貼來開始。

這實際上並不太難完成。你最好的出發點是在ICS上查找SwichPreference的實現。你會發現它非常簡單,大部分工作都是由一個TwoStatePreference超類完成的,而超類又是唯一可用的ICS。幸運的是,使用SwitchPreference實現作爲指導,幾乎可以從字面上複製粘貼(在此答案中一路查看),然後構建您自己的TogglePreference(爲了清楚起見,我們可以稱它爲「頂層」)。

通過這樣做你會得到什麼,如下所示。我爲每種方法添加了一些解釋,以便在這裏限制我的寫作。

TogglePreference。java的

package mh.so.pref; 

import mh.so.R; 
import android.content.Context; 
import android.preference.Preference; 
import android.util.AttributeSet; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.CompoundButton; 
import android.widget.ToggleButton; 

/** 
* A {@link Preference} that provides a two-state toggleable option. 
* <p> 
* This preference will store a boolean into the SharedPreferences. 
*/ 
public class TogglePreference extends TwoStatePreference { 
    private final Listener mListener = new Listener(); 
    private ExternalListener mExternalListener; 

    /** 
    * Construct a new TogglePreference with the given style options. 
    * 
    * @param context The Context that will style this preference 
    * @param attrs Style attributes that differ from the default 
    * @param defStyle Theme attribute defining the default style options 
    */ 
    public TogglePreference(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    /** 
    * Construct a new TogglePreference with the given style options. 
    * 
    * @param context The Context that will style this preference 
    * @param attrs Style attributes that differ from the default 
    */ 
    public TogglePreference(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    /** 
    * Construct a new TogglePreference with default style options. 
    * 
    * @param context The Context that will style this preference 
    */ 
    public TogglePreference(Context context) { 
     this(context, null); 
    } 

    /** Inflates a custom layout for this preference, taking advantage of views with ids that are already 
    * being used in the Preference base class. 
    */ 
    @Override protected View onCreateView(ViewGroup parent) { 
     LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     return inflater.inflate(R.layout.toggle_preference_layout, parent, false); 
    } 

    /** Since the Preference base class handles the icon and summary (or summaryOn and summaryOff in TwoStatePreference) 
    * we only need to handle the ToggleButton here. Simply get it from the previously created layout, set the data 
    * against it and hook up a listener to handle user interaction with the button. 
    */ 
    @Override protected void onBindView(View view) { 
     super.onBindView(view); 

     ToggleButton toggleButton = (ToggleButton) view.findViewById(R.id.toggle_togglebutton); 
     toggleButton.setChecked(isChecked()); 
     toggleButton.setOnCheckedChangeListener(mListener); 
    } 

    /** This gets called when the preference (as a whole) is selected by the user. The TwoStatePreference 
    * implementation changes the actual state of this preference, which we don't want, since we're handling 
    * preference clicks with our 'external' listener. Hence, don't call super.onClick(), but the onPreferenceClick 
    * of our listener. */ 
    @Override protected void onClick() { 
     if (mExternalListener != null) mExternalListener.onPreferenceClick(); 
    } 

    /** Simple interface that defines an external listener that can be notified when the preference has been 
    * been clicked. This may be useful e.g. to navigate to a new activity from your PreferenceActivity, or 
    * display a dialog. */ 
    public static interface ExternalListener { 
     void onPreferenceClick(); 
    } 

    /** Sets an external listener for this preference*/ 
    public void setExternalListener(ExternalListener listener) { 
     mExternalListener = listener; 
    } 

    /** Listener to update the boolean flag that gets stored into the Shared Preferences */ 
    private class Listener implements CompoundButton.OnCheckedChangeListener { 
     @Override 
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
      if (!callChangeListener(isChecked)) { 
       // Listener didn't like it, change it back. 
       // CompoundButton will make sure we don't recurse. 
       buttonView.setChecked(!isChecked); 
       return; 
      } 

      TogglePreference.this.setChecked(isChecked); 
     } 
    } 

} 

這個例子的佈局文件只是在它LinearLayout有三個要素,其中最有趣的一個是ToggleButtonImageViewTextView利用基本類已經完成的工作,方法是在Android命名空間中使用相應的ID。這樣,我們不必擔心這些。請注意,我很確定在Honeycomb之前不會添加圖標選項,因此您可能只想將其添加爲TogglePreference的自定義屬性,並手動將其設置爲始終存在。如果你需要更多具體的指針來解決這個問題,請點擊評論。

無論如何,顯然你可以修改佈局以任何擴展,並將樣式應用於你的喜好。例如,要讓ToggleButton模仿Switch,您可以將背景更改爲其他StateListDrawable和/或更改或完全清除開啓/關閉文本。

toggle_preference_layout.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="?android:attr/listPreferredItemHeight" > 

    <ImageView 
     android:id="@android:id/icon" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_gravity="center_vertical" 
     android:focusable="false" 
     android:focusableInTouchMode="false" /> 

    <TextView 
     android:id="@android:id/summary" 
     android:layout_width="0dp" 
     android:layout_height="wrap_content" 
     android:layout_gravity="center_vertical" 
     android:layout_weight="1" 
     android:focusable="false" 
     android:focusableInTouchMode="false" 
     android:textAppearance="?android:attr/textAppearanceMedium" /> 

    <ToggleButton 
     android:id="@+id/toggle_togglebutton" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_gravity="center_vertical" 
     android:focusable="false" 
     android:focusableInTouchMode="false" /> 

</LinearLayout> 

然後可以使用TogglePreference就像你PreferenceActivity任何其他Preference。通過連接聽衆,當用戶選擇首選項時,可以做任何你喜歡的事情,同時點擊實際的ToggleButton將切換SharedPreferences中的布爾值。

DemoPreferenceActivity.java

package mh.so.pref; 

import mh.so.R; 
import android.os.Bundle; 
import android.preference.PreferenceActivity; 
import android.widget.Toast; 

public class DemoPreferenceActivity extends PreferenceActivity { 

    @Override protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     addPreferencesFromResource(R.xml.prefs); 

     TogglePreference toggle = (TogglePreference) findPreference("toggle_preference"); 
     toggle.setExternalListener(new TogglePreference.ExternalListener() { 
      @Override public void onPreferenceClick() { 
       Toast.makeText(DemoPreferenceActivity.this, "You clicked the preference without changing its value", Toast.LENGTH_LONG).show(); 
      } 
     }); 
    } 

} 

的prefs.xml什麼更多的,但上面TogglePreference一個統一的定義。您可以在Android的名稱空間中提供所有常用屬性。或者,您也可以聲明一些自定義屬性來利用TwoStatePreference的內置功能來處理summaryOnsummaryOff文本。

的prefs.xml

<?xml version="1.0" encoding="utf-8"?> 
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > 

    <PreferenceCategory android:title="Toggle preferences" > 
     <mh.so.pref.TogglePreference xmlns:app="http://schemas.android.com/apk/res/mh.so" 
      android:key="toggle_preference" 
      android:summary="Summary" 
      android:icon="@drawable/icon" /> 
    </PreferenceCategory> 

</PreferenceScreen> 

最後,從ICS中導出的TwoStatePreference類。它與原來的幾乎沒有什麼不同,爲此您可以找到源overhere

package mh.so.pref; 

import android.content.Context; 
import android.content.SharedPreferences; 
import android.content.res.TypedArray; 
import android.os.Parcel; 
import android.os.Parcelable; 
import android.preference.Preference; 
import android.util.AttributeSet; 
import android.view.View; 
import android.widget.TextView; 

/** 
* Common base class for preferences that have two selectable states, persist a 
* boolean value in SharedPreferences, and may have dependent preferences that are 
* enabled/disabled based on the current state. 
*/ 
public abstract class TwoStatePreference extends Preference { 

    private CharSequence mSummaryOn; 
    private CharSequence mSummaryOff; 
    private boolean mChecked; 
    private boolean mDisableDependentsState; 


    public TwoStatePreference(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    public TwoStatePreference(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
    } 

    public TwoStatePreference(Context context) { 
     this(context, null); 
    } 

    @Override 
    protected void onClick() { 
     super.onClick(); 

     boolean newValue = !isChecked(); 

     if (!callChangeListener(newValue)) { 
      return; 
     } 

     setChecked(newValue); 
    } 

    /** 
    * Sets the checked state and saves it to the {@link SharedPreferences}. 
    * 
    * @param checked The checked state. 
    */ 
    public void setChecked(boolean checked) { 
     if (mChecked != checked) { 
      mChecked = checked; 
      persistBoolean(checked); 
      notifyDependencyChange(shouldDisableDependents()); 
      notifyChanged(); 
     } 
    } 

    /** 
    * Returns the checked state. 
    * 
    * @return The checked state. 
    */ 
    public boolean isChecked() { 
     return mChecked; 
    } 

    @Override 
    public boolean shouldDisableDependents() { 
     boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked; 
     return shouldDisable || super.shouldDisableDependents(); 
    } 

    /** 
    * Sets the summary to be shown when checked. 
    * 
    * @param summary The summary to be shown when checked. 
    */ 
    public void setSummaryOn(CharSequence summary) { 
     mSummaryOn = summary; 
     if (isChecked()) { 
      notifyChanged(); 
     } 
    } 

    /** 
    * @see #setSummaryOn(CharSequence) 
    * @param summaryResId The summary as a resource. 
    */ 
    public void setSummaryOn(int summaryResId) { 
     setSummaryOn(getContext().getString(summaryResId)); 
    } 

    /** 
    * Returns the summary to be shown when checked. 
    * @return The summary. 
    */ 
    public CharSequence getSummaryOn() { 
     return mSummaryOn; 
    } 

    /** 
    * Sets the summary to be shown when unchecked. 
    * 
    * @param summary The summary to be shown when unchecked. 
    */ 
    public void setSummaryOff(CharSequence summary) { 
     mSummaryOff = summary; 
     if (!isChecked()) { 
      notifyChanged(); 
     } 
    } 

    /** 
    * @see #setSummaryOff(CharSequence) 
    * @param summaryResId The summary as a resource. 
    */ 
    public void setSummaryOff(int summaryResId) { 
     setSummaryOff(getContext().getString(summaryResId)); 
    } 

    /** 
    * Returns the summary to be shown when unchecked. 
    * @return The summary. 
    */ 
    public CharSequence getSummaryOff() { 
     return mSummaryOff; 
    } 

    /** 
    * Returns whether dependents are disabled when this preference is on ({@code true}) 
    * or when this preference is off ({@code false}). 
    * 
    * @return Whether dependents are disabled when this preference is on ({@code true}) 
    *   or when this preference is off ({@code false}). 
    */ 
    public boolean getDisableDependentsState() { 
     return mDisableDependentsState; 
    } 

    /** 
    * Sets whether dependents are disabled when this preference is on ({@code true}) 
    * or when this preference is off ({@code false}). 
    * 
    * @param disableDependentsState The preference state that should disable dependents. 
    */ 
    public void setDisableDependentsState(boolean disableDependentsState) { 
     mDisableDependentsState = disableDependentsState; 
    } 

    @Override 
    protected Object onGetDefaultValue(TypedArray a, int index) { 
     return a.getBoolean(index, false); 
    } 

    @Override 
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 
     setChecked(restoreValue ? getPersistedBoolean(mChecked) 
       : (Boolean) defaultValue); 
    } 

    /** 
    * Sync a summary view contained within view's subhierarchy with the correct summary text. 
    * @param view View where a summary should be located 
    */ 
    void syncSummaryView(View view) { 
     // Sync the summary view 
     TextView summaryView = (TextView) view.findViewById(android.R.id.summary); 
     if (summaryView != null) { 
      boolean useDefaultSummary = true; 
      if (mChecked && mSummaryOn != null) { 
       summaryView.setText(mSummaryOn); 
       useDefaultSummary = false; 
      } else if (!mChecked && mSummaryOff != null) { 
       summaryView.setText(mSummaryOff); 
       useDefaultSummary = false; 
      } 

      if (useDefaultSummary) { 
       final CharSequence summary = getSummary(); 
       if (summary != null) { 
        summaryView.setText(summary); 
        useDefaultSummary = false; 
       } 
      } 

      int newVisibility = View.GONE; 
      if (!useDefaultSummary) { 
       // Someone has written to it 
       newVisibility = View.VISIBLE; 
      } 
      if (newVisibility != summaryView.getVisibility()) { 
       summaryView.setVisibility(newVisibility); 
      } 
     } 
    } 

    @Override 
    protected Parcelable onSaveInstanceState() { 
     final Parcelable superState = super.onSaveInstanceState(); 
     if (isPersistent()) { 
      // No need to save instance state since it's persistent 
      return superState; 
     } 

     final SavedState myState = new SavedState(superState); 
     myState.checked = isChecked(); 
     return myState; 
    } 

    @Override 
    protected void onRestoreInstanceState(Parcelable state) { 
     if (state == null || !state.getClass().equals(SavedState.class)) { 
      // Didn't save state for us in onSaveInstanceState 
      super.onRestoreInstanceState(state); 
      return; 
     } 

     SavedState myState = (SavedState) state; 
     super.onRestoreInstanceState(myState.getSuperState()); 
     setChecked(myState.checked); 
    } 

    static class SavedState extends BaseSavedState { 
     boolean checked; 

     public SavedState(Parcel source) { 
      super(source); 
      checked = source.readInt() == 1; 
     } 

     @Override 
     public void writeToParcel(Parcel dest, int flags) { 
      super.writeToParcel(dest, flags); 
      dest.writeInt(checked ? 1 : 0); 
     } 

     public SavedState(Parcelable superState) { 
      super(superState); 
     } 

     public static final Parcelable.Creator<SavedState> CREATOR = 
       new Parcelable.Creator<SavedState>() { 
      public SavedState createFromParcel(Parcel in) { 
       return new SavedState(in); 
      } 

      public SavedState[] newArray(int size) { 
       return new SavedState[size]; 
      } 
     }; 
    } 
} 

TogglePreference without any fancy styling applied

0

只需使用SwitchPreference

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <SwitchPreference android:key="test" android:title="This is test toggle switch" /> </PreferenceScreen>

看起來像這樣(只是從我的應用程序樣本,不要理會其他首選項) enter image description here

相關問題