弄过android开发的都知道,系统有一个默认的ToggleButton,但很多人都觉得他很难看,当然也包括我。如果你感觉他不难看,那你就继续使用系统的吧,这篇文章对你来说是多余的了。

今天来写一个模仿微信的ToggleButton控件,是啊,模仿都是模仿"大家之作",腾讯、360等等,也确实,他们设计出来的东西确实好看。

下面看效果图打开状态,关闭状态

先奉献上三张图片的素材,打开背景图,关闭背景图,滑块背景图。这里背景图高度是小于前两个背景图2个像素的。这样才能出现最终的打开和关闭后的滑块上下出现1个像素背景的效果。

开始我们的控件代码,这里我们自定义一个ToggleButton控件,当然要继承自View,下面贴出我的代码

 public class ToggleButton extends View {

     private Bitmap onBackgroundImage;
private Bitmap offBackgroundImage;
private Bitmap blockImage;
private ToggleState state = ToggleState.Open;
private int currentX;
private int mouseDownX = -1;
private boolean isMove = false;
private ToggleState preState;
private boolean isInvalidate = true;
public ToggleButton(Context context) {
super(context);
// TODO Auto-generated constructor stub
} private OnClickListener clickListener = null; /*
* 其实应该是stateChange事件,懒得改了
*/
public void SetOnClickListener(OnClickListener listener) {
if (listener != null)
clickListener = listener;
} @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// TODO Auto-generated method stub
super.onLayout(changed, left, top, right, bottom);
//layout方法是在ondraw方法之前执行,
//在这个做判断是防止用户在设置Image之前setState,
//这样Image为null,系统无法获取block宽度,也就无法设置currentX坐标,
//系统将采用默认Open状态,会出现即使用户设置状态为close,而界面显示仍未open状态,
//在这里判断,是因为当前image和state肯定已经设置完毕了
if (state == ToggleState.Open) {
currentX = 2;
} else {
if (blockImage == null || offBackgroundImage == null || onBackgroundImage == null)
return;
currentX=offBackgroundImage.getWidth()-blockImage.getWidth()-2;
}
}
public enum ToggleState {
Open, Close
} public ToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} public void setOnBackgroundResource(int id) { onBackgroundImage = BitmapFactory.decodeResource(getResources(), id);
} public void setOffBackgroundResource(int id) {
offBackgroundImage = BitmapFactory.decodeResource(getResources(), id); } public void setState(ToggleState state) {
preState = this.state = state;
if (state == ToggleState.Open) {
currentX = 2;
} else {
if (blockImage != null && offBackgroundImage != null)
currentX = offBackgroundImage.getWidth() - blockImage.getWidth() - 2;
}
invalidate();
} public ToggleState getState() {
return state;
} public void setBlockResource(int id) {
blockImage = BitmapFactory.decodeResource(getResources(), id);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(onBackgroundImage.getWidth(), onBackgroundImage.getHeight()); } @Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
// 有效性判断,如果其中有一个为空,拒绝绘制,继续绘制也没有意义
if (blockImage == null || offBackgroundImage == null || onBackgroundImage == null)
return;
if ((currentX + blockImage.getWidth() / 2) > onBackgroundImage.getWidth() / 2) {
canvas.drawBitmap(offBackgroundImage, 0, 0, null);
} else {
canvas.drawBitmap(onBackgroundImage, 0, 0, null);
}
canvas.drawBitmap(blockImage, currentX, 1, null);
} @Override
public boolean onTouchEvent(MotionEvent event) {
currentX = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录鼠标按下时的x
mouseDownX = (int) event.getX();
// 按下的时候不进行重绘
isInvalidate = false;
break;
case MotionEvent.ACTION_MOVE:
// 记录鼠标发生了移动
isMove = true;
break;
case MotionEvent.ACTION_UP:
// 判断鼠标按下和抬起过程中是否发生了移动
// 鼠标抬起时,判断当前x坐标位置
if (isMove) {
// 发生了移动,判断当前位置
if ((currentX + blockImage.getWidth() / 2) > onBackgroundImage.getWidth() / 2) {
// 背景图的后半段
currentX = onBackgroundImage.getWidth() - blockImage.getWidth();
state = ToggleState.Close;
} else {
// 背景图的前半段
currentX = 2;
state = ToggleState.Open;
}
} else {
// 没有发生移动,即为点击事件,更改状态,同时改变滑块位置
if (state == ToggleState.Open) {
state = ToggleState.Close;
currentX = onBackgroundImage.getWidth() - blockImage.getWidth() - 2;
} else {
state = ToggleState.Open;
currentX = 2;
}
}
// 复位,以免影响下一次的触摸事件
isMove = false;
if (preState != state && clickListener != null) {
clickListener.onClick(this);
preState = state;
}
break;
}
if (currentX < 2)
currentX = 2;
if (currentX + blockImage.getWidth() >= onBackgroundImage.getWidth())
currentX = onBackgroundImage.getWidth() - blockImage.getWidth() - 2;
// 通知控件绘制
if (isInvalidate)
invalidate();
else
isInvalidate = true;
return true;
} }

代码里的注释够多吧,哈哈,所以就不进行讲解了。

接下来我们在activity布局文件里面使用这个ToggleButton 控件

 <包名.ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/testToggleButton"
/>

我们在activity里面使用吧

 final ToggleButton toggle=(ToggleButton) findViewById(R.id.testToggleButton);
toggle.setState(ToggleState.Close);
toggle.setOnBackgroundResource(R.drawable.on);
toggle.setOffBackgroundResource(R.drawable.off);
toggle.setBlockResource(R.drawable.block); toggle.SetOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
String state=(toggle.getState()==ToggleState.Open?"打开":"关闭");
Toast.makeText(MainActivity.this, "当前状态:"+state, Toast.LENGTH_SHORT).show();
}
});

注意啊,我上面的一行代码的位置

toggle.setState(ToggleState.Close);

设置状态这行代码放在了设置图片之前,但效果仍然是关闭状态。

但ToggleButton里面如果不重写OnLayout方法,显示出来的状态就只能是打开状态。

自定义控件(模仿微信ToggleButton控件)的更多相关文章

  1. Qt编写自定义控件31-面板仪表盘控件

    一.前言 在Qt自定义控件中,仪表盘控件是数量最多的,写仪表盘都写到快要吐血,可能是因为各种工业控制领域用的比较多吧,而且仪表盘又是比较生动直观的,这次看到百度的echart中有这个控件,所以也来模仿 ...

  2. WPF自定义控件(一)の控件分类

    一.什么是控件(Controls) 控件是指对数据和方法的封装.控件可以有自己的属性和方法,其中属性是控件数据的简单访问者,方法则是控件的一些简单而可见的功能.控件创建过程包括设计.开发.调试(就是所 ...

  3. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  4. android自定义控件(五) 自定义组合控件

    转自http://www.cnblogs.com/hdjjun/archive/2011/10/12/2209467.html 代码为自己编写 目标:实现textview和ImageButton组合, ...

  5. ToggleButton控件

    ToggleButton 两种状态 ·状态button     -继承自CompoundButton ·主要属性:-Android:textOn    -Android:textOff ·主要方法: ...

  6. WPF自定义控件第二 - 转盘按钮控件

    继之前那个控件,又做了一个原理差不多的控件.这个控件主要模仿百度贴吧WP版帖子浏览界面左下角那个弹出的按钮盘.希望对大家有帮助. 这个控件和之前的也差不多,为了不让大家白看,文章最后发干货. 由于这个 ...

  7. Windows phone自定义控件(无外观控件)——FlipPanel

    编码前 无外观自定义控件的定义在上一篇中已经有了,至于这一篇的自定义控件,比之前多加入了状态的变化,就像默认的Button具有Pressed.Normal等状态.在状态转变的同时可以加上一些动画,可以 ...

  8. Qt编写自定义控件48-面板窗体控件

    一.前言 很多时候需要有一个控件,能够替代容器控件,自动容纳多个widget,自适应宽高,然后提供滚动条功能,这就必然需要用到QScrollArea控件,可设置各个子面板的间距等,也在很多系统中用到, ...

  9. Qt编写自定义控件47-面板区域控件

    一.前言 在很多web网页上,经常可以看到一个设备对应一个面板,或者某种同等类型的信息全部放在一个面板上,该面板还可以拖来拖去的,这个控件首次用在智能访客管理平台中,比如身份证信息一个面板,访客信息一 ...

随机推荐

  1. CKEditor配置及使用

    注:使用CKEditor版本为js版本的CKEditor 4,所有配置均参考自CKEditor官方API:http://docs.ckeditor.com/,以及实践经验 一.快速使用 1.引入CKE ...

  2. html使用空格对齐文本(&nbsp;&emsp;&ensp;)

    字符以及HTML实体 描述以及说明   这是我们使用最多的空格,也就是按下space键产生的空格.在HTML中,如果你用空格键产生此空格,空格是不会累加的(只算1个).要使用html实体表示才可累加. ...

  3. 二.CSS的伪类

    CSS的伪类(Pseudo-classes)分为两种:UI伪类和结构化伪类,伪类一般用于向某些选择器添加特殊的效果,伪类选择符用"  : "进行标示,如果是“ ::  ” 表示CS ...

  4. sql调用方法精简

    <%If IsArray(proList) Then%> <UL class="product-ul"> <%For x=0 to Ubound(pr ...

  5. subilme增加对markdown的高亮支持

    sublime2对markdown原生主题支持都没有, 需要通过插件补充 1.插件安装 通过Package Control安装下列插件: Markdown Extended Monokai Exten ...

  6. in/exists not in/not exists null

    in/not in exists/not exists null的理解 两个测试表 create table tmp01 as with tmp as ( select '1' as id from ...

  7. Hadoop集群错误

    1.Hadoop集群所有的DataNode都启动不了解决办法 删除从节点.../usr/hadoop/tmp/dfs/ 下内容,再重新格式化namenode

  8. linux 环境变量【转】

    1.引言 在 linux系统 下,如果你下载并安装了应用程序,很有可能在键入它的名称时出现" command not found "的提示内容.如果每次都到安装目标文件夹内,找到可 ...

  9. 直播开始:'云榨汁机'诞生记--聊聊JavaScript中的'业务建模'

    闭包是JavaScript中的一个重要特性,在之前的博文中,我们说闭包是一个'看似简单,其实很有内涵'的特性.当我们用JavaScript来实现相对复杂的业务建模时,我们可以如何利用'闭包'这个特性呢 ...

  10. 超炫酷web前端的jQuery/HTML5应用搜罗

    作为前端开发者,我们肯定都使用过非常多的jQuery插件,毋庸置疑,jQuery非常流行,尤其是结合HTML5和CSS3以后,让这些jQuery插件有了更多地动画效果,更为绚丽多彩. 1.HTML5/ ...