实现类似IOS中滑动按钮效果

     


       这次为大家介绍一个自己模仿的IOS中滑动按钮的功能,IOS里的圆角滑动按钮是一个很不错的控件,体验比较好,那么Android上能不能实现相似的控件呢?答案肯定是能,实际上不但这样的效果能够实现,只要大家发挥想象力就能够实现更多更好的控件。废话不多说,开始为大家讲解控件的实现过程。

       先讲一下大体的代码流程,实现一个滑动按钮在大方向上与上篇博客的GIF图片播放是相似的,都是实现自定义控件,用的都是自定义控件中继承已有控件进行扩展和修改的方式。既然是一个滑动按钮那么必然存在开和关两种状态,另外,还需要一个控制开关的组成部分,由此可以了解,这个控件需要开关两张图片和一个开关控制图片。我们需要复写View里的OnDraw()方法和onTouch()方法(用于获取必要参数)。下面是本次代码实例的工程结构:

     

        

     控件中用到的三个图片(抠图的时候没仔细抠,大家包涵):


       bg_on   bg_offslipper_btn


     如上,SlipButton就是我们将要实现的滑动按钮代码。工程结构比较简单接下来,上代码,在代码中详细介绍实现过程


package com.example.cirbutton;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;

import android.util.AttributeSet;

import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;


/**
 * 仿IOS滑动按钮
 * @author yufc
 *
 */
public class SlipButton extends View implements OnTouchListener{
	
	/**
	 * 滑动按钮
	 */
	private Bitmap bg_on, bg_off, slipper_btn;
	
	/**
	 * 记录点击位置和按钮位置
	 */
	private float downX, nowX;
	
	/**
	 * 滑动标志
	 */
	private boolean onSliping = false;
	
	/**
	 * 记录当前按钮状态开还是关
	 */
	private boolean nowStatus = false;
	
	/**
	 * 滑动监听器,根据滑动
	 */
	private OnSlipChangedListener listener;
	
	/**
	 * 复写构造方
	 * @param context
	 */
	public SlipButton(Context context) {
		super(context);
		init();
	}

	/**
	 * 复写两个参数的构造方法,以后如果需要扩展时可以在这里读入一些xml文件里的参数
	 * @param context
	 * @param attrs
	 */
	public SlipButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}
	
	/**
	 * 初始化操作把图片资源载入进来,
	 * 为控件设置监听器
	 */
	public void init(){
		
		//图片资源
		bg_on = BitmapFactory.decodeResource(getResources(), R.drawable.on_btn);
		bg_off = BitmapFactory.decodeResource(getResources(), R.drawable.off_btn);
		slipper_btn = BitmapFactory.decodeResource(getResources(), R.drawable.white_btn);
		
		//设置监听器
		setOnTouchListener(this);
	}
	
	//复写的OnDraw方法
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		//矩阵参数,必要,不需知晓具体作用
		Matrix matrix = new Matrix();
		
		//画笔参数,必要,不需知晓具体作用
		Paint paint = new Paint();
		
		//开关按钮的位置
		float x = 0;
		
		//按钮的控制逻辑档滑动不超过一半时显示开,超过则显示关
		if (nowX < (bg_on.getWidth()/2)){
			//绘制“关”按钮
			canvas.drawBitmap(bg_off, matrix, paint);
		}else{
			//绘制“开”按钮
			canvas.drawBitmap(bg_on, matrix, paint);
		}
		
		if (onSliping) {
			
			//控制右边界
			if(nowX >= bg_on.getWidth())
				
				//
				x = bg_on.getWidth() - slipper_btn.getWidth()/2;
			else
				x = nowX - slipper_btn.getWidth()/2;
		}else {
			if(nowStatus){
				//把开关按钮的位置设置到右边
				x = bg_on.getWidth() - slipper_btn.getWidth();
			}else{
				//把开关按钮的位置设置到左边
				x = 0;
			}
		}
		
		//根据x结果来绘制
		if (x < 0 ){
			x = 0;
		}
		else if(x > bg_on.getWidth() - slipper_btn.getWidth()){
			x = bg_on.getWidth() - slipper_btn.getWidth();
		}
		
		//绘制开关控制按钮
		canvas.drawBitmap(slipper_btn, x , 0, paint); 
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:{
			if (event.getX() > bg_off.getWidth() || event.getY() > bg_off.getHeight()){
				return false;
			}else{
				onSliping = true;
				downX = event.getX();
				nowX = downX;
			}
			break;
		}
		case MotionEvent.ACTION_MOVE:{
			nowX = event.getX();
			break;
		}
		case MotionEvent.ACTION_UP:{
			onSliping = false;
			if(event.getX() >= (bg_on.getWidth()/2)){
				nowStatus = true;
				nowX = bg_on.getWidth() - slipper_btn.getWidth();
			}else{
				nowStatus = false;
				nowX = 0;
			}
			
			if(listener != null){
				listener.OnChanged(SlipButton.this, nowStatus);
			}
			break;
		}
		}
		//重新绘制控件
		invalidate();
		return true;
	}
	
	
	
	/**
	 * 为滑动按钮添加状态监听器
	 * @param listener
	 */
	public void setSlipOnChangedListener(OnSlipChangedListener listener){
		this.listener = listener;
	}
	
	
	/**
	 * 设置按钮状态的方法
	 * @param checked
	 */
	public void setChecked(boolean checked){
		if(checked){
			nowX = bg_off.getWidth();
		}else{
			nowX = 0;
		}
		nowStatus = checked;
	}
	
	

	
    /**
     * 回调接口
     * @author yufc
     *
     */
	public interface OnSlipChangedListener {
		public void OnChanged(SlipButton wiperSwitch, boolean checkState);
	}

	/**
	 * 得到现在的开关状态״̬
	 * @return
	 */
	public boolean getSwitchNowStatus(){
		return nowStatus;
	}
	
}


        如代码中所示,定义了downX,nowX两个变量来记录点击下的位置和按钮的当前位置,一个onSliping变量记录滑动状态,nowStatus记录按钮开或者关的状态。然后复写构造方法,这里复写了两个,原生控件的代码里会复写三个构造方法(参见Android源代码),不过第三个参数不常用,所以这里只写了两个。

       然后写一个初始化方法用来初始化变量,载入图片资源并为滑动按钮设置监听器。

        下面是主要代码片段,片段后跟代码详解

		if (nowX < (bg_on.getWidth()/2)){
			//绘制“关”按钮
			canvas.drawBitmap(bg_off, matrix, paint);
		}else{
			//绘制“开”按钮
			canvas.drawBitmap(bg_on, matrix, paint);
		}

       控制如果nowX(即当前手指所在的位置)大于“开”图片的一半时就把背景改为“关”图片,如果当前背景图片小于“开”图片的一半则设置为开图片




		if (onSliping) {
			
			//控制边界
			if(nowX >= bg_on.getWidth())
				
				//
				x = bg_on.getWidth() - slipper_btn.getWidth();
			else
				x = nowX - slipper_btn.getWidth()/2;
		}else {
			if(nowStatus){
				//把开关按钮的位置设置到右边
				x = bg_on.getWidth() - slipper_btn.getWidth();
			}else{
				//把开关按钮的位置设置到左边
				x = 0;
			}
		}


        onSliping是滑动状态标识,如果是正在滑动的状态就先判断边界如果超出边界则使slipper_btn置回边界以内。否则slipper_btn的位置为当前手指位置减掉slipper_btn宽度的一半,如果没有滑动那slipper_btn就是在左边或者右边。



		case MotionEvent.ACTION_DOWN:{
			if (event.getX() > bg_off.getWidth() || event.getY() > bg_off.getHeight()){
				return false;
			}else{
				onSliping = true;
				downX = event.getX();
				nowX = downX;
			}

     这是onTouch方法里的方法用于设置滑动状态标识和记录点击位置
 



case MotionEvent.ACTION_MOVE:{
			nowX = event.getX();


      手指移动时记录下现在的downX



case MotionEvent.ACTION_UP:{
			onSliping = false;
			if(event.getX() >= (bg_on.getWidth()/2)){
				nowStatus = true;
				nowX = bg_on.getWidth() - slipper_btn.getWidth();
			}else{
				nowStatus = false;
				nowX = 0;
			}
			
			if(listener != null){
				listener.OnChanged(SlipButton.this, nowStatus);
			}

      当手指抬起时,当前slipper_btn位置的设置和开关状态的设置,如果设置了监听器需要调用监听器的方法,最后invalidate()一下(也就是在手指抬起时重新绘制View)



      最后是定义回调接口和监听器设置方法,大功告成。

      看一下如果使用我们这个自定义的滑动按钮,首先是要在布局文件声明出来

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rootLinearLayout"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView 
        android:id="@+id/textView"
        android:text="测试滑动按钮"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <com.example.cirbutton.SlipButton
        android:id="@+id/wiperSwitch1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/textView" />
	
</RelativeLayout>

然后是主Activity里使用

package com.example.cirbutton;

import com.example.cirbutton.SlipButton.OnSlipChangedListener;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;


public class MainActivity extends Activity {
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		
		setContentView(R.layout.activity_main);


		final SlipButton slipButton = (SlipButton) findViewById(R.id.wiperSwitch1);
		
		//当通过滑动来改变开关状态时
		slipButton.setSlipOnChangedListener(new OnSlipChangedListener() {
			
			@Override
			public void OnChanged(SlipButton wiperSwitch, boolean checkState) {
				// TODO Auto-generated method stub
				Toast.makeText(getApplicationContext(), "按钮状态改变", Toast.LENGTH_SHORT).show();
			}
		});
		
		//当通过点击来改变开关状态时
		slipButton.setOnClickListener(new OnClickListener() {
			
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				if(slipButton.getSwitchNowStatus()==false){
					slipButton.setChecked(true);
				}else{
					slipButton.setChecked(false);
				}
				
			}
		});
	}

}


贴出本次的Manifest代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.cirbutton"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.cirbutton.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


少不了的效果图:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值