自定義View之入門(一)

我們知道在android的世界各種優(yōu)美而又炫酷控件和動(dòng)畫精彩紛呈,這個(gè)得益于大Google將android開源,市場(chǎng)上android智能手機(jī)也是大方色彩,不僅有華為、聯(lián)想、三星、小米,魅族還有現(xiàn)在異軍突起的vivo、oppo,他們基于android系統(tǒng)定制屬于自己的系統(tǒng),這使得android在不到十年的時(shí)間瘋狂掠奪手機(jī)市場(chǎng)份額,預(yù)計(jì)未來andriod智能手機(jī)仍然占領(lǐng)智能手機(jī)主要市場(chǎng),android系統(tǒng)的快速普及使得Google對(duì)Android系統(tǒng)變革步伐加快,拿幾年前和現(xiàn)在的android手機(jī)對(duì)比使用,不難發(fā)現(xiàn)老版本的android手機(jī)真是卡到家了,這是Google被吐槽最多的缺憾,而如今的android已經(jīng)在很多方面都進(jìn)行過幾次優(yōu)化,例如2012年的黃油計(jì)劃,以及針對(duì)4.4版本對(duì)電池性能的優(yōu)化,Android Runtim虛擬機(jī),Material Design風(fēng)格,運(yùn)行時(shí)權(quán)限,以及針對(duì)未來智能設(shè)備的Android Go系統(tǒng)!!!

納悶,說了那么多,好像跟本節(jié)要講的內(nèi)容有毛線干系,,,(不假思索 ) 好像是耶,說了那么多把a(bǔ)ndroid幾次改變捋一下下而已,那我們就此入題吧....? (廢話那么多!!!---抱怨 ? ? ? ? 大蝦,見諒,見諒)

自定義控件是每個(gè)android工程師成長的必經(jīng)之路,狹路相逢勇者勝,看到自定義控件不要怕,抽出神刀果斷亮劍,多次交鋒下來,你會(huì)發(fā)現(xiàn)你越來越厲害了,自定義View需要練習(xí),現(xiàn)在的我也經(jīng)常看相關(guān)的內(nèi)容,時(shí)間長了不用不寫就忘了,跟大家目前一塊學(xué)習(xí)自定義View,請(qǐng)大蝦以后多多關(guān)照....


自定義一般分為三種,難易程度由易到難,首先難度系數(shù)較低的是自定義擴(kuò)展控件,就是對(duì)已有控件進(jìn)行再次封裝,以滿足自己特殊“癖好”,接著就是自定義組合控件,這個(gè)有的時(shí)候還是比較容易,有的需要多動(dòng)動(dòng)腦筋,那么高潮來了,最難的就是完全自定義View,我們通過直接繼承View/ViewGroup(其實(shí)也是繼承View),對(duì)控件進(jìn)行完全自定義,那么今天我們一塊分享難度系數(shù)比較低的自定義組合控件吧,后面跟大家在分享一些對(duì)已有控件進(jìn)行拓展,以及最后一起學(xué)習(xí)和探討姿勢(shì)高難度的完全自定義View.

假設(shè)一個(gè)應(yīng)用場(chǎng)景,我們需要一個(gè)控件,這個(gè)控件能夠輸入內(nèi)容并且根據(jù)接口校驗(yàn)輸入數(shù)據(jù)正確與否,并對(duì)結(jié)果不同提示也不同,大家有時(shí)候會(huì)用到的輸入校驗(yàn)框,不說話上圖

輸入狀態(tài)
校驗(yàn)狀態(tài)
1不可用狀態(tài) 2檢驗(yàn)狀態(tài)
2 檢驗(yàn)成功狀態(tài)



整個(gè)過程如上圖,接下來我們一點(diǎn)一點(diǎn)分析:

首先自定義一個(gè)控件繼承自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ù)有多個(gè)重載,

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è)構(gòu)造是在代碼中直接創(chuàng)建而調(diào)用,第二個(gè)以及第三個(gè)是在布局文件中創(chuàng)建調(diào)用,這里分attrs是我們?cè)诓季治募卸x的屬性集,后面我們要從這個(gè)屬性集里面取出屬性,defStyleAttr是定義在theme中的一個(gè)引用,這個(gè)引用指向一個(gè)style資源,而這個(gè)style資源包含了一些TypedArray的默認(rèn)值,一般我們默認(rèn)就行。

注意,我們分別在三個(gè)構(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();

}

這里我們?cè)诓季治募懥艘粋€(gè)該控件的布局,該控件是由textView,EditText,ProgressBar和imageView組成,然后通過布局加載器加載該布局,

LayoutInflater.from(context).inflate(R.layout.layout_wms_input_view, this, true);

然后我們將布局里面定義的屬性通過getContext().obtainStyledAttributes()拿到TypedArray,再從這個(gè)屬性集中拿出對(duì)應(yīng)的屬性。

記得后面要recycler一下,那為什么呢??

仁兄注意:使用recycle過后,是將我們之前創(chuàng)建的attrs屬性進(jìn)行回收等待下一次復(fù)用,這樣,每次引用到我們自定義View的組件重新創(chuàng)建的時(shí)候,我們的自定義屬性就不會(huì)重新的重建,GC就不用頻繁的操作這個(gè)對(duì)象,防止了OOM的出現(xiàn)。

接著把我們自定義控件xml文件中對(duì)應(yīng)屬性值設(shè)置個(gè)控件中各個(gè)組件進(jìn)行初始化,這里強(qiáng)行貼圖,這樣大家必須要?jiǎng)邮植僮髁耍葸輣~~(動(dòng)作要快,姿勢(shì)要對(duì))

這里我們進(jìn)行簡(jiǎn)單分析內(nèi)容,大家看到很多-1,到底是啥,我們?cè)匍_始對(duì)變量進(jìn)行了初始化是設(shè)置了默認(rèn)值,當(dāng)我們?cè)賦ml文件中沒有設(shè)置對(duì)應(yīng)的屬性時(shí)候,我們get的值就是這個(gè)默認(rèn)值,要回舉一反三哦,

inputFilters.add(new InputFilter.LengthFilter(maxLength));

edtInput.setFilters(inputFilters.toArray(new InputFilter[inputFilters.size()]));

這里的InputFilter是過濾器,我們對(duì)EditText要多輸入框進(jìn)行長度控制時(shí)就需要?jiǎng)?chuàng)建這個(gè)LengthFilter過濾器,當(dāng)然過濾器有很多,這里就不一一列舉,



最后,我們對(duì)editText設(shè)置了鍵盤輸入監(jiān)聽器(OnEditorActionListener),也就當(dāng)我們按下確認(rèn)的時(shí)候會(huì)進(jìn)行回調(diào),我們?cè)倩卣{(diào)中對(duì)輸入的內(nèi)容進(jìn)行校驗(yàn),所以這個(gè)監(jiān)聽很重要,想要對(duì)這個(gè)監(jiān)聽器了解更多的可以自行搜索,“多動(dòng)動(dòng)手哦,老司機(jī)都是擼出來的,,,,”(咻咻~~)

重點(diǎn)來了,墻裂敲黑板!!!

switchState(inputState);

這又是什么鬼??? 還記得·····那年大明湖畔~~~

oh myGod? 串詞了, 事情的經(jīng)過是這樣的,,, 還記得前面幾張圖片嗎?

我們的控件有很多種狀態(tài),正常輸入狀態(tài),不能輸入狀態(tài),加載狀態(tài),和加載完成以及加載錯(cuò)誤狀態(tài),沒錯(cuò),就是這個(gè)方法“惹的禍”,那我接下來就來“一一拷問”它究竟是如何作案的,肯定也沒那么神奇,畢竟我們都猜到結(jié)局~~

立刻上碼,揚(yáng)鞭奔騰起來,當(dāng)我們?cè)O(shè)置STATE_INPUT狀態(tài)時(shí)都有哪些操作,我只分析一種情況后面的大家應(yīng)該都能理解,首先我們對(duì)控件的背景顏色進(jìn)行設(shè)置,例如控件是輸入狀態(tài)時(shí)背景設(shè)置成藍(lán)色的,這個(gè)rlInputView是啥呢?


原來是個(gè)布局呀,也就是我們整個(gè)控件的外面的布局,

當(dāng)控件狀態(tài)是STATE_INPUT時(shí),我們把其他驗(yàn)證成功和進(jìn)度都設(shè)置為GONE,輸入框通過postDelayed發(fā)送一個(gè)延時(shí)操作獲取焦點(diǎn),

edtInput.postDelayed(new Runnable() {

@Override

public void run() {

edtInput.requestFocus();

}}, 200);

接下來的狀態(tài)設(shè)置跟這個(gè)差別不是很大,就是對(duì)布局種各組件進(jìn)行狀態(tài)設(shè)置,隱藏還是顯示,顏色和背景以及字體等等的設(shè)置,大家細(xì)看就會(huì)的,

不過我們?cè)僭O(shè)置狀態(tài)為STATE_ERROR時(shí)候用到了高亮和全選,

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));}

這個(gè)作用時(shí)給用戶一個(gè)顯眼的提示,并且自動(dòng)全選后可以點(diǎn)刪除鍵一鍵全刪,用戶體驗(yàn)還是很好的,還是看圖吧,

是不是看起來還不錯(cuò)呢,當(dāng)你刪除的時(shí)候就可以全部刪掉,不用再長按選擇全選或者不停的“抖手”,當(dāng)然,我們這樣在布局里面吧所有屬性寫死是不是感覺擴(kuò)展性太差,我們對(duì)外也暴露了一些方法用來修改控件狀態(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ù)雜吧,“真相”終究會(huì)大告天下,講解的過程中我只是將一些重要的地方拿出來說了一下,當(dāng)然還有其他地方?jīng)]有說明,如果你還有點(diǎn)疑惑就看看代碼吧,我會(huì)把代碼放到github上去,本人是個(gè)github新人,分析的過程有的地方并不完美,希望大蝦諒解,如有疑問和意見歡迎指正和提出,讓我們?cè)龠M(jìn)階的道路上一塊進(jìn)步,一起提高~~

github地址:github.com/Scus5761/-View-

祝大家周末愉快!!!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,202評(píng)論 3 426
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,742評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,297評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,688評(píng)論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,875評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,438評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,183評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,384評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,612評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評(píng)論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,093評(píng)論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,330評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容