Android 自定义控件开发入门(一)

作为一个有创意的开发者,或者软件对UI设计的要求比较高,你经常会遇到安卓自带的控件无法满足你的需求的情况,这种时候,我们只能去自己去实现适合项目的控件。同时,安卓也允许你去继承已经存在的控件或者实现你自己的控件以便优化界面和创造更加丰富的用户体验。

 

那么怎样来创建一个新的控件呢? 

这得看需求是怎样的了。

1.需要在原生控件的基本功能上进行扩展,这个时候你只需要继承并对控件进行扩展。通过重写它的事件,onDraw ,但是始终都保持都父类方法的调用。如从已有的高级控件上继承,例如继承一个TextView。

2.需要几个控件的功能的加和,这个时候要把控件组合起来,就是通过合并几个控件来生成一个新控件。比如在ListView中用适配器来将多种控件有机的结合在一起,又如写一个控件是多个控件的组合,一般是自定义布局,可以用一个类继承一个布局。这个布局中包含多个控件。

3.白手起家自己创建一个新的控件。即直接从View,ViewGroup开始绘制控件

4.另外大家不要忘了,还有一个好用的东西<include>标签。  在一个项目中我们可能会需要用到相同的布局设计,如果都写在一个xml文件中,代码显得很冗余,并且可读性也很差,所以我们可以把相同布局的代码单独写成一个模块,然后用到的时候可以通过<include
/> 标签来重用layout代码。

 

作过Android 应用开发的朋友都知道,Android的UI界面都是由View和ViewGroup及其派生类组合而成的。基于安卓UI设计原理,我们作为开发者,完全能够按照自己的意愿开发出项目定制的组件。其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的。AndroidUI界面的一般结构可参见下面的示意图:

可见,作为容器的ViewGroup可以包含作为叶子节点的View,也可以包含作为更低层次的子ViewGroup,而子ViewGroup又可以包含下一层的叶子节点的View和ViewGroup。事实上,这种灵活的View层次结构可以形成非常复杂的UI布局,开发者可据此设计、开发非常精致的UI界面。

ViewGroup可以通过重写onMeasure,onLayout为加入其中的View进行布局和处理,功能十分强大,我们这次先学习View类派生自定义组件:

 

View组件的作用类似于JAVA中Swing里的Panel,是一个矩形的空白区域,不带有任何内容,对于Android应用的其他UI控件来说,都是继承了View组件,然后绘制出来的。所以我们通过View子类并重写View类的方法来派生我们自己的控件。

Android自定义View实现很简单:

继承View,重写构造函数、onDraw,(onMeasure)等函数,下面会逐一列举。

如果自定义的View需要有自定义的属性,需要在values下建立attrs.xml。在其中定义你的属性。在使用到自定义View的xml布局文件中需要加入xmlns:前缀="http://schemas.android.com/apk/res/你的自定义View所在的包路径".在使用自定义属性的时候,使用前缀:属性名,如my:textColor="……"。

 

 

让我们先看一下View类的方法:

   


Category


Methods


Description


Creation


Constructors


There is a form of the constructor that are called when the view is created from code and a form that is called when the view is inflated from a layout file. The second form should parse and apply any attributes defined in the
layout file.


onFinishInflate()


Called after a view and all of its children has been inflated from XML.


Layout


onMeasure(int, int)


Called to determine the size requirements for this view and all of its children.


onLayout(boolean, int, int, int, int)


Called when this view should assign a size and position to all of its children.


onSizeChanged(int, int, int, int)


Called when the size of this view has changed.


Drawing


onDraw(android.graphics.Canvas)


Called when the view should render its content.


Event processing


onKeyDown(int, KeyEvent)


Called when a new hardware key event occurs.


onKeyUp(int, KeyEvent)


Called when a hardware key up event occurs.


onTrackballEvent(MotionEvent)


Called when a trackball motion event occurs.


onTouchEvent(MotionEvent)


Called when a touch screen motion event occurs.


Focus


onFocusChanged(boolean, int, android.graphics.Rect)


Called when the view gains or loses focus.


onWindowFocusChanged(boolean)


Called when the window containing the view gains or loses focus.


Attaching


onAttachedToWindow()


Called when the view is attached to a window.


onDetachedFromWindow()


Called when the view is detached from its window.


onWindowVisibilityChanged(int)


Called when the visibility of the window containing the view has changed.

 

通常可能需要重写以下方法:


1.构造器,至少用来获取Context

2.onFinishlnflate()这是一个回调方法, 当应用从 XML 布局文件加载该组件并利用

它来构建界面之后, 该方法就会被回调。

3.onMeasure(int,int):调用该方法来检测View组件及它所包含的所有子组件的大小.

4.onlayout(boolean,int,int,int,int):当该组件需要分配其子组件的位置、大小时,

该方法就会被回调. View类中布局发生改变时会调用的方法,这个方法是所有View、ViewGroup及其派生类都具有的方法,重载该类可以在布局发生改变时作定制处理,这在实现一些特效时非常有用。

5.onSizeChanged(int,int, int, int):当该组件的大小被改变时回调该方法.

6.onDraw(canves): 当该组件将要绘制它的内容时回调该方法迸行绘制. View类中用于重绘的方法,这个方法是所有View、ViewGroup及其派生类都具有的方法,也是Android UI绘制最重要的方法。开发者可重载该方法,并在重载的方法内部基于参数canvas绘制自己的各种图形、图像效果。

7.onKeyDown(int,KeyEvent): 当某个键被按下时触发该方法.

8.onKayUp(int,KeyEvent), 当松开某个键时触发该方法.

9.onTrackballEvent (MotionEvent): 当发生轨迹球事件时触发该方法.

10.onTouchEvent (MotionEvent): 当发生触摸屏事件时触发该方法.

11.onWindowFocuschanged(boolean): 当该组件得到、失去焦点时触发该方法.

12.onAttachedToWindow():当把该组件放入某个窗口时触发该方法.

13.onDetachedFromWindow(): 当把该组件从某个窗口上分离时触发该方法.

14.onWindowVisibilityChanged(int):当包含该组件的窗口的可见性发生改变时触发该

方法.

另外再补充两个ViewGroup类经常重载的方法:


1.protected void dispatchDraw(Canvas canvas):ViewGroup类及其派生类具有的方法,这个方法主要用于控制子View的绘制分发,重载该方法可改变子View的绘制,进而实现一些复杂的视效。

2.protected boolean drawChild(Canvas canvas, View child, long drawingTime)):ViewGroup类及其派生类具有的方法,这个方法直接控制绘制某局具体的子view,重载该方法可控制具体某个具体子View。

 

在需要开发自定义View的时候,我们不需要列举出上面所有的方法,,而是可以根据业务需要来有选择的使用·上面的方法,下面我们看一个简单的示例程序,在这个示例程序里面我们只需要重写onDraw方法就可以了!

 

示例程序一:

我们要写一个跟随手指移动的小球,思路很简单,只要获取到用户点击屏幕的位置,并且在该位置处重绘小球即可:

下面我们看一下程序:

我注释写的比较清楚,我就说的简略一点:

首先我们写一个类DrawView,也就是我们自定义的控件,继承自View

 

然后我们先写出构造器,获取到Context,这里如果用只含有Context的构造器会在xml里调用控件的时候出错,详情请看我的另外一篇博文:

http://blog.csdn.net/sunmc1204953974/article/details/38101057

下面我们开始写:

//	构造方法
	public DrawView(Context context,AttributeSet attrs){
		super(context,attrs);
	}
//	重写ondraw方法
	@Override
	public void onDraw(Canvas canvas){
		super.onDraw(canvas);
//		创建画笔
		Paint paint = new Paint();
//		设置画笔颜色
		paint.setColor(Color.RED);
//		画出小球
		canvas.drawCircle(circleX, circleY, circleR, paint);
	}

然后不要忘了设置这些数据的setter和getter,因为我们需要再使用这个View的时候加上监听才可以:

//	get set 方法

	public float getCircleX() {
		return circleX;
	}

	public void setCircleX(float circleX) {
		this.circleX = circleX;
	}

	public float getCircleY() {
		return circleY;
	}

	public void setCircleY(float circleY) {
		this.circleY = circleY;
	}

	public float getCircleR() {
		return circleR;
	}

	public void setCircleR(float circleR) {
		this.circleR = circleR;
	}

这样我们的一个简单地自定控件就大功告成了,下面是该类的完整代码:

package com.example.moveball;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class DrawView extends View{

	private float circleX = 40;
	private float circleY = 50;
	private float circleR = 15;

//	构造方法
	public DrawView(Context context,AttributeSet attrs){
		super(context,attrs);
	}

//	重写ondraw方法
	@Override
	public void onDraw(Canvas canvas){
		super.onDraw(canvas);
//		创建画笔
		Paint paint = new Paint();
//		设置画笔颜色
		paint.setColor(Color.RED);
//		画出小球
		canvas.drawCircle(circleX, circleY, circleR, paint);
	}
//	get set 方法

	public float getCircleX() {
		return circleX;
	}

	public void setCircleX(float circleX) {
		this.circleX = circleX;
	}

	public float getCircleY() {
		return circleY;
	}

	public void setCircleY(float circleY) {
		this.circleY = circleY;
	}

	public float getCircleR() {
		return circleR;
	}

	public void setCircleR(float circleR) {
		this.circleR = circleR;
	}

}


之后我们就是像平时使用安卓原生控件那样使用就可以了,我们看一下Activity的代码:


package com.example.moveball;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class MainActivity extends Activity {
//	定义DrawView组件
	DrawView drawView = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//  创建DrawView组件
        drawView = (DrawView)this.findViewById(R.id.drawView);
//  为DrawView组件绑定Touch事件
        drawView.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View arg0, MotionEvent event) {
//				获取坐标并改变小球的坐标
				drawView.setCircleX(event.getX());
				drawView.setCircleY(event.getY());
//				通知draw组件重绘
				drawView.invalidate();
//				返回true表明被执行
				return true;
			}
		});

    }

}

以及xml格式的布局文件:

<?xml version="1.0" encoding="utf-8"?>

<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=".MainActivity" >

    <com.example.moveball.DrawView
        android:id="@+id/drawView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.example.moveball.DrawView>

</RelativeLayout>

这样一个简单的例子就呈现在大家面前了,无论是多么复杂的自定义控件,思路总是这样子的,大家是不是觉得怪怪的,对了,作为一个控件,我们居然还要为了他的实现为其增加麻烦的监听,这就是因为我们重写的方法太少的原因,下一讲再给大家介绍一个经常重写的方法:publicboolean onTouchEvent (MotionEvent event)。

源代码上面已经很详细了,我在最后一篇的最后还会发一个工程上来,欢迎大家一起学习!

我也还是学生,写的不好或者有问题的地方还请多多指教~



时间: 2024-10-26 01:23:56

Android 自定义控件开发入门(一)的相关文章

Android 自定义控件开发入门(二)

上一次我们讲了一堆实现自定义控件的理论基础,列举了View类一些可以重写的方法,我们对这些方法的重写是我们继承View类来派生自定义控件的关键 我通过一个最简单的例子给大家展示了这一个过程,无论是多么复杂的自定义控件,思路总是这样子的,但是因为我们仅仅重写了onDraw方法使得大家觉得怪怪的,作为一个控件,我们居然还要为了他的实现为其增加麻烦的监听,这就不能叫做控件了. 下面再给大家介绍一个经常重写的方法法:publicboolean onTouchEvent (MotionEvent even

Android 自定义控件开发入门 (三)

上两次我们从如何自定义控件讲起,列举了View的一些Api,说明了一些在自定义的时候,可以进行重写的方法,然后通过一个例子的两种写法向大家展示了最基本的自定义控件和我们要充分了解并积极重写View方法的精神,这次我们将继续进行学习!  现在请大家回想一下我们使用安卓原生控件时的感受,一个好的控件是可以在xml中进行各种属性的操作的,而自定义控件往往有一些特殊的需求,今天我要讲的就是安卓给自定义控件添加自定义的属性. 下面再给大家具体介绍一下如果自定义的View需要有自定义的属性我们该如何处理:

一看就懂的Android APP开发入门教程

  这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤一一讲解,非常简明的一个Android APP开发入门教程,需要的朋友可以参考下 工作中有做过手机App项目,前端和android或ios程序员配合完成整个项目的开发,开发过程中与ios程序配合基本没什么问题,而android各种机子和rom的问题很多,这也让我产生了学习android和ios程序开发的兴趣.于是凌晨一点睡不着写了第一个android程序HelloAndroid,po出

《Android应用开发入门经典(第3版)》——第6.8节作业

6.8 作业 Android应用开发入门经典(第3版) 6.8.1 测验 1.EditView中的提示的作用是什么? 2.要实现一个定制的按钮必须要做哪些工作? 3.不确定的ProgressBar和确定的ProgressBar之间的差别是什么? 4.在讨论AsyncTask时publishProgess的含义是什么? 6.8.2 答案 1.在 EditText 视图中显示的提示是对用户的提醒,它会被用户的输入所覆盖. 2.要实现一个定制的按钮必须要使用一个 XML 文件作为一个可绘制的资源.这个

《Android应用开发入门经典(第3版)》——第1.1节建立开发环境

1.1 建立开发环境 Android应用开发入门经典(第3版) 要进行Android开发,首先需要创建一个可工作的开发环境.Android开发使用的是Java开发语言,用于支持Android开发的重要工具都是内置于Eclipse集成开发环境(IDE)中的,当然IntelliJ和基于IntelliJ的Android Studio也是不错的工具.本书使用的工具是Eclipse.要安装Eclipse和支持Android的Eclipse插件(Android Developer Tools),需要下载一个

《Android应用开发入门经典(第3版)》——第1.4节运行应用

1.4 运行应用 Android应用开发入门经典(第3版) 要运行这个应用需要执行下列步骤. 1.选中Hour1App并右击鼠标列出一个选项列表.当然还可以按下Control键并单击Mac.选择Run As,然后选择Android Application,如图1.9所示. 2.在接收到"Android AVD Error"错误消息时可以选择创建一个新的Android虚拟设备.选择Yes. 运行模拟器需要一个Android虚拟设备(AVD),它定义了模拟器中用于测试的设备的参数规格.有很

《Android应用开发入门经典(第3版)》——第1.6节小结

1.6 小结 Android应用开发入门经典(第3版) 本章的目标是开始进行Android开发.第一步是下载和安装Android开发环境.读者使用这个环境生成了一个简单的应用程序并对其进行了修改,通过这一过程学习到了如何以可视化的方式来为Android应用创建用户界面并了解到用户界面本质上是一个XML文件.此外,还向该应用添加了一个简单的动作,这是通过为一个按钮创建一个onClickListener()方法来实现的.

《Android应用开发入门经典(第3版)》——第1.7节问与答

1.7 问与答 Android应用开发入门经典(第3版) 问题:使用ADT包是否是入门的最佳方式? 答案:ADT包是开始进行Android开发的最快和最简单的方式,但读者如果已经有了一个Eclipse实例或者使用的IDE不是Eclipse,那么就需要分别安装Android SDK.平台以及平台工具并继续使用自己熟悉的开发环境. 问题:是否应该使用可视化工具来创建用户界面? 答案:尽管本章仅对这个工具做了一个介绍,但一般来讲需要这样做.随着对Android布局的深入了解,读者可能会发现自己既会使用

《Android应用开发入门经典(第3版)》——第1.8节作业

1.8 作业 Android应用开发入门经典(第3版) 1.8.1 测验 1.在一个activity中,哪个方法用于将一个表示Button的资源与一个类型为Button的变量关联起来? 2.如何使用可视化工具将一个Button添加到屏幕上? 1.8.2 答案 1.一个activity包含一个名为findViewById()方法,该方法将资源与代码中的变量关联起来.特别地,findViewById()接收一个资源id,并返回这个Button或其他与该资源相关联的视图.在这段代码中必须要将返回的视图