参考链接:http://blog.csdn.net/jjwwmlp456/article/details/41076699

简介

Android提供了用于构建UI的强大的组件模型。两个基类:View和ViewGroup。

可用Widget的部分名单包括Button, TextView, EditText, ListView, CheckBox,RadioButton, Gallery, Spinner,以及一些有特别作用的组件: AutoCompleteTextView, ImageSwitcher和 TextSwitcher。

可用的布局有:LinearLayout,FrameLayout,RelativeLayout,AbsoluteLayout,GridLayout (later on api level 14 or v7-support)


基本做法

1. 继承自View或View的子类

2. 重写父类的一些方法,如:onDraw(),onMeasure(),onLayout()等

3. 使用自定义的组件类。


完全自定义组件

1. 最普通的作法是,继承自View,实现你的自定义组件

2. 提供一个构造函数,采用有属性参数的,也可以使用自定义属性

3. 你可能想在组件中创建自己的事件监听器,属性访问器和修改器,或其他行为

4. 几乎肯定要重写onDraw(),onMeasure()。默认onDraw()什么也没作,onMeasure()则设置一个100x100的尺寸。

5. 根据需要重写其他方法 ...


onDraw()和onMeasure()

onDraw(),提供一个Canvas,可以绘制2D图形。

若要绘制3D图形,请继承GLSurfaceView,参见,api-demo下的GLSurfaceViewActivity


onMeasure()测量组件

1. 宽度和高度在需要测量时调用该方法

2.应该进行测量计算组件将需要呈现的宽度和高度。它应该尽量保持传入的规格范围内,尽管它可以选择超过它们(在这种情况下,父视图可以选择做什么,包括裁剪,滚动,抛出一个异常,或者要求onMeasure()再次尝试,或使用不同的测量规格)

3. 宽高计算完毕后,必须调用用setMeasuredDimession(int width, int height),进行设置。否则将抛出一个异常


下面是一些View中可被调用的方法总结(未全部包含,可自行查看类似onXxx的方法):

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(Canvas) Called when the view should render its content.
Event processing onKeyDown(int, KeyEvent) Called when a new key event occurs.
onKeyUp(int, KeyEvent) Called when a 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, 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.


自定义View示例

adi-demo下的示例:LabelView

[java]view plaincopyprint?
  1. /*
  2. *Copyright(C)2007TheAndroidOpenSourceProject
  3. *
  4. *LicensedundertheApacheLicense,Version2.0(the"License");
  5. *youmaynotusethisfileexceptincompliancewiththeLicense.
  6. *YoumayobtainacopyoftheLicenseat
  7. *
  8. *http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. *Unlessrequiredbyapplicablelaworagreedtoinwriting,software
  11. *distributedundertheLicenseisdistributedonan"ASIS"BASIS,
  12. *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
  13. *SeetheLicenseforthespecificlanguagegoverningpermissionsand
  14. *limitationsundertheLicense.
  15. */
  16. packageandroid.widget;
  17. importandroid.content.Context;
  18. importandroid.graphics.Canvas;
  19. importandroid.graphics.Paint;
  20. importandroid.view.View;
  21. /**
  22. *ExampleofhowtowriteacustomsubclassofView.LabelView
  23. *isusedtodrawsimpletextviews.Notethatitdoesnothandle
  24. *styledtextorright-to-leftwritingsystems.
  25. *
  26. */
  27. publicclassLabelViewextendsView{
  28. /**
  29. *Constructor.Thisversionisonlyneededifyouwillbeinstantiating
  30. *theobjectmanually(notfromalayoutXMLfile).
  31. *@paramcontexttheapplicationenvironment
  32. */
  33. publicLabelView(Contextcontext){
  34. super(context);
  35. initLabelView();
  36. }
  37. /**
  38. *Constructobject,initializingwithanyattributesweunderstandfroma
  39. *layoutfile.Theseattributesaredefinedin
  40. *SDK/assets/res/any/classes.xml.
  41. *
  42. *@seeandroid.view.View#View(android.content.Context,android.util.AttributeSet)
  43. publicLabelView(Contextcontext,AttributeSetattrs){
  44. super(context,attrs);
  45. initLabelView();
  46. Resources.StyledAttributesa=context.obtainStyledAttributes(attrs,
  47. R.styleable.LabelView);
  48. CharSequences=a.getString(R.styleable.LabelView_text);
  49. if(s!=null){
  50. setText(s.toString());
  51. }
  52. ColorStateListtextColor=a.getColorList(R.styleable.
  53. LabelView_textColor);
  54. if(textColor!=null){
  55. setTextColor(textColor.getDefaultColor(0));
  56. }
  57. inttextSize=a.getInt(R.styleable.LabelView_textSize,0);
  58. if(textSize>0){
  59. setTextSize(textSize);
  60. }
  61. a.recycle();
  62. }
  63. */
  64. privatevoidinitLabelView(){
  65. mTextPaint=newPaint();
  66. mTextPaint.setAntiAlias(true);
  67. mTextPaint.setTextSize(16);
  68. mTextPaint.setColor(0xFF000000);
  69. mPaddingLeft=3;
  70. mPaddingTop=3;
  71. mPaddingRight=3;
  72. mPaddingBottom=3;
  73. }
  74. /**
  75. *Setsthetexttodisplayinthislabel
  76. *@paramtextThetexttodisplay.Thiswillbedrawnasoneline.
  77. */
  78. publicvoidsetText(Stringtext){
  79. mText=text;
  80. requestLayout();
  81. invalidate();
  82. }
  83. /**
  84. *Setsthetextsizeforthislabel
  85. *@paramsizeFontsize
  86. */
  87. publicvoidsetTextSize(intsize){
  88. mTextPaint.setTextSize(size);
  89. requestLayout();
  90. invalidate();
  91. }
  92. /**
  93. *Setsthetextcolorforthislabel
  94. *@paramcolorARGBvalueforthetext
  95. */
  96. publicvoidsetTextColor(intcolor){
  97. mTextPaint.setColor(color);
  98. invalidate();
  99. }
  100. /**
  101. *@seeandroid.view.View#measure(int,int)
  102. */
  103. @Override
  104. protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
  105. setMeasuredDimension(measureWidth(widthMeasureSpec),
  106. measureHeight(heightMeasureSpec));
  107. }
  108. /**
  109. *Determinesthewidthofthisview
  110. *@parammeasureSpecAmeasureSpecpackedintoanint
  111. *@returnThewidthoftheview,honoringconstraintsfrommeasureSpec
  112. */
  113. privateintmeasureWidth(intmeasureSpec){
  114. intresult;
  115. intspecMode=MeasureSpec.getMode(measureSpec);
  116. intspecSize=MeasureSpec.getSize(measureSpec);
  117. if(specMode==MeasureSpec.EXACTLY){
  118. //Weweretoldhowbigtobe
  119. result=specSize;
  120. }else{
  121. //Measurethetext
  122. result=(int)mTextPaint.measureText(mText)+mPaddingLeft
  123. +mPaddingRight;
  124. if(specMode==MeasureSpec.AT_MOST){
  125. //RespectAT_MOSTvalueifthatwaswhatiscalledforbymeasureSpec
  126. result=Math.min(result,specSize);
  127. }
  128. }
  129. returnresult;
  130. }
  131. /**
  132. *Determinestheheightofthisview
  133. *@parammeasureSpecAmeasureSpecpackedintoanint
  134. *@returnTheheightoftheview,honoringconstraintsfrommeasureSpec
  135. */
  136. privateintmeasureHeight(intmeasureSpec){
  137. intresult;
  138. intspecMode=MeasureSpec.getMode(measureSpec);
  139. intspecSize=MeasureSpec.getSize(measureSpec);
  140. mAscent=(int)mTextPaint.ascent();
  141. if(specMode==MeasureSpec.EXACTLY){
  142. //Weweretoldhowbigtobe
  143. result=specSize;
  144. }else{
  145. //Measurethetext(beware:ascentisanegativenumber)
  146. result=(int)(-mAscent+mTextPaint.descent())+mPaddingTop
  147. +mPaddingBottom;
  148. if(specMode==MeasureSpec.AT_MOST){
  149. //RespectAT_MOSTvalueifthatwaswhatiscalledforbymeasureSpec
  150. result=Math.min(result,specSize);
  151. }
  152. }
  153. returnresult;
  154. }
  155. /**
  156. *Renderthetext
  157. *
  158. *@seeandroid.view.View#onDraw(android.graphics.Canvas)
  159. */
  160. @Override
  161. protectedvoidonDraw(Canvascanvas){
  162. super.onDraw(canvas);
  163. canvas.drawText(mText,mPaddingLeft,mPaddingTop-mAscent,mTextPaint);
  164. }
  165. privatePaintmTextPaint;
  166. privateStringmText;
  167. privateintmAscent;
  168. }

应用该自定义组件的layout xml:

[html]view plaincopyprint?
  1. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis"
  3. android:orientation="vertical"
  4. android:layout_width="match_parent"
  5. android:layout_height="wrap_content">
  6. <com.example.android.apis.view.LabelView
  7. android:background="@drawable/red"
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. app:text="Red"/>
  11. <com.example.android.apis.view.LabelView
  12. android:background="@drawable/blue"
  13. android:layout_width="match_parent"
  14. android:layout_height="wrap_content"
  15. app:text="Blue"app:textSize="20dp"/>
  16. <com.example.android.apis.view.LabelView
  17. android:background="@drawable/green"
  18. android:layout_width="match_parent"
  19. android:layout_height="wrap_content"
  20. app:text="Green"app:textColor="#ffffffff"/>
  21. </LinearLayout>

该示例演示了:

1. 继承自View的完全自定义组件

2. 带参数的构造函数(一些属性参数在xml中设置)。还使用了自定义属性R.styleable.LabelView

3. 一些标准的public 方法,如setText()、setTextSize()、setTextColor()

4. onMeasure()测量组件尺寸,内部由measureWidth(intmeasureSpec) 和measureHeight(intmeasureSpec)来测量。

5. onDraw()将Label绘制到画面Canvas上


复合组件

由一些现有组件,复合成一个新的组件。 要创建一个复合组件: 1. 通常需要创建一个类,继承自一个Layout,或者ViewGroup。 2. 在构造函数中,需要先调用父类相应的构造函数。然后设置一些需要的组件用于复合。可以使用自定义属性 3. 可以创建监听器,监听处理一些可能的动作 4. 可能有一些 属性的 get / set 方法 5. 如果继承自某一Layout类时,不需要重写onDraw()和onMeasure(),因为Layout类中有默认的行为。如有必要,当然也可以重写 6. 可能重写其他一些onXxx(),以达到你想要的效果

复合组件示例

api-demo下的List4和List6里的内部类SpeachView,以下为List6中的源码 [java]view plaincopyprint?
  1. privateclassSpeechViewextendsLinearLayout{
  2. publicSpeechView(Contextcontext,Stringtitle,Stringdialogue,booleanexpanded){
  3. super(context);
  4. this.setOrientation(VERTICAL);
  5. //Herewebuildthechildviewsincode.Theycouldalsohave
  6. //beenspecifiedinanXMLfile.
  7. mTitle=newTextView(context);
  8. mTitle.setText(title);
  9. addView(mTitle,newLinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
  10. mDialogue=newTextView(context);
  11. mDialogue.setText(dialogue);
  12. addView(mDialogue,newLinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
  13. mDialogue.setVisibility(expanded?VISIBLE:GONE);
  14. }
  15. /**
  16. *ConveniencemethodtosetthetitleofaSpeechView
  17. */
  18. publicvoidsetTitle(Stringtitle){
  19. mTitle.setText(title);
  20. }
  21. /**
  22. *ConveniencemethodtosetthedialogueofaSpeechView
  23. */
  24. publicvoidsetDialogue(Stringwords){
  25. mDialogue.setText(words);
  26. }
  27. /**
  28. *Conveniencemethodtoexpandorhidethedialogue
  29. */
  30. publicvoidsetExpanded(booleanexpanded){//该方法在List4中没有
  31. mDialogue.setVisibility(expanded?VISIBLE:GONE);
  32. }
  33. privateTextViewmTitle;
  34. privateTextViewmDialogue;
  35. }
SpeachView,继承了LinearLayout,纵向布局。内部有一个TextView的title,一个TextView的dialogue。List4完全展开两个TextView;List6点击title可以收缩/展开dialogue。

修改现有View类型

继承自一个现有的View,以增强其功能,满足需要。
sdk中有个记事本NotePad的示例工程。其中有一个类就是扩展了EditText。 在NoteEditor类中: [java]view plaincopyprint?
  1. publicstaticclassLinedEditTextextendsEditText{
  2. privateRectmRect;
  3. privatePaintmPaint;
  4. //ThisconstructorisusedbyLayoutInflater
  5. publicLinedEditText(Contextcontext,AttributeSetattrs){
  6. super(context,attrs);
  7. //CreatesaRectandaPaintobject,andsetsthestyleandcolorofthePaintobject.
  8. mRect=newRect();
  9. mPaint=newPaint();
  10. mPaint.setStyle(Paint.Style.STROKE);
  11. mPaint.setColor(0x800000FF);
  12. }
  13. /**
  14. *ThisiscalledtodrawtheLinedEditTextobject
  15. *@paramcanvasThecanvasonwhichthebackgroundisdrawn.
  16. */
  17. @Override
  18. protectedvoidonDraw(Canvascanvas){
  19. //GetsthenumberoflinesoftextintheView.
  20. intcount=getLineCount();//edittext中有几行,edittext继承textview
  21. //GetstheglobalRectandPaintobjects
  22. Rectr=mRect;
  23. Paintpaint=mPaint;
  24. /*
  25. *DrawsonelineintherectangleforeverylineoftextintheEditText
  26. */
  27. for(inti=0;i<count;i++){
  28. //Getsthebaselinecoordinatesforthecurrentlineoftext
  29. intbaseline=getLineBounds(i,r);//将一行的范围坐标赋给矩形r;返回一行y方向上的基准线坐标
  30. /*
  31. *Drawsalineinthebackgroundfromtheleftoftherectangletotheright,
  32. *ataverticalpositiononedipbelowthebaseline,usingthe"paint"object
  33. *fordetails.
  34. */
  35. canvas.drawLine(r.left,baseline+1,r.right,baseline+1,paint);//绘制一条线,宽度为原行的宽度,高度为从基线开始+1个像素
  36. }
  37. //Finishesupbycallingtheparentmethod
  38. super.onDraw(canvas);
  39. }
  40. }


定义

一个public的静态内部类,以便它可以被访问:NoteEditor.MyEditText 它是静态内部类,意味着,它不依靠外部类的成员,不会产生一些“组合的方法”。
继承自EditText

类的初始化

构造函数中,先调用父类的构造方法,并且它是带属性参数的构造函数。在使用时,从一个xml布局文件inflate

重写的方法

只有onDraw()被重写。在onDraw()中绘制了一条蓝色的线,该线从每行文本的的基线开始向下1像素,宽度为行宽。 方法结束前,调用super.onDraw()

使用自定义组件

[html]view plaincopyprint?
  1. <viewxmlns:android="http://schemas.android.com/apk/res/android"
  2. class="com.example.android.notepad.NoteEditor$LinedEditText"
  3. android:id="@+id/note"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:background="@android:color/transparent"
  7. android:padding="5dp"
  8. android:scrollbars="vertical"
  9. android:fadingEdge="vertical"
  10. android:gravity="top"
  11. android:textSize="22sp"
  12. android:capitalize="sentences"
  13. />

使用完全限定类名,引入自定义组件。使用$引用内部类。

更多相关文章

  1. Android设置全屏隐藏状态栏的方法
  2. 使用adb出现假emulator的解决方法
  3. h5
  4. Android(安卓)LayoutInflater深度解析
  5. android 中使用Java反射Reflect,输出类中变量值,方便调试。
  6. android获取状态栏的高度。
  7. Android(安卓)Intent学习
  8. android中BaseActivity的公共方法
  9. Android:Smali语法中文介绍

随机推荐

  1. 使用xmldom在服务器端生成静态html页面
  2. C#-XML操作类的代码实例详解
  3. 详细介绍Web安全之XML注入的示例代码
  4. 详细解析包含中文字符的URL编码问题
  5. 详细介绍XML技术上传文件
  6. 详细介绍如何使用javascript+xml实现分页
  7. 如何使用DOM创建XML的详细介绍
  8. 教你怎样快速从一个XML文件中查找信息的
  9. java读取XML文件的四种方式及比较的代码
  10. 一个简单的XML Schema的示例代码详解