andorid 自定义开关控件

1,实现效果

运用知识,onMeasure 测量,onDraw  绘制, 接口回调

2,实现逻辑

 【1】创建一个类 继承view 实现空参构造

public class ToogleView extends View {

    public ToogleView(Context context, AttributeSet attrs) {   

    

    }

}

【2】布局中全名使用

  • com.xiaoshuai.toogleview.ToogleView 包名类名调用

<RelativeLayout 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"

    tools:context=".MainActivity" >

    <com.xiaoshuai.toogleview.ToogleView

        android:id="@+id/toogleView1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerInParent="true" />

</RelativeLayout>

【3】onMeasure 自己进行测量和图片一样宽和一样高

  • BitmapFactory在ToogleView 构造中获取背景图片的宽和高,获取滑动块的高和宽

private Bitmap mtoogleViewbg;

private Bitmap mSlideBg;

 //[1]先获取开关的背景图片和滑动块的图片(bitmap)

mtoogleViewbg = BitmapFactory.decodeResource(getResources(), R.drawable.toogle_background);

mSlideBg = BitmapFactory.decodeResource(getResources(), R.drawable.toogle_slidebg);

onMeasure测量当前view的大小

  •  

    //测量当前view的大小

    @Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        //这个view大小和背景图片宽高一样

        setMeasuredDimension(mtoogleViewbg.getWidth(), mtoogleViewbg.getHeight());

    }

【4】onDraw 方法中画出控件

/**在这个方法里面对当前view进行绘制**/

    @Override

    protected void onDraw(Canvas canvas) {

        //[1]画开关的背景

        canvas.drawBitmap(mtoogleViewbg, 0, 0, null);

        //[2]画开关的滑块

        canvas.drawBitmap(mSlideBg, 0, 0, null);

   }     

【5】设置开关的状态 

  • MainActivity中 onCreate 方法设置

        //[2]设置开关的状态

//        toogleView.setToogleViewState(true);

  • 实现对应的逻辑

 

   //设置开关的状态

    public void setToogleViewState(boolean b) {

      

    }

【6】自定义接口方法回调内部里面内部的实现逻辑叫调用者自己完成(写到这操作没有效果)

  • 设置开关的监听方法,要传入一个接口对象,定义一个成员变量接收调用对象

private OnToogleViewListener mToogleViewListener;

    /**设置开关的监听方法**/

    public void setOnToogleViewListener(OnToogleViewListener l){

        this.mToogleViewListener = l;

    }

定义监听器对象

  •  
/**

     * 定义监听器对象

     * **/

    public interface OnToogleViewListener{

        //当开关的状态发生改变的时候调用

        void toogleState(boolean result);

    }
  • MainActivity 调用接口 

    toogleView.setOnToogleViewListener(new OnToogleViewListener() {

            

            @Override

            public void toogleState(boolean result) {

                if (result) {

                    Toast.makeText(getApplicationContext(), "开", 0).show();

                }else {

                    Toast.makeText(getApplicationContext(), "关", 0).show();

                }

            }

        });   

     

【7】移动效果的处理

  • 在移动的时候调用invalidate方法重新绘制ondraw方法

/**代表滑块左边界的值**/

    private float slideLeftPosition;

//[2]画开关的滑块

        canvas.drawBitmap(mSlideBg, slideLeftPosition, 0, null);

  • 在构造方法中,给设置边界线为了不叫他滑动出去,滑动的最大距离是left,最小距离是0

/**代表滑动块left的最大边界**/

    private float slideLeftMaxSize;

    //[2]算出滑块left最大边界值

        slideLeftMaxSize = mtoogleViewbg.getWidth() - mSlideBg.getWidth();

  • 不断更改滑动块的X轴的位置

   downX = moveX; 不重新设置按下每次滑动会多一段距离

/**处理当前view的事件**/

    @Override

    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN://按下

            //[1]当手指按下的时候  获取按下x的坐标

            downX = event.getX();

            break;

        case MotionEvent.ACTION_MOVE: //移动

            //[2]当手指移动获取移动的距离  

            float moveX = event.getX();

            float distanceX = moveX - downX;

            slideLeftPosition+=distanceX;

            //[3]对边界进行判断

            if (slideLeftPosition <=0) {

                slideLeftPosition = 0;

            }else if (slideLeftPosition >=slideLeftMaxSize) {

                slideLeftPosition = slideLeftMaxSize;

            }

            downX = moveX;

            

            break;

        case MotionEvent.ACTION_UP: //抬起

            break;

        }

        invalidate();// --->ondraw方法会执行

        return true; //处理事件 消费事件

    }

【8】抬起开关弹动效果处理

        case MotionEvent.ACTION_UP: //抬起

          

                //当手指抬起时候 滑动块是往左移动还是往右移动  

                //算出滑动块的中心点位置 = slideLeftPosition + 滑动块宽度的一半   < 背景宽度的一半 就往左移动 否则往右移动

                if (slideLeftPosition + mSlideBg.getWidth() /2 < mtoogleViewbg.getWidth()/2 ) {

                    slideLeftPosition = 0;

                }else {

                    slideLeftPosition = slideLeftMaxSize;

                }

            }

            break;

【9】开关功能实现(接口回调)

  •  定义一个isHandup默认为false ,在ACTION_UP执行时返回true,onDraw 方法中进行处理

 

   case MotionEvent.ACTION_UP: //抬起

            isHandup = true;

                //当手指抬起时候 滑动块是往左移动还是往右移动  

                //算出滑动块的中心点位置 = slideLeftPosition + 滑动块宽度的一半   < 背景宽度的一半 就往左移动 否则往右移动

                if (slideLeftPosition + mSlideBg.getWidth() /2 < mtoogleViewbg.getWidth()/2 ) {

                    slideLeftPosition = 0;

                }else {

                    slideLeftPosition = slideLeftMaxSize;

                }

            break;
  • 定义一个变量记录isOpen  记录开关真实的状态,默认为false 

/**在这个方法里面对当前view进行绘制**/

    @Override

    protected void onDraw(Canvas canvas) {

        //[1]画开关的背景

        canvas.drawBitmap(mtoogleViewbg, 0, 0, null);

        //[2]画开关的滑块

        canvas.drawBitmap(mSlideBg, slideLeftPosition, 0, null);

        

        //[3]如果是手指抬起 实现开关的功能

        if (isHandup) {

            isHandup = false;

            //[4]获取开关当前的状态

            boolean isOpenTemp = slideLeftPosition > 0;

            if(isOpen != isOpenTemp && mToogleViewListener!=null){

                //[5]条件满足 触发回调方法

                mToogleViewListener.toogleState(isOpenTemp);

                isOpen = isOpenTemp;

            }

        }

    }

接口回调流程:

A类创建方法,方法中变量是接口并且成员变量引用,接口实现一个没有具体的方法,B类调用方法传入接口引用,自己实现具体方法内容。当满足某些条件时,A类中成员变量调用接口方法回调。B类引用指向A类方法。

【10】点击滑动块以外的地方,判断是滑动事件还是点击事件。开关转变

  • ACTION_DOWN中获取按下的坐标,和时间

        

    case MotionEvent.ACTION_DOWN://按下

            //[1]当手指按下的时候  获取按下x的坐标

            downX = event.getX();

            downY = event.getY();

            //[1.1]记录一下时间 按下的时间

            startTime =  System.currentTimeMillis();

            break;

      

      case MotionEvent.ACTION_UP: //抬起

            isHandup = true;

            //[4]判断用户是点击操作还是   按下移动操作抬起操作

            float upX = event.getX();

            long endtime = System.currentTimeMillis()-startTime;

            System.out.println("~~~~~~:"+endtime);

            if (Math.abs(event.getX()-downX)<5 && Math.abs(event.getY()-downY)<5 && System.currentTimeMillis()-startTime<200) {

                //如果点击滑动块以为的地方条件成立说明是点击事件

                if (upX > mSlideBg.getWidth()) {

                    //条件满足 开关是开

                    slideLeftPosition = slideLeftMaxSize;

                }else {

                    slideLeftPosition = 0;

                }

                

            }else {

                //当手指抬起时候 滑动块是往左移动还是往右移动  

                //算出滑动块的中心点位置 = slideLeftPosition + 滑动块宽度的一半   < 背景宽度的一半 就往左移动 否则往右移动

                if (slideLeftPosition + mSlideBg.getWidth() /2 < mtoogleViewbg.getWidth()/2 ) {

                    slideLeftPosition = 0;

                }else {

                    slideLeftPosition = slideLeftMaxSize;

                }

            }

            break;

【11】自定义属性 

  • values 创建attres  文件,name="ToogleView" 和控件名字一致,

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <declare-styleable name="ToogleView">

         <attr name="toogleState" format="boolean" />

    </declare-styleable>

    

</resources>
  • 获取当前命名空间,设置自定义属性

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

    xmlns:tools="http://schemas.android.com/tools"

    xmlns:itheima="http://schemas.android.com/apk/res/com.xiaoshuai.toogleview"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context=".MainActivity" >

    <com.ithiema.toogleview.ToogleView

        android:id="@+id/toogleView1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        xiaoshuai:toogleState="true"

        android:layout_centerInParent="true" />

</RelativeLayout>
  • 在ToogleView构造方法中获取到自己定义的属性

    //[3]获取布局中定义的属性值

        String namespace = "http://schemas.android.com/apk/res/com.xiaoshuai.toogleview";

        boolean toogleState = attrs.getAttributeBooleanValue(namespace, "toogleState", false);

        //[4]设置开关的状态

        setToogleViewState(toogleState);
  • 设置方法 

    //设置开关的状态

    public void setToogleViewState(boolean b) {

        isHandup = true;

        if (b) {

            slideLeftPosition = slideLeftMaxSize;

        }else {

            slideLeftPosition = 0;

        }

    }

猜你喜欢

转载自blog.csdn.net/Cricket_7/article/details/88689659