Here is a high level overview of what you need to know to get started in creating your own View components:html
這裏高度歸納了一些步驟, 教你如何建立你的自定義組件.java
Tip: Extension classes can be defined as inner classes inside the activities that use them. This is useful because it controls access to them but isn't necessary (perhaps you want to create a new public View for wider use in your application). android
Fully customized components can be used to create graphical components that appear however you wish. Perhaps a graphical VU meter that looks like an old analog gauge, or a sing-a-long text view where a bouncing ball moves along the words so you can sing along with a karaoke machine. Either way, you want something that the built-in components just won't do, no matter how you combine them.shell
徹底自定義的組件能夠用來建立任何你想要的圖形組件. 也許是一個像電壓表的圖形計量器, 或者想卡拉OK裏面顯示歌詞的小球隨着音樂滾動. 不管怎樣, 內置的組件或者他們的組合是沒法知足你的需求的.canvas
Fortunately, you can easily create components that look and behave in any way you like, limited perhaps only by your imagination, the size of the screen, and the available processing power (remember that ultimately your application might have to run on something with significantly less power than your desktop workstation).api
幸運的是, 你能夠以你本身的要求輕鬆地建立徹底屬於本身的組件,你會發現不夠用的只是你的想象力、屏幕的尺寸和處理器的性能(記住你的應用程序最後只會在那些性能低於桌面電腦的平臺上面運行)。app
To create a fully customized component:框架
要建立徹底自定義組件:less
The onDraw() method delivers you a Canvas upon which you can implement anything you want: 2D graphics, other standard or custom components, styled text, or anything else you can think of.electron
onDraw() 方法會傳遞進來一個 Canvas(畫布) 對象, 在這個畫布上, 你能夠繪製任何2D圖形, 包括其餘的標準或者自定義組件, 有樣式的文字, 或者任何你能夠想到的東西.
Note: This does not apply to 3D graphics. If you want to use 3D graphics, you must extend SurfaceView instead of View, and draw from a separate thread. See the GLSurfaceViewActivity sample for details.
這裏不適用3D圖形.若是你想使用3D圖形, 你必須繼承 SurfaceView, 而不是View, 而且要在新線程裏繪製.
onMeasure() is a little more involved. onMeasure() is a critical piece of the rendering contract between your component and its container. onMeasure() should be overridden to efficiently and accurately report the measurements of its contained parts. This is made slightly more complex by the requirements of limits from the parent (which are passed in to the onMeasure() method) and by the requirement to call the setMeasuredDimension() method with the measured width and height once they have been calculated. If you fail to call this method from an overridden onMeasure() method, the result will be an exception at measurement time.
onMeasure() 更復雜一點. 它是組件和其容器之間的渲染的決定性部分. 重寫 onMeasure() 時要有效地精確地計算出它包含的內容的尺寸.這有點小麻煩,由於咱們不但要考慮父類的限制(經過onMeasure()傳過來的),同時咱們應該知道一旦測量寬度和高度出來後,就要當即調用setMeasuredDimension(int measuredWidth, int measuredHeight) 方法, 把測量好的寬和高設置進去. 若是重寫的onMeasure方法裏沒有調用這個方法, 在測量時會拋異常.
At a high level, implementing onMeasure() looks something like this:
歸納說來, 實現 onMeasure 會作如下這些事情:
Measure the view and its content to determine the measured width and the measured height. This method is invoked by measure(int, int) and should be overriden by subclasses to provide accurate and efficient measurement of their contents.
測量view和它裏面的內容, 從而肯定測量以後的寬度和高度. 這個方法由 measure() 方法調用, 子類應該重寫他, 提供精確有效的尺寸.
CONTRACT: When overriding this method, you must call setMeasuredDimension(int, int) to store the measured width and height of this view. Failure to do so will trigger an IllegalStateException, thrown by measure(int, int). Calling the superclass' onMeasure(int, int) is a valid use.
約定: 重寫這個方法時, 你必定要調用 setMeasuredDimension(int, int) 方法, 用來保存測量以後的寬和高. 不然會拋異常
The base class implementation of measure defaults to the background size, unless a larger size is allowed by the MeasureSpec. Subclasses should override onMeasure(int, int) to provide better measurements of their content.
父類中的實現只是設置了默認的背景大小, 子類要提供更好的測量尺寸.
If this method is overridden, it is the subclass's responsibility to make sure the measured height and width are at least the view's minimum height and width (getSuggestedMinimumHeight() and getSuggestedMinimumWidth()).
若是子類重寫了這個方法, 子類就要負責保證測量後的寬和高要至少要爲view對象最小的寬和高.
widthMeasureSpec | horizontal space requirements as imposed(強加的) by the parent. The requirements are encoded with View.MeasureSpec. |
---|---|
heightMeasureSpec | vertical space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec. |
Here's a summary of some of the other standard methods that the framework calls on views:
這裏總結了框架會在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(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. |
The CustomView sample in the API Demos provides an example of a customized View. The custom View is defined in the LabelView class.
API Demos 裏的CustomView sample 提供了自定義視圖的例子. LabelView類定義了自定義視圖.
The LabelView sample demonstrates a number of different aspects of custom components:
LabelView的例子展現了自定義組件的方方面面:
You can see some sample usages of the LabelView custom View in custom_view_1.xml from the samples. In particular, you can see a mix of both android: namespace parameters and custom app: namespace parameters. These app: parameters are the custom ones that the LabelView recognizes and works with, and are defined in a styleable inner class inside of the samples R resources definition class.
If you don't want to create a completely customized component, but instead are looking to put together a reusable component that consists of a group of existing controls, then creating a Compound Component (or Compound Control) might fit the bill. In a nutshell, this brings together a number of more atomic controls (or views) into a logical group of items that can be treated as a single thing. For example, a Combo Box can be thought of as a combination of a single line EditText field and an adjacent button with an attached PopupList. If you press the button and select something from the list, it populates the EditText field, but the user can also type something directly into the EditText if they prefer.
若是你不想徹底自定義組件, 而是想把幾個已有控件組合成一個可重用的組件, 那麼就能夠建立一個組合控件. 歸納的說, 這就是把一些原子性的控件作合成了一個控件. 好比說, 選擇框能夠認爲是一個單行的et和一個相鄰的帶彈出列表的按鈕的組合.
In Android, there are actually two other Views readily available to do this: Spinner and AutoCompleteTextView, but regardless, the concept of a Combo Box makes an easy-to-understand example.
事實上, 在android中, 還有另外兩個視圖也是這樣: , 可是cb更容易理解.
To create a compound component: 要建立組合控件:
To create a compound component:
To summarize, the use of a Layout as the basis for a Custom Control has a number of advantages, including:
In the API Demos project that comes with the SDK, there are two List examples — Example 4 and Example 6 under Views/Lists demonstrate a SpeechView which extends LinearLayout to make a component for displaying Speech quotes. The corresponding classes in the sample code are List4.java and List6.java.
There is an even easier option for creating a custom View which is useful in certain circumstances. If there is a component that is already very similar to what you want, you can simply extend that component and just override the behavior that you want to change. You can do all of the things you would do with a fully customized component, but by starting with a more specialized class in the View hierarchy, you can also get a lot of behavior for free that probably does exactly what you want.
For example, the SDK includes a NotePad application in the samples. This demonstrates many aspects of using the Android platform, among them is extending an EditText View to make a lined notepad. This is not a perfect example, and the APIs for doing this might change from this early preview, but it does demonstrate the principles.
If you haven't done so already, import the NotePad sample into Eclipse (or just look at the source using the link provided). In particular look at the definition of MyEditText in the NoteEditor.javafile.
Some points to note here
The class is defined with the following line:
public static class MyEditText extends EditText
As always, the super is called first. Furthermore, this is not a default constructor, but a parameterized one. The EditText is created with these parameters when it is inflated from an XML layout file, thus, our constructor needs to both take them and pass them to the superclass constructor as well.
In this example, there is only one method to be overridden: onDraw() — but there could easily be others needed when you create your own custom components.
For the NotePad sample, overriding the onDraw() method allows us to paint the blue lines on the EditText view canvas (the canvas is passed into the overridden onDraw() method). The super.onDraw() method is called before the method ends. The superclass method should be invoked, but in this case, we do it at the end after we have painted the lines we want to include.
We now have our custom component, but how can we use it? In the NotePad example, the custom component is used directly from the declarative layout, so take a look at note_editor.xml in the res/layout folder.
If your custom View component is not defined as an inner class, then you can, alternatively, declare the View component with the XML element name, and exclude the class attribute. For example:
Notice that the MyEditText class is now a separate class file. When the class is nested in the NoteEditor class, this technique will not work.
And that's all there is to it. Admittedly this is a simple case, but that's the point — creating custom components is only as complicated as you need it to be.
A more sophisticated component may override even more on... methods and introduce some of its own helper methods, substantially customizing its properties and behavior. The only limit is your imagination and what you need the component to do.