热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android自定义控件之开关按钮学习笔记分享

这篇文章主要为大家分享了Android自定义开关按钮的学习笔记,内容丰富,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

今天来讲讲自定义单个控件,就拿开关按钮来讲讲,相信大家见了非常多这样的了,先看看效果:

我们可以看到一个很常见的开关按钮,那就来分析分析。

首先:

这是由两张图片构成:

①一张为有开和关的背景图片

②一张为控制开和关的滑动按钮

第一步:

写个类继承View,并重写几个方法:

第一个为构造函数,重写一个参数的函数和两个参数的函数就够了,因为两个参数的函数能够使用自定义属性

第二个为控制控件的大小–>protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {}

第三个为绘制控件的方法–>protected void onDraw(Canvas canvas) {}

第二步:

将用户指定的两张图片加载进来,这里使用自定义属性加载, 在values目录下新建attrs.xml,在xml文件中指定自定义属性名和自定义属性的字段及值类型(即背景图和滑块图)即可:

<&#63;xml version="1.0" encoding="utf-8"&#63;>

  
    
    
    


各个字段的含义我在这就不讲了,不懂的就去看看前几篇《Android开发笔记之自定义组合控件》有讲过,写好就只需在构造函数中加载进来

public SwitchView(Context context, AttributeSet attrs) {
    super(context, attrs);
    //拿到自定义属性
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.switchView_attrs);
    //拿到自定义字段的值
    Drawable switchBackground = ta.getDrawable(R.styleable.switchView_attrs_background);
    Drawable switchView_slide = ta.getDrawable(R.styleable.switchView_attrs_slide);
    //把值设置到相应组件上
    backgroundBitmap = convertDrawable2BitmapSimple(switchBackground);
    switchSlide = convertDrawable2BitmapSimple(switchView_slide);
  }

不过要注意的是:

因为从自定义属性中取到的是Drawable对象,而我们要的是一个Bitmap对象,所以我们先得把Drawable对象转成Bitmap对象,其实有很多种方法来转,这里就介绍种最简单的方法,借助与BitmapDrawable类:

//将Drawable转成Bitmap
  public Bitmap convertDrawable2BitmapSimple(Drawable drawable) {
    BitmapDrawable bd= (BitmapDrawable)drawable;
    return bd.getBitmap();
  }

第三步:

onMeasure方法来控制控件的大小,我们可以看到这个开关按钮的大小就跟背景的大小一样大,只需要设置为背景的大小:

//控制控件的大小
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (backgroundBitmap != null) {
      //控件大小设置为背景的大小
      setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
    }else {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  }

第四步:

既然这个开关按钮需要通过点击或移动来控制控件的开和关,所以就需要实现onTouchEvent方法,当然应该有三个事件会触发:按下、移动和抬起的时候,每次点击、移动或抬起都需要重绘,我们先来分析下滑块的状态有哪些(应该有四种状态)一开始默认状态为空:

1.点击的时候
2.移动的时候
3.抬起的时候
4.空的时候(即什么都没干的时候)

先分析下点击的时候的情况:

①当按下或移动的坐标大于滑块宽度一半时将滑块右移

②当按下或移动的坐标小于滑块宽度一半时滑块不动

注:防止滑块移至背景外面,最大是滑块右边和背景右边对齐(即最大离左边为背景宽度-滑块宽度)

再来看看移动的时候的情况应该是和点击的时候是一样的。

再来分析抬起的时候的情况:

①如果开关状态是打开的就将滑块移动至右边

②如果开关状态是关闭的就将滑块移动至左边

那怎么判断什么时候是打开状态和关闭状态呢??

①抬起的坐标大于背景宽度一半的时候设为打开状态

②抬起的坐标小于背景宽度坐标一 半的时候设为关闭状态

再来分析下空的时候,可以发现它和抬起的时候的情况是一样的。

第五步:

在onDraw方法中将背景和滑块绘制出来。刚才分析了onTouchEvent方法,这次是一样的,滑块的四个状态分别处理,前面onTouchEvent方法中滑块的状态改变,然后通过invalidate()方法来通知系统重绘。

第六步:

我们做这个自定义控件是为了让用户使用的,现在这个是没有什么用的,用户用不了,所以可以通过设置监听器来对外提供接口。

/** 
   * switchView开关监听接口
   * 
   * */
  interface OnSwitchChangedListener {
    public void onSwitchChange(boolean isOpen);
  }
  /** 
   * 设置 switchView状态监听 
   * */
  public void setOnChangeListener(OnSwitchChangedListener listener) {
    switchListener = listener;
  }

这个监听器中的boolean值需要赋值,那在什么时候赋值呢,应该是在抬起或空的状态的时候给它赋值,因为那个时候才真正确定开关按钮是打开的还是关闭的。

第七步:

到这一步就是来使用了,在布局文件中把自定义的这个控件定义出来




使用定义好的View应该都会用了,不会的去看看《android开发笔记之自定义组合控件》。

核心代码:

布局文件


  

自定义View: SwitchView.java

/**
 * 自定义开关按钮
 * @author Administrator
 *
 */
public class SwitchView extends View {
  //背景图片和滑块图片
  private Bitmap backgroundBitmap,switchSlide;
  //画笔
  private Paint paint;
  //得到的x坐标(点击、移动、抬起)
  private float currentX;
  //判断开关是否打开的标记位
  private boolean isOpen = false;
  //开关打开与关闭的监听器
  private OnSwitchChangedListener switchListener;
  //滑块的四种状态
  public static final int STATE_DOWN = 1; //按下的时候
  public static final int STATE_MOVE = 2; //移动的时候
  public static final int STATE_UP = 3;  //抬起的时候
  public static final int STATE_NOnE= 0; //空的时候(即什么都没干的时候)
  //标记状态(默认为空状态)
  private int state = STATE_NONE;
  public SwitchView(Context context) {
    super(context,null);
  }
  public SwitchView(Context context, AttributeSet attrs) {
    super(context, attrs);
    //拿到自定义属性
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.switchView_attrs);
    //拿到自定义字段的值
    Drawable switchBackground = ta.getDrawable(R.styleable.switchView_attrs_background);
    Drawable switchView_slide = ta.getDrawable(R.styleable.switchView_attrs_slide);
    //把值设置到相应组件上
    backgroundBitmap = convertDrawable2BitmapSimple(switchBackground);
    switchSlide = convertDrawable2BitmapSimple(switchView_slide);
  }
  //将Drawable转成Bitmap
  public Bitmap convertDrawable2BitmapSimple(Drawable drawable) {
    BitmapDrawable bd = (BitmapDrawable)drawable;
    return bd.getBitmap();
  }
  //控制控件的大小
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (backgroundBitmap != null) {
      //控件大小设置为背景的大小
      setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
    }else {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  }
  //绘制控件的样式
  @Override
  protected void onDraw(Canvas canvas) {
    //背景为空的时候才将背景绘制
    if (backgroundBitmap != null) {
      paint = new Paint();
      //第一个参数表示需要画的Bitmap
      //第二个参数表示Bitmap左边离控件的左边距
      //第三个参数表示Bitmap上边离控件的上边距
      //第四个参数表示画笔
      canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
    }
    switch (state) {
    case STATE_DOWN:    //按下和移动的触发事件都一样,都是将滑块移动
    case STATE_MOVE:
      //当按下或移动的坐标大于滑块宽度一半时将滑块右移
      if (currentX > switchSlide.getWidth()/2f) {
        //让滑块向右滑动(重新绘制滑块的位置)
        float left = currentX - switchSlide.getWidth()/2f;
        //防止滑块移至背景外面,最大是滑块右边和背景右边对齐(即最大离左边为背景宽度-滑块宽度)
        float maxLeft = backgroundBitmap.getWidth() - switchSlide.getWidth();
        if (left > maxLeft) {
          left = maxLeft;
        }
        canvas.drawBitmap(switchSlide, left, 0, paint);
      //当按下或移动的坐标小于滑块宽度一半时滑块不动
      }else if (currentX  backgroundBitmap.getWidth()/2f) {
        //滑块在右边,开启
        isOpen = true;
      //抬起的坐标小于背景宽度坐标一 半的时候设为关闭状态
      }else if (currentX 

MainActivity.java

public class MainActivity extends Activity {

  private SwitchView switchView;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    switchView = (SwitchView) findViewById(R.id.switchView);
    switchView.setOnChangeListener(new OnSwitchChangedListener() {
      @Override
      public void onSwitchChange(boolean isOpen) {
        if (isOpen) {
          //打开开关的时候的逻辑
          Toast.makeText(MainActivity.this, "开关打开了", Toast.LENGTH_LONG).show();
        }else {
          //关闭开关的时候的逻辑
          Toast.makeText(MainActivity.this, "开关关闭了", Toast.LENGTH_LONG).show();
        }
      }
    });
  }
}

大家看起来这么简单的一个写了这么多,其实我们学习这个不是为了写这个,比这个好的开源多的是,而是为了学习这种思路与思维,大家赶紧试试吧!

谢谢大家的阅读,也希望大家可以继续关注的更多精彩内容。


推荐阅读
  • Servlet过滤器入门:实现与配置
    本文介绍如何在Java Web应用中实现和配置Servlet过滤器,通过实现`javax.servlet.Filter`接口来创建过滤器,并详细说明其在web.xml文件中的配置方法。 ... [详细]
  • Java 中重写与重载的区别
    本文详细解析了 Java 编程语言中重写(Override)和重载(Overload)的概念及其主要区别,帮助开发者更好地理解和应用这两种多态性机制。 ... [详细]
  • 云屏系统基于嵌入式微系统msOS,旨在解决当前嵌入式彩屏GUI编程中硬件要求高、软件开发复杂、界面效果不佳等问题。该系统通过结合MCU和Android技术,利用Html5+JavaScript实现高效、易用的图形用户界面开发,使嵌入式开发人员能够专注于业务逻辑。 ... [详细]
  • 本文详细记录了一位具有五年半开发经验的候选人,在华为Android高级开发职位面试过程中的经历。从早晨9点到下午5点半,经过了群体面试、技术面试、综合面试及英语面试等多个环节,最终成功通过考核。文章不仅分享了面试心得,还提供了宝贵的面试题资源。 ... [详细]
  • 使用WinForms 实现 RabbitMQ RPC 示例
    本文通过两个WinForms应用程序演示了如何使用RabbitMQ实现远程过程调用(RPC)。一个应用作为客户端发送请求,另一个应用作为服务端处理请求并返回响应。 ... [详细]
  • iOS 开发技巧:TabBarController 自定义与本地通知设置
    本文介绍了如何在 iOS 中自定义 TabBarController 的背景颜色和选中项的颜色,以及如何使用本地通知设置应用程序图标上的提醒个数。通过这些技巧,可以提升应用的用户体验。 ... [详细]
  • 本文介绍了一种根据目标检测结果,从原始XML文件中提取并分析特定类别的方法。通过解析XML文件,筛选出特定类别的图像和标注信息,并保存到新的文件夹中,以便进一步分析和处理。 ... [详细]
  • 本文旨在回顾并总结近期学习的.NET Core基础知识,通过具体的操作指南加深理解,并为初学者提供实用建议,避免常见的错误和陷阱。内容涵盖CentOS的安装配置、.NET Core环境搭建及网站部署等。 ... [详细]
  • 本文介绍了如何使用JFreeChart库创建一个美观且功能丰富的环形图。通过设置主题、字体和颜色等属性,可以生成符合特定需求的图表。 ... [详细]
  • 本文将带您了解Cocos家族的不同版本和分支,特别是Cocos Creator的发展历程及其核心特性,帮助初学者快速入门。 ... [详细]
  • 解决Hive操作无响应问题:drop table和create table的处理方法
    本文详细介绍了在Hive中执行drop table和create table命令时遇到无响应的情况,并提供了完整的解决方案。通过调整MySQL字符集编码,确保Hive数据库与MySQL之间的兼容性,从而有效解决问题。 ... [详细]
  • 本文针对初学者在创建Android项目时遇到的R.java文件错误提供了解决方案,通过实际案例和详细的日志分析,帮助读者快速定位并解决问题。 ... [详细]
  • 本文介绍了如何使用Java代码在Android设备上检测特定应用程序是否已安装。通过创建一个Intent并利用PackageManager查询该Intent的可用性来实现这一功能。 ... [详细]
  • TortoiseSVN与VisualSVN Server的安装及基本操作指南
    本文详细介绍了如何安装VisualSVN Server以及TortoiseSVN客户端,并提供了基本的操作步骤,包括配置仓库、用户管理及权限设置等关键环节。 ... [详细]
  • 本文探讨了天才与疯子之间的微妙界限,介绍了如何利用巨人的工具提升自我,以及如何通过科学决策、数据洞察和智慧的尺度来指导我们的生活和工作。 ... [详细]
author-avatar
手机用户2502897397
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有