这一次我们将会实现一个完整纯粹的自定义控件,而不是像之前的组合控件一样,拿系统的控件来实现;计划分为三部分:自定义控件的基本部分自定义控件的触摸事件的处理自定义控件的自定义属性

下面就开始第一部分的编写,本次以一个定义的开关按钮为例,下面就开始吧:

先看看效果,一个点击开关按钮,实现点击切换开关状态:

为了能够讲解清晰,还是来一些基本的介绍。

首先需要明确的就是自定义控件还是继承自View这个类,Google在View这个类里面提供了相当多的方法供我们使用,使用这些方法我们可以实现相当多的效果和功能,在这里需要用到几个主要的方法;

自定义控件的步骤、用到的主要方法:


1、首先需要定义一个类,继承自View;对于继承View的类,会需要实现至少一个构造方法;实际上这里一共有三个构造方法:

public View (Context context)是在java代码创建视图的时候被调用(使用new的方式),如果是从xml填充的视图,就不会调用这个

public View (Context context, AttributeSet attrs)这个是在xml创建但是没有指定style的时候被调用

public View (Context context, AttributeSet attrs, int defStyle)这个是在第二个基础上添加style的时候被调用的

所以对于这里来说,如果不使用style, 我们重点关注第二个构造方法即可

2、对于任何一个控件来说,它需要显示在我们的界面上,那么肯定需要定义它的大小;

在这里Google提供了一个方法:protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);我们去看这个方法的内部,实际上是调用了protected final void setMeasuredDimension(int measuredWidth, int measuredHeight);这个方法,其中第一个参数是view的宽,第二个参数是view的高,这样我们就可以设置view的宽高了,但是要注意,这样设置的单位都是像素

3、对于一个需要显示的控件来说,我们往往还需要确定它的位置:

这就要求重写onLayout方法;但是实际上这个方法在自定义view的时候使用的不多,原因是因为对于位置来说,控件只有建议权而没有决定权,决定权一般在父控件那里。

4、对于一个控件,需要显示,我们当然需要将它绘制出来,这里就需要重写onDraw方法,来将这个控件绘制出来


5、当控件状态改变的时候,我们很可能需要刷新view的显示状态,这时候就需要调用invalidate()方法,这个方法实际上会重新调用onDraw方法来重绘控件

6、在定义控件的过程中,如果需要对view设置点击事件,可以直接使用setOnClickListener方法,而不需要写view.setOnClickListener;

7、在布局文件中将这个自定义控件定义出来,注意名字要使用全类名;而且,由于是继承自view控件,所以在xml文件中如果是view本身的属性都可以直接使用,比如:android:layout_width等等

这里比较关键的地方就在于这个onDraw方法,我们一起来看一下:

[java] view plaincopy
  1. /**
  2. * 画view的方法,绘制当前view的内容
  3. */
  4. @Override
  5. protected void onDraw(Canvas canvas) {
  6. // super.onDraw(canvas);
  7. Paint paint = new Paint();
  8. // 打开抗锯齿
  9. paint.setAntiAlias(true);
  10. // 画背景
  11. canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
  12. // 画滑块
  13. canvas.drawBitmap(slideButton, slideBtn_left, 0, paint);
  14. }

onDraw方法传入的参数是一个Canvas画布对象,这个实际上跟Java中的差不太多,我们要在画布上画画也需要一个画笔,我们这里也将其初始化出来Paint paint = new Paint(),同时设置了一个抗锯齿效果paint.setAntiAlias(true),然后调用drawBitmap的方法,先后绘制了开关的背景和开关的滑块,分别入下图:

                          

这里要注意的一点就是,drawBitmap(Bitmap bitmap, float left, float top, Paint paint)方法中间的两个float类型的参数,分别代表绘制图形的左上角的x和y的坐标(原点设置在左上角),所以这里如果我们个绘制坐标都传入0,0,那么开关会处在一个关的状态,这里,我们对于滑块使用了一个变量slideBtn_left来设置其位置,那么对于关闭状态,slideBtn_left的值就应该为0,对于开启状态,slideBtn_left的值就应该是backgroundBitmap(背景)的宽度减去slideButton(滑块)的宽度

那么这样一来,机制就比较清楚了,我们只需要在控件上设置一个点击事件,同时设置一个boolean变量代表开关的状态,当点击的时候,切换这个boolean类型的变量为true或者false,同时变化slideButton的值为0或者backgroundBitmap.getWidth()-slideButton.getWidth(),然后再调用invalidate()方法刷新控件,就可以实现基本的开关功能了

下面来看具体的代码,注解比较详细:

自定义控件的类MyToggleButton.java,继承自View:

[java] view plaincopy
  1. package com.example.togglebutton.ui;
  2. import com.example.togglebutton.R;
  3. import android.content.Context;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.graphics.Canvas;
  7. import android.graphics.Paint;
  8. import android.util.AttributeSet;
  9. import android.view.View;
  10. /*
  11. * 自定义view的几个步骤:
  12. * 1、首先需要写一个类来继承自View
  13. * 2、需要得到view的对象,那么需要重写构造方法,其中一参的构造方法用于new,二参的构造方法用于xml布局文件使用,三参的构造方法可以传入一个样式
  14. * 3、需要设置view的大小,那么需要重写onMeasure方法
  15. * 4、需要设置view的位置,那么需要重写onLayout方法,但是这个方法在自定义view的时候用的不多,原因主要在于view的位置主要是由父控件来决定
  16. * 5、需要绘制出所需要显示的view,那么需要重写onDraw方法
  17. * 6、当控件状态改变的时候,需要重绘view,那么调用invalidate();方法,这个方法实际上会重新调用onDraw方法
  18. * 7、在这其中,如果需要对view设置点击事件,可以直接调用setOnClickListener方法
  19. */
  20. public class MyToggleButton extends View {
  21. /**
  22. * 开关按钮的背景
  23. */
  24. private Bitmap backgroundBitmap;
  25. /**
  26. * 开关按钮的滑动部分
  27. */
  28. private Bitmap slideButton;
  29. /**
  30. * 滑动按钮的左边界
  31. */
  32. private float slideBtn_left;
  33. /**
  34. * 当前开关的状态
  35. */
  36. private boolean currentState = false;
  37. /**
  38. * 在代码里面创建对象的时候,使用此构造方法
  39. *
  40. * @param context
  41. */
  42. public MyToggleButton(Context context) {
  43. super(context);
  44. }
  45. /**
  46. * 在布局文件中声明的view,创建时由系统自动调用
  47. *
  48. * @param context
  49. * @param attrs
  50. */
  51. public MyToggleButton(Context context, AttributeSet attrs) {
  52. super(context, attrs);
  53. initView();
  54. }
  55. /**
  56. * 测量尺寸时的回调方法
  57. */
  58. @Override
  59. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  60. // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  61. // 设置当前view的大小 width:view的宽,单位都是像素值 heigth:view的高,单位都是像素值
  62. setMeasuredDimension(backgroundBitmap.getWidth(),
  63. backgroundBitmap.getHeight());
  64. }
  65. // 这个方法对于自定义view的时候帮助不大,因为view的位置一般由父组件来决定的
  66. @Override
  67. protected void onLayout(boolean changed, int left, int top, int right,
  68. int bottom) {
  69. super.onLayout(changed, left, top, right, bottom);
  70. }
  71. /**
  72. * 画view的方法,绘制当前view的内容
  73. */
  74. @Override
  75. protected void onDraw(Canvas canvas) {
  76. // super.onDraw(canvas);
  77. Paint paint = new Paint();
  78. // 打开抗锯齿
  79. paint.setAntiAlias(true);
  80. // 画背景
  81. canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
  82. // 画滑块
  83. canvas.drawBitmap(slideButton, slideBtn_left, 0, paint);
  84. }
  85. /**
  86. * 初始化view
  87. */
  88. private void initView() {
  89. backgroundBitmap = BitmapFactory.decodeResource(getResources(),
  90. R.drawable.switch_background);
  91. slideButton = BitmapFactory.decodeResource(getResources(),
  92. R.drawable.slide_button);
  93. /*
  94. * 点击事件
  95. */
  96. setOnClickListener(new OnClickListener() {
  97. @Override
  98. public void onClick(View v) {
  99. currentState = !currentState;
  100. flushState();
  101. flushView();
  102. }
  103. });
  104. }
  105. /**
  106. * 刷新视图
  107. */
  108. protected void flushView() {
  109. // 刷新当前view会导致ondraw方法的执行
  110. invalidate();
  111. }
  112. /**
  113. * 刷新当前的状态
  114. */
  115. protected void flushState() {
  116. if (currentState) {
  117. slideBtn_left = backgroundBitmap.getWidth()
  118. - slideButton.getWidth();
  119. } else {
  120. slideBtn_left = 0;
  121. }
  122. }
  123. }

在布局文件中将其定义出来:

[html] view plaincopy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. tools:context="${relativePackage}.${activityClass}" >
  6. <com.example.togglebutton.ui.MyToggleButton
  7. android:id="@+id/my_toggle_btn"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:layout_centerInParent="true" />
  11. </RelativeLayout>

在这里由于没有写任何点击触发业务的逻辑,只是一个单纯的控件,所以在MainActivity里面没有加入多的代码:

[java] view plaincopy
  1. package com.example.togglebutton;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. public class MainActivity extends Activity {
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. }
  10. }

至此一个自定义的开关按钮就完成了,后面两篇将会介绍如何在上面实现 点击拖动开关的效果 和如何实现自定义属性,谢谢支持!

Android自定义控件系列二:自定义开关按钮相关推荐

  1. Android自定义控件系列二:自定义开关按钮(一)

    这一次我们将会实现一个完整纯粹的自定义控件,而不是像之前的组合控件一样,拿系统的控件来实现:计划分为三部分:自定义控件的基本部分,自定义控件的触摸事件的处理和自定义控件的自定义属性: 下面就开始第一部 ...

  2. Android自定义控件系列八:详解onMeasure()(二)--利用onMeasure测量来实现图片拉伸永不变形,解决屏幕适配问题

    上一篇文章详细讲解了一下onMeasure/measure方法在Android自定义控件时的原理和作用,参看博文:Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸( ...

  3. Android自定义控件系列--Path综述

    Android自定义控件系列–Path综述 项目源码 点击查看详情 Path 中文 释义为路径 然而它在自定义控件中却有着神一样的着色,这个神,是创造神奇效果的意思 1 Path 的创建 Path p ...

  4. android开发笔记之自定义开关按钮

    今天来讲讲自定义单个控件,就拿开关按钮来讲讲,相信大家见了非常多这样的了,先看看效果: 我们可以看到一个很常见的开关按钮,那就来分析分析. 首先: 这是由两张图片构成: ①一张为有开和关的背景图片 ② ...

  5. Docker系列二~自定义网桥

    Docker系列二 docker自定义网桥 查看本地网络 docker network ls #查看本地所有网络[root@localhost conf.d]# docker network ls N ...

  6. Android自定义控件(二)

    这一篇主要来讲一下自定义控件中的自定义viewgroup,我们以项目中最常用的下拉刷新和加载更多组件为例 简单介绍一下自定义viewgroup时应该怎么做. 分析:下拉刷新和加载更多的原理和步骤 自定 ...

  7. Android自定义控件(二) Android下聚光灯实现

    前言 本篇文章记录Android下实现聚光灯功能,结合上篇文章[Android自定义控件(一) 可滑动的进度条]中进度条控件修改聚光灯的大小和背景透明度. 学习巩固自定义控件知识 说明 1.实现效果 ...

  8. Android自定义控件系列

    原文出处:http://blog.csdn.net/harvic880925/article/details/50995268 前言:在我从C++转到Android时,就被Android里炫彩斑斓的自 ...

  9. Android自定义控件系列之基础篇

    一.概述 在android开发中很多UI控件往往需要进行定制以满足应用的需要或达到更加的效果,接下来就通过一个系列来介绍自定义控件,这里更多是通过一些案例逐步去学习,本系列有一些典型的应用,掌握好了大 ...

最新文章

  1. 工业相机丢帧现象怎么解决?
  2. 【Kotlin】Lambda 表达式 ( 简介 | 表达式语法 | 表达式类型 | 表达式返回值 | 调用方式 | 完整示例 )
  3. ace unlck工具下载_压缩工具:WinRAR 曝出代码执行漏洞,该升级了
  4. Halcon学习笔记——机器视觉应用工程开发思路及相机标定
  5. MS SQL入门基础:更改数据库
  6. stauml工具怎么导入文件_小伙教大家怎么剪辑短视频,1小时就学会添加字幕,值得收藏哦...
  7. SharePoint 2013 Step by Step——How to Create a Lookup Column to Another Site(Cross Site) 阅读目录...
  8. 美国Hack the Army 3.0 漏洞奖励计划启动
  9. 【Ardunio】开发入门教程【二】数据类型
  10. java 垃圾回收入门
  11. 基于tcp实现远程执行命令
  12. Illustrator教程,如何在 Illustrator 中创建和应用图案?
  13. 偷梁换柱:谨防“Synaptics”蠕虫病毒
  14. js禁止中文输入 最简洁的【禁止输入中文】
  15. 鄂州市网站建设多少钱,鄂州市建设企业网站要多少钱
  16. vss备份,使用批处理,每次只能备份当前打开的项目,怎么才能备份所有的项目呢...
  17. HDU 4415 Assassin’s Creed
  18. 李志敏 华中农业大学计算机学院,华中农业大学计算机教育论坛举行
  19. 【数据结构】7-1 软硬车厢交替排列 (13 分)
  20. C#: //todo

热门文章

  1. 读real time localization and 3D reconstruction笔记
  2. app发布前要做的几件事
  3. 7-6 进步排行榜 (10 分)
  4. [MRCTF2020]keyboard
  5. c语言运动的小人编码,C语言,我的小人在动的时候会闪屏,我用的是cls实现的,求知道该怎么办?...
  6. QT实战-计算器-刘桂林-专题视频课程
  7. CoolHash数据库的产品宣言(Fourinone4.0版)
  8. java io处理_java之IO处理
  9. MATLAB蛋白质双向电泳图谱分析
  10. 访问接口返回状态200,但是response无数据