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

以下就開始第一部分的编写。本次以一个定义的开关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. NET调用Com组件事例

    http://blog.csdn.net/shizhiyingnj/article/details/1507948 在程序设计中,往往通过键盘的某个按键来完成相关操作! 下面就来说明如何实现: 1.引 ...

  2. Hitcon 2016 Pwn赛题学习

    PS:这是我很久以前写的,大概是去年刚结束Hitcon2016时写的.写完之后就丢在硬盘里没管了,最近翻出来才想起来写过这个,索性发出来 0x0 前言 Hitcon个人感觉是高质量的比赛,相比国内的C ...

  3. #JS attr和prop的区别

    HTML元素固有属性:使用prop方法 HTML元素自定义属性:使用attr方法

  4. NET WebAPi之断点续传下载1

    ASP.NET WebAPi之断点续传下载(上)   前言 之前一直感觉断点续传比较神秘,于是想去一探究竟,不知从何入手,以为就写写逻辑就行,结果搜索一番,还得了解相关http协议知识,又花了许久功夫 ...

  5. 【LOJ】#2491. 「BJOI2018」求和

    题解 对于50个k都维护一个\(i^k\)前缀和即可 查询的时候就是查询一段连续的区间和,再加上根节点的 代码 #include <bits/stdc++.h> #define fi fi ...

  6. linux设置最大打开文件数

    一.查看当前用户对进程打开文件最大数的限制 $ ulimit -a | grep open 二.系统对进程打开文件最大数是如何限制的 先来看man的一段解析: /proc/sys/fs/file-ma ...

  7. 解决Flume向Kafka多分区写数据

    1  问题背景 Flume向kafka发布数据时,发现kafka接收到的数据总是在一个partition中,而我们希望发布来的数据在所有的partition平均分布 2 解决办法 Flume的官方文档 ...

  8. word2013 如何设置从第三页开始编码 或 如何设置封面页和正文页页码不连续

    首先说明一下 “分节符”作用,它就是用来将整个文档分节的,添加一个分节符,文档就分成1.2两节:添加两个分节符,文档就分成1.2.3节. 当前页面具体是第几节,可以通过点击页眉页脚来查看: 从第三页开 ...

  9. ZOJ Monthly, March 2018 题解

    [题目链接] A. ZOJ 4004 - Easy Number Game 首先肯定是选择值最小的 $2*m$ 进行操作,这些数在操作的时候每次取一个最大的和最小的相乘是最优的. #include & ...

  10. 001.hadoop及hbase部署

    一 环境准备 1.1 相关环境 系统:CentOS 7 #CentOS 6.x系列也可参考,转换相关命令即可. hadoop包:hadoop-2.7.0.tar.gz #下载官方地址:http://w ...