我們知道在android的世界各種優(yōu)美而又炫酷控件和動畫精彩紛呈,這個得益于大Google將android開源,市場上android智能手機也是大方色彩,不僅有華為、聯(lián)想、三星、小米,魅族還有現(xiàn)在異軍突起的vivo、oppo,他們基于android系統(tǒng)定制屬于自己的系統(tǒng),這使得android在不到十年的時間瘋狂掠奪手機市場份額,預計未來andriod智能手機仍然占領智能手機主要市場,android系統(tǒng)的快速普及使得Google對Android系統(tǒng)變革步伐加快,拿幾年前和現(xiàn)在的android手機對比使用,不難發(fā)現(xiàn)老版本的android手機真是卡到家了,這是Google被吐槽最多的缺憾,而如今的android已經在很多方面都進行過幾次優(yōu)化,例如2012年的黃油計劃,以及針對4.4版本對電池性能的優(yōu)化,Android Runtim虛擬機,Material Design風格,運行時權限,以及針對未來智能設備的Android Go系統(tǒng)!!!
納悶,說了那么多,好像跟本節(jié)要講的內容有毛線干系,,,(不假思索 ) 好像是耶,說了那么多把android幾次改變捋一下下而已,那我們就此入題吧....? (廢話那么多!!!---抱怨 ? ? ? ? 大蝦,見諒,見諒)
自定義控件是每個android工程師成長的必經之路,狹路相逢勇者勝,看到自定義控件不要怕,抽出神刀果斷亮劍,多次交鋒下來,你會發(fā)現(xiàn)你越來越厲害了,自定義View需要練習,現(xiàn)在的我也經常看相關的內容,時間長了不用不寫就忘了,跟大家目前一塊學習自定義View,請大蝦以后多多關照....
自定義一般分為三種,難易程度由易到難,首先難度系數(shù)較低的是自定義擴展控件,就是對已有控件進行再次封裝,以滿足自己特殊“癖好”,接著就是自定義組合控件,這個有的時候還是比較容易,有的需要多動動腦筋,那么高潮來了,最難的就是完全自定義View,我們通過直接繼承View/ViewGroup(其實也是繼承View),對控件進行完全自定義,那么今天我們一塊分享難度系數(shù)比較低的自定義組合控件吧,后面跟大家在分享一些對已有控件進行拓展,以及最后一起學習和探討姿勢高難度的完全自定義View.
假設一個應用場景,我們需要一個控件,這個控件能夠輸入內容并且根據(jù)接口校驗輸入數(shù)據(jù)正確與否,并對結果不同提示也不同,大家有時候會用到的輸入校驗框,不說話上圖
整個過程如上圖,接下來我們一點一點分析:
首先自定義一個控件繼承自RelativeLayout,然后自定義屬性,在values目錄下創(chuàng)建attrs資源文件,自定義屬性根節(jié)點為
定義的屬性如下,如圖:
我們來看一下比較重要的屬性,
input_hintText:提示文字,跟EditText屬性hint一樣
input_hintColor:提示文字顏色
inputType:輸入內容類型
input_state:枚舉輸入狀態(tài)
接著看如何自定義控件:
接觸過自定義控件的同學都知道,構造函數(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);}
第一個構造是在代碼中直接創(chuàng)建而調用,第二個以及第三個是在布局文件中創(chuàng)建調用,這里分attrs是我們在布局文件中定義的屬性集,后面我們要從這個屬性集里面取出屬性,defStyleAttr是定義在theme中的一個引用,這個引用指向一個style資源,而這個style資源包含了一些TypedArray的默認值,一般我們默認就行。
注意,我們分別在三個構造函數(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,再從這個屬性集中拿出對應的屬性。
記得后面要recycler一下,那為什么呢??
仁兄注意:使用recycle過后,是將我們之前創(chuàng)建的attrs屬性進行回收等待下一次復用,這樣,每次引用到我們自定義View的組件重新創(chuàng)建的時候,我們的自定義屬性就不會重新的重建,GC就不用頻繁的操作這個對象,防止了OOM的出現(xiàn)。
接著把我們自定義控件xml文件中對應屬性值設置個控件中各個組件進行初始化,這里強行貼圖,這樣大家必須要動手操作了,咻咻~~~(動作要快,姿勢要對)
這里我們進行簡單分析內容,大家看到很多-1,到底是啥,我們再開始對變量進行了初始化是設置了默認值,當我們再xml文件中沒有設置對應的屬性時候,我們get的值就是這個默認值,要回舉一反三哦,
inputFilters.add(new InputFilter.LengthFilter(maxLength));
edtInput.setFilters(inputFilters.toArray(new InputFilter[inputFilters.size()]));
這里的InputFilter是過濾器,我們對EditText要多輸入框進行長度控制時就需要創(chuàng)建這個LengthFilter過濾器,當然過濾器有很多,這里就不一一列舉,
最后,我們對editText設置了鍵盤輸入監(jiān)聽器(OnEditorActionListener),也就當我們按下確認的時候會進行回調,我們再回調中對輸入的內容進行校驗,所以這個監(jiān)聽很重要,想要對這個監(jiān)聽器了解更多的可以自行搜索,“多動動手哦,老司機都是擼出來的,,,,”(咻咻~~)
重點來了,墻裂敲黑板?。?!
switchState(inputState);
這又是什么鬼??? 還記得·····那年大明湖畔~~~
oh myGod? 串詞了, 事情的經過是這樣的,,, 還記得前面幾張圖片嗎?
我們的控件有很多種狀態(tài),正常輸入狀態(tài),不能輸入狀態(tài),加載狀態(tài),和加載完成以及加載錯誤狀態(tài),沒錯,就是這個方法“惹的禍”,那我接下來就來“一一拷問”它究竟是如何作案的,肯定也沒那么神奇,畢竟我們都猜到結局~~
立刻上碼,揚鞭奔騰起來,當我們設置STATE_INPUT狀態(tài)時都有哪些操作,我只分析一種情況后面的大家應該都能理解,首先我們對控件的背景顏色進行設置,例如控件是輸入狀態(tài)時背景設置成藍色的,這個rlInputView是啥呢?
原來是個布局呀,也就是我們整個控件的外面的布局,
當控件狀態(tài)是STATE_INPUT時,我們把其他驗證成功和進度都設置為GONE,輸入框通過postDelayed發(fā)送一個延時操作獲取焦點,
edtInput.postDelayed(new Runnable() {
@Override
public void run() {
edtInput.requestFocus();
}}, 200);
接下來的狀態(tài)設置跟這個差別不是很大,就是對布局種各組件進行狀態(tài)設置,隱藏還是顯示,顏色和背景以及字體等等的設置,大家細看就會的,
不過我們再設置狀態(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));}
這個作用時給用戶一個顯眼的提示,并且自動全選后可以點刪除鍵一鍵全刪,用戶體驗還是很好的,還是看圖吧,
是不是看起來還不錯呢,當你刪除的時候就可以全部刪掉,不用再長按選擇全選或者不停的“抖手”,當然,我們這樣在布局里面吧所有屬性寫死是不是感覺擴展性太差,我們對外也暴露了一些方法用來修改控件狀態(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);
}
看到這里我們控件基本說完了,是不是感覺沒那么復雜吧,“真相”終究會大告天下,講解的過程中我只是將一些重要的地方拿出來說了一下,當然還有其他地方沒有說明,如果你還有點疑惑就看看代碼吧,我會把代碼放到github上去,本人是個github新人,分析的過程有的地方并不完美,希望大蝦諒解,如有疑問和意見歡迎指正和提出,讓我們再進階的道路上一塊進步,一起提高~~
github地址:github.com/Scus5761/-View-
祝大家周末愉快?。?!