那么怎样来创建一个新的控件呢?
这得看需求是怎样的了。
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:前缀="/apk/res/你的自定义View所在的包路径".在使用自定义属性的时候,使用前缀:属性名,如my:textColor="……"。
让我们先看一下View类的方法:
通常可能需要重写以下方法:
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及其派生类都具有的方法,也是AndroidUI绘制最重要的方法。开发者可重载该方法,并在重载的方法内部基于参数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.protectedvoiddispatchDraw(Canvascanvas):ViewGroup类及其派生类具有的方法,这个方法主要用于控制子View的绘制分发,重载该方法可改变子View的绘制,进而实现一些复杂的视效。
2.protectedbooleandrawChild(Canvascanvas,Viewchild,longdrawingTime)):ViewGroup类及其派生类具有的方法,这个方法直接控制绘制某局具体的子view,重载该方法可控制具体某个具体子View。
在需要开发自定义View的时候,我们不需要列举出上面所有的方法,,而是可以根据业务需要来有选择的使用·上面的方法,下面我们看一个简单的示例程序,在这个示例程序里面我们只需要重写onDraw方法就可以了!
示例程序一:
我们要写一个跟随手指移动的小球,思路很简单,只要获取到用户点击屏幕的位置,并且在该位置处重绘小球即可:
下面我们看一下程序:
我注释写的比较清楚,我就说的简略一点:
首先我们写一个类DrawView,也就是我们自定义的控件,继承自View
然后我们先写出构造器,获取到Context,这里如果用只含有Context的构造器会在xml里调用控件的时候出错,详情请看我的另外一篇博文:
/sunmc1204953974/article/details/38101057
下面我们开始写:
[java]view plaincopy //构造方法publicDrawView(Contextcontext,AttributeSetattrs){super(context,attrs);}//重写ondraw方法@OverridepublicvoidonDraw(Canvascanvas){super.onDraw(canvas);//创建画笔Paintpaint=newPaint();//设置画笔颜色paint.setColor(Color.RED);//画出小球canvas.drawCircle(circleX,circleY,circleR,paint);}
然后不要忘了设置这些数据的setter和getter,因为我们需要再使用这个View的时候加上监听才可以:
[java]view plaincopy //getset方法publicfloatgetCircleX(){returncircleX;}publicvoidsetCircleX(floatcircleX){this.circleX=circleX;}publicfloatgetCircleY(){returncircleY;}publicvoidsetCircleY(floatcircleY){this.circleY=circleY;}publicfloatgetCircleR(){returncircleR;}publicvoidsetCircleR(floatcircleR){this.circleR=circleR;}
这样我们的一个简单地自定控件就大功告成了,下面是该类的完整代码:
[java]view plaincopy packagecom.example.moveball;importandroid.content.Context;importandroid.graphics.Canvas;importandroid.graphics.Color;importandroid.graphics.Paint;importandroid.util.AttributeSet;importandroid.view.View;publicclassDrawViewextendsView{privatefloatcircleX=40;privatefloatcircleY=50;privatefloatcircleR=15;//构造方法publicDrawView(Contextcontext,AttributeSetattrs){super(context,attrs);}//重写ondraw方法@OverridepublicvoidonDraw(Canvascanvas){super.onDraw(canvas);//创建画笔Paintpaint=newPaint();//设置画笔颜色paint.setColor(Color.RED);//画出小球canvas.drawCircle(circleX,circleY,circleR,paint);}//getset方法publicfloatgetCircleX(){returncircleX;}publicvoidsetCircleX(floatcircleX){this.circleX=circleX;}publicfloatgetCircleY(){returncircleY;}publicvoidsetCircleY(floatcircleY){this.circleY=circleY;}publicfloatgetCircleR(){returncircleR;}publicvoidsetCircleR(floatcircleR){this.circleR=circleR;}}
之后我们就是像平时使用安卓原生控件那样使用就可以了,我们看一下Activity的代码:
[java]view plaincopy packagecom.example.moveball;importandroid.os.Bundle;importandroid.app.Activity;importandroid.view.Menu;importandroid.view.MotionEvent;importandroid.view.View;importandroid.view.View.OnTouchListener;publicclassMainActivityextendsActivity{//定义DrawView组件DrawViewdrawView=null;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//创建DrawView组件drawView=(DrawView)this.findViewById(R.id.drawView);//为DrawView组件绑定Touch事件drawView.setOnTouchListener(newOnTouchListener(){@OverridepublicbooleanonTouch(Viewarg0,MotionEventevent){//获取坐标并改变小球的坐标drawView.setCircleX(event.getX());drawView.setCircleY(event.getY());//通知draw组件重绘drawView.invalidate();//返回true表明被执行returntrue;}});}}
以及xml格式的布局文件:
[html]view plaincopy <?xmlversion="1.0"encoding="utf-8"?><RelativeLayoutxmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.example.moveball.DrawViewandroid:id="@+id/drawView"android:layout_width="match_parent"android:layout_height="match_parent"></com.example.moveball.DrawView></RelativeLayout>
这样一个简单的例子就呈现在大家面前了,无论是多么复杂的自定义控件,思路总是这样子的,大家是不是觉得怪怪的,对了,作为一个控件,我们居然还要为了他的实现为其增加麻烦的监听,这就是因为我们重写的方法太少的原因,下一讲再给大家介绍一个经常重写的方法:publicbooleanonTouchEvent(MotionEventevent)。
源代码上面已经很详细了,我在最后一篇的最后还会发一个工程上来,欢迎大家一起学习!
我也还是学生,写的不好或者有问题的地方还请多多指教~