id=”@+id/xxx”
android:
layoutWidth=”...”
android:
layoutHeight=”...”>
3.3在Activity类里获得这个控件:
1mXXXView=(XXXView)findViewById(R.id.xxx);
2mXXXView.setListener(mXXXViewListener);
以上这简单的3个步骤就是创建和使用控件的内容了,到这里,如果你是个喜欢着急写代码的人,你也可以先搭一个程序框架出来跑跑看啦。
4.考虑怎么画?
4.1拖动
用户需要能拖动Button,那也就是说我们在控件里需要捕获用户的touchevent,知道用户到底是做了什么动作(ACTION_DOWN,ACTION_MOVE,UP),还有操作的位置在哪里(getX(),getY()).
这些信息从哪里可以知道?
--》onTouchEvent()回调!
4.2动画
动画的本质就是图片+位置+时间差。
在效果图中,用户也可以点击一个状态,让控件滑动。
那这个滑动的过程就是一个动画的。
图片我们有,那怎么把图片画到Canvas上?
-》在onDraw()回调里面画。
在主线程里只要调用invalidate(),就会重新触发onDraw()的执行。
如果我们在一定的时间间隔,在不同的位置重新画图片,不就是动画了?
位置可以从用户行为获得,或者自己计算;
时间差,在Android里面控制时间最容易的是什么?
当然是Handler啦,因为它可以发送delay的消息。
4.3渐变的实现
效果图中还有个渐变的过程,这个看起来好像蛮麻烦,其实也好办。
因为有Alpha的存在。
我们可以在画的时候根据不同的位置,设置Paint不同的Alpha值,一个图片Alpha慢慢减小,另一个图片Alpha慢慢增大。
ok,分析到这里,就大概知道该怎么做了,在onTouchEvent()回调里,获得用户的行为和位置,并记录下来,在适当的时候发送Message给Handler,或者直接调用invalidate()重新画。
在Handler里,接收到信息,就根据当前的状态,更新图片下一个应该出现的位置,然后调用invalidate()触发重新画。
5.计算位置
ok,上面已经确定以什么方式做了,接下来就要用到一点点数学的计算了。
我们要确定图片从哪里开始动,动到哪里结束,还有在什么位置开始切换状态。
先切下图:
(文字也做成了图片,其实凡是涉及到文字的都不应该做成图片,如果有人切换到中文,然后他又不认识onoff呢,而且这些文字应该要可设置的才对。
这里图方便就做成图片了。
)
然后就是一些重要坐标位置啦:
图1 蓝色是那个长条的图片,绿色两块是在两个状态下Button所在的位置。
图2 黄色的区域是两个小的灰色文字图片
图3 这个区域就是文字开始切换的区域
6.伪代码
现在方法也有了,数据也有了,就可以开始写代码了。
为了叙述方便,就用伪代码代替了,下面是最重要的三个部分的伪码:
处理用户行为的逻辑:
1publicbooleanonTouchEvent(MotionEventevent){//处理用户行为
2caseACTION_DOWN:
3if(坐标在图1中蓝色区域){//touch在无效的区域
4return;
5}
6
7if(坐标在图1中绿色区域中Button在的区域){//当前状态是on,就是上面的区域,否则,就是下面的区域
8获得坐标与上边缘的距离gap;
9}else{
10设置正在滑动标志;
11设置动画的方向,发送Message;//会执行到这里的情况是,比如当前状态是on,用户点击了off那一端,那接下来控件就要自动滑动切换到off状态。
12}
13break;
14
15caseACTION_MOVE:
16if(上次Down是在无效区域|正在切换状态){//此时不用响应Move动作。
17return;
18}
19
20if(根据当前的坐标计算,滑块将不在背景区域){
21return;
22}
23
24if(根据当前的坐标计算,在文字交换的区域){
25设置交换标记;
26}
27记录滑块当前位置;
28invalidate();
29break;
30
31caseACTION_UP:
32if(上次Down是在无效区域|正在切换状态){//此时不用响应Up动作。
33return;
34}
35
36取消交换标识;
37if(根据当前坐标计算,最后的状态是on){
38设置滑块位置为on状态时的位置;
39修改状态为on;
40invalidate();
41}else{
42设置滑块位置为off状态时的位置;
43修改状态为off;
44invalidate();
45}
46}
处理自动滑动:
1privateHandlermHandler=newHandler(){//用于处理自动滑动那部分逻辑
2publicvoidhandleMessage(Messagemsg){
3if(计数>20){
4设置当前状态;
5设置滑块的位置;
6取消正在滑动的标志;
7计数归0;
8return;
9}
10
11根据计数,获得interpolator.getInterpolation;//这里用了AccelerateDecelerateInterpolator,让动画有一个加速的效果,其实这么短的距离效果看不出来。
12计算滑块的位置;
13invalidate();
14计数+1;
15sendMessageDelayed(0,20);//20ms后画下一帧。
16}
17
18};
画:
1protectedvoidonDraw(Canvascanvas){//具体画的代码
2画背景;
3
4if(在状态交换区域){
5根据滑块位置这是Paint的Alpha值;
6用上面设置的Paint画那四个小图;//在状态交换的时候,四个小图都是显示的。
7}else{
8根据当前的状态,画on滑块或off滑块;
9}
10}
ok,有上面3部分的内容,基本上就可以了。
下面就是运行起来的效果,(不好表示啦,其实就是效果图那样的)
贴个对应的代码段:
Handler:
1privateHandlermHandler=newHandler(){
2@Override
3publicvoidhandleMessage(Messagemsg){
4if(drawCount>20){
5if(button_status==STATUS_OFF){
6button_status=STATUS_ON;
7buttonY=buttonTopY;
8if(listener!
=null){
9listener.slipToTop();
10}
11}else{
12button_status=STATUS_OFF;
13buttonY=buttonBottomY;
14if(listener!
=null){
15listener.slipToBottom();
16}
17}
18
19isTouchDownAnotherSide=false;
20drawCount=0;
21return;
22}
23
24floatp;
25if(isToBottom){
26p=(float)(drawCount*0.05);
27}else{
28p=(float)(1-drawCount*0.05);
29}
30floatinter=interpolator.getInterpolation(p);
31buttonY=buttonTopY+(buttonBottomY-buttonTopY)*inter;
32
33if(buttonY>=exchangeBeginY&&buttonY<=exchangeEndY){
34isExchange=true;
35}else{
36isExchange=false;
37}
38invalidate();
39drawCount++;
40sendEmptyMessageDelayed(0,20);
41}
42};
ViewCode
onDraw():
1@Override
2protectedvoidonDraw(Canvascanvas){
3//TODOAuto-generatedmethodstub
4canvas.drawBitmap(mBackBitmap,0,0,null);
5
6if(isExchange){
7//inexchangearea,weshouldsetalpha
8PaintmPaint=newPaint();
9
10intalpha=(int)(255-255*(buttonY-25.5)/50);
11mPaint.setAlpha(alpha);
12canvas.drawBitmap(mONBitmap,buttonTopX,buttonY,mPaint);
13canvas.drawBitmap(mOFFTextBitmap,textBottomX,textBottomY,mPaint);
14
15mPaint.setAlpha(255-alpha);
16canvas.drawBitmap(mOFFBitmap,buttonBottomX,buttonY,mPaint);
17canvas.drawBitmap(mONTextBitmap,textTopX,textTopY,mPaint);
18}else{
19if(getNearLocation(0,buttonY)==STATUS_ON){
20canvas.drawBitmap(mONBitmap,buttonTopX,buttonY,null);
21canvas.drawBitmap(mOFFTextBitmap,textBottomX,textBottomY,null);
22}else{
23canvas.drawBitmap(mOFFBitmap,buttonBottomX,buttonY,null);
24canvas.drawBitmap(mONTextBitmap,textTopX,textTopY,null);
25}
26}
27}
ViewCode
onTouchEvent():
1@Override
2publicbooleanonTouchEvent(MotionEventevent){
3//TODOAuto-generatedmethodstub
4
5floatx=event.getX();
6floaty=event.getY();
7switch(event.getAction()){
8caseMotionEvent.ACTION_DOWN:
9
10if(isTouchDownAnotherSide){
11returntrue;
12}
13
14//checkiftouchrightplace
15if(isOutOfFrontBitmap(x,y)){
16isTouchDownValid=false;
17returntrue;
18}
19
20if(listener!
=null){
21listener.touchedDown();
22}
23
24if(isInFrontBitmap(x,y)){
25//touchincurrentmode
26Log.e("Slip","ACTION_DOWN:
yes!
infrontBitmap");
27isTouchDownValid=true;
28touchDownGap=getGap(x,y);
29}else{
30//touchantherside
31Log.e("Slip","ACTION_DOWN:
no!
infrontBitmap");
32isTouchDownValid=false;
33isTouchDownAnotherSide=true;
34if(button_status==STATUS_ON){
35isToBottom=true;
36mHandler.sendEmptyMessage(0);
37}else{
38isToBottom=false;
39mHandler.sendEmptyMessage(0);
40}
41}
42break;
43caseMotionEvent.ACTION_MOVE:
44//iftouchdownwrongplace,weignorenextaction
45if(!
isTouchDownValid||isTouchDownAnotherSide){
46returntrue;
47}
48if(!
isInBackBitmap(x,y)){
49Log.e("Slip","ACTION_MOVE:
no!
isInBackBitmap");
50returntrue;
51}
52if(isInExchangeArea(x,y)){
53isExchange=true;
54}else{
55isExchange=false;
56}
57buttonY=y-touchDownGap;
58this.invalidate();
59
60break;
61caseMotionEvent.ACTION_UP:
62//iftouchdownwrongplace,weignorenextaction
63if(!
isTouchDownValid||isTouchDownAnotherSide){
64Log.e("Slip","ACTION_UP:
no!
isTouchDownValid");
65returntrue;
66}
67
68isExchange=false;
69
70if(getFinalLocation(x,y)==STATUS_ON){
71buttonY=buttonTopY;
72if(button_status!
=STATUS_ON){
73button_status=STATUS_ON;
74Log.e("Slip","ACTION_UP:
STATUS_ON!
getFinalLocation");
75if(listener!
=null){
76listener.slipToTop();
77}
78}else{
79if(listener!
=null){
80listener.touchedUp();
81}
82}
83this.invalidate();
84
85}else{
86buttonY=buttonBottomY;
87if(button_status!
=STATUS_OFF){
88button_status=STATUS_OFF;
89Log.e("Slip","ACTION_UP:
STATUS_OFF!
getFinalLocation");
90if(listener!
=null){
91listener.slipToBottom();
92}
93}else{
94if(listener!
=null){
95listener.touchedUp();
96}
97}
98this.invalidate();
99}
100
101break;
102default:
103break;
104}
105
106returntrue;
107}
ViewCode