完美实现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(); }
- 完美实现Android自定义控件---以自定义带图片和文本的Button为例
- android之实现带图片和文本的Button
- android自定义控件Button 带图片文字
- 【Android学习】自定义View的实现----以圆形图片控件为例
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Androidの实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- Android实现自定义带文字和图片的Button
- android 简单的自定义UI(以Button为例)
- Android自定义圆形图片和文本
- 可靠UDP简述
- PB获取文件最后修改时间
- java数据连接池 c3p0 dbcp proxool
- RTAI用户空间编程
- AppStore审核指南
- 完美实现Android自定义控件---以自定义带图片和文本的Button为例
- c# String.Format数字格式化输出 如 {0:N2} {0:D2} {0:C2}
- 设计有效的“用户行为与反馈效应”循环
- 外连接小解
- 构造顺序,析构顺序
- 帮助你搜索免费矢量,图标和PSD的搜索引擎 - Freepik
- Java 中常用的数据源
- 山东省第三届ACM/ICPC竞赛
- oracle数据库的建立过程