完美实现Android自定义控件---以自定义带图片和文本的Button为例

来源:互联网 发布:怎么在淘宝上买呼死你 编辑:程序博客网 时间:2024/04/30 23:44

像系统自带控件的一样,做一个真正的控件。

第一步:定义一个有图片和文本的布局:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="horizontal"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     > <ImageView     android:id="@+id/widget_image_text_btn_Img"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     /> <TextView     android:id="@+id/widget_image_text_btn_TV"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:textColor="#000000"     android:layout_marginLeft="8dip"     android:layout_gravity="center_vertical"     /> </LinearLayout> 


第二步:定义一个与这个布局对应的类:

package com.widget;import com.example.testdrawable.R;import android.annotation.SuppressLint;import android.content.Context;import android.content.res.ColorStateList;import android.content.res.Resources;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.util.Log;import android.view.KeyEvent;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;public class ImgTextBtnH extends LinearLayout {private static final String TAG = ImgTextBtnH.class.getSimpleName();private ImageView mImg;     private TextView  mTV;     private Button mBtn;    private Context mContext = null;     public ImgTextBtnH(Context context) {         this(context, null);         mContext = context;    }      protected void inflaterLayout(Context context) {        // 导入布局         LayoutInflater.from(context).inflate(R.layout.widget_image_text_btn_horizontal_layout, this, true);     }            @SuppressLint("NewApi")public ImgTextBtnH(Context context, AttributeSet attrs) {         super(context, attrs);         mContext = context;                Resources.Theme theme = context.getTheme();                inflaterLayout(context);        // 导入布局 //        LayoutInflater.from(context).inflate(R.layout.widget_image_text_btn_layout, this, true);         mImg = (ImageView) findViewById(R.id.widget_image_text_btn_Img);         mTV = (TextView) findViewById(R.id.widget_image_text_btn_TV);         mBtn = (Button) findViewById(R.id.widget_image_text_btn_Btn);                //不能够使用ImageView的click方法, 在android里点击事件消息是从内向外传递的,设置了false之后才能传递出来给LinearLayout        mImg.setClickable(false); //谢谢 Brightshadow11111 朋友的提醒                TypedArray typeArray = context.obtainStyledAttributes(attrs,                R.styleable.ImgTextBtn);        if (typeArray == null) {        return;        }                int count = typeArray.getIndexCount();        int resId = 0;        for (int i = 0; i < count; i++) {        int attr = typeArray.getIndex(i);        switch(attr) {        case R.styleable.ImgTextBtn_ImgDraw:        Drawable background = typeArray.getDrawable(attr);        int sdk = android.os.Build.VERSION.SDK_INT;        if(sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {            mImg.setBackgroundDrawable(background);        } else {        mImg.setBackground(background);        }        break;        case R.styleable.ImgTextBtn_ImgDrawWidth:        int imageWidth = typeArray.getDimensionPixelSize(attr, -1);        mImg.setMaxWidth(imageWidth);        mImg.setMinimumWidth(imageWidth);        break;        case R.styleable.ImgTextBtn_ImgDrawHeight:        int imageHeight = typeArray.getDimensionPixelSize(attr, -1);        mImg.setMaxHeight(imageHeight);        mImg.setMinimumHeight(imageHeight);        break;        case R.styleable.ImgTextBtn_ImgDrawMinWidth:        int imageMinWidth = typeArray.getDimensionPixelSize(attr, -1);        mImg.setMinimumHeight(imageMinWidth);        break;        case R.styleable.ImgTextBtn_ImgDrawMinHeight:        int imageMinHeight = typeArray.getDimensionPixelSize(attr, -1);        mImg.setMinimumHeight(imageMinHeight);        break;        case R.styleable.ImgTextBtn_ImgDrawMaxHeight:        int imageMaxHeight = typeArray.getDimensionPixelSize(attr, -1);        mImg.setMaxHeight(imageMaxHeight);        break;        case R.styleable.ImgTextBtn_ImgDrawMaxWidth:        int imageMaxWidth = typeArray.getDimensionPixelSize(attr, -1);        mImg.setMaxWidth(imageMaxWidth);        break;        case R.styleable.ImgTextBtn_TVText:        mTV.setText(typeArray.getText(attr));        break;        case R.styleable.ImgTextBtn_TVTextSize:        mTV.setTextSize(typeArray.getDimensionPixelSize(attr, 15));        break;        }        }        typeArray.recycle();    }         @Override    public void refreshDrawableState() {    // TODO Auto-generated method stub    super.refreshDrawableState();        //------------接下来处理联动效果,关键代码,请认真看-------------------        //------------ ImageView控件的联动刷新 -------------------    Drawable imgDrawable = mImg.getBackground(); //获取drawable资源    Log.d(TAG, "drawable = " + imgDrawable);    if (imgDrawable != null && imgDrawable.isStateful()) {            //关键中的关键,根据当前状态设置drawable的状态,本来应该是LinearLayout的getDrawableState()状态,            //但是现在是实现联动效果,而且获取的ImageView的getDrawState()结果不对。    imgDrawable.setState(this.getDrawableState());     }        //------------- TextView的联动刷新, 抱歉, 无法刷新 ------------------        //这块代码很快写出来了,但是写出来后发现,颜色总是停留在最后一次的颜色上,后面再点击都无法改变颜色了,        //才恍然大悟,mTV.setTextColor()是设置TextView内部的ColorStateList对象的,这样会清掉原先的        //res/color/colorSelector.xml里的设置,导致只有一种颜色值。应该赋值给TextView内部的private int mCurTextColor;        //可惜没有并设置mCurTextColor值的接口,不知到使用反射能否做到,有兴趣朋友可以一试。        //既然Text的color是靠TextPaint刷出来的,那么是否可以改变TextPaint的颜色值呢?发现有接口getPaint可以获取到TextPaint,        //但是设置了color值之后,还是不行,哦,原来在onDraw()函数中,重新设置了颜色值,所以白设了。        //总而言之,联动刷新颜色是不行滴,只能设一个颜色(对于我目前来讲功能已经是足够了) 哈哈,讲的够详细吧~~    ColorStateList mTextColor = mTV.getTextColors();        int color = mTextColor.getColorForState(this.getDrawableState(), 0);        if (mTV.getCurrentTextColor() != color) {        mTV.getPaint().setColor(color);//        mTV.setTextColor(color);        mTV.invalidate();        }                //-----------如果有个Button的话就可以在这里设置改变,效果同上,略---------------//        Drawable btnDrawable = mBtn.getBackground();//    Log.d(TAG, "drawable = " + btnDrawable);//    if (btnDrawable != null && btnDrawable.isStateful()) {//    btnDrawable.setState(this.getDrawableState());//    }//    //    ColorStateList mTextColor2 = mBtn.getTextColors();//    int color2 = mTextColor2.getColorForState(this.getDrawableState(), 0);//        if (mBtn.getCurrentTextColor() != color2) {//        mBtn.setTextColor(color2);//        }     }    /**     * 设置图片资源     */     public void setImgViewResource(int resId) {         mImg.setImageResource(resId);     }         /**     * 设置图片     */     public void setImgViewResource(Bitmap bitmap) {         mImg.setImageBitmap(bitmap);     }      /**     * 设置显示的文字     */     public void setTextViewText(int resId) {         mTV.setText(resId);     }     /**     * 设置显示的文字     */     public void setTextViewText(String text) {         mTV.setText(text);     }  }

第三步:

在value/下面定义一个attrs.xml文件,内容如下:<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="ImgTextBtn">        <attr name="ImgDraw" format="reference"></attr>        <attr name="ImgDrawWidth" format="dimension"></attr>        <attr name="ImgDrawHeight" format="dimension"></attr>        <attr name="ImgDrawMinWidth" format="dimension"></attr>        <attr name="ImgDrawMinHeight" format="dimension"></attr>        <attr name="ImgDrawMaxWidth" format="dimension"></attr>        <attr name="ImgDrawMaxHeight" format="dimension"></attr>                <attr name="TVText" format="reference|string"></attr>        <attr name="TVTextSize" format="dimension"></attr>        <attr name="TVTextColor" format="reference|color"></attr>    </declare-styleable>    </resources>

第四步:

直接在布局中使用

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:CustomAttrs="http://schemas.android.com/apk/res/com.example.testdrawable"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.widget.ImgTextBtn        android:id="@+id/imageTextButton1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical"        android:layout_above="@+id/textView1"        android:layout_centerHorizontal="true"        android:layout_marginBottom="35dp"        android:background="@drawable/preference_round_rect_light_blue"        CustomAttrs:TVText="@string/hello_world"        CustomAttrs:TVTextSize="@dimen/dimens_Padding_normal"        CustomAttrs:ImgDraw="@drawable/test_btn_selector">    </com.widget.ImgTextBtn></RelativeLayout>


第五步:

代码中设置:

ImgTextBtn mBtn = (ImgTextBtn) findViewById(R.id.imageTextButton1);mbtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubToast.makeText(MainActivity.this, "OK OK OK", Toast.LENGTH_LONG).show();}});


运行效果如下:



这样添加上红字部分,终于可以基本完美的实现了一个控件了,点击LinearLayout中的任何地方只会触发这个控件整体
    的onClick()方法,并且控件内部的ImageView或者Button仍然可以跟着改变状态。


    /********************* 完 ***************************************************************************/

   也可以结合着几篇文章看看:
    http://blog.csdn.net/ggdffftghjndjnd/article/details/7738888
    http://www.cnblogs.com/freeliver54/archive/2012/08/10/2631217.html

    以示参考,CSDN的文章编辑功能真是一坨屎,CSDN越来越懒惰了,而且作为技术网站来讲,

    其技术含量真是不敢恭维,尤其是苹果上面的CSDN阅读器,没有比这更屎的软件了!!!!!!!!!!!有时冲动了直接想骂娘!!!!

    /****************************************************************************************************/
    下面是View类的attrs属性,从frameworks/base/core/res/res/values/attrs.xml中抽出来的

 <!-- Attributes that can be used with {@link android.view.View} or         any of its subclasses.  Also see {@link #ViewGroup_Layout} for         attributes that are processed by the view's parent. -->    <declare-styleable name="View">        <!-- Supply an identifier name for this view, to later retrieve it             with {@link android.view.View#findViewById View.findViewById()} or             {@link android.app.Activity#findViewById Activity.findViewById()}.             This must be a             resource reference; typically you set this using the             <code>@+</code> syntax to create a new ID resources.             For example: <code>android:id="@+id/my_id"</code> which             allows you to later retrieve the view             with <code>findViewById(R.id.my_id)</code>. -->        <attr name="id" format="reference" />        <!-- Supply a tag for this view containing a String, to be retrieved             later with {@link android.view.View#getTag View.getTag()} or             searched for with {@link android.view.View#findViewWithTag             View.findViewWithTag()}.  It is generally preferable to use             IDs (through the android:id attribute) instead of tags because             they are faster and allow for compile-time type checking. -->        <attr name="tag" format="string" />        <!-- The initial horizontal scroll offset, in pixels.-->        <attr name="scrollX" format="dimension" />        <!-- The initial vertical scroll offset, in pixels. -->        <attr name="scrollY" format="dimension" />        <!-- A drawable to use as the background.  This can be either a reference             to a full drawable resource (such as a PNG image, 9-patch,             XML state list description, etc), or a solid color such as "#ff000000"            (black). -->        <attr name="background" format="reference|color" />        <!-- Sets the padding, in pixels, of all four edges.  Padding is defined as             space between the edges of the view and the view's content. A views size             will include it's padding.  If a {@link android.R.attr#background}             is provided, the padding will initially be set to that (0 if the             drawable does not have padding).  Explicitly setting a padding value             will override the corresponding padding found in the background. -->        <attr name="padding" format="dimension" />        <!-- Sets the padding, in pixels, of the left edge; see {@link android.R.attr#padding}. -->        <attr name="paddingLeft" format="dimension" />        <!-- Sets the padding, in pixels, of the top edge; see {@link android.R.attr#padding}. -->        <attr name="paddingTop" format="dimension" />        <!-- Sets the padding, in pixels, of the right edge; see {@link android.R.attr#padding}. -->        <attr name="paddingRight" format="dimension" />        <!-- Sets the padding, in pixels, of the bottom edge; see {@link android.R.attr#padding}. -->        <attr name="paddingBottom" format="dimension" />        <!-- Sets the padding, in pixels, of the start edge; see {@link android.R.attr#padding}. -->        <attr name="paddingStart" format="dimension" />        <!-- Sets the padding, in pixels, of the end edge; see {@link android.R.attr#padding}. -->        <attr name="paddingEnd" format="dimension" />        <!-- Boolean that controls whether a view can take focus.  By default the user can not             move focus to a view; by setting this attribute to true the view is             allowed to take focus.  This value does not impact the behavior of             directly calling {@link android.view.View#requestFocus}, which will             always request focus regardless of this view.  It only impacts where             focus navigation will try to move focus. -->        <attr name="focusable" format="boolean" />        <!-- Boolean that controls whether a view can take focus while in touch mode.             If this is true for a view, that view can gain focus when clicked on, and can keep             focus if another view is clicked on that doesn't have this attribute set to true. -->        <attr name="focusableInTouchMode" format="boolean" />        <!-- Controls the initial visibility of the view.  -->        <attr name="visibility">            <!-- Visible on screen; the default value. -->            <enum name="visible" value="0" />            <!-- Not displayed, but taken into account during layout (space is left for it). -->            <enum name="invisible" value="1" />            <!-- Completely hidden, as if the view had not been added. -->            <enum name="gone" value="2" />        </attr>        <!-- Boolean internal attribute to adjust view layout based on             system windows such as the status bar.             If true, adjusts the padding of this view to leave space for the system windows.             Will only take effect if this view is in a non-embedded activity. -->        <attr name="fitsSystemWindows" format="boolean" />        <!-- Defines which scrollbars should be displayed on scrolling or not. -->        <attr name="scrollbars">            <!-- No scrollbar is displayed. -->            <flag name="none" value="0x00000000" />            <!-- Displays horizontal scrollbar only. -->            <flag name="horizontal" value="0x00000100" />            <!-- Displays vertical scrollbar only. -->            <flag name="vertical" value="0x00000200" />        </attr>        <!-- Controls the scrollbar style and position. The scrollbars can be overlaid or             inset. When inset, they add to the padding of the view. And the             scrollbars can be drawn inside the padding area or on the edge of             the view. For example, if a view has a background drawable and you             want to draw the scrollbars inside the padding specified by the             drawable, you can use insideOverlay or insideInset. If you want them             to appear at the edge of the view, ignoring the padding, then you can             use outsideOverlay or outsideInset.-->        <attr name="scrollbarStyle">            <!-- Inside the padding and overlaid -->            <enum name="insideOverlay" value="0x0" />            <!-- Inside the padding and inset -->            <enum name="insideInset" value="0x01000000" />            <!-- Edge of the view and overlaid -->            <enum name="outsideOverlay" value="0x02000000" />            <!-- Edge of the view and inset -->            <enum name="outsideInset" value="0x03000000" />        </attr>        <!-- Set this if the view will serve as a scrolling container, meaing             that it can be resized to shrink its overall window so that there             will be space for an input method.  If not set, the default             value will be true if "scrollbars" has the vertical scrollbar             set, else it will be false. -->        <attr name="isScrollContainer" format="boolean" />          <!-- Defines whether to fade out scrollbars when they are not in use. -->         <attr name="fadeScrollbars" format="boolean" />         <!-- Defines the delay in milliseconds that a scrollbar takes to fade out. -->         <attr name="scrollbarFadeDuration" format="integer" />         <!-- Defines the delay in milliseconds that a scrollbar waits before fade out. -->        <attr name="scrollbarDefaultDelayBeforeFade" format="integer" />        <!-- Sets the width of vertical scrollbars and height of horizontal scrollbars. -->        <attr name="scrollbarSize" format="dimension" />        <!-- Defines the horizontal scrollbar thumb drawable. -->        <attr name="scrollbarThumbHorizontal" format="reference" />        <!-- Defines the vertical scrollbar thumb drawable. -->        <attr name="scrollbarThumbVertical" format="reference" />        <!-- Defines the horizontal scrollbar track drawable. -->        <attr name="scrollbarTrackHorizontal" format="reference" />        <!-- Defines the vertical scrollbar track drawable. -->        <attr name="scrollbarTrackVertical" format="reference" />        <!-- Defines whether the horizontal scrollbar track should always be drawn. -->        <attr name="scrollbarAlwaysDrawHorizontalTrack" format="boolean" />        <!-- Defines whether the vertical scrollbar track should always be drawn. -->        <attr name="scrollbarAlwaysDrawVerticalTrack" format="boolean" />        <!-- {@deprecated This attribute is deprecated and will be ignored as of             API level {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}.             Using fading edges may introduce noticeable performance             degradations and should be used only when required by the application's             visual design. To request fading edges with API level             {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and above,             use the <code>requiresFadingEdge</code> attribute instead.} -->        <attr name="fadingEdge">            <!-- No edge is faded. -->            <flag name="none" value="0x00000000" />            <!-- Fades horizontal edges only. -->            <flag name="horizontal" value="0x00001000" />            <!-- Fades vertical edges only. -->            <flag name="vertical" value="0x00002000" />        </attr>        <!-- Defines which edges should be faded on scrolling. -->        <attr name="requiresFadingEdge">            <!-- No edge is faded. -->            <flag name="none" value="0x00000000" />            <!-- Fades horizontal edges only. -->            <flag name="horizontal" value="0x00001000" />            <!-- Fades vertical edges only. -->            <flag name="vertical" value="0x00002000" />        </attr>        <!-- Defines the length of the fading edges. -->        <attr name="fadingEdgeLength" format="dimension" />        <!-- Defines the next view to give focus to when the next focus is             {@link android.view.View#FOCUS_LEFT}.             If the reference refers to a view that does not exist or is part             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}             will result when the reference is accessed.-->        <attr name="nextFocusLeft" format="reference"/>        <!-- Defines the next view to give focus to when the next focus is             {@link android.view.View#FOCUS_RIGHT}             If the reference refers to a view that does not exist or is part             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}             will result when the reference is accessed.-->        <attr name="nextFocusRight" format="reference"/>        <!-- Defines the next view to give focus to when the next focus is             {@link android.view.View#FOCUS_UP}             If the reference refers to a view that does not exist or is part             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}             will result when the reference is accessed.-->        <attr name="nextFocusUp" format="reference"/>        <!-- Defines the next view to give focus to when the next focus is             {@link android.view.View#FOCUS_DOWN}             If the reference refers to a view that does not exist or is part             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}             will result when the reference is accessed.-->        <attr name="nextFocusDown" format="reference"/>        <!-- Defines the next view to give focus to when the next focus is             {@link android.view.View#FOCUS_FORWARD}             If the reference refers to a view that does not exist or is part             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}             will result when the reference is accessed.-->        <attr name="nextFocusForward" format="reference"/>        <!-- Defines whether this view reacts to click events. -->        <attr name="clickable" format="boolean" />        <!-- Defines whether this view reacts to long click events. -->        <attr name="longClickable" format="boolean" />        <!-- If unset, no state will be saved for this view when it is being             frozen. The default is true, allowing the view to be saved             (however it also must have an ID assigned to it for its             state to be saved).  Setting this to false only disables the             state for this view, not for its children which may still             be saved. -->        <attr name="saveEnabled" format="boolean" />        <!-- Specifies whether to filter touches when the view's window is obscured by             another visible window.  When set to true, the view will not receive touches             whenever a toast, dialog or other window appears above the view's window.             Refer to the {@link android.view.View} security documentation for more details. -->        <attr name="filterTouchesWhenObscured" format="boolean" />        <!-- Defines the quality of translucent drawing caches. This property is used             only when the drawing cache is enabled and translucent. The default value is auto. -->        <attr name="drawingCacheQuality">            <!-- Lets the framework decide what quality level should be used                 for the drawing cache. -->            <enum name="auto" value="0" />            <!-- Low quality. When set to low quality, the drawing cache uses a lower color                 depth, thus losing precision in rendering gradients, but uses less memory. -->            <enum name="low" value="1" />            <!-- High quality. When set to high quality, the drawing cache uses a higher                 color depth but uses more memory. -->            <enum name="high" value="2" />        </attr>        <!-- Controls whether the view's window should keep the screen on             while visible. -->        <attr name="keepScreenOn" format="boolean" />        <!-- When this attribute is set to true, the view gets its drawable state             (focused, pressed, etc.) from its direct parent rather than from itself. -->        <attr name="duplicateParentState" format="boolean" />        <!-- Defines the minimum height of the view. It is not guaranteed             the view will be able to achieve this minimum height (for example,             if its parent layout constrains it with less available height). -->        <attr name="minHeight" />        <!-- Defines the minimum width of the view. It is not guaranteed             the view will be able to achieve this minimum width (for example,             if its parent layout constrains it with less available width). -->        <attr name="minWidth" />        <!-- Boolean that controls whether a view should have sound effects             enabled for events such as clicking and touching. -->        <attr name="soundEffectsEnabled" format="boolean" />        <!-- Boolean that controls whether a view should have haptic feedback             enabled for events such as long presses. -->        <attr name="hapticFeedbackEnabled" format="boolean" />        <!-- Defines text that briefly describes content of the view. This property is used             primarily for accessibility. Since some views do not have textual             representation this attribute can be used for providing such. -->        <attr name="contentDescription" format="string" localization="suggested" />        <!-- Name of the method in this View's context to invoke when the view is             clicked. This name must correspond to a public method that takes             exactly one parameter of type View. For instance, if you specify             <code>android:onClick="sayHello"</code>, you must declare a             <code>public void sayHello(View v)</code> method of your context             (typically, your Activity). -->        <attr name="onClick" format="string" />        <!-- Defines over-scrolling behavior. This property is used only if the             View is scrollable. Over-scrolling is the ability for the user to             receive feedback when attempting to scroll beyond meaningful content. -->        <attr name="overScrollMode">            <!-- Always show over-scroll effects, even if the content fits entirely                 within the available space. -->            <enum name="always" value="0" />            <!-- Only show over-scroll effects if the content is large                 enough to meaningfully scroll. -->            <enum name="ifContentScrolls" value="1" />            <!-- Never show over-scroll effects. -->            <enum name="never" value="2" />        </attr>        <!-- alpha property of the view, as a value between 0 (completely transparent) and 1             (completely opaque). -->        <attr name="alpha" format="float" />        <!-- translation in x of the view. This value is added post-layout to the left             property of the view, which is set by its layout. -->        <attr name="translationX" format="dimension" />        <!-- translation in y of the view. This value is added post-layout to the left             property of the view, which is set by its layout. -->        <attr name="translationY" format="dimension" />        <!-- x location of the pivot point around which the view will rotate and scale.             This xml attribute sets the pivotX property of the View. -->        <attr name="transformPivotX" format="dimension" />        <!-- y location of the pivot point around which the view will rotate and scale.             This xml attribute sets the pivotY property of the View. -->        <attr name="transformPivotY" format="dimension" />        <!-- rotation of the view, in degrees. -->        <attr name="rotation" format="float" />        <!-- rotation of the view around the x axis, in degrees. -->        <attr name="rotationX" format="float" />        <!-- rotation of the view around the y axis, in degrees. -->        <attr name="rotationY" format="float" />        <!-- scale of the view in the x direction. -->        <attr name="scaleX" format="float" />        <!-- scale of the view in the y direction. -->        <attr name="scaleY" format="float" />        <!-- Determines which side the vertical scroll bar should be placed on. -->        <attr name="verticalScrollbarPosition">            <!-- Place the scroll bar wherever the system default determines. -->            <enum name="defaultPosition" value="0" />            <!-- Place the scroll bar on the left. -->            <enum name="left" value="1" />            <!-- Place the scroll bar on the right. -->            <enum name="right" value="2" />        </attr>        <!-- Specifies the type of layer backing this view. The default value is none.             Refer to {@link android.view.View#setLayerType(int, android.graphics.Paint)}             for more information.-->        <attr name="layerType">            <!-- Don't use a layer. -->            <enum name="none" value="0" />            <!-- Use a software layer. Refer to                 {@link android.view.View#setLayerType(int, android.graphics.Paint) for                 more information. -->            <enum name="software" value="1" />            <!-- Use a hardware layer. Refer to                 {@link android.view.View#setLayerType(int, android.graphics.Paint) for                 more information. -->            <enum name="hardware" value="2" />        </attr>        <!-- Defines the direction of layout drawing. This typically is associated with writing             direction of the language script used. The possible values are "ltr" for Left-to-Right,             "rtl" for Right-to-Left, "locale" and "inherit" from parent view. If there is nothing             to inherit, "locale" is used. "locale" falls back to "en-US". "ltr" is the direction             used in "en-US". The default for this attribute is "inherit". -->        <attr name="layoutDirection">            <!-- Left-to-Right -->            <enum name="ltr" value="0" />            <!-- Right-to-Left -->            <enum name="rtl" value="1" />            <!-- Inherit from parent -->            <enum name="inherit" value="2" />            <!-- Locale -->            <enum name="locale" value="3" />        </attr>        <!-- Direction of the text. A heuristic is used to determine the resolved text direction             of paragraphs. -->        <attr name="textDirection" format="integer">            <!-- Default -->            <enum name="inherit" value="0" />            <!-- Default for the root view. The first strong directional character determines the                 paragraph direction.  If there is no strong directional character, the paragraph                 direction is the view’s resolved layout direction. -->            <enum name="firstStrong" value="1" />            <!-- The paragraph direction is RTL if it contains any strong RTL character, otherwise                 it is LTR if it contains any strong LTR characters.  If there are neither, the                 paragraph direction is the view’s resolved layout direction. -->            <enum name="anyRtl" value="2" />            <!-- The paragraph direction is the same as the one held by a 60% majority of the                 characters. If there is no majority then the paragraph direction is the resolved                 layout direction of the View. -->            <enum name="charCount" value="3" />            <!-- The paragraph direction is left to right. -->            <enum name="ltr" value="4" />            <!-- The paragraph direction is right to left. -->            <enum name="rtl" value="5" />        </attr>    </declare-styleable>


View类中对应的属性解析方法:

public View(Context context, AttributeSet attrs, int defStyle) {        this(context);        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View,                defStyle, 0);        Drawable background = null;        int leftPadding = -1;        int topPadding = -1;        int rightPadding = -1;        int bottomPadding = -1;        int startPadding = -1;        int endPadding = -1;        int padding = -1;        int viewFlagValues = 0;        int viewFlagMasks = 0;        boolean setScrollContainer = false;        int x = 0;        int y = 0;        float tx = 0;        float ty = 0;        float rotation = 0;        float rotationX = 0;        float rotationY = 0;        float sx = 1f;        float sy = 1f;        boolean transformSet = false;        int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;        int overScrollMode = mOverScrollMode;        final int N = a.getIndexCount();        for (int i = 0; i < N; i++) {            int attr = a.getIndex(i);            switch (attr) {                case com.android.internal.R.styleable.View_background:                    background = a.getDrawable(attr);                    break;                case com.android.internal.R.styleable.View_padding:                    padding = a.getDimensionPixelSize(attr, -1);                    break;                 case com.android.internal.R.styleable.View_paddingLeft:                    leftPadding = a.getDimensionPixelSize(attr, -1);                    break;                case com.android.internal.R.styleable.View_paddingTop:                    topPadding = a.getDimensionPixelSize(attr, -1);                    break;                case com.android.internal.R.styleable.View_paddingRight:                    rightPadding = a.getDimensionPixelSize(attr, -1);                    break;                case com.android.internal.R.styleable.View_paddingBottom:                    bottomPadding = a.getDimensionPixelSize(attr, -1);                    break;                case com.android.internal.R.styleable.View_paddingStart:                    startPadding = a.getDimensionPixelSize(attr, -1);                    break;                case com.android.internal.R.styleable.View_paddingEnd:                    endPadding = a.getDimensionPixelSize(attr, -1);                    break;                case com.android.internal.R.styleable.View_scrollX:                    x = a.getDimensionPixelOffset(attr, 0);                    break;                case com.android.internal.R.styleable.View_scrollY:                    y = a.getDimensionPixelOffset(attr, 0);                    break;                case com.android.internal.R.styleable.View_alpha:                    setAlpha(a.getFloat(attr, 1f));                    break;                case com.android.internal.R.styleable.View_transformPivotX:                    setPivotX(a.getDimensionPixelOffset(attr, 0));                    break;                case com.android.internal.R.styleable.View_transformPivotY:                    setPivotY(a.getDimensionPixelOffset(attr, 0));                    break;                case com.android.internal.R.styleable.View_translationX:                    tx = a.getDimensionPixelOffset(attr, 0);                    transformSet = true;                    break;                case com.android.internal.R.styleable.View_translationY:                    ty = a.getDimensionPixelOffset(attr, 0);                    transformSet = true;                    break;                case com.android.internal.R.styleable.View_rotation:                    rotation = a.getFloat(attr, 0);                    transformSet = true;                    break;                case com.android.internal.R.styleable.View_rotationX:                    rotationX = a.getFloat(attr, 0);                    transformSet = true;                    break;                case com.android.internal.R.styleable.View_rotationY:                    rotationY = a.getFloat(attr, 0);                    transformSet = true;                    break;                case com.android.internal.R.styleable.View_scaleX:                    sx = a.getFloat(attr, 1f);                    transformSet = true;                    break;                case com.android.internal.R.styleable.View_scaleY:                    sy = a.getFloat(attr, 1f);                    transformSet = true;                    break;                case com.android.internal.R.styleable.View_id:                    mID = a.getResourceId(attr, NO_ID);                    break;                case com.android.internal.R.styleable.View_tag:                    mTag = a.getText(attr);                    break;                case com.android.internal.R.styleable.View_fitsSystemWindows:                    if (a.getBoolean(attr, false)) {                        viewFlagValues |= FITS_SYSTEM_WINDOWS;                        viewFlagMasks |= FITS_SYSTEM_WINDOWS;                    }                    break;                case com.android.internal.R.styleable.View_focusable:                    if (a.getBoolean(attr, false)) {                        viewFlagValues |= FOCUSABLE;                        viewFlagMasks |= FOCUSABLE_MASK;                    }                    break;                case com.android.internal.R.styleable.View_focusableInTouchMode:                    if (a.getBoolean(attr, false)) {                        viewFlagValues |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE;                        viewFlagMasks |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE_MASK;                    }                    break;                case com.android.internal.R.styleable.View_clickable:                    if (a.getBoolean(attr, false)) {                        viewFlagValues |= CLICKABLE;                        viewFlagMasks |= CLICKABLE;                    }                    break;                case com.android.internal.R.styleable.View_longClickable:                    if (a.getBoolean(attr, false)) {                        viewFlagValues |= LONG_CLICKABLE;                        viewFlagMasks |= LONG_CLICKABLE;                    }                    break;                case com.android.internal.R.styleable.View_saveEnabled:                    if (!a.getBoolean(attr, true)) {                        viewFlagValues |= SAVE_DISABLED;                        viewFlagMasks |= SAVE_DISABLED_MASK;                    }                    break;                case com.android.internal.R.styleable.View_duplicateParentState:                    if (a.getBoolean(attr, false)) {                        viewFlagValues |= DUPLICATE_PARENT_STATE;                        viewFlagMasks |= DUPLICATE_PARENT_STATE;                    }                    break;                case com.android.internal.R.styleable.View_visibility:                    final int visibility = a.getInt(attr, 0);                    if (visibility != 0) {                        viewFlagValues |= VISIBILITY_FLAGS[visibility];                        viewFlagMasks |= VISIBILITY_MASK;                    }                    break;                case com.android.internal.R.styleable.View_layoutDirection:                    // Clear any HORIZONTAL_DIRECTION flag already set                    viewFlagValues &= ~LAYOUT_DIRECTION_MASK;                    // Set the HORIZONTAL_DIRECTION flags depending on the value of the attribute                    final int layoutDirection = a.getInt(attr, -1);                    if (layoutDirection != -1) {                        viewFlagValues |= LAYOUT_DIRECTION_FLAGS[layoutDirection];                    } else {                        // Set to default (LAYOUT_DIRECTION_INHERIT)                        viewFlagValues |= LAYOUT_DIRECTION_DEFAULT;                    }                    viewFlagMasks |= LAYOUT_DIRECTION_MASK;                    break;                case com.android.internal.R.styleable.View_drawingCacheQuality:                    final int cacheQuality = a.getInt(attr, 0);                    if (cacheQuality != 0) {                        viewFlagValues |= DRAWING_CACHE_QUALITY_FLAGS[cacheQuality];                        viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK;                    }                    break;                case com.android.internal.R.styleable.View_contentDescription:                    mContentDescription = a.getString(attr);                    break;                case com.android.internal.R.styleable.View_soundEffectsEnabled:                    if (!a.getBoolean(attr, true)) {                        viewFlagValues &= ~SOUND_EFFECTS_ENABLED;                        viewFlagMasks |= SOUND_EFFECTS_ENABLED;                    }                    break;                case com.android.internal.R.styleable.View_hapticFeedbackEnabled:                    if (!a.getBoolean(attr, true)) {                        viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED;                        viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED;                    }                    break;                case R.styleable.View_scrollbars:                    final int scrollbars = a.getInt(attr, SCROLLBARS_NONE);                    if (scrollbars != SCROLLBARS_NONE) {                        viewFlagValues |= scrollbars;                        viewFlagMasks |= SCROLLBARS_MASK;                        initializeScrollbars(a);                    }                    break;                //noinspection deprecation                case R.styleable.View_fadingEdge:                    if (context.getApplicationInfo().targetSdkVersion >= ICE_CREAM_SANDWICH) {                        // Ignore the attribute starting with ICS                        break;                    }                    // With builds < ICS, fall through and apply fading edges                case R.styleable.View_requiresFadingEdge:                    final int fadingEdge = a.getInt(attr, FADING_EDGE_NONE);                    if (fadingEdge != FADING_EDGE_NONE) {                        viewFlagValues |= fadingEdge;                        viewFlagMasks |= FADING_EDGE_MASK;                        initializeFadingEdge(a);                    }                    break;                case R.styleable.View_scrollbarStyle:                    scrollbarStyle = a.getInt(attr, SCROLLBARS_INSIDE_OVERLAY);                    if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {                        viewFlagValues |= scrollbarStyle & SCROLLBARS_STYLE_MASK;                        viewFlagMasks |= SCROLLBARS_STYLE_MASK;                    }                    break;                case R.styleable.View_isScrollContainer:                    setScrollContainer = true;                    if (a.getBoolean(attr, false)) {                        setScrollContainer(true);                    }                    break;                case com.android.internal.R.styleable.View_keepScreenOn:                    if (a.getBoolean(attr, false)) {                        viewFlagValues |= KEEP_SCREEN_ON;                        viewFlagMasks |= KEEP_SCREEN_ON;                    }                    break;                case R.styleable.View_filterTouchesWhenObscured:                    if (a.getBoolean(attr, false)) {                        viewFlagValues |= FILTER_TOUCHES_WHEN_OBSCURED;                        viewFlagMasks |= FILTER_TOUCHES_WHEN_OBSCURED;                    }                    break;                case R.styleable.View_nextFocusLeft:                    mNextFocusLeftId = a.getResourceId(attr, View.NO_ID);                    break;                case R.styleable.View_nextFocusRight:                    mNextFocusRightId = a.getResourceId(attr, View.NO_ID);                    break;                case R.styleable.View_nextFocusUp:                    mNextFocusUpId = a.getResourceId(attr, View.NO_ID);                    break;                case R.styleable.View_nextFocusDown:                    mNextFocusDownId = a.getResourceId(attr, View.NO_ID);                    break;                case R.styleable.View_nextFocusForward:                    mNextFocusForwardId = a.getResourceId(attr, View.NO_ID);                    break;                case R.styleable.View_minWidth:                    mMinWidth = a.getDimensionPixelSize(attr, 0);                    break;                case R.styleable.View_minHeight:                    mMinHeight = a.getDimensionPixelSize(attr, 0);                    break;                case R.styleable.View_onClick:                    if (context.isRestricted()) {                        throw new IllegalStateException("The android:onClick attribute cannot "                                + "be used within a restricted context");                    }                    final String handlerName = a.getString(attr);                    if (handlerName != null) {                        setOnClickListener(new OnClickListener() {                            private Method mHandler;                            public void onClick(View v) {                                if (mHandler == null) {                                    try {                                        mHandler = getContext().getClass().getMethod(handlerName,                                                View.class);                                    } catch (NoSuchMethodException e) {                                        int id = getId();                                        String idText = id == NO_ID ? "" : " with id '"                                                + getContext().getResources().getResourceEntryName(                                                    id) + "'";                                        throw new IllegalStateException("Could not find a method " +                                                handlerName + "(View) in the activity "                                                + getContext().getClass() + " for onClick handler"                                                + " on view " + View.this.getClass() + idText, e);                                    }                                }                                try {                                    mHandler.invoke(getContext(), View.this);                                } catch (IllegalAccessException e) {                                    throw new IllegalStateException("Could not execute non "                                            + "public method of the activity", e);                                } catch (InvocationTargetException e) {                                    throw new IllegalStateException("Could not execute "                                            + "method of the activity", e);                                }                            }                        });                    }                    break;                case R.styleable.View_overScrollMode:                    overScrollMode = a.getInt(attr, OVER_SCROLL_IF_CONTENT_SCROLLS);                    break;                case R.styleable.View_verticalScrollbarPosition:                    mVerticalScrollbarPosition = a.getInt(attr, SCROLLBAR_POSITION_DEFAULT);                    break;                case R.styleable.View_layerType:                    setLayerType(a.getInt(attr, LAYER_TYPE_NONE), null);                    break;                case R.styleable.View_textDirection:                    mTextDirection = a.getInt(attr, DEFAULT_TEXT_DIRECTION);                    break;            }        }        a.recycle();        setOverScrollMode(overScrollMode);        if (background != null) {            setBackgroundDrawable(background);        }        mUserPaddingRelative = (startPadding >= 0 || endPadding >= 0);        // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved        // layout direction). Those cached values will be used later during padding resolution.        mUserPaddingStart = startPadding;        mUserPaddingEnd = endPadding;        if (padding >= 0) {            leftPadding = padding;            topPadding = padding;            rightPadding = padding;            bottomPadding = padding;        }        // If the user specified the padding (either with android:padding or        // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise        // use the default padding or the padding from the background drawable        // (stored at this point in mPadding*)        setPadding(leftPadding >= 0 ? leftPadding : mPaddingLeft,                topPadding >= 0 ? topPadding : mPaddingTop,                rightPadding >= 0 ? rightPadding : mPaddingRight,                bottomPadding >= 0 ? bottomPadding : mPaddingBottom);        if (viewFlagMasks != 0) {            setFlags(viewFlagValues, viewFlagMasks);        }        // Needs to be called after mViewFlags is set        if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {            recomputePadding();        }        if (x != 0 || y != 0) {            scrollTo(x, y);        }        if (transformSet) {            setTranslationX(tx);            setTranslationY(ty);            setRotation(rotation);            setRotationX(rotationX);            setRotationY(rotationY);            setScaleX(sx);            setScaleY(sy);        }        if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) {            setScrollContainer(true);        }        computeOpaqueFlags();    }


	
				
		
原创粉丝点击