This is the content in the Android UI fundamentals
Create a custom view
Creating a custom UI component first inherits a view class.
Start by creating a simple custom view that shows a cross line.
The first thing to do is to create a Crossview class that inherits from view.
publicCrossView(Context context, AttributeSet attrs) { super(context, attrs); }
The second parameter of the constructor is used to pass the XML parameter, which is discussed later. Next we will rewrite the two basic methods: onMeasure
and onDraw
.
Onmeasure
The system invokes the onMeasure
method to determine the dimensions of the view and its child views. Its two parameters are of the type int
, but the two parameters are not ordinary numbers, but two MeasureSpec
, MeasureSpec
is a pattern and an integral size value of the combination, as an integer to achieve. There are several scenarios where the pattern value is:
Mode |
explain |
UNSPECIFIED |
The parent view does not have any restrictions on this view, it can be any size |
At_most |
The view can be any size less than or equal to the MeasureSpec medium size |
Exactly |
Parent view requires that the view must be MeasureSpec of the specified size |
When you create a custom view and override the onMeasure
method, you must handle each case correctly, get the appropriate dimensions, and then you must call the method in, the onMeasure
setMeasureDimensions
parameter is the size you decide, and throws an exception if you do not call.
The following is the overridden onMeasure
method code.
@Override protectedvoidonMeasure(intint heightMeasureSpec) { setMeasuredDimension(calculateMeasure(widthMeasureSpec), calculateMeasure(heightMeasureSpec)); }
Note that the calculateMeasure
methods are defined by ourselves, so let's do this.
Let's first define a default size of 100, which is DP ( I'm not sure ifit's DP).
privatestaticfinalint100;
Multiply the pixel density of the device to get the desired pixel value for the actual display.
int result = (int) (DEFAULT_SIZE * getResources().getDisplayMetrics().density);
Then we need to get the pattern and size from the Measurespec.
int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);
The next step specMode
is to determine result
what the value should be.
MeasureSpec.UNSPECIFIED
Now that the parent control has no requirement for the size of the custom view, I will result in the default size, which means
int result = (int) (DEFAULT_SIZE * getResources().getDisplayMetrics().density);
MeasureSpec.AT_MOST
At this point, the parent control does not think it can exceed the specified size value at this point, so we select the smallest of the specified value and the default value, which is legal in either case.
result = Math.min(specSize, result);
MeasureSpec.EXACTLY
At this point the parent control requires that the child view must be a given dimension, so we let result
it be equal to it.
result = specSize;
To synthesize the above discussion, finally our method code is as follows:
Private Static Final intDefault_size = -;Private int calculatemeasure(intMEASURESPEC) {intresult = (int) (Default_size * getresources (). Getdisplaymetrics (). density);intSpecmode = Measurespec.getmode (Measurespec);intSpecsize = Measurespec.getsize (Measurespec);if(Specmode = = measurespec.exactly) {result = Specsize; }Else if(Specmode = = measurespec.at_most) {result = Math.min (specsize, result); }returnResult }
OnDraw
The method is called when the view should draw its contents. onDraw
before we rewrite it, we'll first create an Paint
object that handles things like color and text size.
CrossView
to create an object by using the constructor Paint
private Paint mPaint; publicCrossView(Context context, AttributeSet attrs) { super(context, attrs); new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(0xffff0000); }
The above code creates a new Paint
object and sets anti-aliasing and color.
Next rewrite the onDraw
method, the template is as follows, canvas.save()
and canvas.restore()
I will not explain it without affecting the subsequent understanding.
@Override protectedvoidonDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); // code goes here canvas.restore(); }
We scaled the canvas based on the dimensions of the view so that we could use a floating-point number from 0 to 1 as the coordinates when we drew the line
Private Static Final float[] mpoints = {0.5F0F0.5F1F0F0.5F1F0.5f};@Override protected void OnDraw(Canvas canvas) {Super. OnDraw (canvas); Canvas.save (); Canvas.scale (GetWidth (), getheight ()); Canvas.drawlines (mpoints, mpaint); Canvas.restore (); }
We add our own custom controls to the activity's XML
<LinearLayout xmlns:android="Http://schemas.android.com/apk/res/android" Xmlns:tools="Http://schemas.android.com/tools" Android:layout_width="Match_parent" Android:layout_height="Match_parent" android:orientation="Vertical" Android:paddingleft="@dimen/activity_horizontal_margin" Android:paddingright="@dimen/activity_horizontal_margin" Android:paddingtop="@dimen/activity_vertical_margin" Android:paddingbottom="@dimen/activity_vertical_margin" Tools:context=". Mainactivity "> <com.shaw.uitest.CrossViewandroid:layout_width="Wrap_content" Android:layout_height="wrap_content" /> <com.shaw.uitest.CrossViewandroid:layout_margintop="10DP"android:layout_ Width="Wrap_content"android:layout_height="wrap_content" /> </linearlayout>
Run it and you can see the beginning of the article screen.
To add custom properties to a custom view
With a custom view, we want it to be configured with custom XML properties, to do this, you need to declare the property, then add a new namespace to the XML layout, and finally process the AttributeSet object that is passed to the custom view constructor.
declaring properties
In the res/values/directory, create a file with a attrs.xml (which can be a different name) and add the following to it:
<?xml version= "1.0" encoding= "Utf-8", <resources ; <declare-styleable name = "cross" ; <attr Span class= "Hljs-attribute" >name = "Android:color" /> <attr name = "rotation" format = "string" /> </declare-styleable ; </resources ;
declare-styleable
element has a name attribute, which is used to reference custom properties in code, each custom attribute is declared with an attr
element that attr
has name and format two properties, name for reference, format for its data type, If you use the default system properties, you do not need to define format, and if you try to define a different format for an existing property, the project cannot build. Declarations in the outer layer attr
can be declare-styleable
reused, just as with system properties, such as:
<?xml version= "1.0" encoding= "Utf-8"?><resources> <attr name="Test" format="string" /> <declare-styleable name="foo"> <attr name="Test" /> </declare-styleable> <declare-styleable name="Bar"> <attr name="Test" /> </declare-styleable></Resources>
You can also create custom values for attributes, such as
<attr name="enum_attr"> <enum name="value1" value="1" /> <enum name="value2" value="2" /></attr><attr name="flag_attr"> <flag Name="Flag1" value="0x01" /> <flag Name="Flag2" value="0x02" /></attr>
enum
and flag
all requirements are integers. The difference is that flag
it can be used |
to splice. For example android:gravity
, the value is flag.
Using attributes in XML
To use the new properties in our XML, you must first declare the namespace for the view. In fact, we often see namespace's statement, such as we often see in the activity's XML file
xmlns:android="http://schemas.android.com/apk/res/android"
This namespace declares that all android
attributes beginning with the keyword can be found in the Android package. To use a custom attribute, you need to declare a new namespace with the new package name, and add a new namespace for the Crossview property below. and add the relevant XML configuration to the custom view:
<LinearLayout xmlns:android="Http://schemas.android.com/apk/res/android" Xmlns:tools="Http://schemas.android.com/tools" Xmlns:crossview="Http://schemas.android.com/apk/res-auto" Android:layout_width="Match_parent" Android:layout_height="Match_parent" android:orientation="Vertical" Android:paddingleft="@dimen/activity_horizontal_margin" Android:paddingright="@dimen/activity_horizontal_margin" Android:paddingtop="@dimen/activity_vertical_margin" Android:paddingbottom="@dimen/activity_vertical_margin" Tools:context=". Mainactivity "> <com.shaw.uitest.CrossViewandroid:layout_width="Wrap_content"android: Layout_height="Wrap_content"crossview:rotation="android:color" = "#ff0000ff"/> <com.shaw.uitest.CrossViewandroid:layout_margintop="10DP"android:layout_ Width="Wrap_content"android:layout_height="Wrap_content"crossview:rotation ="android:color" = "#ff00ff00"/> </linearlayout>
The above declares that all crossview
attributes that begin with (the name can be used in a different way) can be found in res. This is the wording of the Gradle request.
Using XML attributes in your code
CrossView
An object is passed in the constructor AttributeSet
that we can use to get the properties declared in the XML layout.
Update CrossView
The constructor and add the corresponding function and member variables:
Private floatmrotation; Public Crossview(context context, AttributeSet attrs) {Super(context, attrs); Mpaint =NewPaint (); Mpaint.setantialias (true); TypedArray arr = GetContext (). Obtainstyledattributes (Attrs, R.styleable.cross);intcolor = Arr.getcolor (R.styleable.cross_android_color, Color.Black);floatrotation = Arr.getfloat (r.styleable.cross_rotation,0f); Arr.recycle (); SetColor (color); Setrotation (rotation); } Public void SetColor(intColor) {Mpaint.setcolor (color); } Public void setrotation(floatdegree) {mrotation = degree; }
Updated code at the same time onDraw
@Override protectedvoidonDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); canvas.scale(getWidth(), getHeight()); 0.50.5f); canvas.drawLines(mPoints, mPaint); canvas.restore(); }
Our center of rotation is the center of the canvas, not the upper-left corner.
Now run this program, as follows:
Android Custom view with custom properties