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

以下就開始第一部分的编写。本次以一个定义的开关button为例。以下就開始吧:

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

为了可以解说清晰,还是来一些主要的介绍。

首先须要明白的就是自己定义控件还是继承自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方法,我们一起来看一下:

/**
* 画view的方法,绘制当前view的内容
*/
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas); Paint paint = new Paint();
// 打开抗锯齿
paint.setAntiAlias(true); // 画背景
canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
// 画滑块
canvas.drawBitmap(slideButton, slideBtn_left, 0, paint);
}

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3lwMzMxMjAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">       
                  

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3lwMzMxMjAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

这里要注意的一点就是。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:

package com.example.togglebutton.ui;

import com.example.togglebutton.R;

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; /*
* 自己定义view的几个步骤:
* 1、首先须要写一个类来继承自View
* 2、须要得到view的对象。那么须要重写构造方法。当中一參的构造方法用于new。二參的构造方法用于xml布局文件使用,三參的构造方法能够传入一个样式
* 3、须要设置view的大小,那么须要重写onMeasure方法
* 4、须要设置view的位置。那么须要重写onLayout方法,可是这种方法在自己定义view的时候用的不多。原因主要在于view的位置主要是由父控件来决定
* 5、须要绘制出所须要显示的view,那么须要重写onDraw方法
* 6、当控件状态改变的时候。须要重绘view,那么调用invalidate();方法,这种方法实际上会又一次调用onDraw方法
* 7、在这当中。假设须要对view设置点击事件。能够直接调用setOnClickListener方法
*/ public class MyToggleButton extends View { /**
* 开关按钮的背景
*/
private Bitmap backgroundBitmap;
/**
* 开关按钮的滑动部分
*/
private Bitmap slideButton;
/**
* 滑动按钮的左边界
*/
private float slideBtn_left;
/**
* 当前开关的状态
*/
private boolean currentState = false; /**
* 在代码里面创建对象的时候。使用此构造方法
*
* @param context
*/
public MyToggleButton(Context context) {
super(context);
} /**
* 在布局文件里声明的view,创建时由系统自己主动调用
*
* @param context
* @param attrs
*/
public MyToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
} /**
* 測量尺寸时的回调方法
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 设置当前view的大小 width:view的宽,单位都是像素值 heigth:view的高。单位都是像素值
setMeasuredDimension(backgroundBitmap.getWidth(),
backgroundBitmap.getHeight());
} // 这种方法对于自己定义view的时候帮助不大,由于view的位置一般由父组件来决定的
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
} /**
* 画view的方法,绘制当前view的内容
*/
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas); Paint paint = new Paint();
// 打开抗锯齿
paint.setAntiAlias(true); // 画背景
canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
// 画滑块
canvas.drawBitmap(slideButton, slideBtn_left, 0, paint);
} /**
* 初始化view
*/
private void initView() {
backgroundBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_background);
slideButton = BitmapFactory.decodeResource(getResources(),
R.drawable.slide_button); /*
* 点击事件
*/
setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) { currentState = !currentState;
flushState();
flushView();
}
});
} /**
* 刷新视图
*/
protected void flushView() {
// 刷新当前view会导致ondraw方法的运行
invalidate();
} /**
* 刷新当前的状态
*/
protected void flushState() {
if (currentState) {
slideBtn_left = backgroundBitmap.getWidth()
- slideButton.getWidth();
} else {
slideBtn_left = 0;
}
} }

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

<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="${relativePackage}.${activityClass}" > <com.example.togglebutton.ui.MyToggleButton
android:id="@+id/my_toggle_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" /> </RelativeLayout>

在这里因为没有写不论什么点击触发业务的逻辑,仅仅是一个单纯的控件。所以在MainActivity里面没有增加多的代码:

package com.example.togglebutton;

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); }
}

至此一个自己定义的开关button就完毕了,后面两篇将会介绍怎样在上面实现
点击拖动开关的效果 和怎样实现自己定义属性。谢谢支持!

Android自己定义控件系列二:自己定义开关button(一)的更多相关文章

  1. Android自己定义控件系列五:自己定义绚丽水波纹效果

    尊重原创!转载请注明出处:http://blog.csdn.net/cyp331203/article/details/41114551 今天我们来利用Android自己定义控件实现一个比較有趣的效果 ...

  2. Android自己定义控件系列案例【五】

    案例效果: 案例分析: 在开发银行相关client的时候或者开发在线支付相关client的时候常常要求用户绑定银行卡,当中银行卡号一般须要空格分隔显示.最常见的就是每4位数以空格进行分隔.以方便用户实 ...

  3. Android自己定义控件系列三:自己定义开关button(二)

    接上一篇自己定义开关button(一)的内容继续.上一次实现了一个开关button的基本功能.即自己定义了一个控件.开关button,实现了点击切换开关状态的功能.今天我们想在此基础之上.进一步实现触 ...

  4. Android经常使用自己定义控件(二)

           经常使用的Android自己定义控件分享 http://www.see-source.com//androidwidget/list.html?type=&p=1

  5. 【Android】自己定义控件实现可滑动的开关(switch)

    ~转载请注明来源:http://blog.csdn.net/u013015161/article/details/46704745 介绍 昨天晚上写了一个Android的滑动开关, 即SlideSwi ...

  6. android自己定义控件系列教程----视图

    理解android视图 对于android设备我们所示区域事实上和它在底层的绘制有着非常大的关系,非常多时候我们都仅仅关心我们所示,那么在底层一点它究竟是怎么样的一个东西呢?让我们先来看看这个图. w ...

  7. android自己定义控件系列教程-----仿新版优酷评论剧集卡片滑动控件

    我们先来看看优酷的控件是怎么回事? 仅仅响应最后也就是最顶部的卡片的点击事件,假设点击的不是最顶部的卡片那么就先把它放到最顶部.然后在移动到最前面来.重复如次. 知道了这几条那么我们就非常好做了. 里 ...

  8. android动手写控件系列——老猪叫你写相机

    前记:Android这个开源而自由的系统,为我们带来开发便利,同时也埋下太多的深坑.例如调用系统自带的相机就会出现照片丢失,或者其他各种各样的问题.因此,看来自定义一个相机十分的必要. 要自定义相机我 ...

  9. Android自己定义控件系列一:Android怎样实现老版优酷client三级环形菜单

    转载请附上本文链接:http://blog.csdn.net/cyp331203/article/details/40423727 先来看看效果: 一眼看上去好像还挺炫的,感觉比較复杂...实际上并不 ...

随机推荐

  1. nagios使用问题的解决方案

    通过web界面修改某个服务时报错例如对某个服务进行临时安排其执行时间,或者不让它发警告,web页面上都有这样的设置.但是常常会有错误信息如下: Could not open command file ...

  2. Service Mesh 及其主流开源实现解析(转)

    什么是 Service mesh Service Mesh 直译过来是 服务网格,目的是解决系统架构微服务化后的服务间通信和治理问题.服务网格由 sidecar 节点组成.在介绍 service me ...

  3. 探秘Java类加载

    Java是一门面向对象的编程语言. 面向对象以抽象为基础,有封装.继承.多态三大特性. 宇宙万物,经过抽象,均可归入相应的种类.不同种类之间,有着相对井然的分别. Java中的类,便是基于现实世界中的 ...

  4. axios笔记

    参考:http://www.cnblogs.com/Upton/p/6180512.html https://cloud.tencent.com/developer/article/1098141  ...

  5. maven dependencies 报错

    maven配置的环境变量有问题: 用最新的maven替换系统默认的setting.xml文件即可

  6. Fiddler实现移动端手机抓包

    Fiddler是一个http调试代理,它能 够记录所有的你电脑和互联网之间的http通讯,Fiddler 可以也可以让你检查所有的http通讯,设置断点,以及Fiddle 所有的“进出”的数据(指co ...

  7. Mockito 简明教程

    什么是 Mock 测试 Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDB ...

  8. socket编程之select相关

    FD_ZERO,FD_ISSET这些都是套节字结合操作宏 看看MSDN上的select函数, 这是在select   io   模型中的核心,用来管理套节字IO的,避免出现无辜锁定. int   se ...

  9. HTML5+ App开发入门

    HTML5 Plus应用概述 HTML5 Plus移动App,简称5+App,是一种基于HTML.JS.CSS编写的运行于手机端的App,这种App可以通过扩展的JS API任意调用手机的原生能力,实 ...

  10. matlab进阶

    常用功能的实现 获取当前脚本所在目录 current_script_dir = fileparts(mfilename('fullpath')); % 结尾不带'/' 常用函数的说明 bsxfun m ...