• 开关按钮实现


    1、原理和效果图

    总共有两张图片,一张背景图片,一张遮罩图片。背景图片有开关字样,通过遮住一个字来实现开关按钮。继承自View控件,通过Canvas和Paint结合来实现图片的绘制。

    通过canvas的drawBitmap方法和距离左边的位置来绘制图片,调用invalidate方法来不断的更新UI,就可以实现滑动的效果。

    public class MyToggleButton extends View implements OnClickListener{
    	/**
    	 * 做为背景的图片
    	 */
    	private Bitmap backgroundBitmap;
    	/**
    	 * 可以滑动的图片
    	 */
    	private Bitmap slideBtn;
    	private Paint paint;
    	/**
    	 * 滑动按钮的左边届
    	 */
    	private float slideBtn_left;
    	/**
    	 * 当前开关的状态
    	 *  true 为开
    	 */
    	private boolean currState = false;
    	/**
    	 * 判断是否发生拖动,如果拖动了,就不再响应 onclick 事件
    	 */
    	private boolean isDrag = false;
    	/**
    	 * 在代码里面创建对象的时候,使用此构造方法
    	 */
    	public MyToggleButton(Context context) {
    		super(context);
    	}
    
    	/**
    	 * 在布局文件中声名的view,创建时由系统自动调用。
    	 * @param context	上下文对象
    	 * @param attrs		属性集
    	 */
    	public MyToggleButton(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		initView();
    	}
    	
    	/**
    	 * 初始化
    	 */
    	private void initView() {
    		//初始化图片
    		backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
    		slideBtn = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
    		//初始化 画笔
    		paint = new Paint();
    		paint.setAntiAlias(true); // 打开抗矩齿
    		
    		//添加onclick事件监听
    		setOnClickListener(this);
    	}
    
    	/*
    	 * view 对象显示的屏幕上,有几个重要步骤:
    	 * 1、构造方法 创建 对象。
    	 * 2、测量view的大小。	onMeasure(int,int);
    	 * 3、确定view的位置 ,view自身有一些建议权,决定权在 父view手中。  onLayout();
    	 * 4、绘制 view 的内容 。 onDraw(Canvas)
    	 */
    	
    	
    	/**
    	 * 测量尺寸时的回调方法 
    	 */
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    		/**
    		 * 设置当前view的大小
    		 * width  :view的宽度
    		 * height :view的高度   (单位:像素)
    		 */
    		try {
    			setMeasuredDimension(backgroundBitmap.getWidth(),backgroundBitmap.getHeight());
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    
    	
    	@Override
    	/**
    	 * 绘制当前view的内容
    	 */
    	protected void onDraw(Canvas canvas) {
    //		super.onDraw(canvas);
    		
    		// 绘制 背景
    		/*
    		 * backgroundBitmap	要绘制的图片
    		 * left	图片的左边届
    		 * top	图片的上边届
    		 * paint 绘制图片要使用的画笔
    		 */
    		canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
    		 
    		//绘制 可滑动的按钮
    		canvas.drawBitmap(slideBtn, slideBtn_left, 0, paint);
    	}
    
    	
    	@Override
    	/**
    	 * onclick 事件在View.onTouchEvent 中被解析。
    	 * 系统对onclick 事件的解析,过于简陋,只要有down 事件  up 事件,系统即认为 发生了click 事件
    	 * 
    	 */
    	public void onClick(View v) {
    		if(!isDrag){ //如果没有拖动,才执行改变状态的动作
    			currState = !currState;
    			flushState();
    		}
    	}
    
    
    	
    	/**
    	 * down 事件时的x值
    	 */
    	private int firstX;
    	/**
    	 * touch 事件的上一个x值
    	 */
    	private int lastX;
    	
    	@Override
    	public boolean onTouchEvent(MotionEvent event) {
    		super.onTouchEvent(event);
    		
    		switch (event.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			firstX = lastX =(int) event.getX();
    			isDrag = false;
    			break;
    		case MotionEvent.ACTION_MOVE:
    			//判断是否发生拖动
    			if(Math.abs(event.getX()-firstX)>5){
    				isDrag = true;
    			}
    			
    			//计算 手指在屏幕上移动的距离
    			int dis = (int) (event.getX() - lastX);
    			
    			//将本次的位置 设置给lastX
    			lastX = (int) event.getX();
    			
    			//根据手指移动的距离,改变slideBtn_left 的值
    			slideBtn_left = slideBtn_left+dis;
    			break;
    		case MotionEvent.ACTION_UP:
    			//在发生拖动的情况下,根据最后的位置,判断当前开关的状态
    			if (isDrag) {
    				int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth(); // slideBtn左边届最大值
    				/*
    				 * 根据 slideBtn_left 判断,当前应是什么状态
    				 */
    				if (slideBtn_left > maxLeft / 2) { // 此时应为 打开的状态
    					currState = true;
    				} else {
    					currState = false;
    				}
    
    				flushState();
    			}
    			break;
    		}
    		
    		flushView();
    		
    		return true; 
    	}
    
    	/**
    	 * 刷新当前状态
    	 */
    	private void flushState() {
    		if(currState){
    			slideBtn_left = backgroundBitmap.getWidth()-slideBtn.getWidth();
    		}else{
    			slideBtn_left = 0;
    		}
    		mToggleSwitch.openOrClose(currState);
    		flushView(); 
    	}
    	
    	/**
    	 * 刷新当前视力
    	 */
    	private void flushView() {
    		//对 slideBtn_left  的值进行判断 ,确保其在合理的位置 即       0<=slideBtn_left <=  maxLeft
    		int maxLeft = backgroundBitmap.getWidth()-slideBtn.getWidth();	//	slideBtn 左边届最大值
    		
    		//确保 slideBtn_left >= 0
    		slideBtn_left = (slideBtn_left>0)?slideBtn_left:0;
    		
    		//确保 slideBtn_left <=maxLeft
    		slideBtn_left = (slideBtn_left<maxLeft)?slideBtn_left:maxLeft;
    		/*
    		 * 刷新当前视图  导致 执行onDraw执行
    		 */
    		invalidate();
    	}
    
    
    	public void setmToggleSwitch(ToggleSwitch mToggleSwitch) {
    		this.mToggleSwitch = mToggleSwitch;
    	}
    
    	private ToggleSwitch mToggleSwitch;
    	
    	public interface ToggleSwitch {
    		/**
    		 * true 为开 ,false 为关
    		 * @param open
    		 */
    		public void openOrClose(boolean open);
    	}
    }

    2、布局文件

    <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.example.MyToggleButton
            android:id="@+id/my_toggle_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
             />
    
    </RelativeLayout>

    3、调用

    myToggleButton = (MyToggleButton) findViewById(R.id.my_toggle_btn);
    
    		myToggleButton.setmToggleSwitch(new ToggleSwitch() {
    			@Override
    			public void openOrClose(boolean open) {
    				if (open) {
    					Toast.makeText(MainActivity.this, "打开", Toast.LENGTH_SHORT).show();
    				}else {
    					Toast.makeText(MainActivity.this, "关闭", Toast.LENGTH_SHORT).show();
    				}
    			}
    		});




  • 相关阅读:
    小程序mpvue使用scroll-view
    mysql之join浅析
    YApi-v1.9.2部署失败(Accessing non-existent property 'count' of module exports inside circular dependency)的解决方案
    YApi 可视化部署时遇到9090端口被占用时的解决方案
    [转载]最近涉及字符串列表存储,为加快检索速度,搜集了一些哈希函数,C语言的代码保存见内
    字符串的编码检测
    mbcs、unicode,UTF-8、UTF-16等的转换
    支持多重结构的配置信息读取代码,基于VS2008
    配置信息读取代码(VS2012编译通过,使用了C++11特性)
    [转载]TCPMP0.72RC1的编译与移植以及自己另外做UI完整方法
  • 原文地址:https://www.cnblogs.com/lbangel/p/4335854.html
Copyright © 2020-2023  润新知