自定义View实现开关按钮步骤:
写个类继承View,
拷贝包含包名的全路径到xml中,
界面中找到该控件, 设置初始信息,
根据需求绘制界面内容,
响应用户的触摸事件,
创建一个状态更新监听.
显示开关背景和滑动按钮。重写Android绘制渲染函数onMeasure(),onLayout(),onDraw()
可以跟随着触摸滑动,重写TouchEvent()返回true避免阻断其他事件。
开关状态事件监听。定义内部接口。提供设置接口对象方法。在开关状态改变的时候调用接口方法。
<com.example.day_0511_zdyview.MyButton android:layout_width="wrap_content" android:layout_height="wrap_content" />
package com.example.day_0511_zdyview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.Toast; /** * Created by MSI on 2018/5/11. */ public class MyButton extends View { boolean state = false; Bitmap buttonBackGround; Bitmap buttonSlidingBack; private boolean isSliding; private int currentx; OnButtonStateChangedListener MyListener; //这三个方法是让你做初始化业务 //代码中创建控件对象,自动回调的方法 public MyButton(Context context) { this(context,null); } //xml布局中使用此自定义控件,自动回调此方法 public MyButton(Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); } //xml布局中使用该控件,且带有样式时,自动回调 public MyButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } //做初始化对象的方法 private void initView() { ///加载两张背景图,两张图改为Bitmap对象(自定义开关,实际上就是两张图片叠加到一起的效果) buttonSlidingBack = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button_background); buttonBackGround = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //设置背景图片的宽和高 setMeasuredDimension(buttonBackGround.getWidth(),buttonBackGround.getHeight()); } //在组件中绘制内容.我们绝不在OnDraw方法里初始话,因为OnDrawer会被反复调用, // 如果在这里创建对象,会创建许多没有用对象,同时消耗系统资源 //指定控件的宽高 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //1:绘制背景图片 canvas.drawBitmap(buttonBackGround,0,0,null); //2:绘制滑动块的图片,并指定其位置 //判断是否正在滑动中,如果在滑动中 //需要根据手指按下的x轴的值,来去绘制滑动left位置 int onLeft = buttonBackGround.getWidth()-buttonSlidingBack.getWidth(); if (isSliding){ //左右边界超出 就是按下不在中心的位置 int left = currentx-buttonSlidingBack.getWidth()/2; if (left<0){ left=0; }else if(left>onLeft){ left=onLeft; } //当前手指按下的不是滑动块的中心点 //正在滑动中我们应该走这 canvas.drawBitmap(buttonSlidingBack,left,0,null); }else { //不是正在滑动中我们是不是要有一个状态 if (state){ //开的状态 canvas.drawBitmap(buttonSlidingBack,onLeft,0,null); }else { //关的状态 canvas.drawBitmap(buttonSlidingBack,0,0,null); } } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isSliding = true; currentx = (int) event.getX(); break; case MotionEvent.ACTION_MOVE: currentx = (int) event.getX(); break; case MotionEvent.ACTION_UP: isSliding = false; currentx = (int) event.getX(); boolean flag = currentx>buttonBackGround.getWidth()/2; if(flag!=state && MyListener!=null){ MyListener.onButtonState(flag); } if (currentx<buttonBackGround.getWidth()/2){ //当前x 轴的值,<背景图片的一半,进入关的状态 state=false; Toast.makeText(getContext(), "开关是关的状态", Toast.LENGTH_SHORT).show(); }else { //如果当前x轴的值,>背景图片的一半,进入开的状态 state=true; Toast.makeText(getContext(), "开关是开的", Toast.LENGTH_SHORT).show(); } //调用用户的回调事件 break; } //让其重新绘制的方法 invalidate(); return true; } //设置按钮的开关的状态 public void setState(boolean b) { state = b; } //设置按钮的背景颜色 public void setBackgroudColor(int backGround) { buttonBackGround = BitmapFactory.decodeResource(getResources(), backGround); } //设置滑动块的 public void setSlidingColor(int slidingBack) { buttonSlidingBack = BitmapFactory.decodeResource(getResources(), slidingBack); } public void setOnButtonStateChangedListener(OnButtonStateChangedListener listener){ MyListener =listener; } }