1. Introduction
In development, the View plays a very important role, it is directly presented to the user, so it makes sense to show the user a beautiful and efficient View. The Android system provides a variety of view components, such as TextView, ImageView, Button, etc. It also provides a combination of RelativeLayout, LinearLayout, FrameLayout and other components, using these components to achieve a good view effect. Sometimes, however, we need to implement a more personalized and has the characteristics of visual effect, use the system to provide the component is difficult to meet this need, a custom View will come in handy at this time, this article mainly analyzes the inheritance the View class implements a custom View to process, to create a custom View to meet certain requirements.
2. Customize the View flow
2.1 Create class and inherit View
This example creates a class called CustomView and needs to implement its constructor. In order to use the attributes of the CustomView in the XML layout, you need to provide at least one constructor containing Context and AttributeSet parameters, as shown below:
public class CustomView extends View {
public CustomView(Context context) {
this(context,null);
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs); init(context, attrs); }}Copy the code
2.2 Providing custom Attributes
In order to set the attributes of the View component in the XML layout as provided by the system, you need to provide custom View attributes. Create a new attrs. XML file in the RES /values path and edit the attribute name and format in the file. Boolean value, color value, dimension value, enum value, flags value, float value, fraction value, integer value, reference resource ID. The following is an example:
<resources>
<declare-styleable name="CustomView">
<attr name="textContent" format="string|reference" />
<attr name="textSize" format="dimension|reference" />
<attr name="textColor" format="color|reference" />
<attr name="circleColor" format="color|reference" />
</declare-styleable>
</resources>
Copy the code
Using custom attributes in the XML layout, you need to provide namespace, a namespace format, such as: XMLNS: [alias] = “schemas.android.com/apk/res/ [pa… name], there is also a common namespace: XMLNS: app = “schemas.android.com/apk/res-aut…
<? xml version="1.0" encoding="utf-8"? > <RelativeLayout 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"
tools:context=".MainActivity">
<com.android.viewdemo.CustomView
android:id="@+id/cv_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
app:textContent="Android"
app:textSize="50sp"
app:textColor="@color/teal_200"
app:circleColor="@color/purple_500"/>
</RelativeLayout>
Copy the code
After the set property values in the XML layout, then there is the custom to obtain these attributes in the View of value, called the context. ObtainStyledAttributes () returns TypedArray array, TypedArray call the corresponding method gets an attribute value, If typeDarray.getString (r.tyleable.customView_textContent) is called to retrieve a string, the typedArray object calls TypeDarray.recycle () to recycle resources, as shown in the following example:
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView, 0.0);
try {
textContent = typedArray.getString(R.styleable.CustomView_textContent);
textSize = typedArray.getDimensionPixelSize(R.styleable.CustomView_textSize, 50);
textColor = typedArray.getColor(R.styleable.CustomView_textColor, 0);
circleColor = typedArray.getColor(R.styleable.CustomView_circleColor, 0);
} finally {
typedArray.recycle();
}
Copy the code
2.3 Provide getter and setter methods for properties
Not only can the properties of a custom View be set in the XML layout, but getter and setter methods should also be provided to change the properties in the code. When setter methods are called to change the properties, invalidate() is called to invalidate the current View when the View’s appearance changes. The onDraw() method is then triggered to redraw the View. If the size or shape of the View changes, requestLayout() is called to request a new layout. Note that the invalidate() method is called in the UI thread and postInvalidate() is called in the non-UI thread. The following is an example:
public void setTextContent(String textContent) {
this.textContent = textContent;
// called in the UI thread when the appearance changes
invalidate();
// Change the size and shape of the call, not necessarily not called to improve performance
requestLayout();
}
Copy the code
2.4 Override the onMeasure() method
This method is used to control the size of the View, so that the parent View knows the desired size of the View. After calculating the desired size of the View, call setMeasuredDimension() and pass in the calculated width and height, as shown in the following example.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
if (modeWidth == MeasureSpec.EXACTLY) {
width = sizeWidth;
} else if (modeWidth == MeasureSpec.AT_MOST) {
width = Math.min(defaultWidth, sizeWidth);
} else {
width = defaultWidth;
}
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
if (modeHeight == MeasureSpec.EXACTLY) {
height = sizeHeight;
} else if (modeHeight == MeasureSpec.AT_MOST) {
height = Math.min(defaultHeight, sizeHeight);
} else {
height = defaultHeight;
}
setMeasuredDimension(width, height);
}
Copy the code
2.5 Override onSizeChanged() method
The onSizeChanged() method is called when the View’s size changes. The onSizeChanged() method takes four parameters: new width, new height, old width, old height, which is critical to drawing the View properly. Parameters such as position and size required for drawing need to be calculated in this method, as shown in the following example:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
textY = (float) h / 2;
centerX = (float) w / 2;
centerY = (float) h / 2;
maxCircleRadius = (float) (w - 20) / 2;
}
Copy the code
2.6 Initializing Paint
The Canvas handles what to draw, such as dots, lines, circles, rectangles, etc. Paint handles how to draw, such as the color to draw, whether to fill, transparency, etc. The Canvas can be obtained by overriding onDraw(). Paint requires creating one or more Paint objects during initialization, as shown in the following example:
paintText = new Paint();
paintText.setAntiAlias(true);
paintText.setTextSize(textSize);
paintText.setColor(textColor);
paintText.setStyle(Paint.Style.FILL);
paintCircle = new Paint();
paintCircle.setAntiAlias(true);
paintCircle.setColor(circleColor);
paintCircle.setStyle(Paint.Style.STROKE);
paintCircle.setStrokeWidth(10);
Copy the code
2.7 Rewrite onDraw() to draw a View
Drawing a View is an important step. It presents the visible interface to the user. Once you override the onDraw() method, it provides a Canvas that performs the drawing with the Paint brush. Such as drawLine() to draw a line segment, drawText() to draw a text, drawPoint() to draw a point, drawRect() to draw a rectangle, etc., pass in the calculated parameters and brush to draw the corresponding graph, example is as follows:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(textContent, 30, textY + 30, paintText);
canvas.drawCircle(centerX, centerY, circleRadius, paintCircle);
}
Copy the code
2.8 Responding to User gestures
The View will often interact with the user, so it needs to respond to and process the user’s gesture operations. Generally speaking, it needs to rewrite onTouchEvent(MotionEvent Event) to handle gesture operations in this method. Common gesture operations include pressing, sliding, lifting, etc., and add business logic to this method. The following is an example:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
Copy the code
In addition, you can use the GestureDetector class to implement more gesture detection, such as double click, long press, scroll, etc.
2.9 Adding Animation Effects
In order to make the custom View more attractive and natural, you need to add some animation effects. In this case, you can use the property animation to modify the properties of the View, as shown in the following example:
ObjectAnimator textAlpha = ObjectAnimator.ofInt(this."textAlpha".255.50);
textAlpha.setDuration(2000);
textAlpha.setRepeatCount(ValueAnimator.INFINITE);
textAlpha.setRepeatMode(ValueAnimator.RESTART);
textAlpha.start();
textAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue(); setTextAlpha(animatedValue); }}); ObjectAnimator circle = ObjectAnimator.ofFloat(this."circleRadius".0.0 f, maxCircleRadius);
circle.setDuration(2000);
circle.setRepeatCount(ValueAnimator.INFINITE);
circle.setRepeatMode(ValueAnimator.RESTART);
circle.start();
circle.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue(); setCircleRadius(animatedValue); }});Copy the code
2.10 Provide callback interfaces externally
A custom View should also provide a callback interface to pass some events and data to facilitate the caller to process the corresponding logic. Common operations are to define some interfaces in the View, define some events in the interface, and provide callback interface methods externally, as shown in the following example:
public interface OnCircleAnimationStartListener {
void onCircleAnimationStart(a);
}
public void setOnCircleAnimationStartListener(OnCircleAnimationStartListener onCircleAnimationStartListener) {
this.onCircleAnimationStartListener = onCircleAnimationStartListener;
}
cv_view.setOnCircleAnimationStartListener(new CustomView.OnCircleAnimationStartListener() {
@Override
public void onCircleAnimationStart(a) {}});Copy the code
3. Summary
Custom View is very practical, in the system components can not meet the requirements, we can use a custom View to achieve the purpose. This paper analyzes the process of implementing a custom View, This includes customizing View properties, providing getter and setter methods for properties, overwriting onMeasure(), overwriting onSizeChanged(), initializing Paint, overwriting onDraw(), responding to user gestures, adding animation effects, and providing callback interfaces externally. Depending on actual needs, these links may not be implemented, or other links may be added.