自定义控件(视图)2期笔记05:自定义控件之继承自View(滑动开关)
1. 开关按钮点击效果,如下:

2. 继承已有View实现自定义View

3. 下面通过一个案例实现滑动开关的案例:
(1)新建一个新的Android工程,命名为" 开关按钮",接下来我们按照上面的步骤来:自定义类MyToggleButton继承自View。

(2)编写设计activity_main.xml布局文件,如下:
<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"
tools:context="com.himi.togglebtn.MainActivity" > <com.himi.togglebtn.MyToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/my_toggle_btn"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"/> </RelativeLayout>
注意这里使用的自定的MyToggleButton(View),要使用全路径。
(3)编写自定义的MyToggleButton。
• 重写onMeasure()方法,指定控件大小;
• 重写onDraw()方法,绘制控件内容。
package com.himi.togglebtn; 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.view.View;
import android.view.View.OnClickListener; public class MyToggleButton extends View implements OnClickListener { //作为背景的图片
private Bitmap backgroundBitmap;
//滑动的开关图片
private Bitmap slidebtn;
private Paint paint; //滑动按钮的左边界
private float slidebtn_left; /**
* 当前开关的状态
* true :为开
* false:为关
*/
private boolean currState = false; /**
* 我们在代码里面创建对象的时候,使用此构造方法
* @param context
*/
public MyToggleButton(Context context) {
super(context);
// TODO 自动生成的构造函数存根
} /**
* 在布局文件xml中声明的View,创建时候由系统自动调用此构造方法。
* 倘若我们(使用全路径)在xml布局文件中,声明使用这个自定义的View,但是我们没有这个构造方法,就会报错(系统不能找到这个构造)
* @param context
* @param attrs
*/
public MyToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
} /**
* 这个构造方法比上面的构造方法都了一个参数 defStyle,这个参数View默认的样式,这里可以重新这个构造,设置defStyle
* 改变生成自定义的View的样式style
* @param context
* @param attrs
* @param defStyle
*/
public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO 自动生成的构造函数存根
} //初始化
private void initView() {
//初始化图片
backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
slidebtn = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
//初始化画笔
paint = new Paint();
paint.setAntiAlias(true);//打开抗锯齿
//添加Onclick事件监听
setOnClickListener(this);
} /*
* View对象显示在屏幕上,有几个重要步骤:
* 1. 构造方法 创建 对象.
* 2. 测量View的大小. onMeasure(int, int):系统调用的方法,获知View的大小
* 3. 确定View的位置,View自身有一些建议权,View位置决定权在父View手中. onLayout(): ViewGroup调用
* 4. 绘制View的内容 onDraw(canvas)
*
*/ /**
*
* 测量尺寸时候的回调方法
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置当前View的大小
* width :当前View的宽度
* height:当前view的高度(单位:像素)
*/
setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
} /**
* 自定义的View,作用不大
* 确定位置的时候,系统调用的方法(我们不用关心),这里我们就不改写这个方法
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
// TODO 自动生成的方法存根
super.onLayout(changed, left, top, right, bottom);
} /**
* 绘制当前View的内容
*/
@Override
protected void onDraw(Canvas canvas) {
// TODO 自动生成的方法存根
//super.onDraw(canvas);
//绘制背景图
/*
* backgroundBitmap:要绘制的图片
* left 图片的左边界
* top 图片的上边界
* paint 绘制图片要使用的画笔
*/
canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
//绘制可滑动的按钮
canvas.drawBitmap(slidebtn, slidebtn_left, 0, paint);
} public void onClick(View v) {
currState = ! currState;
flushState();//刷新当前开关状态 } /**
* 刷新当前开关视图
*/
private void flushState() {
if(currState) {
slidebtn_left = backgroundBitmap.getWidth()-slidebtn.getWidth();
}else {
slidebtn_left =0;
} /*
* invalidate():刷新当前View,会导致onDraw方法的执行
* 上面只是设置参数设置,下面必须将上面的参数传递到onDraw方法中,利用onDraw方法重新绘制,才能实现刷新的效果
*
*/
invalidate(); } }
代码逻辑如下:

备注:
初始状态slideBtn 左边为0

开的时候slideBtn left值为background.width-slidebtn.width

与此同时,MainActivity.java,如下:
package com.himi.togglebtn; import android.app.Activity;
import android.os.Bundle; public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} }
(4)布署程序到模拟器上如下:

(5)实现开关的弹性滑动,上面的只能让开关左右切换,不能手机拖动滑动开关,用户体验不好,我们要优化。
MainActivity.java,修改为:
package com.himi.togglebtn; 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.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener; public class MyToggleButton extends View implements OnClickListener { //作为背景的图片
private Bitmap backgroundBitmap;
//滑动的开关图片
private Bitmap slidebtn;
private Paint paint; //滑动按钮的左边界
private float slidebtn_left; /**
* 当前开关的状态
* true :为开
* false:为关
*/
private boolean currState = false; /**
* 我们在代码里面创建对象的时候,使用此构造方法
* @param context
*/
public MyToggleButton(Context context) {
super(context);
// TODO 自动生成的构造函数存根
} /**
* 在布局文件xml中声明的View,创建时候由系统自动调用此构造方法。
* 倘若我们(使用全路径)在xml布局文件中,声明使用这个自定义的View,但是我们没有这个构造方法,就会报错(系统不能找到这个构造)
* @param context
* @param attrs
*/
public MyToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
} /**
* 这个构造方法比上面的构造方法都了一个参数 defStyle,这个参数View默认的样式,这里可以重新这个构造,设置defStyle
* 改变生成自定义的View的样式style
* @param context
* @param attrs
* @param defStyle
*/
public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO 自动生成的构造函数存根
} //初始化
private void initView() {
//初始化图片
backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
slidebtn = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
//初始化画笔
paint = new Paint();
paint.setAntiAlias(true);//打开抗锯齿
//添加Onclick事件监听
setOnClickListener(this);
} /*
* View对象显示在屏幕上,有几个重要步骤:
* 1. 构造方法 创建 对象.
* 2. 测量View的大小. onMeasure(int, int):系统调用的方法,获知View的大小
* 3. 确定View的位置,View自身有一些建议权,View位置决定权在父View手中. onLayout(): ViewGroup调用
* 4. 绘制View的内容 onDraw(canvas)
*
*/ /**
*
* 测量尺寸时候的回调方法
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置当前View的大小
* width :当前View的宽度
* height:当前view的高度(单位:像素)
*/
setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
} /**
* 自定义的View,作用不大
* 确定位置的时候,系统调用的方法(我们不用关心),这里我们就不改写这个方法
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
// TODO 自动生成的方法存根
super.onLayout(changed, left, top, right, bottom);
} /**
* 绘制当前View的内容
*/
@Override
protected void onDraw(Canvas canvas) {
// TODO 自动生成的方法存根
//super.onDraw(canvas);
//绘制背景图
/*
* backgroundBitmap:要绘制的图片
* left 图片的左边界
* top 图片的上边界
* paint 绘制图片要使用的画笔
*/
canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
//绘制可滑动的按钮
canvas.drawBitmap(slidebtn, slidebtn_left, 0, paint);
} /**
* 判断是否发生拖到
* 如果拖动了,就不再响应Onclick事件
* true:发生拖动
* false:没有发生拖动
*/
private boolean isDrag = false; /**
* onClick事件在view.onTouchEvent中被解析
* 系统对Onclick事件的解析,过于简陋,只要有down事件和up事件,系统即认为发生了click事件
*/
public void onClick(View v) {
/*
* 如果没有拖动,才执行改变状态的动作
*/
if(!isDrag) {
currState = ! currState;
flushState();//刷新当前开关状态
}
} /**
* 刷新当前开关视图
*/
private void flushState() {
if(currState) {
slidebtn_left = backgroundBitmap.getWidth()-slidebtn.getWidth();
}else {
slidebtn_left =0;
} flushView();
} public void flushView() {
/**
* 对slidebtn_left的值进行判断
* 0 <= slidebtn_left <= backgroundwidth-slidebtnwidth(这样才能保证滑动的开关不会滑动越界)
*
*/
int maxLeft = backgroundBitmap.getWidth()-slidebtn.getWidth();//slidebtn左边界最大值
//确保slidebtn_left >= 0
slidebtn_left =(slidebtn_left>0)?slidebtn_left:0;
//确保slidebtn_left <=maxLeft
slidebtn_left = (slidebtn_left<maxLeft)?slidebtn_left:maxLeft; //告诉系统我需要刷新当前视图,只要当前视图可见状态,就会调用onDraw方法重新绘制,达到刷新视图的效果
invalidate();
} /**
* down事件时的x值
*/
private int firstX;
/**
* touch事件的上一个x值
*/
private int lastX; @Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
firstX = lastX = (int) event.getX();
isDrag = false;
break;
case MotionEvent.ACTION_MOVE:
//判断是否发生拖动
if(Math.abs(event.getX()-lastX)>5) {
isDrag = true;
} //计算手指在屏幕上移动的距离
int dis = (int) (event.getX()-lastX);
//将本次的位置设置给lastX
lastX = (int) event.getX();
//根据手指移动的距离,改变slidebtn_left的值
slidebtn_left = slidebtn_left+dis;
break;
case MotionEvent.ACTION_UP: //在发生拖动的情况下,根据最后的位置,判断当前的开关的状态
if(isDrag){
int maxLeft = backgroundBitmap.getWidth()-slidebtn.getWidth();//slidebtn左边界最大值
/**
* 根据slidebtn_left判断,当前应该是什么状态
*
*/
if(slidebtn_left>maxLeft/2) {//应为打开状态
currState = true;
}else {
currState = false;
}
flushState();
} break; }
flushView();
return true;
} }
代码逻辑如下:

布署程序到模拟器上,如下:

自定义控件(视图)2期笔记05:自定义控件之继承自View(滑动开关)的更多相关文章
- 自定义控件(视图)2期笔记01:自定义控件之自定义View的步骤
1. 根据Android Developers官网的介绍,自定义控件你需要以下的步骤: (1)创建View (2)处理View的布局 (3)绘制View (4)与用户进行交互 (5)优化已定义的Vie ...
- Android笔记——Android自定义控件
目录: 1.自定义控件概述 01_什么是自定义控件 Android系统中,继承Android系统自带的View或者ViewGroup控件或者系统自带的控件,并在这基础上增加或者重新组合成我们想要的效果 ...
- 《uml大战需求分析》阅读笔记05
<uml大战需求分析>阅读笔记05 这次我主要阅读了这本书的第九十章,通过看这章的知识了解了不少的知识开发某系统的重要前提是:这个系统有谁在用?这些人通过这个系统能做什么事? 一般搞清楚这 ...
- MYSQL视图的学习笔记
MYSQL视图的学习笔记,学至Tarena金牌讲师,金色晨曦科技公司技术总监沙利穆 课程笔记的综合. 视图及图形化工具 1. 视图的定义 视图就是从一个或多个表中,导出来的表,是一个虚 ...
- 强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods)
强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods) 学习笔记: Reinforcement Learning: An Introduction, Richard S ...
- JS自学笔记05
JS自学笔记05 1.例题 产生随机的16进制颜色 function getColor(){ var str="#"; var arr=["0","1 ...
- JAVA自学笔记05
JAVA自学笔记05 1.方法 1)方法就是完成特定功能的代码块,类似C语言中的函数. 2)格式: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,-){ 函数体; return ...
- 机器学习实战(Machine Learning in Action)学习笔记————05.Logistic回归
机器学习实战(Machine Learning in Action)学习笔记————05.Logistic回归 关键字:Logistic回归.python.源码解析.测试作者:米仓山下时间:2018- ...
- CS229 笔记05
CS229 笔记05 生成学习方法 判别学习方法的主要思想是假设属于不同target的样本,服从不同的分布. 例如 \(P(x|y=0) \sim {\scr N}(\mu_1,\sigma_1^2) ...
随机推荐
- 30.SSH配置文件模板.md
[toc] 1.struts2 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts ...
- cas sso单点登录系列5_cas单点登录增加验证码功能完整步骤
转:http://blog.csdn.net/ae6623/article/details/8919718 本篇教程cas-server端下载地址:解压后,直接放到tomcat的webapp目录下就能 ...
- 何为 pimpl ?
前言 你是否总因头文件包含冲突而苦恼? 你是否因头文件包含错乱而苦恼? 你是否因封装暴露了数据而苦恼? 你是否因经常改动实现而导致重新编译而苦恼? 在这里, 这些问题都不是问题, 跟随作者, 揭秘pi ...
- PHP程序异常处理实现方法
一个异常(Exception)则是在一个程序执行过程中出现一个例外或是一个事件,它中断了指令的运行,跳转到其他程序模块继续执行.所以异常处理经常被当作程序的控制流程使用.无论是错误还是异常,应用程序都 ...
- SCJP_104——题目分析(5)
18. public class Test { public static void add3(Integer i) { int val=i.intvalue(); val+=3; i=new Int ...
- Pycharm常用快捷键(后期慢慢补充)
用到一个,就补充一个,慢慢来,找到自己常用的快捷键. CTRL /: 注释.取消注释行 CTRL Q: 在参数列表位置,显示可以输入的所有参数. #查看参数的详细信息
- 转:你需要知道的NoSQL数据库10件事
你需要知道的NoSQL数据库10件事 NoSQL的5个优势 1.弹性扩展 多年来,数据库管理员一直依赖于向上扩展(scale up)-随着数据库负载的增加购买更大的数据库服务器―而不是向外扩展-随着负 ...
- hdu How many integers can you find
题意:找出小于n是m个数每个数的倍数的数的个数. 思路:用二进制表示是那几个数的倍数. 二进制进行容斥,去掉小于0的数. #include <cstdio> #include <cs ...
- BZOJ1984: 月下“毛景树”
1984: 月下“毛景树” Time Limit: 20 Sec Memory Limit: 64 MBSubmit: 713 Solved: 245[Submit][Status] Descri ...
- HDU 4421 Bit Magic(奇葩式解法)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=4421 题目大意: 给了你一段代码, 用一个数组的数 对其进行那段代码的处理,是可以得到一个矩阵 让你判 ...