preface
- Custom Views are a must for Android developers
- Today, I’m going to teach you how to write a custom View by hand, and clarify all the points that a custom View should pay attention to
directory
1. Customize the classification of views
Custom views are divided into two categories, as shown below:
2. Detailed introduction & Application scenarios
The types and usage scenarios of user-defined views are as follows:
3. Use caution points
When using a custom View, there are a lot of attention points (pits), I hope you should pay attention to:
3.1 Special attributes are supported
- Support wrap_content
The wrap_content attribute is invalidated if it is not treated specially in onMeasure ()
Why doesn’t your custom View wrap_content work?
- Supports padding & Margin
If not, the padding and margin properties (in the case of ViewGroup) are invalidated
- For controls that inherit from a View, the padding is handled in draw()
- For controls that inherit ViewGroup, padding and margin directly affect measure and layout procedures
3.2 Multithreading should use POST directly
View internal itself provides a post series of methods, can completely replace the role of the Handler, use more convenient, direct.
3.3 Avoiding Memory Leaks
The View contains threads or animations: When the View exits or is not visible, remember to stop the View contains threads and animations in time, otherwise it will cause memory leaks.
How to start or stop a thread/animation:
- Start thread/animation: use
The onAttachedToWindow ()
Because this method is called when the Activity containing the View is started- Stop thread/animation: use
The onDetachedFromWindow ()
Because this method is called when the Activity containing the View exits or the current View is removed
3.4 Handle sliding conflicts well
When a View has sliding nesting, it must handle sliding conflicts, otherwise it will seriously affect the display of the View.
4. Concrete examples
Next, I will use the most commonly used inherited View in the custom View to illustrate the specific application of custom View and need to pay attention to the point
4.1 Introduction to VIew Inheritance
In the following examples, I will explain:
- How to implement a basic custom View
- How to support wrap_content & PADDING
- How to provide custom properties for custom Views (colors, etc.)
- Example: Draw a solid circle
4.2 Procedure
- Create custom View class (inherit View class)
- Layout file to add custom View components and display
- Note the Settings (support wrap_content & padding attributes, custom attributes, etc.)
Step 1: Create a custom View class (inheriting the View class)
/** * circleView.java * uses the following methods to draw the contents of a custom View: */ public class extends View {Paint mPaint1; Public CircleView(Context Context){super(Context); public CircleView(Context Context); // Initialize the brush operation in the constructor init(); } // If the View is declared in.xml, Public CircleView(Context Context,AttributeSet attrs){super(Context, attrs); init(); } public CircleView(Context Context,AttributeSet attrs,int defStyleAttr) public CircleView(Context Context,AttributeSet attrs,int defStyleAttr ){ super(context, attrs,defStyleAttr); init(); } public CircleView(Context Context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } private void init() {// Create a brush mPaint1 = new Paint (); // Set the pen Color to BLUE mpaint1.setcolor (color.blue); // Set the brush width to 10px mpaint1.setstrokeWidth (5f); // Set the brush mode to FILL mpaint1.setstyle (paint.style.fill); } @override protected void onDraw(Canvas) {super.ondraw (Canvas); Int width = getWidth(); int height = getHeight(); Int r = math.min (width, height)/2; int r = math.min (width, height)/2; / / draw a circle (blue) / / center = controls the center of the radius = width, high minimum value from canvas. Methods like drawCircle (width / 2, height / 2, r, mPaint1); }}Copy the code
Special attention:
- View constructor (); View constructor ()
Understand View constructors
- For why Draw content in clone onDraw() to achieve, specifically see the article I wrote: custom View Draw process – the most easy to understand custom View principle series (4)
Step 2: Add custom View class components and display to the layout file
/**
* 1. 在布局文件中添加自定义View类的组件
* activity_main.xml
*/
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context="scut.carson_ho.diy_view.MainActivity">
<!-- 注意添加自定义View组件的标签名:包名 + 自定义View类名-->
<!-- 控件背景设置为黑色-->
<scut.carson_ho.diy_view.CircleView
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#000000"
</RelativeLayout>
/**
* 2. 设置显示
* MainActivity.java
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Copy the code
At this point, a basic custom View has been implemented, as shown below.
Step 3: Note the point Settings (support wrap_content & PADDING attributes, custom attributes, etc.)
Let’s move on to the custom View property customization issue:
- How do I manually support the WRAP_content attribute
- How do I manually support the PADDING property
- How to provide custom properties for custom Views (colors, etc.)
A. Manually support the WRAP_content attribute
Let’s look at the difference between wrap_content and match_parent
Android :layout_width="wrap_content" // View width="match_parent" android:layout_width="match_parent" // Before Android API 8 called "fill_parent"Copy the code
The wrap_content attribute does not take effect unless you manually set it to support the wrap_content attribute.
See my article: Why doesn’t your custom View wrap_content work?
B. Supports the padding property
Padding property: Used to set the margin of the control content relative to the control edge;
Margin property (also called: margin) : control edge relative to the parent control margin (parent control), the specific difference is as follows:
The padding property does not take effect in a custom View unless it is manually configured to support the padding property.
<? The XML version = "1.0" encoding = "utf-8"? > <RelativeLayout 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" tools:context="scut.carson_ho.diy_view.MainActivity"> <scut.carson_ho.diy_view.CircleView Android :layout_width="match_parent" Android :layout_height="match_parent" </RelativeLayout> </RelativeLayout>Copy the code
The solution
The padding property value passed in is considered when drawing (four directions).
Set onDraw () in the clone of the custom View class
/** * Override protected void onDraw(Canvas) {super.ondraw (Canvas); Final int paddingLeft = getPaddingLeft(); final int paddingRight = getPaddingRight(); final int paddingTop = getPaddingTop(); final int paddingBottom = getPaddingBottom(); Int width = getWidth() -paddingleft-paddingright; int height = getHeight() - paddingTop - paddingBottom ; Int r = math.min (width, height)/2; int r = math.min (width, height)/2; DrawCircle (paddingLeft+width/2,paddingTop+height/2,r,mPaint1); paddingLeft+width/2, r,mPaint1); }Copy the code
C. Provide custom attributes
In addition to the usual system properties that start with Android: (as shown below), custom views in many scenarios also require properties that the system does not have, namely custom properties.
Android :layout_width="match_parent" Android :layout_height="match_parent" Android :background="#000000" android:padding="30dp"Copy the code
The steps to implement custom attributes are as follows:
- Create an XML file for custom attributes in the values directory
- Resolve the values of custom properties in the constructor of a custom View
- Use custom properties in layout files
I’ll explain each step in detail below
Step 1: Create an XML file for custom attributes in the values directory
/** * attrs_circle_view.xml */ <? The XML version = "1.0" encoding = "utf-8"? > <resources> // Custom attributes collection :CircleView // Under this collection, set different custom attributes < declaration-styleable name="CircleView"> // Under the attr tag, set the required custom attributes // <attr name="circle_color" format="color"/> </declare-styleable> </resources>Copy the code
For custom attribute types & the format is as follows:
< -- 1. Reference: Use a resource ID --> <declare-styleable name=" name "> <attr name="background" format="reference" /> </declare-styleable> // Use format // 1. Java code private int ResID; private Drawable ResDraw; ResID = typedArray.getResourceId(R.styleable.SuperEditText_background, R.drawable.background); ResDraw = getResources().getDrawable(ResID); <ImageView Android :layout_width="42dip" Android :layout_height="42dip" App :background="@drawable "/> <-- 2. Color value --> <declare-styleable name=" name "> <attr name="textColor" format="color" /> </declare-styleable> // Format using <TextView Android :layout_width="42dip" Android :layout_height="42dip" Android :textColor="#00FF00" /> <-- 3. Boolean: Boolean --> <declare-styleable name=" name "> <attr name="focusable" format=" Boolean "/> </declare-styleable> // Format uses <Button Android :focusable="true" /> <-- 4.dimension: <declare-styleable name=" name "> <attr name="layout_width" format="dimension" /> </declare-styleable> <Button android:layout_width="42dip" Android :layout_height="42dip" /> <-- 5. float: <declare-styleable name="AlphaAnimation"> <attr name="fromAlpha" format="float" /> <attr name="toAlpha" Format ="float" /> </declare-styleable> // Use <alpha Android :fromAlpha="1.0" Android :toAlpha="0.7" /> <-- 6. Integer: Integer value --> <declare-styleable name="AnimatedRotateDrawable"> <attr name="frameDuration" format="integer" /> <attr Name ="framesCount" format="integer" /> </declare-styleable> // The format is <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" android:frameDuration="100" android:framesCount="12" /> <-- 7. The string: String --> <declare-styleable name="MapView"> <attr name="apiKey" format="string" /> </declare-styleable> // Format used < com. Google. Android. Maps. MapView the android: apiKey = "0 jokq80od1jl9c6haja99ugxcris2cgjko_bc_g" / > < -- 8. Fraction: Percentage --> <declare-styleable name="RotateDrawable"> <attr name="pivotX" format="fraction" /> <attr name="pivotY" Format = "fraction" / > < / declare - styleable > / / format using the < rotate XMLNS: android = "http://schemas.android.com/apk/res/android" <-- 9. Enum :pivotX=" pivotY" Enumeration value --> <declare-styleable name=" name "> <attr name="orientation"> <enum name="horizontal" value="0" /> <enum Name ="vertical" value="1" /> </attr> </declare-styleable> <LinearLayout Android :layout_width="fill_parent" Android :layout_height="fill_parent" /> <-- 10. Flag: Bit or operation --> <declare- Styleable Name =" Name "> <attr name="windowSoftInputMode"> <flag name="stateUnspecified" value="0" /> <flag name="stateUnchanged" value="1" /> <flag name="stateHidden" value="2" /> <flag name="stateAlwaysHidden" value="3" /> <flag name="stateVisible" value="4" /> <flag name="stateAlwaysVisible" value="5" /> <flag name="adjustUnspecified" value="0x00" /> <flag name="adjustResize" value="0x10" /> <flag name="adjustPan" value="0x20" /> <flag AdjustNothing ="adjustNothing" value="0x30" /> </attr> </declare- Styleable >, Android: label = "@ string/app_name" android: windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category The android: name = "android. Intent. The category. The LAUNCHER" / > < / intent - filter > < / activity > < - pay special attention to: Attribute definitions can be specified when a variety of types of values - > < declare - styleable name = "name" > < attr name = "background" format = "reference | color" / > </declare-styleable> // use <ImageView Android :layout_width=" 42DIP "Android :layout_height="42dip" Android: background = "@ drawable/picture ID | # 00 ff00" / >Copy the code
Step 2: Parse the values of the custom properties in the constructor of the custom View
Public CircleView(Context Context, AttributeSet attrs) {this(Context, AttributeSet attrs); attrs,0); // super(context,attrs); init(); public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); / / load the custom attribute set CircleView TypedArray a = context. ObtainStyledAttributes (attrs, R.s tyleable. CircleView); CircleView_circle_color // Pass the parsed attribute into the circle pen color variable (essentially the custom circle pen color) // The second parameter is the default setting Color(that is, if circle_color is not specified) mColor = A.gottcolor (r.starleable. CircleView_circle_color, color.red); // Release resources after parsing a.ricycle (); init(); }Copy the code
Step 3: Use custom properties in the layout file
/** * activity_main.xml */ <? The XML version = "1.0" encoding = "utf-8"? > <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <! - you must add schemas statement to use custom attributes - > 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" tools:context="scut.carson_ho.diy_view.MainActivity" > <! Package name + custom View class name --> <! <scut.carson_ho.diy_view.CircleView Android :layout_width="wrap_content" android:layout_height="wrap_content" android:background="#000000" android:padding="30dp" <! Circle_color ="#FF4081" /> </RelativeLayout>Copy the code
At this point, a more formal custom View is complete.
Complete code download
Carson_Ho github: the specific application of custom View
5. To summarize
- This article has carried on the comprehensive analysis to the custom View concrete application and the attention point
- For more on Android custom Views, stay tuned: Carson takes you to Android
Welcome to attentionCarson takes you through Android blogs
Blog link: juejin.cn/user/252413…