Steps for developing custom controls:
1. Understanding the working principle of View
2. Write subclasses inherited from View
3. Adding attributes to custom View classes
4. Drawing Control
5. Response to user messages
6. Custom callback function
I. View Structure Principle
The view structure design of Android system also adopts the combination mode, that is, View is the base class of all graphics, and Viewgroup extends View inheritance to view container class.
View defines the basic operation of drawing
The basic operation is completed by three functions: measure(), layout(), draw(), which contains three sub-methods: onMeasure(), onLayout(), and onDraw(). The specific operation is as follows:
1. measure OPERATION
The measure operation is mainly used to calculate the size of the view, that is, the width and length of the view. Defined as final type in view, subclasses are not required to be modified. The measure() function calls the following functions:
(1) onMeasure(), the view size will be determined here, that is to say, measure is only a wrapping of onMeasure. Subclasses can override onMeasure() method to achieve their own way of calculating the view size, and save the calculation results through setMeasured Dimension (width, height).
2. layout operation
The layout operation is used to set the position of the view on the screen. Defined as final type in view, subclasses are not required to be modified. There are two basic operations in the layout() function:
(1) setFrame (l,t,r,b), l,t,r,b) is the specific location of the child view in the parent view. This function is used to save these parameters.
(2) onLayout(), which does nothing in View, provides the function mainly for viewGroup type layout sub-view;
3. draw operation
draw operation uses the parameters obtained from the first two parts to display the view on the screen, which completes the whole drawing work. Subclasses should not modify this method either, because the basic operations of drawing are defined internally:
(1) Drawing background;
(2) If you want the view to display the gradient box, there will be some preparatory work here;
(3) Drawing the view itself, that is, calling onDraw() function. onDraw() is an empty function in view, that is to say, a specific view must override the function to achieve its own display (for example, TextView implements the process of drawing text here). ViewGroup does not need to implement this function, because as a container it is "content-free" and contains multiple sub-views, and sub-views have implemented their own drawing methods, so it only needs to tell sub-views to draw themselves, that is, the following dispatchDraw() method;
(4) Draw a sub-view, dispatchDraw() function. In view, this is an empty function. The specific view does not need to implement this method. It is specially prepared for the container class, that is, the container class must implement this method.
(5) If necessary (the application calls setVertical Fading Edge or setHorizontal Fading Edge), start drawing the gradient box;
(6) Drawing scrollbars;
As you can see from the above, a custom View requires at least two methods to override onMeasure() and onDraw().
2. Construction Method of View Class
Three main ways to create custom controls:
1) Inheritance of existing controls to achieve custom controls: mainly when the control to be implemented and existing controls are similar in many ways, through the expansion of existing controls to meet the requirements.
2) Custom control can be realized by inheriting a layout file. Generally speaking, it can be realized by this way when composing control.
Note that the onDraw method is not used at this time. In the construction advertisement, the layout file of the custom control is loaded by inflater, and then the graphical interface of the custom control is loaded by addView.
3) Custom control is realized by inheriting view class, and component interface is drawn by GDI. Generally, it can not be realized by the above two ways.
3. Two ways to add attributes to custom View:
1) Defined in the View class. AttributeSet is introduced into the constructor to find the attribute name of the XML layout, and then to find the resource ID that it refers to.
Case: Implement a picture with text (picture and text are redrawn by onDraw method)
public class MyView extends View {
private String mtext;
private int msrc;
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
int resourceId = 0;
int textId = attrs.getAttributeResourceValue(null, "Text",0);
int srcId = attrs.getAttributeResourceValue(null, "Src", 0);
mtext = context.getResources().getText(textId).toString();
msrc = srcId;
}
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.RED);
InputStream is = getResources().openRawResource(msrc);
Bitmap mBitmap = BitmapFactory.decodeStream(is);
int bh = mBitmap.getHeight();
int bw = mBitmap.getWidth();
canvas.drawBitmap(mBitmap, 0,0, paint);
//canvas.drawCircle(40, 90, 15, paint);
canvas.drawText(mtext, bw/2, 30, paint);
}
}
Layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.myimageview2.MyView
android:id="@+id/myView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Text="@string/hello_world"
Src="@drawable/xh"/>
</LinearLayout>
Attribute Text, Src is read in the constructor of the custom View class.
Design sketch:
2) Register attributes for View through XML. Like Android's standard attribute notation.
Case: Implement an ImageView with text description (Image View + TextView combination, text description, location can be set in the layout file)
public class MyImageView extends LinearLayout {
public MyImageView(Context context) {
super(context);
}
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
int resourceId = -1;
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.MyImageView);
ImageView iv = new ImageView(context);
TextView tv = new TextView(context);
int N = typedArray.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.MyImageView_Oriental:
resourceId = typedArray.getInt(
R.styleable.MyImageView_Oriental, 0);
this.setOrientation(resourceId == 1 ? LinearLayout.HORIZONTAL
: LinearLayout.VERTICAL);
break;
case R.styleable.MyImageView_Text:
resourceId = typedArray.getResourceId(
R.styleable.MyImageView_Text, 0);
tv.setText(resourceId > 0 ? typedArray.getResources().getText(
resourceId) : typedArray
.getString(R.styleable.MyImageView_Text));
break;
case R.styleable.MyImageView_Src:
resourceId = typedArray.getResourceId(
R.styleable.MyImageView_Src, 0);
iv.setImageResource(resourceId > 0 ?resourceId:R.drawable.ic_launcher);
break;
}
}
addView(iv);
addView(tv);
typedArray.recycle();
}
}
Attribute declaration is made in attrs.xml, and the files are placed in the values directory
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyImageView">
<attr name="Text" format="reference|string"></attr>
<attr name="Oriental" >
<enum name="Horizontal" value="1"></enum>
<enum name="Vertical" value="0"></enum>
</attr>
<attr name="Src" format="reference|integer"></attr>
</declare-styleable>
</resources>
MainActivity layout file: Define the namespace xmlns:uview= "http://schemas.android.com/apk/res/com.example.myimageview2" (com.example.myimageview2 is the package name you defined in manifest)
Then you can use it as if you were using the properties of the system: uview:Oriental= "Vertical"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:uview="http://schemas.android.com/apk/res/com.example.myimageview2"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<com.example.myimageview2.MyImageView
android:id="@+id/myImageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
uview:Text="This is a picture description."
uview:Src="@drawable/tw"
uview:Oriental="Vertical">
</com.example.myimageview2.MyImageView>
</LinearLayout>
IV. Control Drawing onDraw()
5. The Method of Customizing View
onFinishInflate() callback method, which is called when the application loads the component from XML and uses it to build the interface
onMeasure() detects the size of View components and their subcomponents
onLayout() When the component needs to allocate the location and size of its subcomponents
onSizeChange() When the size of the component is changed
onDraw() When the component is going to draw its content
onKeyDown When a keyboard is pressed
onKeyUp When a keyboard is loosened
OnTrackball Event When Trackball Event Occurs
onTouchEvent When a touch screen event occurs
OnWindows FocusChanged (boolean) when the component gets and loses focus
onAtrrachedToWindow() When the component is placed in a window
onDetachedFromWindow() Method triggered when the component is separated from a window
OnWindows Visibility Changed (int): Method triggered when the visibility of the window containing the component changes