8

如何像 Android 原生设置应用一样为 SwitchReference 进行设置导航?

当你点击 WiFi 区域(不是在开关上)时,它将导航到一个新屏幕:

点击无线网络

即使我没有点击开关,我的应用程序也只会将开关从 ON 更改为 OFF,反之亦然。

我正在使用PreferenceFragment和 xml 作为屏幕。我的偏好是遵循Android PreferenceFragment 文档中的示例。我在 ICS 4.0 API 14 上开发我的应用程序。

有人知道怎么做吗?

编辑:

我的 XML 如下所示:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <PreferenceCategory
        android:layout="@layout/preference_category"
        android:title="User Settings" >
        <SwitchPreference
            android:key="pref_autorun"
            android:layout="@layout/preference"
            android:summary="Autorun SMODE on boot"
            android:title="Autorun SMODE" />
        <SwitchPreference
            android:key="pref_wifi_control"
            android:layout="@layout/preference"
            android:selectable="false"
            android:summary="Controls your Wi-Fi radio automatically based on hotspot availability"
            android:title="Wi-Fi Radio Control" />
    </PreferenceCategory>
</PreferenceScreen> 
4

3 回答 3

5

通过查看股票设置应用程序的来源,您可以找到他们是如何做到的。

基本上,他们使用自定义的 ArrayAdapter(很像您对 ListView 所做的)来显示带有 Switch 按钮的行。对于第二个屏幕,他们只需使用 ActionBar 中可用的 CustomView。

我写了一篇带有示例代码的文章,展示了如何在项目中做到这一点。不过要小心,这只能在 API 级别 14 或更高版本中使用,因此如果您针对旧设备,请保留旧样式偏好屏幕。

于 2012-06-27T05:40:12.407 回答
1

我在这里看到2个问题:1.如何在切换区域外收听偏好点击?2.如何在actionbar中放一个switch?

我会回答问题1:

我创建了一个新SwitchPreference类并覆盖了该onClick方法什么都不做。这可以防止在开关外部单击时更​​改设置。

public class SwitchPreference extends android.preference.SwitchPreference {
    @Override
    protected void onClick() {
    }
}

用法(xml):

<android.util.SwitchPreference
    android:key="whatever"
    android:title="Whatever" />

用法(java):

SwitchPreference switchPreference = (SwitchPreference) findPreference("whatever");
switchPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
    @Override
    public boolean onPreferenceClick(Preference preference) {
        DialogUtils.showToast(Preferences.this, "Clicked outside switch");
        return true;
    }
});
switchPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        DialogUtils.showToast(Preferences.this, "Clicked inside switch and setting changed");
        return true;
    }
});
于 2014-06-30T10:04:06.227 回答
1

我试了一下,有点挣扎。我想我会分享我的经验。

起初我尝试了XGouchet的答案,因为它的投票最多。该解决方案相当复杂,需要使用非常酷但不适合我正在做的事情的首选项标头。我决定对我来说最简单的事情就是将我的preferenceFragment 包裹在一个常规片段中,并使用常规视图伪造开关首选项。我挖掘了 android 源以获得偏好并想出了这个

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.lezyne.link.ui.homeScreen.settingsTab.SettingsFragment">

<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal">


</FrameLayout>

<View
    android:layout_width="fill_parent"
    android:layout_height="1dp"
    android:layout_marginLeft="15dp"
    android:layout_marginRight="15dp"
    android:background="#e1e1e1" />

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:orientation="horizontal"
    android:paddingRight="?android:attr/scrollbarSize">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="6dip"
        android:layout_marginLeft="15dip"
        android:layout_marginRight="6dip"
        android:layout_marginTop="6dip"
        android:layout_weight="1">

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@android:id/title"
            android:layout_below="@android:id/title"
            android:maxLines="4"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="?android:attr/textColorSecondary" />

    </RelativeLayout>

    <!-- Preference should place its actual preference widget here. -->
    <RelativeLayout
        android:id="@+id/widget_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:orientation="vertical"
        android:paddingEnd="36dp">


        <Switch
            android:thumb="@drawable/switch_inner"
            android:id="@+id/switch1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true" />

        <TextView
            android:id="@+id/textView6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="2dp"
            android:text="@string/Notifications"
            android:textAppearance="?android:attr/textAppearanceMedium" />

    </RelativeLayout>

</LinearLayout>


<View
    android:layout_width="fill_parent"
    android:layout_height="1dp"
    android:layout_marginLeft="15dp"
    android:layout_marginRight="15dp"
    android:background="#e1e1e1" />


 </LinearLayout>

顶部的 FrameLayout 得到一个偏好片段,如下所示

    getChildFragmentManager()
            .beginTransaction()
            .replace(R.id.fragment_container, new SettingsPreferencesFragment(), "SettingsPreferencesFragment")
            .commit();

然后我可以像在任何常规视图中一样分配我的点击侦听器。SettingsPreferencesFragment 只是完全标准的偏好片段。

这个解决方案看起来不错,但后来我注意到平板电脑上出现了奇怪的布局问题。我意识到我很难让这个解决方案在所有设备上看起来都正确,我需要使用真正的 switchPreference 而不是假的。

============== 解决方案2 ===============

AlikElzin-kilaka 的解决方案既好又简单,但在我尝试时不起作用。我第二次尝试确保我没有做错。我一直在玩,想出了一些似乎有效的东西。他有一个很好的观点

这里有2个问题: 1.如何在切换区域外听偏好点击?2.如何在actionbar中放一个switch?

真的只有问题(1)值得回答,因为问题2已经在这里其他地方得到了回答

我意识到在首选项中访问视图的唯一方法是创建一个子类并覆盖onBind。所以我想出了这个 SwitchPreference 的子类,它为作为整个视图的开关创建单独的点击处理程序。它仍然是一个黑客。

public class MySwitchPreference extends SwitchPreference {
public MySwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}


public MySwitchPreference(Context context, AttributeSet attrs) {
    super(context, attrs);
    getView(null,null);

}

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

public interface SwitchClickListener{
    public void onSwitchClicked(boolean checked);
    public void onPreferenceClicked();
}
private SwitchClickListener listener = null;
public void setSwitchClickListener(SwitchClickListener listener){
    this.listener = listener;
}

public Switch findSwitchWidget(View view){
    if (view instanceof  Switch){
        return (Switch)view;
    }
    if (view instanceof ViewGroup){
        ViewGroup viewGroup = (ViewGroup)view;
        for (int i = 0; i < viewGroup.getChildCount();i++){
            View child = viewGroup.getChildAt(i);
            if (child instanceof ViewGroup){
                Switch result = findSwitchWidget(child);
                if (result!=null) return result;
            }
            if (child instanceof Switch){
                return (Switch)child;
            }
        }
    }
    return null;
}

protected void onBindView (View view){
    super.onBindView(view);

    final Switch switchView = findSwitchWidget(view);
    if (switchView!=null){
        switchView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener!=null) listener.onSwitchClicked(switchView.isChecked());
            }
        });
        switchView.setFocusable(true);
        switchView.setEnabled(true);
    }

    view.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (listener!=null) listener.onPreferenceClicked();
        }
    });

}
} 

它使用递归函数 findSwitchWidget 遍历树,直到找到 Switch。我宁愿写这样的代码:

Switch switchView = view.findViewById(android.R.id.switchView);

但似乎没有办法获得我所知道的内部 id 值。无论如何,一旦我们有了实际的开关,我们就可以为它和容器视图分配监听器。切换首选项不会自动更新,因此需要自己保存首选项。

MySwitchPreference switchPreference = (MySwitchPreference) findPreference("whatever");
    switchPreference.setSwitchClickListener(new MySwitchPreference.SwitchClickListener() {
        @Override
        public void onSwitchClicked(boolean checked) {
            //Save the preference value here 
        }

        @Override
        public void onPreferenceClicked() {
            //Launch the new preference screen or activity here
        }
    });

希望这第二次黑客不会回来咬我。

有人看到这种方法的任何潜在缺陷吗?

我还在 github https://gist.github.com/marchold/45e22839eb94aa14dfb5上放了一个稍微改进的代码版本

于 2015-01-11T18:25:08.080 回答