gpt4 book ai didi

自定义滑动按钮为例图文剖析Android自定义View绘制

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章自定义滑动按钮为例图文剖析Android自定义View绘制由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

自定义view一直是横在android开发者面前的一道坎.

1、view和viewgroup的关系 。

从view和viewgroup的关系来看,viewgroup继承view.

view的子类,多是功能型的控件,提供绘制的样式,比如imageview,textview等,而viewgroup的子类,多用于管理控件的大小,位置,如linearlayout,relativelayout等,从下图可以看出 。

自定义滑动按钮为例图文剖析Android自定义View绘制

从实际应用中看,他们又是组合关系,我们在布局中,常常是一个viewgroup嵌套多个viewgroup或view,而被嵌套的viewgroup又会嵌套多个viewgroup或view 。

如下 。

自定义滑动按钮为例图文剖析Android自定义View绘制

2、view的绘制流程 。

从view源码来看,主要关系三个方法:

1、measure():测量      一个final方法,控制控件的大小 2、layout():布局          用来控制自己的布局位置           有相对性,只相对于自己的父类布局,不关心祖宗布局 3、draw():绘制           用来控制控件的显示样式 。

流程:  流程 measure --> layout --> draw 。

对应于我们要实现的方法是 。

onmeasure() 。

onlayout() 。

ondraw() 。

实际绘制中,我们的思考顺序一般是这样的:

是否需要控制控件的大小-->是-->onmeasure() (1)如果这个自定义view不是viewgroup,onmeasure()方法调用setmeasuredeminsion(width,height):用来设置自己的大小 (2)如果是viewgroup,onmeasure()方法调用 ,child.measure()测量孩子的大小,给出孩子的期望大小值,之后-->setmeasuredeminsion(width,height):用来设置自己的大小 。

是否需要控制控件的摆放位置-->是 -->onlayout () 。

是否需要控制控件的样子-->是 -->ondraw ()-->canvas的绘制 。

下面是我绘制的流程图:

自定义滑动按钮为例图文剖析Android自定义View绘制

下面以自定义滑动按钮为例,说明自定义view的绘制流程 。

我们期待实现这样的效果:

自定义滑动按钮为例图文剖析Android自定义View绘制

拖动或点击按钮,开关向右滑动,变成 。

自定义滑动按钮为例图文剖析Android自定义View绘制

其中开关能随着手指的触摸滑动到相应位置,直到最后才固定在开关位置上 。

新建一个类继承自view,实现其两个构造方法 。

?
1
2
3
4
5
6
7
8
9
10
public class switchbuttonview extends view {
 
   
   public switchbuttonview(context context) {
     this (context, null );
   }
 
   public switchbuttonview(context context, attributeset attrs) {
     super (context, attrs);
   }

drawable资源中添加这两张图片 。

自定义滑动按钮为例图文剖析Android自定义View绘制自定义滑动按钮为例图文剖析Android自定义View绘制

借此,我们可以用onmeasure()确定这个控件的大小 。

?
1
2
3
4
5
6
7
@override
   protected void onmeasure( int widthmeasurespec, int heightmeasurespec) {
 
     mswitchbutton = bitmapfactory.decoderesource(getresources(), r.drawable.switch_background);
     mslidebutton = bitmapfactory.decoderesource(getresources(), r.drawable.slide_button_background);
     setmeasureddimension(mswitchbutton.getwidth(), mswitchbutton.getheight());
   }

这个控件并不需要控制其摆放位置,略过onlayout(); 。

接下来ondraw()确定其形状.

但我们需要根据我们点击控件的不同行为来确定形状,这需要重写ontouchevent() 。

其中的逻辑是:

当按下时,触发事件motionevent.action_down,若此时状态为关:

(1)若手指触摸点(通过event.getx()得到)在按钮的 中线右侧,按钮向右滑动一段距离(event.getx()与开关控件一半宽度之差,具体看下文源码) 。

(2)若手指触摸点在按钮中线左侧,按钮依旧处于最左(即“关”的状态).

若此时状态为开:

(1)若手指触摸点在按钮中线左侧,按钮向左滑动一段距离 。

(2)若手指触摸点在按钮中线右侧,按钮依旧处于最右(即“开”的状态) 。

当滑动时,触发时间motionevent.action_move,逻辑与按下时一致 。

注意,ontouchevent()需要设置返回值 为 return true,否则无法响应滑动事件 。

当手指收起时,若开关中线位于整个控件中线左侧,设置状态为关,反之,设置为开.

具体源码如下所示:(还对外提供了一个暴露此时开关状态的接口) 。

自定义view部分 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package com.lian.switchtogglebutton;
 
import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.canvas;
import android.graphics.paint;
import android.util.attributeset;
import android.util.log;
import android.view.motionevent;
import android.view.view;
 
/**
  * created by lian on 2016/3/20.
  */
public class switchbuttonview extends view {
 
   private static final int state_null = 0 ; //默认状态
   private static final int state_down = 1 ;
   private static final int state_move = 2 ;
   private static final int state_up = 3 ;
 
   private bitmap mslidebutton;
   private bitmap mswitchbutton;
   private paint mpaint = new paint();
   private int buttonstate = state_null;
   private float mdistance;
   private boolean isopened = false ;
   private onswitchlistener mlistener;
 
   public switchbuttonview(context context) {
     this (context, null );
   }
 
   public switchbuttonview(context context, attributeset attrs) {
     super (context, attrs);
   }
 
   @override
   protected void onmeasure( int widthmeasurespec, int heightmeasurespec) {
 
     mswitchbutton = bitmapfactory.decoderesource(getresources(), r.drawable.switch_background);
     mslidebutton = bitmapfactory.decoderesource(getresources(), r.drawable.slide_button_background);
     setmeasureddimension(mswitchbutton.getwidth(), mswitchbutton.getheight());
   }
 
   @override
   protected void ondraw(canvas canvas) {
     super .ondraw(canvas);
     if (mswitchbutton!= null ){
       canvas.drawbitmap(mswitchbutton, 0 , 0 , mpaint);
     }
     //buttonstate的值在ontouchevent()中确定
     switch (buttonstate){
       case state_down:
       case state_move:
         if (!isopened){
           float middle = mslidebutton.getwidth() / 2f;
           if (mdistance > middle) {
             float max = mswitchbutton.getwidth() - mslidebutton.getwidth();
             float left = mdistance - middle;
             if (left >= max) {
               left = max;
             }
             canvas.drawbitmap(mslidebutton,left, 0 ,mpaint);
           }
 
           else {
 
             canvas.drawbitmap(mslidebutton, 0 , 0 ,mpaint);
           }
         } else {
           float middle = mswitchbutton.getwidth() - mslidebutton.getwidth() / 2f;
           if (mdistance < middle){
             float left = mdistance-mslidebutton.getwidth()/2f;
             float min = 0 ;
             if (left < 0 ){
               left = min;
             }
             canvas.drawbitmap(mslidebutton,left, 0 ,mpaint);
           } else {
             canvas.drawbitmap(mslidebutton,mswitchbutton.getwidth()-mslidebutton.getwidth(), 0 ,mpaint);
           }
         }
 
 
 
         break ;
 
       case state_null:
       case state_up:
         if (isopened){
           log.d( "开关" , "开着的" );
           canvas.drawbitmap(mslidebutton,mswitchbutton.getwidth()-mslidebutton.getwidth(), 0 ,mpaint);
         } else {
           log.d( "开关" , "关着的" );
           canvas.drawbitmap(mslidebutton, 0 , 0 ,mpaint);
         }
         break ;
 
       default :
         break ;
     }
 
   }
 
   @override
   public boolean ontouchevent(motionevent event) {
 
     switch (event.getaction()){
       case motionevent.action_down:
         mdistance = event.getx();
         log.d( "down" , "按下" );
         buttonstate = state_down;
         invalidate();
         break ;
 
       case motionevent.action_move:
         buttonstate = state_move;
         mdistance = event.getx();
         log.d( "move" , "移动" );
         invalidate();
         break ;
 
       case motionevent.action_up:
         mdistance = event.getx();
         buttonstate = state_up;
         log.d( "up" , "起开" );
         if (mdistance >= mswitchbutton.getwidth() / 2f){
           isopened = true ;
         } else {
           isopened = false ;
         }
         if (mlistener != null ){
           mlistener.onswitchchanged(isopened);
         }
         invalidate();
         break ;
       default :
         break ;
     }
 
     return true ;
   }
 
   public void setonswitchlistener(onswitchlistener listener){
     this .mlistener = listener;
   }
 
   public interface onswitchlistener{
     void onswitchchanged( boolean isopened);
   }
}

demoactivity:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.lian.switchtogglebutton;
 
import android.os.bundle;
import android.support.v7.app.appcompatactivity;
import android.widget.toast;
 
public class mainactivity extends appcompatactivity {
 
   @override
   protected void oncreate(bundle savedinstancestate) {
     super .oncreate(savedinstancestate);
     setcontentview(r.layout.activity_main);
 
     switchbuttonview switchbuttonview = (switchbuttonview) findviewbyid(r.id.switchbutton);
     switchbuttonview.setonswitchlistener( new switchbuttonview.onswitchlistener() {
       @override
       public void onswitchchanged( boolean isopened) {
         if (isopened) {
           toast.maketext(mainactivity. this , "打开" , toast.length_short).show();
         } else {
           toast.maketext(mainactivity. this , "关闭" , toast.length_short).show();
         }
       }
     });
   }
}

布局:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version= "1.0" encoding= "utf-8" ?>
<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"
   android:paddingbottom= "@dimen/activity_vertical_margin"
   android:paddingleft= "@dimen/activity_horizontal_margin"
   android:paddingright= "@dimen/activity_horizontal_margin"
   android:paddingtop= "@dimen/activity_vertical_margin"
   tools:context= "com.lian.switchtogglebutton.mainactivity" >
 
   <com.lian.switchtogglebutton.switchbuttonview
     android:id= "@+id/switchbutton"
     android:layout_width= "wrap_content"
     android:layout_height= "wrap_content"
     />
</relativelayout>

以上就是本文的全部内容,希望对大家的学习有所帮助.

最后此篇关于自定义滑动按钮为例图文剖析Android自定义View绘制的文章就讲到这里了,如果你想了解更多关于自定义滑动按钮为例图文剖析Android自定义View绘制的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com