The custom View
In the process of actual use, we often receive such requirements, such as circular pedometer, bar chart, circular avatar, etc. The common idea is to Google github to see if there is a control we need, but what if there is no such control online? In this case, we often need to customize the View to meet the requirements.
Let’s start down the path of custom controls
About custom control, general hui follows a few routines
- Start by overriding the onMeasure() method
- Next, rewrite the onDraw() method
- Known as onMeasure()
Method is used to remeasure and resize the control. We know that the control is resized using the width and height tags. There are usually three types of assignment:
- First, assign a value directly, such as the exact size of 15dp
- Secondly match_parent
- And of course wrap_parent
If you already have these properties, why rewrite the onMeasure? Just call the View method. But imagine if you designed a circular control, and the user set wrap_parent to width and height, and sent you a rectangular image. You have to “square” ah. So you need to rewrite the onMeasure method to set its width and height equal.
So how do you override the onMeasure() method?
First type the onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Copy the code
At this time, you will wonder, it is clearly redraw the size, so give me the width and height of the line? Int widthMeasureSpec, int heightMeasureSpec, int widthMeasureSpec It’s easy to understand. We all know that data in a computer is stored in binary. And, as I said before, there are three ways to assign the size of a View, so how many bits do you need to store binary numbers on a computer? The answer is clear -> two. And as you can see, both of these arguments are int. Int data is stored in 32 bits on a computer. So Google was smart enough to divide the 30 into two parts. The first two bits store the type, and the next 28 bits store the size.
Start overwriting the onMeasure() method
First of all, we need to determine the type and then calculate the size, so let’s write a method to calculate and return the size.
Measurement model | Said mean |
---|---|
UNSPECIFIED | The parent container has no restrictions on the current View, which can take any size |
EXACTLY | The current size is the size that the current View should take |
AT_MOST | The current size is the maximum size that the current View can take |
private int getMySize(int defaultSize, int measureSpec) {
// set a defaultSize defaultSize
int mySize = defaultSize;
// Get the type
int mode = MeasureSpec.getMode(measureSpec);
// Get the size
int size = MeasureSpec.getSize(measureSpec);
switch (mode) {
case MeasureSpec.UNSPECIFIED: {// If no size is specified, the default size is used
mySize = defaultSize;
break;
}
case MeasureSpec.AT_MOST: {// If the measurement mode is size
// We will take the maximum size, you can take other values
mySize = size;
break;
}
case MeasureSpec.EXACTLY: {// If the size is fixed, do not change it
mySize = size;
break; }}return mySize;
}
Copy the code
Then we call it from onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Get the length and width respectively
int width = getMySize(100, widthMeasureSpec);
int height = getMySize(100, heightMeasureSpec);
// Here I have a circular control example
// So the length and width are equal
if (width < height) {
height = width;
} else {
width = height;
}
// Set the size
setMeasuredDimension(width, height);
}
Copy the code
Apply trial effects in XML
<?xml version="1.0" encoding="utf-8"? >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context=".activities.MainActivity">
<com.entry.android_view_user_defined_first.views.MyView
android:layout_width="100dp"
android:layout_height="100dp"
app:default_size="@drawable/ic_launcher_background"/>
</LinearLayout>
Copy the code
So now that I’ve redrawn it, let’s run it for a second
We were shocked, said the good control? ! Don’t worry, we haven’t painted it yet, so it’s transparent. So I’m going to rewrite the onDraw() method, in the onDraw() method. (I’m going to create a new object in the onDraw() method for the sake of writing.
We draw the graphics on canvas.
@Override
protected void onDraw(Canvas canvas) {
// Call the parent View's onDraw function, because the View class does something for us
// Basic drawing functions, such as drawing background colors, background images, etc
super.onDraw(canvas);
int r = getMeasuredWidth() / 2;// It can also be getMeasuredHeight()/2, in this case we have set the width and height to be equal
Log.d(TAG, r + "");
// The abscissa of the center of the circle is the starting left position of the current View + the radius
int centerX = r;
// The vertical coordinate of the center of the circle is the top starting position of the current View + the radius
int centerY = r;
// Define a gray brush and draw a circle
Paint bacPaint = new Paint();
bacPaint.setColor(Color.GRAY);
canvas.drawCircle(centerX, centerY, r, bacPaint);
// Define the blue brush and draw the text
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(60);
canvas.drawText("Greater fool".0, r+paint.getTextSize()/2, paint);
}
Copy the code
Run the
And you’re done! But thinking may find: use this way, we can only use the parent class controls the attributes, but sometimes we need more features, such as: image controls need to change the transparency, card controls need to set the shadow value and so on, then the properties of the parent class controls obviously not enough, then we will begin to implement a custom layout.
Custom layout attributes XML attributes
start
Since custom layout properties generally only need to be operated on onDraw(). So onMeasure() and other methods to rewrite I will not be verbose, here I am going to inherit the word view to implement a textView-like control.
First, let’s now add a custom layout property to the res/values/styles file.
<resources>
<! -- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<! -- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<! Define the attribute collection name -->
<declare-styleable name="MyView">
<! -- we define default_size property as index type pixel dp etc -->
<attr name="text_size" format="dimension"/>
<attr name="text_color" format="color"/>
<attr name="text_text" format="string"/>
</declare-styleable>
</resources>
Copy the code
What do these labels mean?
First of all:
MyView is the name of a custom layout properties, is also the label is also the entrance, in ontouch, use the context. ObtainStyledAttributes (attrs, R.s tyleable. MyView); Gets all the children of the custom layout property.
Second:
The name in attr is the name of your property, such as text_size, text_color, and text_text.
<com.entry.android_view_user_defined_first.views.MyView
android:layout_width="100dp"
android:layout_height="100dp"
app:text_text="hello world"
app:text_size="20sp"
app:text_color="@color/colorAccent"/>
Copy the code
Finally:
The format TAB, the format tag specifies the data type, specific can see this article, I will not repeat here – > blog.csdn.net/pgalxx/arti…
Parsing and referencing
We defined the property and assigned it a value in the layout, so how do we get its actual value in the custom control? Let’s first write down the constructor, where we get the size of these values:
private int textSize;
private String textText;
private int textColor;
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);
textSize = array.getDimensionPixelSize(R.styleable.MyView_text_size, 15);
textText = array.getString(R.styleable.MyView_text_text);
textColor = array.getColor(R.styleable.MyView_text_color,Color.BLACK);
array.recycle();
}
Copy the code
- Creates a TypeArray object that stores the values passed in for custom properties. The obtainStyledAttributes method takes two more parameters. The second parameter is the tag in the styles.xml file, the tag of the attribute collection, which is called R.styleable+name in the R file
- We then get the value passed in, based on the array object. The first argument is the property in the property collection, R file name: r.styleable + property collection name + underscore + property name, and the second argument is the default value if the property is not set
- Finally, remember to recycle the TypedArray object
Let’s rewrite the onDraw() method.
Since in the constructor we already have the basic values, so in onDraw() we can just draw these things and go straight to the code:
@Override
protected void onDraw(Canvas canvas) {
// Call the parent View's onDraw function, because the View class does something for us
// Basic drawing functions, such as drawing background colors, background images, etc
super.onDraw(canvas);
int r = getMeasuredWidth() / 2;// It can also be getMeasuredHeight()/2, in this case we have set the width and height to be equal
// The abscissa of the center of the circle is the starting left position of the current View + the radius
int centerX = r;
// The vertical coordinate of the center of the circle is the top starting position of the current View + the radius
int centerY = r;
// Define a gray brush and draw a circle
Paint bacPaint = new Paint();
bacPaint.setColor(Color.GRAY);
canvas.drawCircle(centerX, centerY, r, bacPaint);
// Define the blue brush and draw the text
Paint paint = new Paint();
paint.setColor(textColor);
paint.setTextSize(textSize);
canvas.drawText(textText, 0, r+paint.getTextSize()/2, paint);
}
Copy the code
Run it: Perfect!
Write in the last
This article is a summary of my personal learning process. If you find any mistakes, please point them out in the comments section. Thank you 🙏