自定义view-SlideSwitch开源详解

转眼间从事android开发也有一段时间了,今天想把自己学到的东西与大家分享下,今天给大家带来的是自定义view——SlideSwitch开源控件的讲解。


SlideSwitch是一个开关的效果,这里贴上两张效果图:


自定义View的实现方式可以分为三种,自绘控件、组合控件、以及继承控件。今天要讲的就是自绘控件。主要步骤分为:

一、定义View的属性信息。  //   attrs.xml 文件

二、在View中检索定义的属性  // TypedArray检索

三、重写onMeasure() /**/  测量:决定View的大小

四、重写onLayout()  /**/    布局:决定View在ViewGroup中的位置

五、重写onDraw()             绘制:如何绘制这个View

其中三、四步视具体情况而定,不是必须重写的,这里讲解的就是第四步没有重写。


一、定义view的属性

首先在项目的res/values/  文件夹下建立一个attrs.xml 文件, 在里面定义我们的属性和声明我们的样式。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="slideswitch">
        <attr name="themeColor" format="color" />
        <attr name="isOpen" format="boolean" />
        <attr name="shape">
            <enum name="rect" value="1" />
            <enum name="circle" value="2" />
        </attr>
    </declare-styleable>

</resources>

 

这里普及下基本知识;自定义View会用到TypedArrayattrs.xmlAttributeSet知识,这里先说下attrs.xml(其他的在后面用到会讲)

attrs中标签declare-styleablename属性代表了接下来定义的属性的所属控件(这里name只是用来区分不同的declare-styleable,不一定和控件的名称一致,只是方便阅读)。标签attr就是用来的定义具体的属性,name代表属性名,format代表属性的类型。

attrs.xml文件中属性类型format值的格式

引用型reference

例如:Tools:background = “@drawable/图片ID”

颜色型color

例如tools:textColor = “#ffffff”

布尔型boolean

例如tools: focusable = “true”

尺寸型dimension

例如tools: layout_width = “42dip”

浮点型float

例如tools: fromAlpha = “1.0”

整型integer

例如tools: frameDuration = “100”

字符串string

例如tools: apiKey = “dsegergegasefwg”

百分数fraction

例如tools: pivotx = “200%”

枚举型enum

< attr name="orientation">

  < enum name="horizontal" value="0" />

  < enum name="vertical" value="1" />

< /attr>

使用:android:orientation = "vertical"

标志位、位或运算,格式如下:

< attr name="windowSoftInputMode">

  < flag name = "stateUnspecified" value = "0" />

  < flag name = "stateUnchanged" value = "1" />

  < flag name = "stateHidden" value = "2" />

  < flag name = "stateAlwaysHidden" value = "3" />

  < flag name = "stateVisible" value = "4" />

  < flag name = "stateAlwaysVisible" value = "5" />

  < flag name = "adjustUnspecified" value = "0x00" />

  < flag name = "adjustResize" value = "0x10" />

  < flag name = "adjustPan" value = "0x20" />

  < flag name = "adjustNothing" value = "0x30" />

< /attr>

XML中使用:

android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">

属性定义可以指定多种类型:

定义:< attr name = "background" format = "reference|color" />

使用:android:background = "@drawable/图片ID|#00FF00"

这里我们只用到了布尔型、颜色型、枚举型。

二、在View中检索定义的属性

public SlideSwitch(Context context,  AttributeSet attrs,  int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		listener = null;
		paint = new Paint();
		paint.setAntiAlias(true);
		//TypedArray是一个用来存放由context.obtainStyledAttributes获得的属性的数组  
		//属性的名称是styleable中的名称+“_”+属性名称  
		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.slideswitch);
		color_theme = a.getColor(R.styleable.slideswitch_themeColor,
				DEFAULT_COLOR_THEME);// 滑动背景颜色,默认值
		isOpen = a.getBoolean(R.styleable.slideswitch_isOpen, false);// 开关是打开还是关闭,默认false 
		shape = a.getInt(R.styleable.slideswitch_shape, SHAPE_RECT);// 形状属性
		//在使用完成后,一定要调用recycle方法  
		a.recycle();
	}

	public SlideSwitch(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public SlideSwitch(Context context) {
		this(context, null);
	}


通过第一步我们自定义了三个属性color_themeisOpenshape。但是怎么让编译器知道我们自定义的三个属性呢?这里得第二步就是实现绑定我们自定义属性的过程。


 

这里讲讲上面提到的 TypedArray:

它通常与Context类的obtainStyledAttributes方法一起使用,作为一个不同类型的数据的容器使用。这句一般是使用在自定义View的构造方法中的,其中attrs是构造方法的形参,而R.styleable.slideswitch是和attrs.xml相关的。其中 slideswitch attrs.xmldeclare-styleablename属性的值。如果这个自定义Viewattrs.xml文件中对应的declare-styleablename属性值为A,那么这里就写R.styleable.A 。

其中包括很多方法用来获取这个容器中包含的值 如:.getColor() 获取颜色值.getDimension() 获取尺寸值。这些方法一般都有这两个参数int index, int defValue。其中index为用来查找属性的检索值。如果是在attrs.xml文件中定义的属性,就是R.styleable.xx_yy。 xx代表declare-styleablename值,yy代表attrname值。defValue代表默认值,即如果在xml文件中没有设置,可以使用默认值来进行设置。

这里还有个  AttributeSet:

AttributeSet是一个属性的集合,与一个在XML文件中的标签相联系。如在自定义View中,构造方法中会有一个AttributeSet类型的参数,这个参数就和XML中定义的自定义View相联系的。一般不需要直接使用它。

好了添加完上面那些就完成了对自定义view的属性构造,那大家一定会有疑问,这些个方法是用来干什么的?为什么要添加他们?而我们怎么知道什么情况下该添加哪些构造方法呢?别急!这里我们接着瞅:

·public SlideSwitchContext context

 当在代码中创建对象时我们只需要重写这个方法。例如我们在代码中用new关键字声明一个对象。

·public SlideSwitch (Context context, AttributeSet attrs)

官方的文档是:

Constructor that is called when inflating a view from XML. This is called when a view is being constructed from an XML file, supplying attributes that were specified in the XML file. This version uses a default style of 0, so the only attribute values applied are those in the Context's Theme and the given AttributeSet

从这可以知道这个方法是当我们在xml文件来创建一个view对象的时候调用。xml文件一般是布局文件,就是现实控件的时候调用。例如我们在布局文件中添加一个类似TextView控件一样,只不过这个自定义view需要加上完整的路径名。

·public SlideSwitch (Context context, AttributeSet attrsint defStyle)

官方的文档是:

Perform inflation from XML and apply a class-specific base style. This constructor of View allows subclasses to use their own base style when they are inflating. For example, a Button class's constructor would call this version of the super class constructor and supply R.attr.buttonStyle fordefStyle; this allows the theme's button style to modify all of the base view attributes (in particular its background) as well as the Button class's attributes.

大意是:当我们需要通过xml给view定义style主题的时候允许我们按照自己的需求去定义自己的主题。就像我们要给一个Button定义新的背景主题就可以通过此方法。当然给button定义背景其实写一个xml然后设置他的background属性就好了。这里主要说的是自定义view的主题设置需要用的此方法。  

例如上面我们自己添加的color_themeisOpenshape这三个属性值就得重写这个方法。

好了,接下来就是看看一个view怎么显示的过程了。

三、重写onMeasure()

<strong> </strong>  @Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int width = measureDimension(280, widthMeasureSpec);
		int height = measureDimension(140, heightMeasureSpec);
	// 这里是当滑块形状是圆形时,如果view的宽度小于高度了就给宽度重新赋值为高的两倍。
		if (shape == SHAPE_CIRCLE) {
			if (width < height)
				width = height * 2;
		}
		//ViewGroup初始是没有大小的,自定义时需要在onMeasure中设置。
		setMeasuredDimension(width, height);
		initDrawingVal();//对于一些值的初始化,后面会讲到。
	}

//根据测量的值返回不同的结果值
	public int measureDimension(int defaultSize, int measureSpec) {
		int result;
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);
		if (specMode == MeasureSpec.EXACTLY) {
			result = specSize;
		} else {
			result = defaultSize; // UNSPECIFIED
			if (specMode == MeasureSpec.AT_MOST) {
				result = Math.min(result, specSize);
			}
		}
		return result;
	}


onMeasure()方法顾名思义就是测量view的大小,此方法接收两个参数,宽详细测量值(widthMeasureSpec)和高详细测量值(heightMeasureSpec)。这两个值分别用于确定视图的宽度和高度的规格和大小。MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。specMode一共有三种类型,如下所示:

1. EXACTLY
表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。


2. AT_MOST
表示子视图最多只能是specSize中指定的大小,开发人员应该尽可能小得去设置这个视图,并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。


3. UNSPECIFIED
表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到。

基本上知道上面几点就差不多了,但是学习要讲求专研,对于里面的具体实现细节我们要是能知道就最好不过了。这里介于内部实现比较多就不占用篇幅来写了,大家有兴趣的可以参考下:

http://blog.csdn.net/a396901990/article/details/36475213
http://blog.csdn.net/guolin_blog/article/details/16330267 (郭霖大神)
这两篇博客已经写的很详细了,相信大家看完就能了解了,这里我们还是继续写对于SlideSwitch的解析。


在画view前我们需要初始化一些参数:

public void initDrawingVal( ) {
		int width = getMeasuredWidth();
		int height = getMeasuredHeight();
		backCircleRect = new RectF();
		frontCircleRect = new RectF();
		frontRect = new Rect();
		backRect = new Rect(0, 0, width, height);
		min_left = RIM_SIZE;
		if (shape == SHAPE_RECT)
			max_left = width / 2;
		else
			max_left = width - (height - 2 * RIM_SIZE) - RIM_SIZE;
		if (isOpen) {
			frontRect_left = max_left;
			alpha = 255;
		} else {
			frontRect_left = RIM_SIZE;
			alpha = 0;
		}
		frontRect_left_begin = frontRect_left;
	}


上面这个方法是对view的一些初始化操作,RIM_SIZE 是滑块与边框的边距,默认6,max_left 是滑块打开时滑块右边缘与边框最右边的距离,当shape是长方形时就是整个view宽度的一半,当是圆形时就是整个宽度减去滑块的直径与边缘的距离,滑块直径就是(height - 2 * RIM_SIZE),不清楚的在纸上拿笔画下就知道了。min_left是滑块关闭时与边缘的距离,这里就是RIM_SIZE的大小6。


经过初始化操作之后我们就得开始制作我们的view了,这里是重头戏:

五、重写onDraw() 

@Override
	protected void onDraw(Canvas canvas) {
		if (shape == SHAPE_RECT) {
			paint.setColor(Color.GRAY);  //画笔为灰色,先绘制背景颜色,也是开关关闭时候的背景色
			canvas.drawRect(backRect, paint);
			paint.setColor(color_theme);  //此处是绘制开关开启时候的背景颜色
			paint.setAlpha(alpha);
			canvas.drawRect(backRect, paint); 
			//此处是设置滑块的大小及位置。
			frontRect.set(frontRect_left, RIM_SIZE, frontRect_left
					+ getMeasuredWidth() / 2 - RIM_SIZE, getMeasuredHeight()
					- RIM_SIZE);
			paint.setColor(Color.WHITE);
			canvas.drawRect(frontRect, paint);
		} else {
			// draw circle
			int radius;
			radius = backRect.height() / 2 - RIM_SIZE;
			paint.setColor(Color.GRAY);
			backCircleRect.set(backRect);
			canvas.drawRoundRect(backCircleRect, radius, radius, paint);
			paint.setColor(color_theme);
			paint.setAlpha(alpha);
			canvas.drawRoundRect(backCircleRect, radius, radius, paint);
			frontRect.set(frontRect_left, RIM_SIZE, frontRect_left
					+ backRect.height() - 2 * RIM_SIZE, backRect.height()
					- RIM_SIZE);
			frontCircleRect.set(frontRect);
			paint.setColor(Color.WHITE);
			canvas.drawRoundRect(frontCircleRect, radius, radius, paint);
		}
	}



上面这个方法就是正真显示我们view的地方,这里我们会通过画笔来画出我们想要的效果。基本上两种形状的画法都是相同的,这里有点需要知道的就是Rect(left, top, right, bottom)几个参数的意义,它代表的是一个矩形的左上角和右下角,我们都知道一个矩形知道了左上和右下两点就可以确定一个矩形了,这里正是如此。这里一点需要注意的是frontRect_left这个值,我前面说他是“实时记录滑块滑动时离左边的距离”,这里正好体现了一点,我们知道onDraw()是view刷新一次就执行一次,当手指按住滑块滑动时frontRect_left这个值是实时变化的,y上的距离一直是RIM_SIZE = 6不需要改变,所以左上角一点的x实时变化着,y一直不变,对应的右下角也是如此。


这里没有对画矩形和画圆角矩形的方法做讲解,我相信大家网上搜一下应该一大堆。


好了,基本的图形我们已经画出来了,但是怎么让它随着我们手指的移动而滑动呢?这里需要通过重写 onTouchEvent方法来完成:(对手势监听不太了解的可以看看http://blog.csdn.net/mackkill/article/details/50923717)

@Override
	public boolean onTouchEvent(MotionEvent event) {
		getParent().requestDisallowInterceptTouchEvent(true); //通知父控件勿拦截本控件touch事件 
		if (slideable == false)
			return super.onTouchEvent(event);
		int action = MotionEventCompat.getActionMasked(event);//这是用于多指触控
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			eventStartX = (int) event.getRawX();//手指按下点
			break;
		case MotionEvent.ACTION_MOVE:
			eventLastX = (int) event.getRawX();
			diffX = eventLastX - eventStartX;
			int tempX = diffX + frontRect_left_begin;//手指滑动的距离加上离左边的距离
			tempX = (tempX > max_left ? max_left : tempX);
			tempX = (tempX < min_left ? min_left : tempX);
		//当tempX处于min_left和max_left之间时代表滑块正处于滑动状态,需要根据透明度来改变背景色,调用刷新
			if (tempX >= min_left && tempX <= max_left) {
				frontRect_left = tempX;
				alpha = (int) (255 * (float) tempX / (float) max_left);
				invalidateView();
			}
			break;
		case MotionEvent.ACTION_UP:
			int wholeX = (int) (event.getRawX() - eventStartX);
			frontRect_left_begin = frontRect_left;
			boolean toRight;
			//frontRect_left就是滑块滑动的距离,这里判断他是否滑过了中心点,如果过了,我们就让它自动划过去。
			toRight = (frontRect_left_begin > max_left / 2 ? true : false);
			//这个加上绝对值来判断实际滑动的距离
			if (Math.abs(wholeX) < 3) {
				toRight = !toRight;
			}
			moveToDest(toRight); // 后面会讲到。
			break;
		default:
			break;
		}
		return true;
	}


这一段代码是对手势的监听处理,上面注释也很详细了,其中有一点需要提一下,就是第三行代码
getParent().requestDisallowInterceptTouchEvent(true);这个是之前做的时候发现的一个bug,就是如果把按钮放在了srcollview这样可以滑动的布局里面话会造成事件冲突,需要上面这一句来进行拦截,这一块就涉及到事件的分发机制了,这是很重要的一点。事件分发机制也是很重要的一块,以后有时间可以单独来出来讲讲,需要的同学可以上网搜搜,有很多这方面的文章。


当我们点击一下后滑块会自动移动,这是通过下面的动画来完成的:

public void moveToDest(final boolean toRight) {
		ValueAnimator toDestAnim = ValueAnimator.ofInt(frontRect_left,
				toRight ? max_left : min_left);
		toDestAnim.setDuration(500);
		toDestAnim.setInterpolator(new AccelerateDecelerateInterpolator());
		toDestAnim.start();
		toDestAnim.addUpdateListener(new AnimatorUpdateListener() {

			@Override
			public void onAnimationUpdate(ValueAnimator animation) {
				frontRect_left = (Integer) animation.getAnimatedValue();
				alpha = (int) (255 * (float) frontRect_left / (float) max_left);
				invalidateView();
			}
		});
		toDestAnim.addListener(new AnimatorListenerAdapter() {
			@Override
			public void onAnimationEnd(Animator animation) {
				if (toRight) {
					isOpen = true;
					if (listener != null)
						listener.open();
					frontRect_left_begin = max_left;
				} else {
					isOpen = false;
					if (listener != null)
						listener.close();
					frontRect_left_begin = min_left;
				}
			}
		});
	}



注意到上一段最后调用的moveToDest(final boolean toRight)方法,这其实就是对属性动画的操作,根据toRight来判断是否进行滑动,往哪边滑动。属性动画也是学习android不可不知的,android真是博大精深,缺少一点知识都不行,对于属性动画这里就不进行讲解了,一两句也是讲不清楚。这里推荐郭大神的博客

http://blog.csdn.net/guolin_blog/article/details/43536355,有兴趣的同学自己去看吧。  最后属性动画结束后不忘初始化frontRect_left_begin 的值。


接下来贴出全部的代码大家看看:

public class SlideSwitch extends View {

	static final int SHAPE_RECT = 1;   	//表示长方形
	public static final int SHAPE_CIRCLE = 2;  	//表示圆形
	private static final int RIM_SIZE = 6;     	// 开关滑块与边框的边距,默认6像素。
	private static final int DEFAULT_COLOR_THEME = Color.parseColor("#ff00ee00"); //默认滑动颜色
	// 3 attributes
	private int color_theme;   					// 自定义主题颜色参数
	private boolean isOpen;    					// 开关是否打开
	private int shape;							// 对于形状的判断
	// varials of drawing
	private Paint paint;       					// 画笔
	private Rect backRect;				  		// 长方形的背景边框
	private Rect frontRect;			 			// 长方形的滑块
	private RectF frontCircleRect;  			//圆形的滑块
	private RectF backCircleRect;  				//圆形的背景边框
	private int alpha;		  					// 滑动滑块时的背景渐变效果,通过修改透明度值实现
	private int max_left;		  				// 当滑块打开时距离左边的距离
	private int min_left;						// 当滑块关闭时距离左边的距离
	private int frontRect_left; 				// 实时记录滑块滑动时离左边的距离
	private int frontRect_left_begin = RIM_SIZE;  // 记录滑块离左边的距离(max_left,min_left,手指抬起时)
	private int eventStartX;					//手指按下的位置
	private int eventLastX;						//手指抬起时的位置
	private int diffX = 0;			 			//手指滑动的距离
	private boolean slideable = true;			//控制滑块可否滑动
	private SlideListener listener;				//滑动开关的监听接口

	public interface SlideListener {
		public void open();

		public void close();
	}

	public SlideSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		listener = null;
		paint = new Paint();
		paint.setAntiAlias(true);
		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.slideswitch);
		color_theme = a.getColor(R.styleable.slideswitch_themeColor,
				DEFAULT_COLOR_THEME);
		isOpen = a.getBoolean(R.styleable.slideswitch_isOpen, false);
		shape = a.getInt(R.styleable.slideswitch_shape, SHAPE_RECT);
		a.recycle();
	}

	public SlideSwitch(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public SlideSwitch(Context context) {
		this(context, null);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int width = measureDimension(280, widthMeasureSpec);
		int height = measureDimension(140, heightMeasureSpec);

		if (shape == SHAPE_CIRCLE) {
			if (width < height)
				width = height * 2;
		}
		// ViewGroup初始是没有大小的,自定义时需要在onMeasure中设置。
		setMeasuredDimension(width, height);
		initDrawingVal();
	}

	public void initDrawingVal() {
		int width = getMeasuredWidth();
		int height = getMeasuredHeight();

		backCircleRect = new RectF();
		frontCircleRect = new RectF();
		frontRect = new Rect();
		backRect = new Rect(0, 0, width, height);
		min_left = RIM_SIZE;
		if (shape == SHAPE_RECT)
			max_left = width / 2;
		else
			max_left = width - (height - 2 * RIM_SIZE) - RIM_SIZE;
		if (isOpen) {
			frontRect_left = max_left;
			alpha = 255;
		} else {
			frontRect_left = RIM_SIZE;
			alpha = 0;
		}
		frontRect_left_begin = frontRect_left;
	}

	public int measureDimension(int defaultSize, int measureSpec) {
		int result;
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);
		if (specMode == MeasureSpec.EXACTLY) {
			result = specSize;
		} else {
			result = defaultSize; // UNSPECIFIED
			if (specMode == MeasureSpec.AT_MOST) {
				result = Math.min(result, specSize);
			}
		}
		return result;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		if (shape == SHAPE_RECT) {
			paint.setColor(Color.GRAY);
			canvas.drawRect(backRect, paint);
			paint.setColor(color_theme);
			paint.setAlpha(alpha);
			canvas.drawRect(backRect, paint);
			frontRect.set(frontRect_left, RIM_SIZE, frontRect_left
					+ getMeasuredWidth() / 2 - RIM_SIZE, getMeasuredHeight()
					- RIM_SIZE);
			paint.setColor(Color.WHITE);
			canvas.drawRect(frontRect, paint);
		} else {
			// draw circle
			int radius;
			radius = backRect.height() / 2 - RIM_SIZE;

			paint.setColor(Color.GRAY);
			backCircleRect.set(backRect);
			canvas.drawRoundRect(backCircleRect, radius, radius, paint);
			paint.setColor(color_theme);
			paint.setAlpha(alpha);
			canvas.drawRoundRect(backCircleRect, radius, radius, paint);
			frontRect.set(frontRect_left, RIM_SIZE,
					frontRect_left + backRect.height() - 2 * RIM_SIZE,
					backRect.height() - RIM_SIZE);
			frontCircleRect.set(frontRect);
			paint.setColor(Color.WHITE);
			canvas.drawRoundRect(frontCircleRect, radius, radius, paint);
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		getParent().requestDisallowInterceptTouchEvent(true); // 通知父控件勿拦截本控件touch事件
		if (slideable == false)
			return super.onTouchEvent(event);
		int action = MotionEventCompat.getActionMasked(event);
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			eventStartX = (int) event.getRawX();
			break;
		case MotionEvent.ACTION_MOVE:
			eventLastX = (int) event.getRawX();
			diffX = eventLastX - eventStartX;
			int tempX = diffX + frontRect_left_begin;
			tempX = (tempX > max_left ? max_left : tempX);
			tempX = (tempX < min_left ? min_left : tempX);

			if (tempX >= min_left && tempX <= max_left) {
				frontRect_left = tempX;
				alpha = (int) (255 * (float) tempX / (float) max_left);
				invalidateView();
			}
			break;
		case MotionEvent.ACTION_UP:
			int wholeX = (int) (event.getRawX() - eventStartX);
			frontRect_left_begin = frontRect_left;
			boolean toRight;
			toRight = (frontRect_left_begin > max_left / 2 ? true : false);
			if (Math.abs(wholeX) < 3) {
				toRight = !toRight;
			}
			moveToDest(toRight);
			break;
		default:
			break;
		}
		return true;
	}

	/**
	 * draw again
	 */
	private void invalidateView() {
		if (Looper.getMainLooper() == Looper.myLooper()) {
			invalidate();
		} else {
			postInvalidate();
		}
	}

	public void setSlideListener(SlideListener listener) {
		this.listener = listener;
	}

	public void moveToDest(final boolean toRight) {
		ValueAnimator toDestAnim = ValueAnimator.ofInt(frontRect_left,
				toRight ? max_left : min_left);
		toDestAnim.setDuration(500);
		toDestAnim.setInterpolator(new AccelerateDecelerateInterpolator());
		toDestAnim.start();
		toDestAnim.addUpdateListener(new AnimatorUpdateListener() {

			@Override
			public void onAnimationUpdate(ValueAnimator animation) {
				frontRect_left = (Integer) animation.getAnimatedValue();
				alpha = (int) (255 * (float) frontRect_left / (float) max_left);
				invalidateView();
			}
		});
		toDestAnim.addListener(new AnimatorListenerAdapter() {
			@Override
			public void onAnimationEnd(Animator animation) {
				if (toRight) {
					isOpen = true;
					if (listener != null)
						listener.open();
					frontRect_left_begin = max_left;
				} else {
					isOpen = false;
					if (listener != null)
						listener.close();
					frontRect_left_begin = min_left;
				}
			}
		});
	}

	public void setState(boolean isOpen) {
		this.isOpen = isOpen;
		initDrawingVal();
		invalidateView();
		if (listener != null)
			if (isOpen == true) {
				listener.open();
			} else {
				listener.close();
			}
	}

	public void setShapeType(int shapeType) {
		this.shape = shapeType;
	}

	public void setSlideable(boolean slideable) {
		this.slideable = slideable;
	}

	@Override
	protected void onRestoreInstanceState(Parcelable state) {
		if (state instanceof Bundle) {
			Bundle bundle = (Bundle) state;
			this.isOpen = bundle.getBoolean("isOpen");
			state = bundle.getParcelable("instanceState");
		}
		super.onRestoreInstanceState(state);
	}

	@Override
	protected Parcelable onSaveInstanceState() {
		Bundle bundle = new Bundle();
		bundle.putParcelable("instanceState", super.onSaveInstanceState());
		bundle.putBoolean("isOpen", this.isOpen);
		return bundle;
	}
}


 

接着在xml文件中声明就可以使用了:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:slideswitch="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical"
    >
<com.slideswitchbutton.SlideSwitch
        android:id="@+id/slidebt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"
        slideswitch:isOpen="true"
        slideswitch:shape="circle"
        slideswitch:themeColor="#ff73aa00" >
       </com.slideswitchbutton.SlideSwitch>
</LinearLayout>



首先我们记得加上命名空间:
xmlns:slideswitch="http://schemas.android.com/apk/res-auto"
其中com.slideswitchbutton.SlideSwitch这句是需要显示的控件所代表的类。com.slideswitchbutton是类的包名,SlideSwitch是类名。这个类肯定是继承自View的自定义类,可以是在工程中直接源码添加xxxx.java的,也可以是在libs目录下自己新添加的jar包里面的。如果是jar包里面的一个类,则路径就是jar包里面,这个类的路径。

最后在Activity里面声明调用就好了:

public class MainActivity extends Activity {

	private SlideSwitch slide;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		slide = (SlideSwitch) findViewById(R.id.slidebt);
//		slide.setState(true);  //设置状态
		slide.setSlideListener(new SlideListener() {	
			@Override
			public void open() {
				// TODO Auto-generated method stub
				Toast.makeText(MainActivity.this,"打开了..." , Toast.LENGTH_SHORT).show();  
			}
			@Override
			public void close() {
				// TODO Auto-generated method stub
				Toast.makeText(MainActivity.this,"关闭了..." , Toast.LENGTH_SHORT).show();  
			}
		});
}


好了今天讲解就到这里了,第一次发博客讲的不好的地方请担待。(突然发现写博客也是好累的。自己理解了简单,但是要讲解出来还要别人能懂也是很难的。在此敬佩大神前辈们)。

ps:这个html编辑器太难用了,改了好几次版面还是有问题,希望大家见谅。以后改用Mark编辑器!!!


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值