前一段时间在做项目的时候遇到了一个问题,美工在设计的时候设计的是一个iPhone中的开关,但是都知道Android中的Switch开关和IOS中的不同,这样就需要通过动画来实现一个iPhone开关了。

通常我们设置界面采用的是PreferenceActivity

package me.imid.movablecheckbox;import android.os.Bundle;import android.preference.PreferenceActivity;public class MovableCheckboxActivity extends PreferenceActivity {        @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);           addPreferencesFromResource(R.xml.testpreference);    }}

有关PreferenceActivity请看:http://blog.csdn.net/dawanganban/article/details/19082949

我们的基本思路是将CheckBox自定义成我们想要的样子,然后再重写CheckBoxPreference将自定义的CheckBox载入。

1、重写CheckBox

package me.imid.view;import me.imid.movablecheckbox.R;import android.content.Context;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.RectF;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.ViewConfiguration;import android.view.ViewParent;import android.widget.CheckBox;public class SwitchButton extends CheckBox {    private Paint mPaint;    private ViewParent mParent;    private Bitmap mBottom;    private Bitmap mCurBtnPic;    private Bitmap mBtnPressed;    private Bitmap mBtnNormal;    private Bitmap mFrame;    private Bitmap mMask;    private RectF mSaveLayerRectF;    private PorterDuffXfermode mXfermode;    private float mFirstDownY; // 首次按下的Y    private float mFirstDownX; // 首次按下的X    private float mRealPos; // 图片的绘制位置    private float mBtnPos; // 按钮的位置    private float mBtnOnPos; // 开关打开的位置    private float mBtnOffPos; // 开关关闭的位置    private float mMaskWidth;    private float mMaskHeight;    private float mBtnWidth;    private float mBtnInitPos;    private int mClickTimeout;    private int mTouchSlop;    private final int MAX_ALPHA = 255;    private int mAlpha = MAX_ALPHA;    private boolean mChecked = false;    private boolean mBroadcasting;    private boolean mTurningOn;    private PerformClick mPerformClick;    private OnCheckedChangeListener mOnCheckedChangeListener;    private OnCheckedChangeListener mOnCheckedChangeWidgetListener;    private boolean mAnimating;    private final float VELOCITY = 350;    private float mVelocity;    private final float EXTENDED_OFFSET_Y = 15;    private float mExtendOffsetY; // Y轴方向扩大的区域,增大点击区域    private float mAnimationPosition;    private float mAnimatedVelocity;    public SwitchButton(Context context, AttributeSet attrs) {        this(context, attrs, android.R.attr.checkboxStyle);    }    public SwitchButton(Context context) {        this(context, null);    }    public SwitchButton(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initView(context);    }    private void initView(Context context) {        mPaint = new Paint();        mPaint.setColor(Color.WHITE);        Resources resources = context.getResources();        // get viewConfiguration        mClickTimeout = ViewConfiguration.getPressedStateDuration()                + ViewConfiguration.getTapTimeout();        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();        // get Bitmap        mBottom = BitmapFactory.decodeResource(resources, R.drawable.bottom);        mBtnPressed = BitmapFactory.decodeResource(resources, R.drawable.btn_pressed);        mBtnNormal = BitmapFactory.decodeResource(resources, R.drawable.btn_unpressed);        mFrame = BitmapFactory.decodeResource(resources, R.drawable.frame);        mMask = BitmapFactory.decodeResource(resources, R.drawable.mask);        mCurBtnPic = mBtnNormal;        mBtnWidth = mBtnPressed.getWidth();        mMaskWidth = mMask.getWidth();        mMaskHeight = mMask.getHeight();        mBtnOffPos = mBtnWidth / 2;        mBtnOnPos = mMaskWidth - mBtnWidth / 2;        mBtnPos = mChecked ? mBtnOnPos : mBtnOffPos;        mRealPos = getRealPos(mBtnPos);        final float density = getResources().getDisplayMetrics().density;        mVelocity = (int) (VELOCITY * density + 0.5f);        mExtendOffsetY = (int) (EXTENDED_OFFSET_Y * density + 0.5f);        mSaveLayerRectF = new RectF(0, mExtendOffsetY, mMask.getWidth(), mMask.getHeight()                + mExtendOffsetY);        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);    }    @Override    public void setEnabled(boolean enabled) {        mAlpha = enabled ? MAX_ALPHA : MAX_ALPHA / 2;        super.setEnabled(enabled);    }    public boolean isChecked() {        return mChecked;    }    public void toggle() {        setChecked(!mChecked);    }    /**     * 内部调用此方法设置checked状态,此方法会延迟执行各种回调函数,保证动画的流畅度     *      * @param checked     */    private void setCheckedDelayed(final boolean checked) {        this.postDelayed(new Runnable() {            @Override            public void run() {                setChecked(checked);            }        }, 10);    }    /**     * <p>     * Changes the checked state of this button.     * </p>     *      * @param checked true to check the button, false to uncheck it     */    public void setChecked(boolean checked) {        if (mChecked != checked) {            mChecked = checked;            mBtnPos = checked ? mBtnOnPos : mBtnOffPos;            mRealPos = getRealPos(mBtnPos);            invalidate();            // Avoid infinite recursions if setChecked() is called from a            // listener            if (mBroadcasting) {                return;            }            mBroadcasting = true;            if (mOnCheckedChangeListener != null) {                mOnCheckedChangeListener.onCheckedChanged(SwitchButton.this, mChecked);            }            if (mOnCheckedChangeWidgetListener != null) {                mOnCheckedChangeWidgetListener.onCheckedChanged(SwitchButton.this, mChecked);            }            mBroadcasting = false;        }    }    /**     * Register a callback to be invoked when the checked state of this button     * changes.     *      * @param listener the callback to call on checked state change     */    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {        mOnCheckedChangeListener = listener;    }    /**     * Register a callback to be invoked when the checked state of this button     * changes. This callback is used for internal purpose only.     *      * @param listener the callback to call on checked state change     * @hide     */    void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {        mOnCheckedChangeWidgetListener = listener;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        int action = event.getAction();        float x = event.getX();        float y = event.getY();        float deltaX = Math.abs(x - mFirstDownX);        float deltaY = Math.abs(y - mFirstDownY);        switch (action) {            case MotionEvent.ACTION_DOWN:                attemptClaimDrag();                mFirstDownX = x;                mFirstDownY = y;                mCurBtnPic = mBtnPressed;                mBtnInitPos = mChecked ? mBtnOnPos : mBtnOffPos;                break;            case MotionEvent.ACTION_MOVE:                float time = event.getEventTime() - event.getDownTime();                mBtnPos = mBtnInitPos + event.getX() - mFirstDownX;                if (mBtnPos >= mBtnOffPos) {                    mBtnPos = mBtnOffPos;                }                if (mBtnPos <= mBtnOnPos) {                    mBtnPos = mBtnOnPos;                }                mTurningOn = mBtnPos > (mBtnOffPos - mBtnOnPos) / 2 + mBtnOnPos;                mRealPos = getRealPos(mBtnPos);                break;            case MotionEvent.ACTION_UP:                mCurBtnPic = mBtnNormal;                time = event.getEventTime() - event.getDownTime();                if (deltaY < mTouchSlop && deltaX < mTouchSlop && time < mClickTimeout) {                    if (mPerformClick == null) {                        mPerformClick = new PerformClick();                    }                    if (!post(mPerformClick)) {                        performClick();                    }                } else {                    startAnimation(!mTurningOn);                }                break;        }        invalidate();        return isEnabled();    }    private final class PerformClick implements Runnable {        public void run() {            performClick();        }    }    @Override    public boolean performClick() {        startAnimation(!mChecked);        return true;    }    /**     * Tries to claim the user's drag motion, and requests disallowing any     * ancestors from stealing events in the drag.     */    private void attemptClaimDrag() {        mParent = getParent();        if (mParent != null) {            mParent.requestDisallowInterceptTouchEvent(true);        }    }    /**     * 将btnPos转换成RealPos     *      * @param btnPos     * @return     */    private float getRealPos(float btnPos) {        return btnPos - mBtnWidth / 2;    }    @Override    protected void onDraw(Canvas canvas) {        canvas.saveLayerAlpha(mSaveLayerRectF, mAlpha, Canvas.MATRIX_SAVE_FLAG                | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG                | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);        // 绘制蒙板        canvas.drawBitmap(mMask, 0, mExtendOffsetY, mPaint);        mPaint.setXfermode(mXfermode);        // 绘制底部图片        canvas.drawBitmap(mBottom, mRealPos, mExtendOffsetY, mPaint);        mPaint.setXfermode(null);        // 绘制边框        canvas.drawBitmap(mFrame, 0, mExtendOffsetY, mPaint);        // 绘制按钮        canvas.drawBitmap(mCurBtnPic, mRealPos, mExtendOffsetY, mPaint);        canvas.restore();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension((int) mMaskWidth, (int) (mMaskHeight + 2 * mExtendOffsetY));    }    private void startAnimation(boolean turnOn) {        mAnimating = true;        mAnimatedVelocity = turnOn ? -mVelocity : mVelocity;        mAnimationPosition = mBtnPos;        new SwitchAnimation().run();    }    private void stopAnimation() {        mAnimating = false;    }    private final class SwitchAnimation implements Runnable {        @Override        public void run() {            if (!mAnimating) {                return;            }            doAnimation();            FrameAnimationController.requestAnimationFrame(this);        }    }    private void doAnimation() {        mAnimationPosition += mAnimatedVelocity * FrameAnimationController.ANIMATION_FRAME_DURATION                / 1000;        if (mAnimationPosition <= mBtnOnPos) {            stopAnimation();            mAnimationPosition = mBtnOnPos;            setCheckedDelayed(true);        } else if (mAnimationPosition >= mBtnOffPos) {            stopAnimation();            mAnimationPosition = mBtnOffPos;            setCheckedDelayed(false);        }        moveView(mAnimationPosition);    }    private void moveView(float position) {        mBtnPos = position;        mRealPos = getRealPos(mBtnPos);        invalidate();    }}
2、新建一个布局文件preference_widget_checkbox.xml

<?xml version="1.0" encoding="utf-8"?><me.imid.view.SwitchButton xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/checkbox"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="right|center" />
3、重写CheckBoxPreference并通过Inflater加载布局文件,同时屏蔽原有点击事件

package me.imid.preference;import me.imid.movablecheckbox.R;import me.imid.view.SwitchButton;import android.app.Service;import android.content.Context;import android.preference.PreferenceActivity;import android.text.TextUtils;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.view.accessibility.AccessibilityEvent;import android.view.accessibility.AccessibilityManager;import android.widget.Checkable;import android.widget.CompoundButton;import android.widget.CompoundButton.OnCheckedChangeListener;import android.widget.TextView;public class CheckBoxPreference extends android.preference.CheckBoxPreference {private Context mContext;private int mLayoutResId = R.layout.preference;private int mWidgetLayoutResId = R.layout.preference_widget_checkbox;private boolean mShouldDisableView = true;private CharSequence mSummaryOn;private CharSequence mSummaryOff;private boolean mSendAccessibilityEventViewClickedType;private AccessibilityManager mAccessibilityManager;public CheckBoxPreference(Context context, AttributeSet attrset,int defStyle) {super(context, attrset);mContext = context;mSummaryOn = getSummaryOn();mSummaryOff = getSummaryOff();mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Service.ACCESSIBILITY_SERVICE);}public CheckBoxPreference(Context context, AttributeSet attrs) {this(context, attrs, android.R.attr.checkBoxPreferenceStyle);}public CheckBoxPreference(Context context) {this(context, null);}/** * Creates the View to be shown for this Preference in the * {@link PreferenceActivity}. The default behavior is to inflate the main * layout of this Preference (see {@link #setLayoutResource(int)}. If * changing this behavior, please specify a {@link ViewGroup} with ID * {@link android.R.id#widget_frame}. * <p> * Make sure to call through to the superclass's implementation. *  * @param parent *            The parent that this View will eventually be attached to. * @return The View that displays this Preference. * @see #onBindView(View) */protected View onCreateView(ViewGroup parent) {final LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);final View layout = layoutInflater.inflate(mLayoutResId, parent, false);if (mWidgetLayoutResId != 0) {final ViewGroup widgetFrame = (ViewGroup) layout.findViewById(R.id.widget_frame);layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);}return layout;}@Overrideprotected void onBindView(View view) {// 屏蔽item点击事件view.setClickable(false);TextView textView = (TextView) view.findViewById(R.id.title);if (textView != null) {textView.setText(getTitle());}textView = (TextView) view.findViewById(R.id.summary);if (textView != null) {final CharSequence summary = getSummary();if (!TextUtils.isEmpty(summary)) {if (textView.getVisibility() != View.VISIBLE) {textView.setVisibility(View.VISIBLE);}textView.setText(getSummary());} else {if (textView.getVisibility() != View.GONE) {textView.setVisibility(View.GONE);}}}if (mShouldDisableView) {setEnabledStateOnViews(view, isEnabled());}View checkboxView = view.findViewById(R.id.checkbox);if (checkboxView != null && checkboxView instanceof Checkable) {((Checkable) checkboxView).setChecked(isChecked());SwitchButton switchButton = (SwitchButton) checkboxView;switchButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {// TODO Auto-generated method stubmSendAccessibilityEventViewClickedType = true;if (!callChangeListener(isChecked)) {return;}setChecked(isChecked);}});// send an event to announce the value change of the CheckBox and is// done here// because clicking a preference does not immediately change the// checked state// for example when enabling the WiFiif (mSendAccessibilityEventViewClickedType&& mAccessibilityManager.isEnabled()&& checkboxView.isEnabled()) {mSendAccessibilityEventViewClickedType = false;int eventType = AccessibilityEvent.TYPE_VIEW_CLICKED;checkboxView.sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));}}// Sync the summary viewTextView summaryView = (TextView) view.findViewById(R.id.summary);if (summaryView != null) {boolean useDefaultSummary = true;if (isChecked() && mSummaryOn != null) {summaryView.setText(mSummaryOn);useDefaultSummary = false;} else if (!isChecked() && 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 itnewVisibility = View.VISIBLE;}if (newVisibility != summaryView.getVisibility()) {summaryView.setVisibility(newVisibility);}}}/** * Makes sure the view (and any children) get the enabled state changed. */private void setEnabledStateOnViews(View v, boolean enabled) {v.setEnabled(enabled);if (v instanceof ViewGroup) {final ViewGroup vg = (ViewGroup) v;for (int i = vg.getChildCount() - 1; i >= 0; i--) {setEnabledStateOnViews(vg.getChildAt(i), enabled);}}}}
4、在res/xml下新建选项设置布局文件

<?xml version="1.0" encoding="utf-8"?><PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >    <me.imid.preference.CheckBoxPreference        android:defaultValue="true"        android:enabled="false"        android:summary="summary"        android:title="MyCheckbox(disabled)" />    <me.imid.preference.CheckBoxPreference        android:defaultValue="true"        android:dependency="checkbox"        android:summaryOff="off"        android:summaryOn="on"        android:title="MyCheckbox(enabled)" />    <me.imid.preference.CheckBoxPreference        android:defaultValue="false"        android:key="checkbox"        android:summaryOff="off"        android:summaryOn="on"        android:title="MyCheckbox(enabled)" />    <CheckBoxPreference        android:defaultValue="true"        android:enabled="false"        android:summaryOff="off"        android:summaryOn="on"        android:title="defalt checkbox(disabled)" />    <CheckBoxPreference        android:defaultValue="true"        android:dependency="checkbox1"        android:summaryOff="off"        android:summaryOn="on"        android:title="defalt checkbox(enabled)" />    <CheckBoxPreference        android:defaultValue="false"        android:key="checkbox1"        android:summaryOff="off"        android:summaryOn="on"        android:title="defalt checkbox(enabled)" /></PreferenceScreen>
运行结果:







更多相关文章

  1. 使ImageView充满整个控件的方法
  2. Android(安卓)Canvas使用
  3. 随笔12
  4. Android(安卓)重写物理返回键
  5. Android(安卓)OpenGL ES 开发(二): OpenGL ES 环境搭建
  6. (4.1.24)Android(安卓)官方推荐 DialogFragment 创建对话框
  7. (ios实现)用c/c++混合编程方式为ios/android实现一个自绘日期选
  8. Android(安卓)开机视频
  9. 【Android】OpenGL ES---绘制3D图形、应用纹理贴图

随机推荐

  1. 健身小管家--android app源码
  2. Android(安卓)软键盘在有scollview,纵向vi
  3. Android——天气预报(酷欧天气)(第三篇)
  4. Android(安卓)KTX简介
  5. Android中Handler Runnable与Thread的区
  6. Android——猜数字小游戏
  7. Android菜单留痕
  8. 转2:EditView:inputType常用取值
  9. Android报错:android.os.NetworkOnMainThr
  10. Android(安卓)UI线程和非UI线程