[Android L or M ]解除SwitchPreference与Preference的绑定事件


需求描述

默认情况,Android的两个控件SwitchPreference和CheckBoxPreference的事件处理是和Preference整个区域的事件绑定在一起的,然而,有时需要将其事件分开处理,即点击Preference整个区域时,不会改变SwitchPreference状态,仅当点击SwitchPreference时才去处理SwitchPreference的开关状态,如点击Preference整个区域弹出一个对话框或跳转到某个界面,点击SwitchPreference时仅是改变开关状态,不弹出对话框或不跳转.这样的需求该如何实现呢?下面将会列举几个常用实现方法:

SwitchPreference和CheckBoxPreference都是继承自TwoStatePreference,下面仅以SwitchPreference介绍,CheckBoxPreference的实现方式是一样的.


【声明】欢迎转载,但请保留文章原始出处:http://blog.csdn.net/yelangjueqi/article/details/46754711


一 继承SwitchPreference重新复写一个Preference


import android.content.Context;
import android.preference.SwitchPreference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Switch;

import com.wtk.gesture.utils.MyLogger;

public class SmartSwitchPreference extends SwitchPreference {
    private static final String CLASS_TAG = MyLogger.APP_TAG + "/" + SmartSwitchPreference.class.getSimpleName();

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

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

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

    @Override
    protected void onClick() {
        Log.d(CLASS_TAG, "onClick()");
    }

    //下面这段代码,在Android L版本(5.0)之前是不需要的,在Android L版本上必须要有,否则switch获取不到点击事件
    //此处废了我不少时间查找原因:从KK移植到L上面就不起作用了.因此L版本上面必须要有下面这段
    @Override
    protected View onCreateView(ViewGroup parent) {
        View view = super.onCreateView(parent);
        Switch v = (Switch) view.findViewById(com.android.internal.R.id.switchWidget);
        if (v != null) {
            v.setClickable(true);
        }
        return view;
    }
}


不足之处:如果在Android L or M上面运行,仅适用于源码环境,因为
com.android.internal.R.id.switchWidget 是私有的


再提供一个案例点击switch或整个开关区域 弹出一个确认框,当用户确认之后再去改变Switch开关的状态

import android.content.Context;
import android.preference.SwitchPreference;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Switch;
import android.util.Log;
import android.util.TypedValue;

/**
*
* 点击switch或整个开关区域 弹出一个确认框,当用户确认之后再去改变Switch开关的状态
*
*
*/
public class SwitchPreferenceOnly extends SwitchPreference {
    private static final String CLASS_TAG = SwitchPreferenceOnly.class.getSimpleName();

    private Switch switchView = null;
    private OnSwitchCheckedChangeListener mOnSwitchCheckedListener;

    public interface OnSwitchCheckedChangeListener {
        public boolean OnSwitchCheckedChanged(Switch compoundButton, boolean checked);
    }

    public void setOnSwitchCheckedChangeListener(OnSwitchCheckedChangeListener listener) {
        mOnSwitchCheckedListener = listener;
    }

    public SwitchPreferenceOnly(Context paramContext) {
        super(paramContext);
    }

    public SwitchPreferenceOnly(Context paramContext, AttributeSet paramAttributeSet) {
        this(paramContext, paramAttributeSet, com.android.internal.R.attr.switchPreferenceStyle);
    }

    public SwitchPreferenceOnly(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
        super(paramContext, paramAttributeSet, paramInt);
    }

    @Override
    protected View onCreateView(ViewGroup parent) {
        View mView = super.onCreateView(parent);
        switchView = (Switch) mView.findViewById(com.android.internal.R.id.switchWidget);

        // false to disable flow,because switch click event can't produce water ripple effect
        if (false && switchView != null) {
		    //Switch和Preference事件分割开了
            switchView.setClickable(true);
            switchView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (event.getAction() == MotionEvent.ACTION_UP
                            || event.getAction() == MotionEvent.ACTION_CANCEL) {

                        switchView.playSoundEffect(SoundEffectConstants.CLICK);// play click sound
                        handleSwitchChangeState();
                    }
                    return true;// return true to ignore click event
                }
            });
        }
        return mView;
    }

    public void toggle() {
        if (switchView != null) {
            handleSwitchChangeState();
        }
    }

    private void handleSwitchChangeState() {
        if (switchView != null) {
            switchView.setChecked(!switchView.isChecked());
            if (mOnSwitchCheckedListener != null) {
                mOnSwitchCheckedListener.OnSwitchCheckedChanged(switchView,
                        switchView.isChecked());
            }
        }
    }

    @Override
    public void onClick() {
        // not do anything here
    }
}

使用方法:

public class MainActivity extends PreferenceActivity implements
        Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {

private SwitchPreferenceOnly mFingerprintStatusBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.xxx);

        mFingerprintStatusBar.setOnPreferenceClickListener(this);
        mFingerprintStatusBar.setOnSwitchCheckedChangeListener(new SwitchPreferenceOnly.OnSwitchCheckedChangeListener() {
                    @Override
                    public boolean OnSwitchCheckedChanged(Switch compoundButton, boolean checked) {
                        // TODO
                        return false;
                    }
                });
    }
    
    @Override
    public boolean onPreferenceClick(Preference pref) {
        if (pref instanceof Preference && (KEY_FINGERPRINT_STATUS_BAR.equals(pref.getKey()))) {
            mFingerprintStatusBar.toggle();
        }
        return false;
    }
......   
}



二 通过switch控件实现


调用Preference的setWidgetLayoutResource方法实现控件替换


1. Layout布局文件:smart_gesture_switch.xml
<?xml version="1.0" encoding="utf-8"?>
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/prefrence_switch_id"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

</Switch>


上面布局中的Switch也可以替换成Checkbox or RadioButton

2. 引用smart_gesture_switch布局:GestureSwitchPreference.java

import android.content.Context;
import android.preference.SwitchPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.Switch;
import android.widget.Toast;

import com.wtk.gesture.quick.R;
import com.wtk.gesture.utils.MyLogger;


public class GestureSwitchPreference extends SwitchPreference {
    private static final String CLASS_TAG = MyLogger.APP_TAG + "/" + GestureSwitchPreference.class.getSimpleName();

    private Switch mSwitch;
    private boolean mChecked = false;
    private Context mContext;

    // // ///Custom Listenr Start
    // private OnRadioButtonCheckedListener mOnRadioButtonCheckedListener;
    //
    // public interface OnRadioButtonCheckedListener {
    // public void OnRadioButtonChecked(boolean isScreenOffView);
    // }
    //
    // public void setOnRadioButtonCheckedListener(OnRadioButtonCheckedListener
    // listener) {
    // mOnRadioButtonCheckedListener = listener;
    // }
    //
    // // ///Custom Listenr End

    public GestureSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
    }

    public GestureSwitchPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        //通过调用setWidgetLayoutResource方法来更新preference的widgetLayout,即更新控件区域
        setWidgetLayoutResource(R.layout.smart_gesture_switch);
    }

    public GestureSwitchPreference(Context context) {
        super(context);
        mContext = context;
        //通过调用setWidgetLayoutResource方法来更新preference的widgetLayout,即更新控件区域
        setWidgetLayoutResource(R.layout.smart_gesture_switch);
    }

    @Override
    protected void onBindView(View view) {
        mSwitch = (Switch) view.findViewById(R.id.prefrence_switch_id);
        //view即是代表的preference整个区域,可以对该view进行事件监听,也就是实现了preference整个区域的点击事件
        view.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                showToast("section-all");
                //此处调用自定义的监听器A方法,该监听器A接口应由使用GestureSwitchPreference的类来实现,从而实现
                //preference整个区域的点击事件.注:监听器A的定义可以参考OnRadioButtonCheckedListener接口的定义
            }
        });

        //switch开关的点击事件
        if (mSwitch != null) {
            mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                @Override
                public void onCheckedChanged(CompoundButton button, boolean checked) {
                    mChecked = checked;
                    showToast("only-switch-section");
                    //此处调用自定义的监听器B方法,该监听器B接口应由使用GestureSwitchPreference的类来实现,从而实现
                    //preference的switch点击事件.注:监听器B的定义可以参考OnRadioButtonCheckedListener接口的定义
                }
            });
        }
        setChecked(mChecked);
        super.onBindView(view);
    }

    public boolean isChecked() {
        return mChecked;
    }

    public void setChecked(boolean bChecked) {
        mChecked = bChecked;
        if (mSwitch != null) {
            mSwitch.setChecked(bChecked);
        }
    }

    private void showToast(String info) {
        Toast mToast = null;
        if (mToast == null) {
            mToast = Toast.makeText(mContext, info, 5000);
        }
        mToast.setText(info);
        mToast.show();
    }
}



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

    <com.wtk.gesture.modules.GestureSwitchPreference
        android:key="GestureSwitchPreference"
        android:summary="概要"
        android:title="标题" />

</PreferenceScreen>



4. 主界面:SmartQuickGestureSettings.java
public class SmartQuickGestureSettings extends PreferenceActivity {
    private static final String TAG = MyLogger.APP_TAG + "/" + SmartQuickGestureSettings.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.smart_quick_gesture_settings);
    }
}




5. 效果图:
点击preference整个区域


点击switch:





上述实现方式不足之处是:
A 代码量比较大
B 需要主动维护switch开关的状态,否则退出再重新进入时switch开关状态依旧是原来状态

三 扩展:完全自定义Preference布局


1 .SmartGesturePrefrence.java

import android.content.Context;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.Toast;

import com.wtk.gesture.quick.R;
import com.wtk.gesture.utils.MyLogger;

public class SmartGesturePrefrence extends Preference {
    private static final String CLASS_TAG = MyLogger.APP_TAG + "/" + SmartGesturePrefrence.class.getSimpleName();
    public static boolean isScreenOffBtn = true;// default display gesture view
    private Context mContext;

    private OnRadioButtonCheckedListener mOnRadioButtonCheckedListener;

    public interface OnRadioButtonCheckedListener {
        public void OnRadioButtonChecked(boolean isScreenOffView);
    }

    public void setOnRadioButtonCheckedListener(OnRadioButtonCheckedListener listener) {
        mOnRadioButtonCheckedListener = listener;
    }

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

    public SmartGesturePrefrence(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        mContext = context;
    }

    public SmartGesturePrefrence(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        setLayoutResource(R.layout.gesture_preference_layout);
    }

    @Override
    protected void onBindView(final View view) {
        super.onBindView(view);
        RadioGroup mRadioGroup = (RadioGroup) view.findViewById(R.id.radiogroup_gesture);

        RadioButton mScreenOffButton = (RadioButton) view.findViewById(R.id.btn_screen_off);
        RadioButton mPhoneCallingButton = (RadioButton) view.findViewById(R.id.btn_phone_calling);

        if (isScreenOffBtn) {
            mScreenOffButton.setChecked(true);
        } else {
            mPhoneCallingButton.setChecked(true);
        }

        mRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {

                switch (checkedId) {
                case R.id.btn_screen_off:
                    isScreenOffBtn = true;
                    if (mOnRadioButtonCheckedListener != null) {
                        mOnRadioButtonCheckedListener.OnRadioButtonChecked(true);
                    }
                    showToast("screen_off");
                    break;

                case R.id.btn_phone_calling:
                    isScreenOffBtn = false;
                    if (mOnRadioButtonCheckedListener != null) {
                        mOnRadioButtonCheckedListener.OnRadioButtonChecked(false);
                    }
                    showToast("phone_calling");
                    break;
                }
            }
        });
    }

    private void showToast(String info) {
        Toast mToast = null;
        if (mToast == null) {
            mToast = Toast.makeText(mContext, info, 5000);
        }
        mToast.setText(info);
        mToast.show();
    }
}


2. gesture_preference_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:minHeight="20dp"
    android:orientation="vertical"
    android:paddingEnd="?android:attr/scrollbarSize"
    android:paddingStart="?android:attr/scrollbarSize" >

    <RadioGroup
        android:id="@+id/radiogroup_gesture"
        android:layout_width="wrap_content"
        android:layout_height="52dip"
        android:layout_marginLeft="0dip"
        android:layout_marginRight="0dip"
        android:layout_marginTop="6dip"
        android:background="@android:color/black"
        android:gravity="center_vertical"
        android:orientation="horizontal" >

        <RadioButton
            android:id="@+id/btn_screen_off"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/zzz_radio_selector"
            android:button="@null"
            android:gravity="center"
            android:text="@string/title_mode_idle"
            android:textSize="16sp" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@null"
            android:scaleType="centerCrop"
            android:src="@drawable/zzz_gesture_tab_space" />

        <RadioButton
            android:id="@+id/btn_phone_calling"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/zzz_radio_selector"
            android:button="@null"
            android:gravity="center"
            android:text="@string/title_mode_call"
            android:textSize="16sp" />
    </RadioGroup>

    <TextView
        android:id="@+id/hint_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="6dip"
        android:layout_marginRight="6dip"
        android:ellipsize="marquee"
        android:fadingEdge="horizontal"
        android:singleLine="true"
        android:text="@string/gesture_operate_description"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:visibility="gone" />

</LinearLayout>



3.效果图:


posted @ 2015-07-04 12:55  行走的思想  阅读(32)  评论(0编辑  收藏  举报  来源