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. java计算一个月有多少天和多少周

    import java.util.Calendar; /** * 功能概述:计算指定年月的天数和周数<br> * 创建时间:2010-5-17 下午05:25:58<br> * ...

  2. Testlink接口使用方法-python语言远程调用

    deepin@deepin-pc:~/test$ cat libclienttestlink.py #!/usr/bin/env python3 # -*- coding: utf-8 -*- #! ...

  3. Python自动化运维之17、Python操作 Memcache、Redis、RabbitMQ

    一.Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的 ...

  4. Sqlserver 正则替换函数的一种实现

    --函数 IF OBJECT_ID(N'dbo.RegexReplace') IS NOT NULL DROP FUNCTION dbo.RegexReplace GO CREATE FUNCTION ...

  5. 触摸事件 - UIControlEvents

    首先,UIControlEvents有这个几种: UIControlEventTouchDown           = 1 <<  0,      // on all touch dow ...

  6. Hazelcase 简介

    原博客地址:http://blog.csdn.net/zhu_tianwei/article/details/47984599 Hazelcast是一种内存数据网格in-memory data gri ...

  7. codemirror 插件

    做在线词典编辑的时候.里面有些自定义标签.类似html标签一样. 为了让编辑编辑.改成了  <动词></动词> 所以引用了 codemirror插件 此插件绝对牛逼 它主要功能 ...

  8. poj 2195Going Home

    http://poj.org/problem?id=2195 #include<cstdio> #include<cstring> #include<cmath> ...

  9. UOJ 218 火车管理

    http://uoj.ac/problem/218 思路:建立一个可持久化线段树,代表这个位置的火车是哪辆,然后再弄一个线段树维护答案. 如果询问,直接询问线段树. 如果区间压入,直接在主席树上面压入 ...

  10. Python 命令行非阻塞输入

    很久很久以前,系windows平台下,用C语言写过一款贪食蛇游戏,cmd界面,用kbhit()函数实现非阻塞输入.系windows平台下用python依然可以调用msvcrt.khbit实现非阻塞监听 ...