Android:MaterialTextField(TextInputLayout之另一種實現(xiàn))

轉(zhuǎn)發(fā)請注明原創(chuàng):http://blog.csdn.net/snailbaby_soko/article/details/53893047

Google 在 2015 的 IO 大會上,給我們帶來了更加詳細的 Material Design 設(shè)計規(guī)范,同時,也給我們帶來了全新的 Android Design Support Library,在這個 support 庫里面,Google 給我們提供了更加規(guī)范的MD 設(shè)計風格的控件,這不得不說是一個良心之作。相信很多小伙子已經(jīng)體驗過其中的一些控件了,但 FRANCE 的一位設(shè)計師 Florent Champigny 卻發(fā)現(xiàn)了更有趣的實現(xiàn)MaterialTextField 。看圖

效果圖

比 Google 推薦的 TextInputLayout 動感吧,作為走在進階道路上的苦逼程序員,不能說拿來能用就行,必須知其然知其所以然,于是 fork 下來一步一步解剖代碼。
我打算站在作者的位置上思考,要實現(xiàn)這個 MaterialTextField 具體會怎么去考慮。


需求分析:
既然 Google 推薦了 TextInputLayout,那首先看看它是繼承了什么來拓展,里面還有什么可以讓我們定義的。分析了一番代碼后,盡管能拓展的地方有很多,如輸入錯誤能設(shè)置錯誤提示,框框變顏色等等,但它繼承了 LinearLayout ,也就是說布局上限制了能拓展的方向,想在它的身上實現(xiàn)這個動感的輸入框效果,可能會很棘手。

在分析代碼的時候,有意思地發(fā)現(xiàn) TextInputLayout 展開的時候用到了 ValueAnimatorCompat 來做動畫,那就簡單了。只要我弄個容器放 label、editText、icon 這些小控件,再讓他們用動畫展開和收縮就行了吧。思路上沒問題之后,就該考慮容器要怎么樣定義才方便使用了。

場景使用:
我們是針對開發(fā)者的角度來定義吧,那么怎樣才能減輕開發(fā)者的工作來快速實現(xiàn)這個效果。在業(yè)務(wù)上,開發(fā)者除了要處理 editText 的內(nèi)容是否滿足某些條件外,其它貌似也不需要他們?nèi)ヌ幚怼D敲次覀兙涂梢园堰@些 label、icon 當作成 MaterialTextField 的一個屬性,可以直接在 xml 的控件上添加的屬性。

下面是見證奇跡的時候
把夢幻變成現(xiàn)實:
第一步自定義屬性:
mtf_cardCollapsedHeight 為未展開狀態(tài)下 label 下面白色那線條的高度;
mtf_hasFocus 是否獲得焦點;
mtf_openKeyboardOnFocus 展示是是否需要打開軟鍵盤;
mtf_animationDuration 展開和收縮時動畫執(zhí)行的時間;
這些非必須屬性并非在第一時間就想到,而是在逐步編碼過程中想到,所以說不要第一時間把事情考慮的太復雜才是最重要的。

<?xml version="1.0" encoding="utf-8"?>
<resources> 
<declare-styleable name="MaterialTextField"> 
  <attr name="mtf_cardCollapsedHeight" format="dimension"/> 
  <attr name="mtf_labelColor" format="color"/> 
  <attr name="mtf_image" format="reference"/> 
  <attr name="mtf_hasFocus" format="boolean"/> 
  <attr name="mtf_animationDuration" format="integer"/> 
  <attr name="mtf_openKeyboardOnFocus" format="boolean"/> 
</declare-styleable>
</resources>```

第二步處理自定義的屬性:
```java
TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MaterialTextField);
ANIMATION_DURATION = styledAttrs.getInteger(R.styleable.MaterialTextField_mtf_animationDuration, 400);
.
.
.```

諸如此類獲取自定義的屬性就不一一貼代碼了,大家可以 fork 項目下來慢慢看。
再下來就重寫 onFinishInflate() 來獲得子控件來初始化一些參數(shù)和設(shè)置了(具體看看項目源碼),相信有些同學不太了解這個函數(shù)在什么時候調(diào)用,官方對這個周期方法的解釋為:
**onFinishInflate()**: 當View中所有的子控件 均被映射成xml后觸發(fā)。

其實我們一般使用 View 的流程是在 onCreate 中使用 setContentView 來設(shè)置要顯示 Layout 文件或直接創(chuàng)建一個 View,在當設(shè)置了 ContentView 之后系統(tǒng)會對這個 View 進行解析,然后回調(diào)當前視圖 View 中的onFinishInflate 方法。只有解析了這個 View 我們才能在這個View容器中獲取到擁有Id的組件,同樣因為系統(tǒng)解析完 View 之后才會調(diào)用 onFinishInflate 方法,所以我們自定義組件時可以 onFinishInflate 方法中獲取指定子View的引用。

第三步處理邏輯:展開的動畫、收縮的動畫,其實都用了v4包的 ViewCompat 來制作動畫。主要是過程中對那些子控件做一些位移、漸變的效果,再有就是對獲得焦點是否彈出軟鍵盤(作者的項目對軟鍵盤彈出收回的邏輯處理不太妥當,體驗上不太友好)。
```java 
public void expand() {//展開的動畫處理 
if (!expanded) { 
  ViewCompat.animate(editText).alpha(1f).setDuration(ANIMATION_DURATION); 
  ViewCompat.animate(card).scaleY(1f).setDuration(ANIMATION_DURATION); 
  ViewCompat.animate(label).alpha(0.4f).scaleX(0.7f).scaleY(0.7f).
    translationY(-labelTopMargin).setDuration(ANIMATION_DURATION);  
  ViewCompat.animate(image).alpha(1f).scaleX(1f).scaleY(1f) .setDuration(ANIMATION_DURATION);
  if (editText != null) { 
    editText.requestFocus(); 
  } 

  if (OPEN_KEYBOARD_ON_FOCUS) { 
    ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
      .showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); } expanded = true; 
  } 
}```

```java 
public void reduce() {//收縮的動畫處理 
if (expanded) { 
  final int heightInitial = getContext().getResources().getDimensionPixelOffset(R.dimen.mtf_cardHeight_final);
  ViewCompat.animate(label).alpha(1).scaleX(1).scaleY(1).
    translationY(0).setDuration(ANIMATION_DURATION); 
  ViewCompat.animate(image).alpha(0).scaleX(0.4f).scaleY(0.4f) .setDuration(ANIMATION_DURATION); 
  ViewCompat.animate(editText).alpha(1f).setUpdateListener(
      new ViewPropertyAnimatorUpdateListener() { 
      @Override 
      public void onAnimationUpdate(View view) { 
        float value = ViewCompat.getAlpha(view); //percentage 
        card.getLayoutParams().height = 
        (int) (value * (heightInitial - cardCollapsedHeight) + cardCollapsedHeight); card.requestLayout(); } }
      ) .setDuration(ANIMATION_DURATION); 
      ViewCompat.animate(card).scaleY(reducedScale).setDuration(ANIMATION_DURATION); 
     
 if (OPEN_KEYBOARD_ON_FOCUS) { 
    ((InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE)).
      toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); 
  } 
  editText.clearFocus(); expanded = false; 
  } 
}```

上舞臺:![](http://upload-images.jianshu.io/upload_images/3616302-f38fdeb02c4e96ff?imageMogr2/auto-orient/strip)其實軟鍵盤的彈出收縮邏輯換成,體驗會比較好。但是還是會出現(xiàn),當收縮狀態(tài)軟鍵盤還是不消失的小bug,如果再加上一些標記來記錄軟鍵盤的狀態(tài)來控制是否該彈出還是隱藏會好一點。
```java
imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);```
如果有什么說的不好,可以提出來。來互相傷害吧...
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,104評論 25 708
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標下拉刷新...
    皇小弟閱讀 46,877評論 22 665
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,200評論 4 61
  • 為人師表不容易、滿腹經(jīng)綸正己心;平生最怕少知道、時刻準備育人心!
    神于天圣于地閱讀 131評論 0 0
  • 總是感慨歲月匆匆,秋風四起,微涼浸潤時尤其覺得日子過得快!似乎漫漫暑假已變?yōu)檫b遠的過去,轉(zhuǎn)逝來的是對明年...
    嚴麗紅閱讀 795評論 3 1