Android from drawing controls

The development process, we will inevitably need to use some custom View, Custom View can generally be divided into three categories:

  ① derived classes View - View substantially generally after legacy systems, new / reset some custom properties, e.g. justified the TextView;

  ②  combination Class View - The View certain basic systems together to form a new View, for example, the end of the band "×" (Empty) is EditText, EditText is combined together and ImageView achieved; 

  ③  from drawing View - special design of the controls, the time can not be achieved through two ways, we need to consider to be processed by the self-draw, this article, we will focus on the implementation of such a View.

 

Let be described by customizing a circular Button (DCircleButton):

View custom steps:

 ① View custom properties;

 ② getting a property value in the constructor View View of the custom;

 The method of measuring the size override ③ onMeasure (int, int); (depending on whether needs to be rewritten as required);

 ④ override drawing method onDraw (Canvas c); 

 ⑤ in the layout XML files, using a custom View of the property.

 

1. Custom View properties:

New attrs.xml properties file in the directory res / values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--name 是自定义控件的类名-->
    <declare-styleable name="DCircleButton" parent="android.widget.Button">
        <attr name="txtSize" format="dimension"/>
        <attr name="text" format="string"/>
        <attr name="txtColor" format="color"/>
        <attr name="txtBackgroundColor" format="color"/>
    </declare-styleable>
</resources>

 

Custom attributes in two steps:
  ① theme style definition controls;
  ② defined attribute names and types.

As the above xml file is custom DCirclebutton the theme style, theme styles to define a property, some people may have tangled behind the format field which attributes units? If you are using AS develop, then the IDE automatically prompted, basically include the following:
a Dimension (font size) string (string) color (color) boolean (boolean) float (float) integer (integer) enmu (medals cited) fraction (percentage) and the like.

2. Obtain the property value in the constructor, and plotted

The first step : Inheritance View, realization (AS will be prompted to) four,

public DCircleButton(Context context) {
    super(context);
}
public DCircleButton(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}
public DCircleButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public DCircleButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

The second step , to rewrite the four construction, let it step by step progression:

public DCircleButton(Context context) {
    super(context, null);
}
public DCircleButton(Context context, AttributeSet attrs) {
    super(context, attrs, 0);
}
public DCircleButton(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr, 0);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public DCircleButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

The third step , we get property values in the last method:

private void initAttrs(Context context, AttributeSet attrs) {
    TypedArray tArr = context.obtainStyledAttributes(attrs, R.styleable.DCircleButton);
    if (null != tArr) {
        txtColor = tArr.getColor(R.styleable.DCircleButton_txtColor, Color.BLACK); // 获取文字颜色
        txtSize = tArr.getDimensionPixelSize(R.styleable.DCircleButton_txtSize, 18);  // 获取文字大小
        txt = tArr.getString(R.styleable.DCircleButton_text); // 获取文字内容
        backgroundColor = tArr.getColor(R.styleable.DCircleButton_txtBackgroundColor, Color.GRAY); // 获取文字背景颜色
        tArr.recycle();
    }
}

The fourth step , draw

/ * Font color * * / 
Private  int txtColor;
 / ** font background color * * / 
Private  int backgroundColor;
 / ** font size * * / 
Private  int txtSize;
 / ** button text * * / 
Private String TXT;
 / * radius * * / 
Private  a float mDrawableRadius; 

/ * Font background brush * * / 
Private the Paint mBackgroundPaint;
 / * Font brush * * / 
Private the Paint mTxtPaint; 

public DCircleButton (the context context, AttributeSet attrs,int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initAttrs(context, attrs);
    init();
}
/** 初始化 **/
private void init() {
    mBackgroundPaint = new Paint();
    mBackgroundPaint.setColor(backgroundColor);

    mTxtPaint = new Paint();
    mTxtPaint.setTextAlign(Paint.Align.CENTER);
    mTxtPaint.setColor(txtColor);
    mTxtPaint.setTextSize(txtSize);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mDrawableRadius = Math.min(getWidth() >> 1, getHeight() >> 1);
    canvas.drawCircle(getWidth() >> 1, getHeight() >> 1, mDrawableRadius, mBackgroundPaint);
    if (null != txt)
        canvas.drawText(txt, getWidth() >> 1, getHeight() >> 1, mTxtPaint);
}

 3. layout application

<dinn.circle.button.DCircleButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:text="我是按钮"
    app:txtBackgroundColor="@android:color/holo_orange_dark"
    app:txtColor="@android:color/white"
    app:txtSize="20sp" />

 4. Run results

 

This time back to full screen buttons are found, but the size of the property layout we set for "wrap_content". The fact is that we are in the process of custom View also has a onMeasure method is not rewritten.

The rewrite control View size onMeasure

When you do not have to rewrite onMeasure method, the system call the default method onMeasure.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

The role of this method are: the size of the measurement control . In fact, the Android system when loading layout is measured by the size of each sub-View system I need to tell Father View occupy much space, then the parent View will decide how much space to allocate View child according to their size.

Well, from the above point of view, when you set the View in the layout size "wrap_content", actually measured the size of the system is "match_parent". Why is it like this?

It was from MeasureSpec mode of specMode talking about. There are three modes:

MeasureSpec.EXACTLY : parent view is the view desired size of the sub specSize specified size; generally set or clear values MATCH_PARENT.

MeasureSpec.AT_MOST : subview size is at most the size specSize; represents a sub-layout within the maximum limit, generally WARP_CONTENT.

MeasureSpec.UNSPECIFIED : parent view does not impose any restrictions applet, applet can be any size you want; how much you want to represent sub layout much, rarely used.

 

We look at the system source code super.onMeasure (widthMeasureSpec, heightMeasureSpec); how to achieve:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
    }
    return result;
}

() Method seen from the above code getDefaultSize, and the original MeasureSpec.AT_MOST MeasureSpec.EXACTLY taking the same branch, that is, the parent view is the view desired size of the sub specSize size specified.

The default value is to get out to fill the entire parent layout. Therefore, no matter the size of your layout is "wrap_content" or "match_parent" the effect is to fill the entire parent layout. I want to "wrap_content" effect how to do? Then only rewrite the method onMeasure.

@Override
 protected  void onMeasure ( int widthMeasureSpec, int heightMeasureSpec) {
     // measurement mode 
    int widthMode = MeasureSpec.getMode (widthMeasureSpec);
     // parent layout desired size of the sub layout, which is provided if the layout is a fixed value, which here take the layout the minimum value of the fixed value and the value in the parent layout size
     // If the match_parent, taking the size of the parent layout 
    int widthSize = MeasureSpec.getSize (widthMeasureSpec);
     int heightMode = MeasureSpec.getMode (heightMeasureSpec);
     int heightSize = MeasureSpec.getSize (heightMeasureSpec); 

    int width;
     int  height;
    Rect mBounds =new Rect();
    if (widthMode == MeasureSpec.EXACTLY) {
        width = widthSize;
    } else {
        mTxtPaint.setTextSize(txtSize);
        mTxtPaint.getTextBounds(txt, 0, txt.length(), mBounds);
        float textWidth = mBounds.width();
        int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
        width = desired;
    }

    if (heightMode == MeasureSpec.EXACTLY) {
        height =heightSize; 
    } the else { 
        height = width; 
    } 
    // Finally, call the parent class method, the size of the parent to View layout. 
    setMeasuredDimension (width, height); 
}

The final effect achieved is as follows:

 

Guess you like

Origin www.cnblogs.com/steffen/p/9941947.html