View编程(5): 自定义View_01_ApiDemo源码研究

android提供的APIDemo中,在/res/values下面有个attrs.xml文件。


其内容如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
          http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<resources>
    <!-- These are the attributes that we want to retrieve from the theme
         in app/PreferencesFromCode.java -->
    <declare-styleable name="TogglePrefAttrs">
        <attr name="android:preferenceLayoutChild" />
    </declare-styleable>
    
    <!-- These are the attributes that we want to retrieve from the theme
         in view/Gallery1.java -->
    <declare-styleable name="Gallery1">
        <attr name="android:galleryItemBackground" />
    </declare-styleable>
    
     <declare-styleable name="LabelView">
        <attr name="text" format="string" />
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
    </declare-styleable>
</resources>
当时,很迷茫。不知道这是什么东西???!!!但是今天需要使用这方面的知识,所以研究一下。

在APIDemo中,com.example.android.apis.view包中CustomView1.java文件中:

setContentView(R.layout.custom_view_1);

那么,我们研究一下Google提供的该例程吧!

1. CustomView.java

package com.example.android.apis.view;

// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import com.example.android.apis.R;

import android.app.Activity;
import android.os.Bundle;


/**
 * Demonstrates creating a Screen that uses custom views. This example uses
 * {@link com.example.android.apis.view.LabelView}, which is defined in
 * SDK/src/com/example/android/apis/view/LabelView.java.
 * 
 */
public class CustomView1 extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.custom_view_1);
    }
}
该代码很简单,就是加载一个xml文件,作为显示视图。
2. custom_view_1.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
    
    <com.example.android.apis.view.LabelView
            android:background="@drawable/red"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" 
            app:text="Red"/>
    
    <com.example.android.apis.view.LabelView
            android:background="@drawable/blue"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" 
            app:text="Blue" app:textSize="20dp"/>
    
    <com.example.android.apis.view.LabelView
            android:background="@drawable/green"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" 
            app:text="Green" app:textColor="#ffffffff" />

</LinearLayout>
可以看出,该xml文件使用了自定义的控件LabelView ,但是你可以看见有一个很陌生的标签app。这是什么东西?

原来,这是自定义的属性文件。所以我们可以自定义属性。但是该属性文件必须是attrs.xml必须放在res/vaules下面。

注意:

 xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis"

app来自这里。

3. attrs.xml

<resources>
    <!-- These are the attributes that we want to retrieve from the theme
         in app/PreferencesFromCode.java -->
    <declare-styleable name="TogglePrefAttrs">
        <attr name="android:preferenceLayoutChild" />
    </declare-styleable>
    
    <!-- These are the attributes that we want to retrieve from the theme
         in view/Gallery1.java -->
    <declare-styleable name="Gallery1">
        <attr name="android:galleryItemBackground" />
    </declare-styleable>
    
     <declare-styleable name="LabelView">
        <attr name="text" format="string" />
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
    </declare-styleable>
</resources>

4. LabelView.java

package com.example.android.apis.view;

// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import com.example.android.apis.R;


/**
 * Example of how to write a custom subclass of View. LabelView
 * is used to draw simple text views. Note that it does not handle
 * styled text or right-to-left writing systems.
 *
 */
public class LabelView extends View {
    private Paint mTextPaint;
    private String mText;
    private int mAscent;
    
    /**
     * Constructor.  This version is only needed if you will be instantiating
     * the object manually (not from a layout XML file).
     * @param context
     */
    public LabelView(Context context) {
        super(context);
        initLabelView();
    }

    /**
     * Construct object, initializing with any attributes we understand from a
     * layout file. These attributes are defined in
     * SDK/assets/res/any/classes.xml.
     * 
     * @see android.view.View#View(android.content.Context, android.util.AttributeSet)
     */
    public LabelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initLabelView();

        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.LabelView);

        CharSequence s = a.getString(R.styleable.LabelView_text);
        if (s != null) {
            setText(s.toString());
        }

        // Retrieve the color(s) to be used for this view and apply them.
        // Note, if you only care about supporting a single color, that you
        // can instead call a.getColor() and pass that to setTextColor().
        setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000));

        int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);
        if (textSize > 0) {
            setTextSize(textSize);
        }

        a.recycle();
    }

    private final void initLabelView() {
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(16);
        mTextPaint.setColor(0xFF000000);
        setPadding(3, 3, 3, 3);
    }

    /**
     * Sets the text to display in this label
     * @param text The text to display. This will be drawn as one line.
     */
    public void setText(String text) {
        mText = text;
        requestLayout();
        invalidate();
    }

    /**
     * Sets the text size for this label
     * @param size Font size
     */
    public void setTextSize(int size) {
        mTextPaint.setTextSize(size);
        requestLayout();
        invalidate();
    }

    /**
     * Sets the text color for this label.
     * @param color ARGB value for the text
     */
    public void setTextColor(int color) {
        mTextPaint.setColor(color);
        invalidate();
    }

    /**
     * @see android.view.View#measure(int, int)
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec));
    }

    /**
     * Determines the width of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text
            result = (int) mTextPaint.measureText(mText) + getPaddingLeft()
                    + getPaddingRight();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }

        return result;
    }

    /**
     * Determines the height of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        mAscent = (int) mTextPaint.ascent();
        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text (beware: ascent is a negative number)
            result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop()
                    + getPaddingBottom();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    /**
     * Render the text
     * 
     * @see android.view.View#onDraw(android.graphics.Canvas)
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);
    }
}
运行效果:

回调方法调用:

D/mark    ( 2924): LabelView(Context context, AttributeSet attrs) is invoked!
D/mark    ( 2924): LabelView(Context context, AttributeSet attrs) is invoked!
D/mark    ( 2924): LabelView(Context context, AttributeSet attrs) is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onDraw() is invoked!
D/mark    ( 2924): onDraw() is invoked!
D/mark    ( 2924): onDraw() is invoked!
这里注意:
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
D/mark    ( 2924): onMeasure() is invoked!
也就是说,onMeasure()方法被回调六次。那么为什么呢?请关注View编程(6): 自定义View_02_ApiDemo源码研究博文。

©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页