zoukankan      html  css  js  c++  java
  • Android-自定义开关(升级版)

    效果图:

    定义一个类,取名为MySwitch.java,此类去继承View,为何是继承View而不是去继承ViewGroup呢,是因为自定义开关没有子控件,之需要操作自身绘制即可

    package custom.view.upgrade.my_switch;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.Drawable;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    import custom.view.R;
    
    public class MySwitch extends View implements View.OnClickListener {
    
        private static String TAG = MySwitch.class.getSimpleName();
    
        private Paint mPaint;
    
        /**
         * 让布局中来指定实例化,得到属性集合AttributeSet
         * @param context
         * @param attrs
         */
        public MySwitch(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
    
            initView(context, attrs);
            initListener();
        }
    
        // 定义按钮背景图片
        private Bitmap bmSwitchBackground;
    
        // 定义按钮拖动的图片
        private Bitmap bmSwitchDrag;
    
        // 定义开关的状态 true || false , 默认是关闭状态
        private boolean switchStatus;
    
        // 定义开关的临时记录状态
        private boolean tempSwitchStatus;
    
        // 定义按钮拖动距离左边的距离
        private int dragLife = -1;
    
        /**
         * 初始化工作
         */
        private void initView(Context context, AttributeSet attrs) {
            mPaint = new Paint();
            mPaint.setAntiAlias(true); // 抗锯齿
    
            // 获取属性
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySwitch);
            bmSwitchBackground = ((BitmapDrawable) typedArray.getDrawable(R.styleable.MySwitch_switch_background)).getBitmap();
            bmSwitchDrag = ((BitmapDrawable) typedArray.getDrawable(R.styleable.MySwitch_switch_drag)).getBitmap();
            switchStatus = typedArray.getBoolean(R.styleable.MySwitch_switch_status, false);
        }
    
        public void setBmSwitchBackground(int switchBackground) {
            this.bmSwitchBackground = BitmapFactory.decodeResource(getResources() ,switchBackground);
        }
    
        public void setBmSwitchDrag(int switchDrag) {
            this.bmSwitchDrag = BitmapFactory.decodeResource(getResources(), switchDrag);
        }
    
        public void setSwitChStatus(boolean switchStatus) {
            this.switchStatus = switchStatus;
            dragLife = -1;
        }
    
        /**
         * 初始化事件
         */
        private void initListener() {
            setOnClickListener(this);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            // 宽度是:按钮背景的宽度
            // 高度是:按钮背景的高度
            // 测量自身View
            setMeasuredDimension(bmSwitchBackground.getWidth(), bmSwitchBackground.getHeight());
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            // 绘制按钮背景
            canvas.drawBitmap(bmSwitchBackground, 0, 0, mPaint);
    
            if (dragLife != -1) {
                canvas.drawBitmap(bmSwitchDrag, dragLife, 0 , mPaint);
            } else if (dragLife == -1) {
                if (switchStatus) {
                    // 打开状态
                    // 滑动点向右就是开启状态
                    int openDragLife = getLifeDragMaxValue();
                    canvas.drawBitmap(bmSwitchDrag, openDragLife, 0, mPaint);
                    moveEndX = openDragLife;
                } else {
                    // 关闭状态
                    canvas.drawBitmap(bmSwitchDrag, 0, 0, mPaint);
                    moveEndX = 0;
                }
    
                // 当开关的状态发生变化后,回调方法告诉用户,开关改变了
                if (null != onSwitchChangeListener && switchStatusChange) {
                    if (tempSwitchStatus != switchStatus) {
                        onSwitchChangeListener.onSwitchChange(switchStatus);
                    }
                }
            }
        }
    
        private float downX;
        private int moveEndX;
    
        private float clickDown;
        private float clickMove;
    
        // 开关状态是否发送了改变
        private boolean switchStatusChange;
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event); // 必须要调用此方法,onClick点击事件方法才会生效
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downX = event.getX();
                    isClick = true;
                    clickDown = event.getX();
                    switchStatusChange = false;
                    tempSwitchStatus = switchStatus;
                    break;
                case MotionEvent.ACTION_MOVE:
                    // Log.d(TAG, ">>>>>不加等于:" + (int) (event.getX() - downX));
                    moveEndX += (int) (event.getX() - downX);
                    Log.d(TAG,">>>>>>加等于:" + moveEndX);
    
                    if (moveEndX > getLifeDragMaxValue()) {
                        moveEndX = getLifeDragMaxValue();
                    } else if (moveEndX < 0){
                        moveEndX = 0;
                    }
    
                    dragLife = moveEndX;
                    invalidate();
    
                    downX = event.getX();
    
                    clickMove = downX;
                    if (Math.abs(clickMove - clickDown) > 5) {
                        isClick = false;
                    }
    
                    break;
                case MotionEvent.ACTION_UP:
                    if (dragLife > (getLifeDragMaxValue() / 2)) {
                        dragLife = -1;
                        switchStatus = true;
                        switchStatusChange = true;
                    } else if (dragLife >= 0){
                        dragLife  = -1;
                        switchStatus = false;
                        switchStatusChange = true;
                    } else {
                        switchStatusChange = false;
                    }
                    invalidate();
                    // upX = (int) event.getX();
                    break;
                default:
                    break;
            }
            return true;
        }
    
        private int getLifeDragMaxValue() {
            return bmSwitchBackground.getWidth() - bmSwitchDrag.getWidth();
        }
    
        /*@Override
        protected void onFinishInflate() {
            super.onFinishInflate();
    
            if (switchStatus) {
                moveEndX = getLifeDragMaxValue();
                Log.d(TAG, ">>>>>>>>>>>>>>>>>onFinishInflate()............ getLifeDragMaxValue():" + getLifeDragMaxValue());
            }
        }*/
    
        /**
         * 定义点击事件状态
         */
        private boolean isClick = true;
    
        @Override
        public void onClick(View v) {
            Log.d(TAG, "onClick() isClick:" + isClick);
            if (isClick) {
                if (switchStatus) {
                    switchStatus = false;
                    switchStatusChange = true;
                } else {
                    switchStatus = true;
                    switchStatusChange = true;
                }
                // switchStatus = (switchStatus==true?false:true);
                dragLife = -1;
                invalidate();
            }
        }
    
        private OnSwitchChangeListener onSwitchChangeListener;
    
        /**
         * 用户设置的 状态监听
         * @param onSwitchChangeListener
         */
        public void setOnSwitchChangeListener(OnSwitchChangeListener onSwitchChangeListener) {
            this.onSwitchChangeListener = onSwitchChangeListener;
        }
    }

    布局文件中去引用写好的自定义开关类

    并设置自定义属性:

         myswitch:switch_background="@mipmap/switch_background"
            myswitch:switch_drag="@mipmap/switch_drag"
            myswitch:switch_status="true"
    <!-- 自定义开关升级版 -->
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:myswitch="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".upgrade.MainActivity">
    
        <!-- 使用wrap_content,是因为不知道按钮的背景有多大,更加按钮图片的改变而变化 -->
        <custom.view.upgrade.my_switch.MySwitch
            android:id="@+id/custom_myswitch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            myswitch:switch_background="@mipmap/switch_background"
            myswitch:switch_drag="@mipmap/switch_drag"
            myswitch:switch_status="true"
            />
    
    </RelativeLayout>

    自定义规则arrts.xml文件声明:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <declare-styleable name="MySwitch">
    
            <attr name="switch_status" format="boolean" />
    
            <attr name="switch_background" format="reference" />
    
            <attr name="switch_drag" format="reference" />
    
        </declare-styleable>
    
    </resources>

    模拟用户来使用:

    MySwitch mySwitch = findViewById(R.id.custom_myswitch);
    
            // 设置开关的背景图片
            mySwitch.setBmSwitchBackground(R.mipmap.switch_background);
    
            // 设置开关拖动的图片
            mySwitch.setBmSwitchDrag(R.mipmap.switch_drag);
    
            // 设置开关的状态,打开、关闭
            mySwitch.setSwitChStatus(false);
    
            mySwitch.setOnSwitchChangeListener(new OnSwitchChangeListener() {
                @Override
                public void onSwitchChange(boolean switchChangeStatus) {
                    String result;
                    if (switchChangeStatus) {
                        result = "打开";
                    } else {
                        result = "关闭";
                    }
                    Toast.makeText(MainActivity.this, "开关已" + result, Toast.LENGTH_SHORT).show();
                }
            });
  • 相关阅读:
    概述各种事务隔离级别发生的影响
    linux内核的经典书籍
    sso 登录,网页跳转的实现方式
    初探移动网站的架构和设计
    C# PrintDocument 打印
    .Text分页技术(1)分页的存储过程分析
    SQL2008使用CTE递归查询批量插入500万数据
    自己写的Web服务器
    OMCS 语音视频框架
    ESFramework4.x
  • 原文地址:https://www.cnblogs.com/android-deli/p/9695275.html
Copyright © 2011-2022 走看看