代码改变世界

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

2011-12-14 16:06  tang768168  阅读(279)  评论(0编辑  收藏  举报

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

其内容如下:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!-- Copyright (C) 2007 The Android Open Source Project  
  3.   
  4.      Licensed under the Apache License, Version 2.0 (the "License");  
  5.      you may not use this file except in compliance with the License.  
  6.      You may obtain a copy of the License at  
  7.     
  8.           http://www.apache.org/licenses/LICENSE-2.0  
  9.     
  10.      Unless required by applicable law or agreed to in writing, software  
  11.      distributed under the License is distributed on an "AS IS" BASIS,  
  12.      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13.      See the License for the specific language governing permissions and  
  14.      limitations under the License.  
  15. -->  
  16.   
  17. <resources>  
  18.     <!-- These are the attributes that we want to retrieve from the theme  
  19.          in app/PreferencesFromCode.java -->  
  20.     <declare-styleable name="TogglePrefAttrs">  
  21.         <attr name="android:preferenceLayoutChild" />  
  22.     </declare-styleable>  
  23.       
  24.     <!-- These are the attributes that we want to retrieve from the theme  
  25.          in view/Gallery1.java -->  
  26.     <declare-styleable name="Gallery1">  
  27.         <attr name="android:galleryItemBackground" />  
  28.     </declare-styleable>  
  29.       
  30.      <declare-styleable name="LabelView">  
  31.         <attr name="text" format="string" />  
  32.         <attr name="textColor" format="color" />  
  33.         <attr name="textSize" format="dimension" />  
  34.     </declare-styleable>  
  35. </resources>  

当时,很迷茫。不知道这是什么东西???!!!但是今天需要使用这方面的知识,所以研究一下。

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

  1. setContentView(R.layout.custom_view_1);  

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

1. CustomView.java

  1. package com.example.android.apis.view;  
  2.   
  3. // Need the following import to get access to the app resources, since this  
  4. // class is in a sub-package.  
  5. import com.example.android.apis.R;  
  6.   
  7. import android.app.Activity;  
  8. import android.os.Bundle;  
  9.   
  10.   
  11. /** 
  12.  * Demonstrates creating a Screen that uses custom views. This example uses 
  13.  * {@link com.example.android.apis.view.LabelView}, which is defined in 
  14.  * SDK/src/com/example/android/apis/view/LabelView.java. 
  15.  *  
  16.  */  
  17. public class CustomView1 extends Activity {  
  18.   
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.custom_view_1);  
  23.     }  
  24. }  

该代码很简单,就是加载一个xml文件,作为显示视图。
2. custom_view_1.xml

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.         xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis"  
  3.         android:orientation="vertical"  
  4.         android:layout_width="fill_parent"  
  5.         android:layout_height="wrap_content">  
  6.       
  7.     <com.example.android.apis.view.LabelView  
  8.             android:background="@drawable/red"  
  9.             android:layout_width="fill_parent"  
  10.             android:layout_height="wrap_content"   
  11.             app:text="Red"/>  
  12.       
  13.     <com.example.android.apis.view.LabelView  
  14.             android:background="@drawable/blue"  
  15.             android:layout_width="fill_parent"  
  16.             android:layout_height="wrap_content"   
  17.             app:text="Blue" app:textSize="20dp"/>  
  18.       
  19.     <com.example.android.apis.view.LabelView  
  20.             android:background="@drawable/green"  
  21.             android:layout_width="fill_parent"  
  22.             android:layout_height="wrap_content"   
  23.             app:text="Green" app:textColor="#ffffffff" />  
  24.   
  25. </LinearLayout>  

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

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

注意:

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

app来自这里。

3. attrs.xml

  1. <resources>  
  2.     <!-- These are the attributes that we want to retrieve from the theme  
  3.          in app/PreferencesFromCode.java -->  
  4.     <declare-styleable name="TogglePrefAttrs">  
  5.         <attr name="android:preferenceLayoutChild" />  
  6.     </declare-styleable>  
  7.       
  8.     <!-- These are the attributes that we want to retrieve from the theme  
  9.          in view/Gallery1.java -->  
  10.     <declare-styleable name="Gallery1">  
  11.         <attr name="android:galleryItemBackground" />  
  12.     </declare-styleable>  
  13.       
  14.      <declare-styleable name="LabelView">  
  15.         <attr name="text" format="string" />  
  16.         <attr name="textColor" format="color" />  
  17.         <attr name="textSize" format="dimension" />  
  18.     </declare-styleable>  
  19. </resources>  

4. LabelView.java

  1. package com.example.android.apis.view;  
  2.   
  3. // Need the following import to get access to the app resources, since this  
  4. // class is in a sub-package.  
  5. import android.content.Context;  
  6. import android.content.res.TypedArray;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Paint;  
  9. import android.util.AttributeSet;  
  10. import android.view.View;  
  11.   
  12. import com.example.android.apis.R;  
  13.   
  14.   
  15. /** 
  16.  * Example of how to write a custom subclass of View. LabelView 
  17.  * is used to draw simple text views. Note that it does not handle 
  18.  * styled text or right-to-left writing systems. 
  19.  * 
  20.  */  
  21. public class LabelView extends View {  
  22.     private Paint mTextPaint;  
  23.     private String mText;  
  24.     private int mAscent;  
  25.       
  26.     /** 
  27.      * Constructor.  This version is only needed if you will be instantiating 
  28.      * the object manually (not from a layout XML file). 
  29.      * @param context 
  30.      */  
  31.     public LabelView(Context context) {  
  32.         super(context);  
  33.         initLabelView();  
  34.     }  
  35.   
  36.     /** 
  37.      * Construct object, initializing with any attributes we understand from a 
  38.      * layout file. These attributes are defined in 
  39.      * SDK/assets/res/any/classes.xml. 
  40.      *  
  41.      * @see android.view.View#View(android.content.Context, android.util.AttributeSet) 
  42.      */  
  43.     public LabelView(Context context, AttributeSet attrs) {  
  44.         super(context, attrs);  
  45.         initLabelView();  
  46.   
  47.         TypedArray a = context.obtainStyledAttributes(attrs,  
  48.                 R.styleable.LabelView);  
  49.   
  50.         CharSequence s = a.getString(R.styleable.LabelView_text);  
  51.         if (s != null) {  
  52.             setText(s.toString());  
  53.         }  
  54.   
  55.         // Retrieve the color(s) to be used for this view and apply them.  
  56.         // Note, if you only care about supporting a single color, that you  
  57.         // can instead call a.getColor() and pass that to setTextColor().  
  58.         setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000));  
  59.   
  60.         int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);  
  61.         if (textSize > 0) {  
  62.             setTextSize(textSize);  
  63.         }  
  64.   
  65.         a.recycle();  
  66.     }  
  67.   
  68.     private final void initLabelView() {  
  69.         mTextPaint = new Paint();  
  70.         mTextPaint.setAntiAlias(true);  
  71.         mTextPaint.setTextSize(16);  
  72.         mTextPaint.setColor(0xFF000000);  
  73.         setPadding(3333);  
  74.     }  
  75.   
  76.     /** 
  77.      * Sets the text to display in this label 
  78.      * @param text The text to display. This will be drawn as one line. 
  79.      */  
  80.     public void setText(String text) {  
  81.         mText = text;  
  82.         requestLayout();  
  83.         invalidate();  
  84.     }  
  85.   
  86.     /** 
  87.      * Sets the text size for this label 
  88.      * @param size Font size 
  89.      */  
  90.     public void setTextSize(int size) {  
  91.         mTextPaint.setTextSize(size);  
  92.         requestLayout();  
  93.         invalidate();  
  94.     }  
  95.   
  96.     /** 
  97.      * Sets the text color for this label. 
  98.      * @param color ARGB value for the text 
  99.      */  
  100.     public void setTextColor(int color) {  
  101.         mTextPaint.setColor(color);  
  102.         invalidate();  
  103.     }  
  104.   
  105.     /** 
  106.      * @see android.view.View#measure(int, int) 
  107.      */  
  108.     @Override  
  109.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  110.         setMeasuredDimension(measureWidth(widthMeasureSpec),  
  111.                 measureHeight(heightMeasureSpec));  
  112.     }  
  113.   
  114.     /** 
  115.      * Determines the width of this view 
  116.      * @param measureSpec A measureSpec packed into an int 
  117.      * @return The width of the view, honoring constraints from measureSpec 
  118.      */  
  119.     private int measureWidth(int measureSpec) {  
  120.         int result = 0;  
  121.         int specMode = MeasureSpec.getMode(measureSpec);  
  122.         int specSize = MeasureSpec.getSize(measureSpec);  
  123.   
  124.         if (specMode == MeasureSpec.EXACTLY) {  
  125.             // We were told how big to be  
  126.             result = specSize;  
  127.         } else {  
  128.             // Measure the text  
  129.             result = (int) mTextPaint.measureText(mText) + getPaddingLeft()  
  130.                     + getPaddingRight();  
  131.             if (specMode == MeasureSpec.AT_MOST) {  
  132.                 // Respect AT_MOST value if that was what is called for by measureSpec  
  133.                 result = Math.min(result, specSize);  
  134.             }  
  135.         }  
  136.   
  137.         return result;  
  138.     }  
  139.   
  140.     /** 
  141.      * Determines the height of this view 
  142.      * @param measureSpec A measureSpec packed into an int 
  143.      * @return The height of the view, honoring constraints from measureSpec 
  144.      */  
  145.     private int measureHeight(int measureSpec) {  
  146.         int result = 0;  
  147.         int specMode = MeasureSpec.getMode(measureSpec);  
  148.         int specSize = MeasureSpec.getSize(measureSpec);  
  149.   
  150.         mAscent = (int) mTextPaint.ascent();  
  151.         if (specMode == MeasureSpec.EXACTLY) {  
  152.             // We were told how big to be  
  153.             result = specSize;  
  154.         } else {  
  155.             // Measure the text (beware: ascent is a negative number)  
  156.             result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop()  
  157.                     + getPaddingBottom();  
  158.             if (specMode == MeasureSpec.AT_MOST) {  
  159.                 // Respect AT_MOST value if that was what is called for by measureSpec  
  160.                 result = Math.min(result, specSize);  
  161.             }  
  162.         }  
  163.         return result;  
  164.     }  
  165.   
  166.     /** 
  167.      * Render the text 
  168.      *  
  169.      * @see android.view.View#onDraw(android.graphics.Canvas) 
  170.      */  
  171.     @Override  
  172.     protected void onDraw(Canvas canvas) {  
  173.         super.onDraw(canvas);  
  174.         canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);  
  175.     }  
  176. }  

运行效果:

回调方法调用:

  1. D/mark    ( 2924): LabelView(Context context, AttributeSet attrs) is invoked!  
  2. D/mark    ( 2924): LabelView(Context context, AttributeSet attrs) is invoked!  
  3. D/mark    ( 2924): LabelView(Context context, AttributeSet attrs) is invoked!  
  4. D/mark    ( 2924): onMeasure() is invoked!  
  5. D/mark    ( 2924): onMeasure() is invoked!  
  6. D/mark    ( 2924): onMeasure() is invoked!  
  7. D/mark    ( 2924): onMeasure() is invoked!  
  8. D/mark    ( 2924): onMeasure() is invoked!  
  9. D/mark    ( 2924): onMeasure() is invoked!  
  10. D/mark    ( 2924): onMeasure() is invoked!  
  11. D/mark    ( 2924): onMeasure() is invoked!  
  12. D/mark    ( 2924): onMeasure() is invoked!  
  13. D/mark    ( 2924): onDraw() is invoked!  
  14. D/mark    ( 2924): onDraw() is invoked!  
  15. D/mark    ( 2924): onDraw() is invoked!  

这里注意:

  1. D/mark    ( 2924): onMeasure() is invoked!  
  2. D/mark    ( 2924): onMeasure() is invoked!  
  3. D/mark    ( 2924): onMeasure() is invoked!  
  4. D/mark    ( 2924): onMeasure() is invoked!  
  5. D/mark    ( 2924): onMeasure() is invoked!  
  6. D/mark    ( 2924): onMeasure() is invoked!  
  7. D/mark    ( 2924): onMeasure() is invoked!  
  8. D/mark    ( 2924): onMeasure() is invoked!  
  9. D/mark    ( 2924): onMeasure() is invoked!  

也就是说,onMeasure()方法被回调六次。那么为什么呢?请关注View编程(6): 自定义View_02_ApiDemo源码研究博文。