首页 > 其他分享 >Android 自定义组件之如何实现自定义组件

Android 自定义组件之如何实现自定义组件

时间:2022-12-06 10:06:11浏览次数:62  
标签:自定义 int text 组件 Android View android view



简介

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.
17. package android.widget;
18.
19. import android.content.Context;
20. import android.graphics.Canvas;
21. import android.graphics.Paint;
22. import android.view.View;
23.
24. /**
25. * Example of how to write a custom subclass of View. LabelView
26. * is used to draw simple text views. Note that it does not handle
27. * styled text or right-to-left writing systems.
28. *
29. */
30. public class LabelView extends View {
31. /**
32. * Constructor. This version is only needed if you will be instantiating
33. * the object manually (not from a layout XML file).
34. * @param context the application environment
35. */
36. public LabelView(Context context) {
37. super(context);
38. initLabelView();
39. }
40.
41. /**
42. * Construct object, initializing with any attributes we understand from a
43. * layout file. These attributes are defined in
44. * SDK/assets/res/any/classes.xml.
45. *
46. * @see android.view.View#View(android.content.Context, android.util.AttributeSet)
47. public LabelView(Context context, AttributeSet attrs) {
48. super(context, attrs);
49. initLabelView();
50.
51. Resources.StyledAttributes a = context.obtainStyledAttributes(attrs,
52. R.styleable.LabelView);
53.
54. CharSequence s = a.getString(R.styleable.LabelView_text);
55. if (s != null) {
56. setText(s.toString());
57. }
58.
59. ColorStateList textColor = a.getColorList(R.styleable.
60. LabelView_textColor);
61. if (textColor != null) {
62. setTextColor(textColor.getDefaultColor(0));
63. }
64.
65. int textSize = a.getInt(R.styleable.LabelView_textSize, 0);
66. if (textSize > 0) {
67. setTextSize(textSize);
68. }
69.
70. a.recycle();
71. }
72.
73. */
74. private void initLabelView() {
75. new Paint();
76. true);
77. 16);
78. 0xFF000000);
79.
80. 3;
81. 3;
82. 3;
83. 3;
84. }
85.
86. /**
87. * Sets the text to display in this label
88. * @param text The text to display. This will be drawn as one line.
89. */
90. public void setText(String text) {
91. mText = text;
92. requestLayout();
93. invalidate();
94. }
95.
96. /**
97. * Sets the text size for this label
98. * @param size Font size
99. */
100. public void setTextSize(int size) {
101. mTextPaint.setTextSize(size);
102. requestLayout();
103. invalidate();
104. }
105.
106. /**
107. * Sets the text color for this label
108. * @param color ARGB value for the text
109. */
110. public void setTextColor(int color) {
111. mTextPaint.setColor(color);
112. invalidate();
113. }
114.
115.
116. /**
117. * @see android.view.View#measure(int, int)
118. */
119. @Override
120. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
121. setMeasuredDimension(measureWidth(widthMeasureSpec),
122. measureHeight(heightMeasureSpec));
123. }
124.
125. /**
126. * Determines the width of this view
127. * @param measureSpec A measureSpec packed into an int
128. * @return The width of the view, honoring constraints from measureSpec
129. */
130. private int measureWidth(int measureSpec) {
131. int result;
132. int specMode = MeasureSpec.getMode(measureSpec);
133. int specSize = MeasureSpec.getSize(measureSpec);
134.
135. if (specMode == MeasureSpec.EXACTLY) {
136. // We were told how big to be
137. result = specSize;
138. else {
139. // Measure the text
140. int) mTextPaint.measureText(mText) + mPaddingLeft
141. + mPaddingRight;
142. if (specMode == MeasureSpec.AT_MOST) {
143. // Respect AT_MOST value if that was what is called for by measureSpec
144. result = Math.min(result, specSize);
145. }
146. }
147.
148. return result;
149. }
150.
151. /**
152. * Determines the height of this view
153. * @param measureSpec A measureSpec packed into an int
154. * @return The height of the view, honoring constraints from measureSpec
155. */
156. private int measureHeight(int measureSpec) {
157. int result;
158. int specMode = MeasureSpec.getMode(measureSpec);
159. int specSize = MeasureSpec.getSize(measureSpec);
160.
161. int) mTextPaint.ascent();
162. if (specMode == MeasureSpec.EXACTLY) {
163. // We were told how big to be
164. result = specSize;
165. else {
166. // Measure the text (beware: ascent is a negative number)
167. int) (-mAscent + mTextPaint.descent()) + mPaddingTop
168. + mPaddingBottom;
169. if (specMode == MeasureSpec.AT_MOST) {
170. // Respect AT_MOST value if that was what is called for by measureSpec
171. result = Math.min(result, specSize);
172. }
173. }
174. return result;
175. }
176.
177. /**
178. * Render the text
179. *
180. * @see android.view.View#onDraw(android.graphics.Canvas)
181. */
182. @Override
183. protected void onDraw(Canvas canvas) {
184. super.onDraw(canvas);
185. canvas.drawText(mText, mPaddingLeft, mPaddingTop - mAscent, mTextPaint);
186. }
187.
188. private Paint mTextPaint;
189. private String mText;
190. private int mAscent;
191. }


应用该自定义组件的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.
7. <com.example.android.apis.view.LabelView
8. android:background="@drawable/red"
9. android:layout_width="match_parent"
10. android:layout_height="wrap_content"
11. app:text="Red"/>
12.
13. <com.example.android.apis.view.LabelView
14. android:background="@drawable/blue"
15. android:layout_width="match_parent"
16. android:layout_height="wrap_content"
17. app:text="Blue" app:textSize="20dp"/>
18.
19. <com.example.android.apis.view.LabelView
20. android:background="@drawable/green"
21. android:layout_width="match_parent"
22. android:layout_height="wrap_content"
23. app:text="Green" app:textColor="#ffffffff" />
24.
25. </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.
5. this.setOrientation(VERTICAL);
6.
7. // Here we build the child views in code. They could also have
8. // been specified in an XML file.
9.
10. new TextView(context);
11. mTitle.setText(title);
12. new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
13.
14. new TextView(context);
15. mDialogue.setText(dialogue);
16. new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
17.
18. mDialogue.setVisibility(expanded ? VISIBLE : GONE);
19. }
20.
21. /**
22. * Convenience method to set the title of a SpeechView
23. */
24. public void setTitle(String title) {
25. mTitle.setText(title);
26. }
27.
28. /**
29. * Convenience method to set the dialogue of a SpeechView
30. */
31. public void setDialogue(String words) {
32. mDialogue.setText(words);
33. }
34.
35. /**
36. * Convenience method to expand or hide the dialogue
37. */
38. public void setExpanded(boolean expanded) {//该方法在List4中没有
39. mDialogue.setVisibility(expanded ? VISIBLE : GONE);
40. }
41.
42. private TextView mTitle;
43. private TextView mDialogue;
44. }


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


定义


一个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. />


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

标签:自定义,int,text,组件,Android,View,android,view
From: https://blog.51cto.com/u_11407799/5914566

相关文章

  • Android高仿京东、天猫下拉刷新
    说到下拉刷新,相信大家都不陌生,现在基本上每个项目都会用到。我们公司的项目一直都是使用SwipeRefreshLayout,官方的MaterialDesign风格,好用少Bug。现在下拉刷新大概有下面几......
  • Android开源框架Universal-Image-Loader基本介绍和使用
    相信大家平时做Android应用的时候,多少会接触到异步加载图片,或者加载大量图片的问题,这个开源框架就是用来做这些工作的。该项目存在于Github上面​​https://github.com/nost......
  • Android集成友盟推送功能
    友盟是中国最大的移动开发者服务平台,为移动开发者提供免费的应用统计分析、社交分享、消息推送、自动更新、在线参数、移动推广效果分析、微社区等app开发和运营解决方案。......
  • Android PullToRefresh上拉和下拉刷新
    Github链接:​​https://github.com/chrisbanes/Android-PullToRefresh​​GUIDE:​​https://github.com/chrisbanes/Android-PullToRefresh/wiki/Quick-Start-Guide​​1.......
  • Android Widget 小部件(二) 使用configure
    在添加Widget之前需要做一些处理操作,可以使用 配置活动在​​上一篇​​的实现基础上,加上配置活动(configure=activity)。这时添加Widget时,会先打开一个Activity,进行配置操......
  • Android高仿网易新闻客户端之首页
    关于实现网易新闻客户端的界面,以前写过很多博客,请参考:​​Android实现网易新闻客户端效果​​​​Android实现网易新闻客户端侧滑菜单(一)​​​​Android实现网易新闻客户端......
  • Android高仿网易新闻客户端之侧滑菜单
    承接上一篇文章:​​Android高仿网易新闻客户端之动态添加标签​​,今天实现侧滑菜单的效果。关于侧滑菜单,有很多种实现方式:1.自定义ViewGroup请参考:​​Android实现网易新......
  • Android 自定义标题栏Title Bar
    在Android自定义标题栏,步骤很简单:1.在onCreate方法中声明如下代码:requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);setContentView(R.id.activity_main);getWindow().......
  • 《“透视”个人大数据》项目开发小记 --(三)Android APP 开发(1)技术经验总结
       项目的AndroidAPP应用软件经过不断实践改进,技术上已逐渐成熟,这为进一步的提升及新项目的开发奠定了基础。开发过程中积累的一些技术和经验将渐次的与同行分享。......
  • react-native 导航组件react-navigation详解
    react-native导航组件react-navigation详解Lyan_2ab3关注0.2622018.08.1318:08:47字数716阅读2,285继续上篇文章,还是先看下上篇文章react-navigation的官......