【自定义控件】自定义属性
做 Android布局是件很享受的事,這得益于他良好的xml方式。使用xml可以快速 有效的為軟件定義界面。可是有時候我們總感覺官方定義的一些基本組件不夠用,自定義組件就不可避免了。那么如何才能做到像官方提供的那些組件一樣用xml 來定義他的屬性呢?現(xiàn)在我們就來討論一下他的用法。
一、在res/values文件下定義一個attrs.xml文件,代碼如下:
<?xml version="1.0" encoding="utf-8"?>?
<resources>?
??? <declare-styleable name="ToolBar">?
??????? <attr name="buttonNum" format="integer"/>?
??????? <attr name="itemBackground" format="reference|color"/>?
??? </declare-styleable>?
</resources>
二、在布局xml中如下使用該屬性:
<?xml version="1.0" encoding="utf-8"?>?
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"?
??? xmlns:toolbar="http://schemas.android.com/apk/res/cn.zzm.toolbar"?
??? android:orientation="vertical"?
??? android:layout_width="fill_parent"?
??? android:layout_height="fill_parent"?
??? >?
??? <cn.zzm.toolbar.ToolBar android:id="@+id/gridview_toolbar"?
??????? android:layout_width="fill_parent"?
??????? android:layout_height="wrap_content"?
??????? android:layout_alignParentBottom="true"?
??????? android:background="@drawable/control_bar"?
??????? android:gravity="center"?
??????? toolbar:buttonNum="5"?
??????? toolbar:itemBackground="@drawable/control_bar_item_bg"/>?
</RelativeLayout>
三、在自定義組件中,可以如下獲得xml中定義的值:
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ToolBar);?
buttonNum = a.getInt(R.styleable.ToolBar_buttonNum, 5);?
itemBg = a.getResourceId(R.styleable.ToolBar_itemBackground, -1);
a.recycle();
就這么簡單的三步,即可完成對自定義屬性的使用。
*********************************************************************
好了,基本用法已經(jīng)講完了,現(xiàn)在來看看一些注意點和知識點吧。
首先來看看attrs.xml文件。
該文件是定義屬 性名和格式的地方,需要用<declare-styleable name="ToolBar"></declare-styleable>包圍所有屬性。其中name為該屬性集的名字,主要用途是標 識該屬性集。那在什么地方會用到呢?主要是在第三步。看到?jīng)]?在獲取某屬性標識時,用 到"R.styleable.ToolBar_buttonNum",很顯然,他在每個屬性前面都加了"ToolBar_"。
在來看看各種屬性都有些什么類型吧:string , integer , dimension , reference , color , enum.
前面幾種的聲明方式都是一致的,例如:<attr name="buttonNum" format="integer"/>。?
只有enum是不同的,用法舉例:
<attr name="testEnum">?
??? <enum name="fill_parent" value="-1"/>?
??? <enum name="wrap_content" value="-2"/>?
</attr>
如果該屬性可同時傳兩種不同的屬性,則可以用“|”分割開即可。
?
讓我們再來看看布局xml中需要注意的事項。
首先得聲明一下:xmlns:toolbar=http://schemas.android.com/apk/res/cn.zzm.toolbar?
注意,“toolbar”可以換成其他的任何名字,后面的url地址必須最后一部分必須用上自定義組件的包名。自定義屬性了,在屬性名前加上“toolbar”即可。
?
最后來看看java代碼中的注意事項。
在自定義組件的構(gòu)造函數(shù)中,用
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ToolBar);
來獲得對屬性集 的引用,然后就可以用“a”的各種方法來獲取相應(yīng)的屬性值了。這里需要注意的是,如果使用的方法和獲取值的類型不對的話,則會返回默 認值。因此,如果一個屬性是帶兩個及以上不用類型的屬性,需要做多次判斷,知道讀取完畢后才能判斷應(yīng)該賦予何值。當然,在取完值的時候別忘了回收資源哦!
屬性詳解:
1. reference:參考某一資源ID。(1)屬性定義: ? ??????????? <declare-styleable name = "名稱"> ?????????????????? <attr name = "background" format = "reference" /> ??????????? </declare-styleable> ? ????(2)屬性使用: ? ???????????? <ImageView ???????????????????? android:layout_width = "42dip" ?????????????????????android:layout_height = "42dip" ???????????????????? android:background?= "@drawable/圖片ID" ???????????????????? /> ? 2. color:顏色值。 ? ????(1)屬性定義: ? ??????????? <declare-styleable name = "名稱"> ?????????????????? <attr name = "textColor" format = "color" /> ??????????? </declare-styleable> ? ??? (2)屬性使用: ? ??????????? <TextView ???????????????????? android:layout_width = "42dip" ?????????????????????android:layout_height = "42dip" ???????????????????? android:textColor?= "#00FF00" ???????????????????? /> ? 3. boolean:布爾值。 ? ??? (1)屬性定義: ? ??????????? <declare-styleable name = "名稱"> ?????????????????? <attr name = "focusable" format = "boolean" /> ??????????? </declare-styleable> ? ????(2)屬性使用: ? ??????????? <Button ??????????????????? android:layout_width = "42dip" ??????????????????? android:layout_height = "42dip" ??????????????????? android:focusable?= "true" ??????????????????? /> ? 4. dimension:尺寸值。 ? ??? (1)屬性定義: ? ??????????? <declare-styleable name = "名稱"> ?????????????????? <attr name = "layout_width" format = "dimension" /> ??????????? </declare-styleable> ? ????(2)屬性使用: ? ??????????? <Button ??????????????????? android:layout_width?= "42dip" ??????????????????? android:layout_height?= "42dip" ??????????????????? /> ? 5. float:浮點值。 ? ??? (1)屬性定義: ? ??????????? <declare-styleable name = "AlphaAnimation"> ?????????????????? <attr name = "fromAlpha" format = "float" /> ?????????????????? <attr name = "toAlpha" format = "float" /> ??????????? </declare-styleable> ? ????(2)屬性使用: ? ??????????? <alpha ?????????????????? android:fromAlpha?= "1.0" ?????????????????? android:toAlpha?= "0.7" ?????????????????? /> ? 6. integer:整型值。 ? ??? (1)屬性定義: ? ??????????? <declare-styleable name = "AnimatedRotateDrawable"> ?????????????????? <attr name = "visible" /> ?????????????????? <attr name = "frameDuration" format="integer" /> ?????????????????? <attr name = "framesCount" format="integer" /> ?????????????????? <attr name = "pivotX" /> ?????????????????? <attr name = "pivotY" /> ?????????????????? <attr name = "drawable" /> ??????????? </declare-styleable> ? ????(2)屬性使用: ? ??????????? <animated-rotate ?????????????????? xmlns:android = "http://schemas.android.com/apk/res/android"?? ?????????????????? android:drawable = "@drawable/圖片ID"?? ???????????????? ? android:pivotX = "50%"?? ?????????????????? android:pivotY = "50%"?? ?????????????????? android:framesCount?= "12"?? ?????????????????? android:frameDuration?= "100" ???????????????????/> ? 7. string:字符串。 ? ??? (1)屬性定義: ? ??????????? <declare-styleable name = "MapView"> ?????????????????? <attr name = "apiKey" format = "string" /> ????????????</declare-styleable> ? ????(2)屬性使用: ? ??????????? <com.google.android.maps.MapView ??????????????????? android:layout_width = "fill_parent" ????????????????????android:layout_height = "fill_parent" ????????????????????android:apiKey?= "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g" ??????????????????? /> ? 8. fraction:百分數(shù)。 ? ??? (1)屬性定義: ? ??????????? <declare-styleable name="RotateDrawable"> ?????????????????? <attr name = "visible" /> ?????????????????? <attr name = "fromDegrees" format = "float" /> ?????????????????? <attr name = "toDegrees" format = "float" /> ?????????????????? <attr name = "pivotX" format = "fraction" /> ?????????????????? <attr name = "pivotY" format = "fraction" /> ?????????????????? <attr name = "drawable" /> ????????????</declare-styleable> ? ????(2)屬性使用: ? ??????????? <rotate ?????????????????? xmlns:android = "http://schemas.android.com/apk/res/android"? ???????????? android:interpolator = "@anim/動畫ID" ?????????????????? android:fromDegrees = "0"? ???????????? android:toDegrees = "360" ?????????????????? android:pivotX?= "200%" ?????????????????? android:pivotY?= "300%"? ???????????? android:duration = "5000" ?????????????????? android:repeatMode = "restart" ???????????????????android:repeatCount = "infinite" ?????????????????? /> ? 9. enum:枚舉值。 ? ??? (1)屬性定義: ? ??????????? <declare-styleable name="名稱"> ?????????????????? <attr name="orientation"> ???????????????????????? ?<enum?name="horizontal" value="0" /> ??????????????????????????<enum?name="vertical" value="1" /> ???????????????????</attr>???????????? ??????????? </declare-styleable> ? ????(2)屬性使用: ? ????????????<LinearLayout ??????????????????? xmlns:android = "http://schemas.android.com/apk/res/android" ??????????????????? android:orientation?= "vertical" ??????????????????? android:layout_width = "fill_parent" ??????????????????? android:layout_height = "fill_parent" ??????????????????? > ????????????</LinearLayout> ? 10. flag:位或運算。 ? ???? (1)屬性定義: ? ???????????? <declare-styleable name="名稱"> ??????????????????? <attr name="windowSoftInputMode"> ??????????????????????????? <flag?name = "stateUnspecified" value = "0" /> ??????????????????????????? <flag?name = "stateUnchanged" value = "1" /> ??????????????????????????? <flag?name = "stateHidden" value = "2" /> ??????????????????????????? <flag?name =?"stateAlwaysHidden" value = "3" /> ??????????????????????????? <flag?name = "stateVisible" value = "4" /> ??????????????????????????? <flag?name = "stateAlwaysVisible" value = "5" /> ??????????????????????????? <flag?name = "adjustUnspecified" value = "0x00" /> ??????????????????????????? <flag?name = "adjustResize" value = "0x10" /> ??????????????????????????? <flag?name = "adjustPan" value = "0x20" /> ??????????????????????????? <flag?name = "adjustNothing" value = "0x30" /> ???????????????????? </attr>????????? ???????????? </declare-styleable> ? ???? (2)屬性使用: ? ????????????<activity ?????????????????? android:name = ".StyleAndThemeActivity" ?????????????????? android:label = "@string/app_name" ?????????????????? android:windowSoftInputMode?= "stateUnspecified | stateUnchanged | stateHidden"> ???????????????????<intent-filter> ????????????????????????? <action android:name = "android.intent.action.MAIN" /> ????????????????????????? <category android:name = "android.intent.category.LAUNCHER" /> ?????????????????? </intent-filter> ???????????? </activity> ? ?????注意: ? ?????屬性定義時可以指定多種類型值。 ? ??? (1)屬性定義: ? ??????????? <declare-styleable name = "名稱"> ?????????????????? <attr name = "background" format = "reference|color" /> ??????????? </declare-styleable> ? ????(2)屬性使用: ? ???????????? <ImageView ???????????????????? android:layout_width = "42dip" ?????????????????????android:layout_height = "42dip" ???????????????????? android:background?= "@drawable/圖片ID|#00FF00" ???????????????????? />自定義組合控件:第一個實現(xiàn)一個帶圖片和文字的按鈕,如圖所示:整個過程可以分四步走。第一步,定義一個layout,實現(xiàn)按鈕內(nèi)部的布局。代碼如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/iv" android:src="@drawable/confirm" android:paddingTop="5dip" android:paddingBottom="5dip" android:paddingLeft="40dip" android:layout_gravity="center_vertical" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="確定" android:textColor="#000000" android:id="@+id/tv" android:layout_marginLeft="8dip" android:layout_gravity="center_vertical" /> </LinearLayout>這個xml實現(xiàn)一個左圖右字的布局,接下來寫一個類繼承LinearLayout,導(dǎo)入剛剛的布局,并且設(shè)置需要的方法,從而使的能在代碼中控制這個自定義控件內(nèi)容的顯示。代碼如下:
package com.notice.ib;import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; public class ImageBt extends LinearLayout { private ImageView iv; private TextView tv; public ImageBt(Context context) { this(context, null); } public ImageBt(Context context, AttributeSet attrs) { super(context, attrs); // 導(dǎo)入布局 LayoutInflater.from(context).inflate(R.layout.custombt, this, true); iv = (ImageView) findViewById(R.id.iv); tv = (TextView) findViewById(R.id.tv); } /** * 設(shè)置圖片資源 */ public void setImageResource(int resId) { iv.setImageResource(resId); } /** * 設(shè)置顯示的文字 */ public void setTextViewText(String text) { tv.setText(text); } }第三步,在需要使用這個自定義控件的layout中加入這控件,只需要在xml中加入即可。方法如下:
<RelativeLayoutandroid:orientation="horizontal"android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" > <com.notice.ib.ImageBt android:id="@+id/bt_confirm" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_alignParentBottom="true" android:background="@drawable/btbg" android:clickable="true" android:focusable="true" /> <com.notice.ib.ImageBt android:id="@+id/bt_cancel" android:layout_toRightOf="@id/bt_confirm" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_alignParentBottom="true" android:background="@drawable/btbg" android:clickable="true" android:focusable="true" /> </RelativeLayout>注意的是,控件標簽使用完整的類名即可。為了給按鈕一個點擊效果,你需要給他一個selector背景,這里就不說了。
最后一步,即在activity中設(shè)置該控件的內(nèi)容。當然,在xml中也可以設(shè)置,但是只能設(shè)置一個,當我們需要兩次使用這樣的控件,并且顯示內(nèi)容 不同時就不行了。在activity中設(shè)置也非常簡單,我們在ImageBt這個類中已經(jīng)寫好了相應(yīng)的方法,簡單調(diào)用即可。代碼如下:
public class MainActivity extends Activity {private ImageBt ib1; private ImageBt ib2; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.login); ib1 = (ImageBt) findViewById(R.id.bt_confirm); ib2 = (ImageBt) findViewById(R.id.bt_cancel); ib1.setTextViewText("確定"); ib1.setImageResource(R.drawable.confirm); ib2.setTextViewText("取消"); ib2.setImageResource(R.drawable.cancel); ib1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //在這里可以實現(xiàn)點擊事件 } }); } }這樣,一個帶文字和圖片的組合按鈕控件就完成了。這樣梳理一下,使用還是非常簡單的。組合控件能做的事還非常多,主要是在類似上例中的ImageBt類中寫好要使用的方法即可。
再來看一個組合控件,帶刪除按鈕的EidtText。即在用戶輸入后,會出現(xiàn)刪除按鈕,點擊即可取消用戶輸入。
定義方法和上例一樣。首先寫一個自定義控件的布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:id="@+id/et" android:layout_width="fill_parent" android:layout_height="wrap_content" android:singleLine="true" /> <ImageButton android:id="@+id/ib" android:visibility="gone" android:src="@drawable/menu_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#00000000" android:layout_alignRight="@+id/et" /> </RelativeLayout>實現(xiàn)輸入框右側(cè)帶按鈕效果,注意將按鈕隱藏。然后寫一個EditCancel類,實現(xiàn)刪除用戶輸入功能。這里用到了TextWatch這個接口,監(jiān)聽輸入框中的文字變化。使用也很簡單,實現(xiàn)他的三個方法即可。看代碼:
package com.notice.ce;import android.content.Context; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; public class EditCancel extends LinearLayout implements EdtInterface { ImageButton ib; EditText et; public EditCancel(Context context) { super(context); } public EditCancel(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.custom_editview, this, true); init(); } private void init() { ib = (ImageButton) findViewById(R.id.ib); et = (EditText) findViewById(R.id.et); et.addTextChangedListener(tw);// 為輸入框綁定一個監(jiān)聽文字變化的監(jiān)聽器 // 添加按鈕點擊事件 ib.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { hideBtn();// 隱藏按鈕 et.setText("");// 設(shè)置輸入框內(nèi)容為空 } }); } // 當輸入框狀態(tài)改變時,會調(diào)用相應(yīng)的方法 TextWatcher tw = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // TODO Auto-generated method stub } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub } // 在文字改變后調(diào)用 @Override public void afterTextChanged(Editable s) { if (s.length() == 0) { hideBtn();// 隱藏按鈕 } else { showBtn();// 顯示按鈕 } } }; @Override public void hideBtn() { // 設(shè)置按鈕不可見 if (ib.isShown()) ib.setVisibility(View.GONE); } @Override public void showBtn() { // 設(shè)置按鈕可見 if (!ib.isShown()) ib.setVisibility(View.VISIBLE); } } interface EdtInterface { public void hideBtn(); public void showBtn(); }在TextWatch接口的afterTextChanged方法中對文字進行判斷,若長度為0,就隱藏按鈕,否則,顯示按鈕。
另外,實現(xiàn)ImageButton(即那個叉)的點擊事件,刪除輸入框中的內(nèi)容,并隱藏按鈕。
后面兩步的實現(xiàn)就是加入到實際布局中,就不再寫出來了,和上例的步驟一樣的。最后顯示效果如圖:
?
?
學會靈活的使用組合控件,對UI開發(fā)會有很大幫助。
Android 自定義控件外觀
首先我們看下系統(tǒng)的RadioButton:?
RadioButton長成什么樣子是由其Background、Button等屬性決定的,Android系統(tǒng)?
使用style定義了默認的屬性,在android源碼?
android/frameworks/base/core/res/res/values/styles.xml中可以看到默認的定義:
即其背景圖是btn_radio_label_background,其button的樣子是btn_radio?
btn_radio_label_background是什么??
其路徑是android/frameworks/base/core/res/res/drawable-mdpi/btn_radio_label_background.9.png?
可以看到是一個NinePatch圖片,用來做背景,可以拉伸填充。?
btn_radio是什么??
其路徑是android/frameworks/base/core/res/res/drawable/btn_radio.xml?
是個xml定義的drawable,打開看其內(nèi)容:
定義了不同狀態(tài)下radioButton長成什么樣子。?
如果不知道selector是什么,就要去看下Android SDK文檔中Dev Guide->Application Resources->Resource Types。?
以下面一個item為例:?
<item android:state_checked="true" android:state_pressed="true"?
????????? android:drawable="@drawable/btn_radio_on_pressed" />?
意思即為當radiobutton被選中時,并且被按下時,其Button應(yīng)該長成btn_radio_on_pressed這個樣子。?
?
文件是android/frameworks/base/core/res/res/drawable-mdpi/btn_radio_on_pressed.png?
drawable的item中可以有以下屬性:?
android:drawable="@[package:]drawable/drawable_resource"
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_selected=["true" | "false"]
android:state_active=["true" | "false"]
android:state_checkable=["true" | "false"]?
android:state_checked=["true" | "false"]?
android:state_enabled=["true" | "false"]?
android:state_window_focused=["true" | "false"]?
當按鈕的狀態(tài)和某個item匹配后,就會使用此item定義的drawable作為按鈕圖片。?
從上面分析我們?nèi)绻薷腞adioButton的外觀,那么步驟應(yīng)該是:?
(1)制作一個9patch的圖片作為背景圖?
準備一副PNG圖片,其中白色為透明色,是否需要透明各人根據(jù)自己需要決定。?
?
運行SDK/tools/draw9patch?
在可伸縮的范圍周圍加上黑色的線告知系統(tǒng)這些區(qū)域可以伸縮。?
制作完的圖片,周圍多了黑色線。?
?
(2)針對不同的狀態(tài)提供按鈕圖片?
enabled, on: 紫色外框、紅色中心點?
?
enabled, off:只有紫色外框?
?
enabled, on, pressed:黃色外框,紅色中心點?
?
enabled, off, pressed:黃色外框?
?
disabled, on: 灰色外框、灰色中心點?
?
disabled, off: 灰色外框?
?
其余的狀態(tài)此處就不再定義。?
(3)使用xml描述一個drawable?
在res/drawable/創(chuàng)建custom_radio_btn.xml
Item順序是有講究的,條件限定越細致,則應(yīng)該放到前面。比如這兒如果把1,2行和3,4行的item交換,那么pressed的就永遠無法觸發(fā)了,因為有item已經(jīng)滿足條件返回了。可以理解為代碼中的if語句。?
(4)創(chuàng)建一個自定義的style,并應(yīng)用到RaidioButton的style屬性上
運行ap即可看到此RadioButton的外觀已經(jīng)改變,此demo可以看到文字被按鈕遮蓋了一部分,?
這兒是因第一步制作9patch圖片時沒有留出按鈕圖片空間來,稍作修改即可。?
轉(zhuǎn)載于:https://www.cnblogs.com/jasonxcj/p/5037567.html
總結(jié)
以上是生活随笔為你收集整理的【自定义控件】自定义属性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自信!京东方预计2023年柔性AMOLE
- 下一篇: 小米回应汽车营销负责人周钘离职 因个人及