参考链接: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

  1. /*
  2. * Copyright (C) 2007 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. *      http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package android.widget;
  17. import android.content.Context;
  18. import android.graphics.Canvas;
  19. import android.graphics.Paint;
  20. import android.view.View;
  21. /**
  22. * Example of how to write a custom subclass of View. LabelView
  23. * is used to draw simple text views. Note that it does not handle
  24. * styled text or right-to-left writing systems.
  25. *
  26. */
  27. public class LabelView extends View {
  28. /**
  29. * Constructor.  This version is only needed if you will be instantiating
  30. * the object manually (not from a layout XML file).
  31. * @param context the application environment
  32. */
  33. public LabelView(Context context) {
  34. super(context);
  35. initLabelView();
  36. }
  37. /**
  38. * Construct object, initializing with any attributes we understand from a
  39. * layout file. These attributes are defined in
  40. * SDK/assets/res/any/classes.xml.
  41. *
  42. * @see android.view.View#View(android.content.Context, android.util.AttributeSet)
  43. public LabelView(Context context, AttributeSet attrs) {
  44. super(context, attrs);
  45. initLabelView();
  46. Resources.StyledAttributes a = context.obtainStyledAttributes(attrs,
  47. R.styleable.LabelView);
  48. CharSequence s = a.getString(R.styleable.LabelView_text);
  49. if (s != null) {
  50. setText(s.toString());
  51. }
  52. ColorStateList textColor = a.getColorList(R.styleable.
  53. LabelView_textColor);
  54. if (textColor != null) {
  55. setTextColor(textColor.getDefaultColor(0));
  56. }
  57. int textSize = a.getInt(R.styleable.LabelView_textSize, 0);
  58. if (textSize > 0) {
  59. setTextSize(textSize);
  60. }
  61. a.recycle();
  62. }
  63. */
  64. private void initLabelView() {
  65. mTextPaint = new Paint();
  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. * Sets the text to display in this label
  76. * @param text The text to display. This will be drawn as one line.
  77. */
  78. public void setText(String text) {
  79. mText = text;
  80. requestLayout();
  81. invalidate();
  82. }
  83. /**
  84. * Sets the text size for this label
  85. * @param size Font size
  86. */
  87. public void setTextSize(int size) {
  88. mTextPaint.setTextSize(size);
  89. requestLayout();
  90. invalidate();
  91. }
  92. /**
  93. * Sets the text color for this label
  94. * @param color ARGB value for the text
  95. */
  96. public void setTextColor(int color) {
  97. mTextPaint.setColor(color);
  98. invalidate();
  99. }
  100. /**
  101. * @see android.view.View#measure(int, int)
  102. */
  103. @Override
  104. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  105. setMeasuredDimension(measureWidth(widthMeasureSpec),
  106. measureHeight(heightMeasureSpec));
  107. }
  108. /**
  109. * Determines the width of this view
  110. * @param measureSpec A measureSpec packed into an int
  111. * @return The width of the view, honoring constraints from measureSpec
  112. */
  113. private int measureWidth(int measureSpec) {
  114. int result;
  115. int specMode = MeasureSpec.getMode(measureSpec);
  116. int specSize = MeasureSpec.getSize(measureSpec);
  117. if (specMode == MeasureSpec.EXACTLY) {
  118. // We were told how big to be
  119. result = specSize;
  120. } else {
  121. // Measure the text
  122. result = (int) mTextPaint.measureText(mText) + mPaddingLeft
  123. + mPaddingRight;
  124. if (specMode == MeasureSpec.AT_MOST) {
  125. // Respect AT_MOST value if that was what is called for by measureSpec
  126. result = Math.min(result, specSize);
  127. }
  128. }
  129. return result;
  130. }
  131. /**
  132. * Determines the height of this view
  133. * @param measureSpec A measureSpec packed into an int
  134. * @return The height of the view, honoring constraints from measureSpec
  135. */
  136. private int measureHeight(int measureSpec) {
  137. int result;
  138. int specMode = MeasureSpec.getMode(measureSpec);
  139. int specSize = MeasureSpec.getSize(measureSpec);
  140. mAscent = (int) mTextPaint.ascent();
  141. if (specMode == MeasureSpec.EXACTLY) {
  142. // We were told how big to be
  143. result = specSize;
  144. } else {
  145. // Measure the text (beware: ascent is a negative number)
  146. result = (int) (-mAscent + mTextPaint.descent()) + mPaddingTop
  147. + mPaddingBottom;
  148. if (specMode == MeasureSpec.AT_MOST) {
  149. // Respect AT_MOST value if that was what is called for by measureSpec
  150. result = Math.min(result, specSize);
  151. }
  152. }
  153. return result;
  154. }
  155. /**
  156. * Render the text
  157. *
  158. * @see android.view.View#onDraw(android.graphics.Canvas)
  159. */
  160. @Override
  161. protected void onDraw(Canvas canvas) {
  162. super.onDraw(canvas);
  163. canvas.drawText(mText, mPaddingLeft, mPaddingTop - mAscent, mTextPaint);
  164. }
  165. private Paint mTextPaint;
  166. private String mText;
  167. private int mAscent;
  168. }

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

  1. <LinearLayout xmlns: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(int measureSpec) 和 measureHeight(int measureSpec)来测量。

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中的源码
  1. private class SpeechView extends LinearLayout {
  2. public SpeechView(Context context, String title, String dialogue, boolean expanded) {
  3. super(context);
  4. this.setOrientation(VERTICAL);
  5. // Here we build the child views in code. They could also have
  6. // been specified in an XML file.
  7. mTitle = new TextView(context);
  8. mTitle.setText(title);
  9. addView(mTitle, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
  10. mDialogue = new TextView(context);
  11. mDialogue.setText(dialogue);
  12. addView(mDialogue, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
  13. mDialogue.setVisibility(expanded ? VISIBLE : GONE);
  14. }
  15. /**
  16. * Convenience method to set the title of a SpeechView
  17. */
  18. public void setTitle(String title) {
  19. mTitle.setText(title);
  20. }
  21. /**
  22. * Convenience method to set the dialogue of a SpeechView
  23. */
  24. public void setDialogue(String words) {
  25. mDialogue.setText(words);
  26. }
  27. /**
  28. * Convenience method to expand or hide the dialogue
  29. */
  30. public void setExpanded(boolean expanded) {//该方法在List4中没有
  31. mDialogue.setVisibility(expanded ? VISIBLE : GONE);
  32. }
  33. private TextView mTitle;
  34. private TextView mDialogue;
  35. }

SpeachView,继承了LinearLayout,纵向布局。内部有一个TextView的title,一个TextView的dialogue。List4完全展开两个TextView;List6点击title可以收缩/展开dialogue。


修改现有View类型

继承自一个现有的View,以增强其功能,满足需要。

sdk中有个记事本NotePad的示例工程。其中有一个类就是扩展了EditText。
在NoteEditor类中:
  1. public static class LinedEditText extends EditText {
  2. private Rect mRect;
  3. private Paint mPaint;
  4. // This constructor is used by LayoutInflater
  5. public LinedEditText(Context context, AttributeSet attrs) {
  6. super(context, attrs);
  7. // Creates a Rect and a Paint object, and sets the style and color of the Paint object.
  8. mRect = new Rect();
  9. mPaint = new Paint();
  10. mPaint.setStyle(Paint.Style.STROKE);
  11. mPaint.setColor(0x800000FF);
  12. }
  13. /**
  14. * This is called to draw the LinedEditText object
  15. * @param canvas The canvas on which the background is drawn.
  16. */
  17. @Override
  18. protected void onDraw(Canvas canvas) {
  19. // Gets the number of lines of text in the View.
  20. int count = getLineCount(); //edittext中有几行,  edittext继承textview
  21. // Gets the global Rect and Paint objects
  22. Rect r = mRect;
  23. Paint paint = mPaint;
  24. /*
  25. * Draws one line in the rectangle for every line of text in the EditText
  26. */
  27. for (int i = 0; i < count; i++) {
  28. // Gets the baseline coordinates for the current line of text
  29. int baseline = getLineBounds(i, r);//将一行的范围坐标赋给矩形r;返回一行y方向上的基准线坐标
  30. /*
  31. * Draws a line in the background from the left of the rectangle to the right,
  32. * at a vertical position one dip below the baseline, using the "paint" object
  33. * for details.
  34. */
  35. canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);//绘制一条线,宽度为原行的宽度,高度为从基线开始+1个像素
  36. }
  37. // Finishes up by calling the parent method
  38. super.onDraw(canvas);
  39. }
  40. }

定义

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

类的初始化

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

重写的方法

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

使用自定义组件

  1. <view xmlns: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. />

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

Android 自定义组件之如何实现自定义组件的更多相关文章

  1. vue_组件间通信:自定义事件、消息发布与订阅、槽

    自定义事件 只能用于 子组件 向 父组件 发送数据 可以取代函数类型的 props 在父组件: 给子组件@add-todo-event="addTodo" 在子组件: 相关方法中, ...

  2. 微信小程序之自定义select下拉选项框组件

    知识点:组件,animation,获取当前点击元素的索引与内容 微信小程序中没有select下拉选项框,所以只有自定义.自定义的话,可以选择模板的方式,也可以选择组件的方式来创建. 这次我选择了组件, ...

  3. 微信小程序自定义组件封装及父子间组件传值

    首先在我们可以直接写到需要的 page 中,然后再进行抽取组件,自定义组件建议 wxzx-xxx 命名 官网地址:https://developers.weixin.qq.com/miniprogra ...

  4. 微信小程序 - 自定义swiper dots样式(非组件)

      自定义须知: :组件内无法使用自定义样式变化,需要自定义 :原理就是利用swiper change事件与下标index匹配,显示不同的样式 swiper组件须知: :一旦swiper用于组件化,就 ...

  5. Vue基础-自定义事件的表单输入组件、自定义组件的 v-model

    Vue 测试版本:Vue.js v2.5.13 学习 Vue 的自定义事件的表单输入组件,觉得文档讲的不太细致,所以这里再细化一下: 如果不用 v-model,代码应该是这样: <myinput ...

  6. Wuss Weapp 一款高质量,组件齐全,高自定义的微信小程序 UI 组件库

    Wuss Weapp 一款高质量,组件齐全,高自定义的微信小程序 UI 组件库 文档 https://phonycode.github.io/wuss-weapp 扫码体验 使用微信扫一扫体验小程序组 ...

  7. vue2.0 自定义 侧滑删除(LeftSlider)组件

    1.自定义侧滑删除组件 LeftSlider.vue <!-- 侧滑删除 组件 --> <template> <div class="delete"& ...

  8. 微信小程序 自定义组件(modal) 引入组件

    项目结构: 步骤一:创建组件 声明这一组文件为自定义组件 modal.json { "component": true, // 自定义组件声明 "usingCompone ...

  9. DRF框架(九)——drf偏移分页组件、drf游标分页组件(了解)、自定义过滤器、过滤器插件django-filter

    drf偏移分页组件 paginations.py from rest_framework.pagination import LimitOffsetPagination class MyLimitOf ...

随机推荐

  1. Python-Cpython解释器支持的进程与线程

    一.Python并发编程之多进程 1. multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在pyt ...

  2. 解决hash冲突的办法

    1.开发定址法 2.再哈希法 3.链地址法 4.建立一个公共溢出区

  3. 第四课 Makefile文件的制作(下)

    1序言: 前面一节课讲解了Makefile的基础知识包括原理.预定义以及命令格式,这样是可以完成一个自动编译的文件,这些知识可以帮你完成.想想mak真是强大啊,可能有些同志发现了如果项目文件太多每个目 ...

  4. HDU - 6406 Taotao Picks Apples (RMQ+dp+二分)

    题意:N个高度为hi的果子,摘果子的个数是从位置1开始从左到右的严格递增子序列的个数.有M次操作,每次操作对初始序列修改位置p的果子高度为q.每次操作后输出修改后能摘到得数目. 分析:将序列分为左.右 ...

  5. LightOJ - 1336 Sigma Function(约数和+整数拆分)

    题干中给出函数公式: 其中pi为n的每个素因数,ei为其个数.设该函数为F(x),其意义为x的约数之和.问在1-n中有多少x,令F(x)为偶数. 分析:设f(p)为(p^(e+1)-1)/(p-1). ...

  6. SSDB系列文章推荐

    1. 下载和安装: http://ssdb.io/docs/zh_cn/install.html 2. SSDB 文档     http://ssdb.io/docs/zh_cn/index.html ...

  7. 一个linux命令(6/13):traceroute命令

    通过traceroute 我们可以知道信息从你的计算机到互联网另一端的主机是走的什么路径.当然每次数据包由某一同样的出发点(source)到达某一同样的目的地(destination)走的路径可能会不 ...

  8. os包方法

    os包中实现了平台无关的接口,设计向Unix风格,但是错误处理是go风格,当os包使用时,如果失败之后返回错误类型而不是错误数量. os包中函数设计方式和Unix类似,下面来看一下. func Chd ...

  9. 【bzoj3170】[Tjoi2013]松鼠聚会(数学题)

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3170 这道题要在n个点中求一个点使其他点到该点的切比雪夫距离最小. 有个结论:$ (x ...

  10. git 分支管理 (转自廖雪峰的git教程)

    在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支.截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支.HEAD严格来说不是指向提交,而 ...