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(滑动开关)的更多相关文章

  1. 自定义控件(视图)2期笔记01:自定义控件之自定义View的步骤

    1. 根据Android Developers官网的介绍,自定义控件你需要以下的步骤: (1)创建View (2)处理View的布局 (3)绘制View (4)与用户进行交互 (5)优化已定义的Vie ...

  2. Android笔记——Android自定义控件

    目录: 1.自定义控件概述 01_什么是自定义控件 Android系统中,继承Android系统自带的View或者ViewGroup控件或者系统自带的控件,并在这基础上增加或者重新组合成我们想要的效果 ...

  3. 《uml大战需求分析》阅读笔记05

    <uml大战需求分析>阅读笔记05 这次我主要阅读了这本书的第九十章,通过看这章的知识了解了不少的知识开发某系统的重要前提是:这个系统有谁在用?这些人通过这个系统能做什么事? 一般搞清楚这 ...

  4. MYSQL视图的学习笔记

    MYSQL视图的学习笔记,学至Tarena金牌讲师,金色晨曦科技公司技术总监沙利穆 课程笔记的综合. 视图及图形化工具   1.       视图的定义 视图就是从一个或多个表中,导出来的表,是一个虚 ...

  5. 强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods)

    强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods) 学习笔记: Reinforcement Learning: An Introduction, Richard S ...

  6. JS自学笔记05

    JS自学笔记05 1.例题 产生随机的16进制颜色 function getColor(){ var str="#"; var arr=["0","1 ...

  7. JAVA自学笔记05

    JAVA自学笔记05 1.方法 1)方法就是完成特定功能的代码块,类似C语言中的函数. 2)格式: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,-){ 函数体; return ...

  8. 机器学习实战(Machine Learning in Action)学习笔记————05.Logistic回归

    机器学习实战(Machine Learning in Action)学习笔记————05.Logistic回归 关键字:Logistic回归.python.源码解析.测试作者:米仓山下时间:2018- ...

  9. CS229 笔记05

    CS229 笔记05 生成学习方法 判别学习方法的主要思想是假设属于不同target的样本,服从不同的分布. 例如 \(P(x|y=0) \sim {\scr N}(\mu_1,\sigma_1^2) ...

随机推荐

  1. 通过Unity依赖注入

    前言 Unity容器的思想起始于我在为Web Client Sofitware Factory项目工作的时候,微软的patterns&practices团队已经使用依赖注入的概念好几年了在那时 ...

  2. Hibernate 多对多关联Demo

    以学生[Student ]与课程[Course ]之间的关系为例: //Course .java public class Course implements Serializable { priva ...

  3. cocos2d-x 获取当前播放第几帧最高效的方法

    前言 把互联网翻了个遍, 所有的方法都千篇一律. 用循环去判断! 很神奇的是, 几乎所有博文举的例子, 连数字都是一样的. 这么一个效率烂成渣的方法, 居然被普遍赞同. 以下是广为流传的方法. ; i ...

  4. Linux文件编程实例

    //捕获fopen调用中的错误 #include <stdio.h> #include <errno.h> #include <string.h> #define  ...

  5. C#.net 货币格式转换

    /// <summary> /// 输入Float格式数字,将其转换为货币表达方式 /// </summary> /// <param name="ftype& ...

  6. 或许是python yield最好的答案 ?

    地址:http://pyzh.readthedocs.org/en/latest/the-python-yield-keyword-explained.html#yield 译者: hit9 原文: ...

  7. iOS开发之WKWebView简单使用

    iOS开发之WKWebView简单使用   iOS开发之 WKWebVeiw使用 想用UIWebVeiw做的,但是突然想起来在iOS8中出了一个新的WKWebView,算是UIWebVeiw的升级版. ...

  8. 利用花生壳在自己电脑上建立外网可访问的svn

    下载花生壳并注册账号 2.花生壳会送你一个免费的二级域名 3.登陆到路由器界面192.168.0.1或者192.168.0.0进入动态dns选项输入你的花生壳账号密码 4.在路由器设置界面设置转发规则 ...

  9. 手机Android音视频采集与直播推送,实现单兵、移动监控类应用

    从安卓智能手机.平板,到可穿戴的Android Ware.眼镜.手表.再到Android汽车.智能家居.电视,甚至最近看新闻,日本出的几款机器人都是Android系统的,再把目光放回监控行业,传统监控 ...

  10. DSP开发资源总结,经典书籍,论坛

    OMAP4开发资源总结: 一.TI OMAP4官网介绍: http://www.ti.com.cn/general/cn/docs/wtbu/wtbuproductcontent.tsp?templa ...