zoukankan      html  css  js  c++  java
  • 自定义开关滑动按钮控件

    package com.loaderman.switch;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    
    /**
     * 控件绘制流程:
     * <p>
     * 1. measure方法来确定控件尺寸
     * 2. layout方法确定控件位置
     * 3. draw方法绘制控件内容
     * <p>
     * measure->layout->draw
     * onMeasure->onLayout->onDraw
     * <p>
     * 1280x720  xhdpi
     * 480x800 hdpi
     */
    public class MySwitch extends View {
    
        private Paint paint;
        private Bitmap mBitmapBg;
        private Bitmap mBitmapSlide;
        private int MAX_LEFT;//最大左边距
        private int mSlideLeft;//当前左边距
        private boolean isOpen = false;//当前开关状态
        private static final String NAMESPACE = "http://schemas.android.com/apk/res-auto";
        public MySwitch(Context context) {
            this(context, null);
        }
        public MySwitch(Context context, AttributeSet attrs) {
            this(context, attrs, -1);
        }
        public MySwitch(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
            //设置开关初始状态
            isOpen = attrs.getAttributeBooleanValue(NAMESPACE, "isOpen", false);
            if(isOpen) {
                mSlideLeft = MAX_LEFT;
            }else {
                mSlideLeft = 0;
            }
            //设置滑块图片
            int slideDrawable = attrs.getAttributeResourceValue(NAMESPACE, "slideDrawable", -1);
            mBitmapSlide = BitmapFactory.decodeResource(getResources(), slideDrawable);
    
            invalidate();
        }
        private void init() {
            paint = new Paint();
            paint.setColor(Color.RED);
            //初始化背景图片
            mBitmapBg = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
            //滑块图片
            mBitmapSlide = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
            MAX_LEFT = mBitmapBg.getWidth() - mBitmapSlide.getWidth();
            //设置点击事件
            this.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (isClick) {
                        if (isOpen) {
                            mSlideLeft = 0;
                            isOpen = false;
                        } else {
                            mSlideLeft = MAX_LEFT;
                            isOpen = true;
                        }
    
                        //刷新当前控件
                        invalidate();//此方法会导致onDraw重新走一遍
    
                        //调用回调对象的方法, 来回传数据
                        if (listener != null) {
                            listener.onCheckedChanged(isOpen);
                        }
                    }
                }
            });
            //this.setOnTouchListener();
        }
        private int startX;
        private int moveX;//记录移动总距离
        private boolean isClick;//标记当前是否是点击事件
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //1.记录起点坐标
                    startX = (int) event.getX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    //2. 记录移动后坐标
                    int endX = (int) event.getX();
                    //3. 计算偏移量
                    int dx = endX - startX;
                    moveX += Math.abs(dx);
                    //4. 根据偏移量更新控件位置
                    mSlideLeft += dx;
                    //避免越界
                    if (mSlideLeft < 0) {
                        mSlideLeft = 0;
                    }
                    if (mSlideLeft > MAX_LEFT) {
                        mSlideLeft = MAX_LEFT;
                    }
                    invalidate();
                    //5. 重新初始化起点坐标
                    startX = endX;
                    break;
                case MotionEvent.ACTION_UP:
                    //根据移动总距离确定是否是点击事件
                    if (moveX > 5) {//允许5个像素的误差
                        //移动
                        isClick = false;
                    } else {
                        //点击
                        isClick = true;
                    }
                    moveX = 0;//移动总距离一定要归0;
                    if (!isClick) {
                        //确定最终开关状态
                        if (mSlideLeft < MAX_LEFT / 2) {
                            mSlideLeft = 0;
                            isOpen = false;
                        } else {
                            mSlideLeft = MAX_LEFT;
                            isOpen = true;
                        }
    
                        invalidate();
                        //调用回调对象的方法, 来回传数据
                        if (listener != null) {
                            listener.onCheckedChanged(isOpen);
                        }
                    }
                    break;
                default:
                    break;
            }
            //return true;//消费了此事件,导致onClick无法响应  ACTION_DOWN + ACTION_MOVE..... + ACTION_UP
            //return false;//不消费事件,只会走ACTION_DOWN, 后续所有事件不都不处理了
            return super.onTouchEvent(event);//由于onClick的回调是在super.onTouchEvent完成的,
            // 所以必须返回父类的onTouchEvent才能保证onClick也能够响应事件
        }
        //重写此方法可以修改控件尺寸
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            //setMeasuredDimension(100, 100);
            setMeasuredDimension(mBitmapBg.getWidth(), mBitmapBg.getHeight());
            System.out.println("onMeasure....");
        }
        //设置控件位置
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            System.out.println("onLayout....");
        }
        //绘制控件
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //canvas.drawRect(0, 0, 200, 200, paint);
            System.out.println("onDraw......");
            //在左上角绘制图片
            canvas.drawBitmap(mBitmapBg, 0, 0, null);
            canvas.drawBitmap(mBitmapSlide, mSlideLeft, 0, null);
        }
        private OnCheckedChangeListener listener;
        public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
            this.listener = listener;
        }
        //定义回调接口
        public interface OnCheckedChangeListener {
            void onCheckedChanged(boolean isChecked);
        }
    //    public void smsBackup(OnCheckedChangeListener callback) {
    //
    //        callback.onCheckedChanged(true);
    //    }
    }
    
    package com.loaderman.switch;
    
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.widget.Toast;
    public class MainActivity extends AppCompatActivity {
        private MySwitch mSwitch;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mSwitch = (MySwitch) findViewById(R.id.my_switch);
            mSwitch.setOnCheckedChangeListener(new MySwitch.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(boolean isChecked) {
                    Toast.makeText(MainActivity.this, "开关状态:" + isChecked, Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.loaderman.switch.MainActivity">
        <com.loaderman.switch.MySwitch
            android:id="@+id/my_switch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            app:isOpen="true"
            app:slideDrawable="@drawable/slide_button"
            />
    </RelativeLayout>
    

     自定义属性atts_my_switch.xml

    <resources>
        <declare-styleable name="MySwitch">
            <!--开关状态-->
            <attr name="isOpen" format="boolean"/>
            <!--滑块图片-->
            <attr name="slideDrawable" format="reference"/>
        </declare-styleable>
    </resources>
    

     效果图:

  • 相关阅读:
    如何在SQL/400中计算两日期间的天数
    委托、事件与Observer设计模式
    C# 杂项
    泛型学习(二)
    委托
    操作符重载 (operator)
    一个解决方案多个项目
    将方法绑定到委托
    事件
    密封方法 密封类(sealed)
  • 原文地址:https://www.cnblogs.com/loaderman/p/6514478.html
Copyright © 2011-2022 走看看