转眼间从事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会用到TypedArray,attrs.xml和AttributeSet知识,这里先说下attrs.xml(其他的在后面用到会讲):
attrs中标签declare-styleable的name属性代表了接下来定义的属性的所属控件(这里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_theme、isOpen、shape。但是怎么让编译器知道我们自定义的三个属性呢?这里得第二步就是实现绑定我们自定义属性的过程。
这里讲讲上面提到的 TypedArray:
它通常与Context类的obtainStyledAttributes方法一起使用,作为一个不同类型的数据的容器使用。这句一般是使用在自定义View的构造方法中的,其中attrs是构造方法的形参,而R.styleable.slideswitch是和attrs.xml相关的。其中 slideswitch 是attrs.xml中declare-styleable的name属性的值。如果这个自定义View在attrs.xml文件中对应的declare-styleable的name属性值为A,那么这里就写R.styleable.A 。
其中包括很多方法用来获取这个容器中包含的值 如:.getColor() 获取颜色值.getDimension() 获取尺寸值。这些方法一般都有这两个参数int index, int defValue。其中index为用来查找属性的检索值。如果是在attrs.xml文件中定义的属性,就是R.styleable.xx_yy。 xx代表declare-styleable的name值,yy代表attr的name值。defValue代表默认值,即如果在xml文件中没有设置,可以使用默认值来进行设置。
这里还有个 AttributeSet:
AttributeSet是一个属性的集合,与一个在XML文件中的标签相联系。如在自定义View中,构造方法中会有一个AttributeSet类型的参数,这个参数就和XML中定义的自定义View相联系的。一般不需要直接使用它。
好了添加完上面那些就完成了对自定义view的属性构造,那大家一定会有疑问,这些个方法是用来干什么的?为什么要添加他们?而我们怎么知道什么情况下该添加哪些构造方法呢?别急!这里我们接着瞅:
·public SlideSwitch(Context 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 attrs,int 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_theme、isOpen、shape这三个属性值就得重写这个方法。
好了,接下来就是看看一个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编辑器!!!