我們知道在android的世界各種優(yōu)美而又炫酷控件和動畫精彩紛呈,這個得益于大Google將android開源,市場上android智能手機(jī)也是大方色彩,不僅有華為、聯(lián)想、三星、小米,魅族還有現(xiàn)在異軍突起的vivo、oppo,他們基于android系統(tǒng)定制屬于自己的系統(tǒng),這使得android在不到十年的時間瘋狂掠奪手機(jī)市場份額,預(yù)計(jì)未來andriod智能手機(jī)仍然占領(lǐng)智能手機(jī)主要市場,android系統(tǒng)的快速普及使得Google對Android系統(tǒng)變革步伐加快,拿幾年前和現(xiàn)在的android手機(jī)對比使用,不難發(fā)現(xiàn)老版本的android手機(jī)真是卡到家了,這是Google被吐槽最多的缺憾,而如今的android已經(jīng)在很多方面都進(jìn)行過幾次優(yōu)化,例如2012年的黃油計(jì)劃,以及針對4.4版本對電池性能的優(yōu)化,Android Runtim虛擬機(jī),Material Design風(fēng)格,運(yùn)行時權(quán)限,以及針對未來智能設(shè)備的Android Go系統(tǒng)?。?!
納悶,說了那么多,好像跟本節(jié)要講的內(nèi)容有毛線干系,,,(不假思索 ) 好像是耶,說了那么多把a(bǔ)ndroid幾次改變捋一下下而已,那我們就此入題吧....? (廢話那么多?。?!---抱怨 ? ? ? ? 大蝦,見諒,見諒)
自定義控件是每個android工程師成長的必經(jīng)之路,狹路相逢勇者勝,看到自定義控件不要怕,抽出神刀果斷亮劍,多次交鋒下來,你會發(fā)現(xiàn)你越來越厲害了,自定義View需要練習(xí),現(xiàn)在的我也經(jīng)??聪嚓P(guān)的內(nèi)容,時間長了不用不寫就忘了,跟大家目前一塊學(xué)習(xí)自定義View,請大蝦以后多多關(guān)照....
自定義一般分為三種,難易程度由易到難,首先難度系數(shù)較低的是自定義擴(kuò)展控件,就是對已有控件進(jìn)行再次封裝,以滿足自己特殊“癖好”,接著就是自定義組合控件,這個有的時候還是比較容易,有的需要多動動腦筋,那么高潮來了,最難的就是完全自定義View,我們通過直接繼承View/ViewGroup(其實(shí)也是繼承View),對控件進(jìn)行完全自定義,那么今天我們一塊分享難度系數(shù)比較低的自定義組合控件吧,后面跟大家在分享一些對已有控件進(jìn)行拓展,以及最后一起學(xué)習(xí)和探討姿勢高難度的完全自定義View.
假設(shè)一個應(yīng)用場景,我們需要一個控件,這個控件能夠輸入內(nèi)容并且根據(jù)接口校驗(yàn)輸入數(shù)據(jù)正確與否,并對結(jié)果不同提示也不同,大家有時候會用到的輸入校驗(yàn)框,不說話上圖
整個過程如上圖,接下來我們一點(diǎn)一點(diǎn)分析:
首先自定義一個控件繼承自RelativeLayout,然后自定義屬性,在values目錄下創(chuàng)建attrs資源文件,自定義屬性根節(jié)點(diǎn)為
定義的屬性如下,如圖:

我們來看一下比較重要的屬性,
input_hintText:提示文字,跟EditText屬性hint一樣
input_hintColor:提示文字顏色
inputType:輸入內(nèi)容類型
input_state:枚舉輸入狀態(tài)
接著看如何自定義控件:
接觸過自定義控件的同學(xué)都知道,構(gòu)造函數(shù)有多個重載,
public WmsInputView(Context context) {? ? super(context);? ? this.context = context;? ? init(null, 0);}
public WmsInputView(Context context, AttributeSet attrs) {? ? super(context, attrs);? ? this.context = context;? ? init(attrs, 0);}
public WmsInputView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);? this.context = context;? ? init(attrs, defStyle);}
第一個構(gòu)造是在代碼中直接創(chuàng)建而調(diào)用,第二個以及第三個是在布局文件中創(chuàng)建調(diào)用,這里分attrs是我們在布局文件中定義的屬性集,后面我們要從這個屬性集里面取出屬性,defStyleAttr是定義在theme中的一個引用,這個引用指向一個style資源,而這個style資源包含了一些TypedArray的默認(rèn)值,一般我們默認(rèn)就行。
注意,我們分別在三個構(gòu)造函數(shù)中init()來初始化屬性,
private void init(AttributeSet attrs, int defStyle) {
LayoutInflater.from(context).inflate(R.layout.layout_wms_input_view, this, true);
rlInputView = (RelativeLayout) findViewById(R.id.rl_input);
iconInner = (ImageView) findViewById(R.id.icon_checked_inner);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
edtInput = (EditText) findViewById(R.id.et_input);? ? // Load attributes
final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.WmsInputView, defStyle, 0);
inputState = a.getInt(R.styleable.WmsInputView_input_state, STATE_INPUT);
hintText = a.getString(R.styleable.WmsInputView_input_hintText);
hintColor = a.getColor(R.styleable.WmsInputView_input_hintColor, hintColor);
inputColor = a.getColor(R.styleable.WmsInputView_inputColor, getResources().getColor(R.color.home_header_check_store));
inputSize = a.getDimensionPixelSize(R.styleable.WmsInputView_inputSize, inputSize);
inputType = a.getInteger(R.styleable.WmsInputView_android_inputType, InputType.TYPE_CLASS_TEXT);
maxLength = a.getInt(R.styleable.WmsInputView_android_maxLength, maxLength);
maxLines = a.getInt(R.styleable.WmsInputView_android_maxLines, maxLines);
inputText = a.getString(R.styleable.WmsInputView_android_text);
a.recycle();
initViews();
}
這里我們在布局文件寫了一個該控件的布局,該控件是由textView,EditText,ProgressBar和imageView組成,然后通過布局加載器加載該布局,
LayoutInflater.from(context).inflate(R.layout.layout_wms_input_view, this, true);
然后我們將布局里面定義的屬性通過getContext().obtainStyledAttributes()拿到TypedArray,再從這個屬性集中拿出對應(yīng)的屬性。
記得后面要recycler一下,那為什么呢??
仁兄注意:使用recycle過后,是將我們之前創(chuàng)建的attrs屬性進(jìn)行回收等待下一次復(fù)用,這樣,每次引用到我們自定義View的組件重新創(chuàng)建的時候,我們的自定義屬性就不會重新的重建,GC就不用頻繁的操作這個對象,防止了OOM的出現(xiàn)。
接著把我們自定義控件xml文件中對應(yīng)屬性值設(shè)置個控件中各個組件進(jìn)行初始化,這里強(qiáng)行貼圖,這樣大家必須要動手操作了,咻咻~~~(動作要快,姿勢要對)
這里我們進(jìn)行簡單分析內(nèi)容,大家看到很多-1,到底是啥,我們再開始對變量進(jìn)行了初始化是設(shè)置了默認(rèn)值,當(dāng)我們再xml文件中沒有設(shè)置對應(yīng)的屬性時候,我們get的值就是這個默認(rèn)值,要回舉一反三哦,
inputFilters.add(new InputFilter.LengthFilter(maxLength));
edtInput.setFilters(inputFilters.toArray(new InputFilter[inputFilters.size()]));
這里的InputFilter是過濾器,我們對EditText要多輸入框進(jìn)行長度控制時就需要創(chuàng)建這個LengthFilter過濾器,當(dāng)然過濾器有很多,這里就不一一列舉,
最后,我們對editText設(shè)置了鍵盤輸入監(jiān)聽器(OnEditorActionListener),也就當(dāng)我們按下確認(rèn)的時候會進(jìn)行回調(diào),我們再回調(diào)中對輸入的內(nèi)容進(jìn)行校驗(yàn),所以這個監(jiān)聽很重要,想要對這個監(jiān)聽器了解更多的可以自行搜索,“多動動手哦,老司機(jī)都是擼出來的,,,,”(咻咻~~)
重點(diǎn)來了,墻裂敲黑板?。。?/p>
switchState(inputState);
這又是什么鬼??? 還記得·····那年大明湖畔~~~
oh myGod? 串詞了, 事情的經(jīng)過是這樣的,,, 還記得前面幾張圖片嗎?
我們的控件有很多種狀態(tài),正常輸入狀態(tài),不能輸入狀態(tài),加載狀態(tài),和加載完成以及加載錯誤狀態(tài),沒錯,就是這個方法“惹的禍”,那我接下來就來“一一拷問”它究竟是如何作案的,肯定也沒那么神奇,畢竟我們都猜到結(jié)局~~
立刻上碼,揚(yáng)鞭奔騰起來,當(dāng)我們設(shè)置STATE_INPUT狀態(tài)時都有哪些操作,我只分析一種情況后面的大家應(yīng)該都能理解,首先我們對控件的背景顏色進(jìn)行設(shè)置,例如控件是輸入狀態(tài)時背景設(shè)置成藍(lán)色的,這個rlInputView是啥呢?

原來是個布局呀,也就是我們整個控件的外面的布局,
當(dāng)控件狀態(tài)是STATE_INPUT時,我們把其他驗(yàn)證成功和進(jìn)度都設(shè)置為GONE,輸入框通過postDelayed發(fā)送一個延時操作獲取焦點(diǎn),
edtInput.postDelayed(new Runnable() {
@Override
public void run() {
edtInput.requestFocus();
}}, 200);
接下來的狀態(tài)設(shè)置跟這個差別不是很大,就是對布局種各組件進(jìn)行狀態(tài)設(shè)置,隱藏還是顯示,顏色和背景以及字體等等的設(shè)置,大家細(xì)看就會的,
不過我們再設(shè)置狀態(tài)為STATE_ERROR時候用到了高亮和全選,
void setErrorText(String errorText) {
if (TextUtils.isEmpty(errorText)) {
return;
}
edtInput.setTextColor(ContextCompat.getColor(context, R.color.text_error));
edtInput.selectAll();
edtInput.setHighlightColor(context.getResources().getColor(R.color.bg_text_error));}
這個作用時給用戶一個顯眼的提示,并且自動全選后可以點(diǎn)刪除鍵一鍵全刪,用戶體驗(yàn)還是很好的,還是看圖吧,
是不是看起來還不錯呢,當(dāng)你刪除的時候就可以全部刪掉,不用再長按選擇全選或者不停的“抖手”,當(dāng)然,我們這樣在布局里面吧所有屬性寫死是不是感覺擴(kuò)展性太差,我們對外也暴露了一些方法用來修改控件狀態(tài)和屬性值,
public void setText(String text) {
edtInput.setText(TextUtils.isEmpty(text) ? "" : text);
edtInput.setSelection(text.length());}/** * 獲取輸入文本 * * @return */
public String getInputText() {
return edtInput.getText().toString().trim();}/** * 獲取輸入控件 * @return */
public EditText getEditInputView() {
if (edtInput != null) return edtInput;
return null;
}
public interface OnCompleteListener {
void onComplete(String inputText);
}
看到這里我們控件基本說完了,是不是感覺沒那么復(fù)雜吧,“真相”終究會大告天下,講解的過程中我只是將一些重要的地方拿出來說了一下,當(dāng)然還有其他地方?jīng)]有說明,如果你還有點(diǎn)疑惑就看看代碼吧,我會把代碼放到github上去,本人是個github新人,分析的過程有的地方并不完美,希望大蝦諒解,如有疑問和意見歡迎指正和提出,讓我們再進(jìn)階的道路上一塊進(jìn)步,一起提高~~
github地址:github.com/Scus5761/-View-
祝大家周末愉快?。。?/p>