強大的提示控件TextInputLayout使用以及源碼分析

    最近比較火的幾個組件Android Design Support Library包含8個新的組件,最低支持Android2.1。使用TextInputLaypout這個控件可以非常方便的做出用戶登錄界面帳號密碼輸入框的效果。

一   TextInputLayout的使用

  1.1    使用這些新的組件之前先引用庫,在 build.gradle 文件中加上這段代碼

          compile 'com.android.support:design:25.0.1'

   這裏使用TextInputLaout簡單寫了一個註冊信息,輸入姓名和密碼

  佈局代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context="com.textinputlayout.MainActivity">

  <ScrollView
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:fillViewport="false">
      <LinearLayout
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:orientation="vertical"
          >
          <android.support.design.widget.TextInputLayout
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              app:hintAnimationEnabled="true"
              app:errorEnabled="true"
              android:id="@+id/textInputId"
              >
              <AutoCompleteTextView
                  android:id="@+id/nameId"
                  android:layout_height="wrap_content"
                  android:layout_width="match_parent"
                  android:hint="@string/tipName"
                  android:inputType="textEmailAddress"
                  android:maxLines="1"
                  android:singleLine="true"
                  >

              </>

           </android.support.design.widget.TextInputLayout>
          <android.support.design.widget.TextInputLayout
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              app:hintAnimationEnabled="true"
              app:errorEnabled="true"
              >
              <EditText
                  android:id="@+id/psdId"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:hint="@string/tipPsd"
                  android:maxLines="1"
                  android:singleLine="true"
                  android:inputType="textPassword"
                  />
          </android.support.design.widget.TextInputLayout>
             <Button
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:text="@string/subBt"
                 android:textSize="18sp"
                 android:layout_marginTop="10dp"
                 />
      </LinearLayout>

  </ScrollView>
</LinearLayout>
在MainActivity中
package com.textinputlayout;

import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;

/**
 * @autor:liangyuanyuan
 * @date:2016/12/21
 * @des:textInputText demo學習
 */
public class MainActivity extends AppCompatActivity {
  private TextInputLayout mTextInputLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextInputLayout= (TextInputLayout) findViewById(R.id.textInputId);
       mTextInputLayout.getEditText().addTextChangedListener(new MinLengthWatch("輸入的文字長度最大6",mTextInputLayout));
        //開啓計時器
        mTextInputLayout.setCounterEnabled(true);
        mTextInputLayout.setCounterMaxLength(10);
    }

    class MinLengthWatch implements TextWatcher{

        private String errStr;
        private TextInputLayout mTextInputLayout;
       public  MinLengthWatch(String errStr,TextInputLayout mTextInputLayout){
            this.errStr=errStr;
           this.mTextInputLayout=mTextInputLayout;
        }
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

        }

        @Override
        public void afterTextChanged(Editable editable) {

            //檢測輸入的文字的長度是否符合要求,長度低於6
            if(mTextInputLayout.getEditText().getText().toString().length()<=6){
                mTextInputLayout.setErrorEnabled(false);
            }else{
                mTextInputLayout.setErrorEnabled(true);
                //設置錯誤的提示
                mTextInputLayout.setError(errStr);
            }
        }
    }
}
  1.2 簡單說明一下這個控件的使用

使用TextInputLayout非常簡單,它是一個ViewGroup,裏面可以包裹EditText或者AutoCompleteTextView,以下幾個屬性和方法需要聲明一下:

app:hintAnimationEnabled="true"可以開啓動畫,這個爲true時,獲得焦點的時候hint提示問題會動畫地移動上去。
app:errorEnabled="true"時,開啓錯誤提示
textInputLayout.setCounterEnabled(true);用於 開啓計數
textInputLayout.setCounterMaxLength(10);設置最大輸入限制數

textInputLayout.setError(errorStr);設置錯誤提示的信息

textInputLayout.getEditText().addTextChangedListener()用於給textInputLayout包裹的EditText設置內容變化監聽,我們可以自己重寫一個監聽實現裏面的方法進行相關邏輯的處理


1.3   效果如下:

     

注意項:

TextInputLayout中至少嵌套一個EditText。

二  TextInputLayout源碼分析

TextInputLayout繼承自LinearLayout,說明它是一個ViewGroup

[java] view plain copy
  1. public class TextInputLayout extendsLinearLayout  

先從構造函數開始看起

[java] view plain copy
  1. public TextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
  2.         // Can't call through to super(Context, AttributeSet, int) since it doesn't exist on API 10  
  3.         super(context, attrs);  
  4.   
  5.         setOrientation(VERTICAL);  
  6.         setWillNotDraw(false);  
  7.         setAddStatesFromChildren(true);  

這裏它自動設置了VERTICAL的Orientation,說明這個TextInputLayout是一個豎直的排列,那字數超過部分的提示,在哪裏添加的呢?說明在源碼中必定有添加這個提示的邏輯,這裏我們後面在討論,先繼續往下看

[java] view plain copy
  1. mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);  
  2.         mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator());  
  3.         mCollapsingTextHelper.setCollapsedTextGravity(Gravity.TOP | GravityCompat.START);  
  4.   
  5.         final TypedArray a = context.obtainStyledAttributes(attrs,  
  6.                 R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);  
  7.         mHint = a.getText(R.styleable.TextInputLayout_android_hint);  
  8.         mHintAnimationEnabled = a.getBoolean(  
  9.                 R.styleable.TextInputLayout_hintAnimationEnabled, true);  

這裏出現了一個mCollapsingTextHelper,通過它可以設置文字大小的加速動畫,FAST_OUT_SLOW_IN_INTERPOLATOR,快出慢進的效果,還有設置位置的加速器setPositionInterpolator,setCollapsedTextGravity設置摺疊文字的Gravity,看來這個mCollapsingTextHelper的作用還是很強大的,我們後面再看它的源碼,先繼續往下看

[java] view plain copy
  1. final TypedArray a = context.obtainStyledAttributes(attrs,  
  2.                 R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);  
  3.         mHint = a.getText(R.styleable.TextInputLayout_android_hint);  
  4.         mHintAnimationEnabled = a.getBoolean(  
  5.                 R.styleable.TextInputLayout_hintAnimationEnabled, true);  
  6.   
  7.         if (a.hasValue(R.styleable.TextInputLayout_android_textColorHint)) {  
  8.             mDefaultTextColor = mFocusedTextColor =  
  9.                     a.getColorStateList(R.styleable.TextInputLayout_android_textColorHint);  
  10.         }  
  11.   
  12.         final int hintAppearance = a.getResourceId(  
  13.                 R.styleable.TextInputLayout_hintTextAppearance, -1);  
  14.         if (hintAppearance != -1) {  
  15.             setHintTextAppearance(  
  16.                     a.getResourceId(R.styleable.TextInputLayout_hintTextAppearance, 0));  
  17.         }  
  18.   
  19.         mErrorTextAppearance = a.getResourceId(R.styleable.TextInputLayout_errorTextAppearance, 0);  
  20.         final boolean errorEnabled = a.getBoolean(R.styleable.TextInputLayout_errorEnabled, false);  
  21. a.recycle();  

從TypedArray中取出一些用戶給TextInputLayout設置的屬性,比如給hint設置的文字,mHintAnimationEnabled,hint內文字的動畫是否可用,還有hintAppearance的值,mErrorTextAppearance是錯誤提示文字的樣式,errorEnabled是否開啓錯誤提示

[java] view plain copy
  1. setErrorEnabled(errorEnabled);  

並通過setErrorEnabled把errorEnabled的值設置給TextInputLayout,TextInputLayout是一個ViewGroup,所以addView方法是必須的

[java] view plain copy
  1.  public void addView(View child, int index, ViewGroup.LayoutParams params) {  
  2.         if (child instanceof EditText) {  
  3.             setEditText((EditText) child);  
  4.             super.addView(child, 0, updateEditTextMargin(params));  
  5.         } else {  
  6.             // Carry on adding the View...  
  7.             super.addView(child, index, params);  
  8.         }  
  9. }  

只有當child 是 EditText的時候,會調用自身的setEditText方法,然後調用父類LinearLayout的addView方法,如果不是EditText,也調用父類的addView方法,查看setEditText方法 內部

[java] view plain copy
  1. private void setEditText(EditText editText) {  
  2.         // If we already have an EditText, throw an exception  
  3.         if (mEditText != null) {  
  4.             throw new IllegalArgumentException("We already have an EditText, can only have one");  
  5.         }  
  6.         mEditText = editText;  
  7.   
  8.         // Use the EditText's typeface, and it's text size for our expanded text  
  9.         mCollapsingTextHelper.setTypeface(mEditText.getTypeface());  
  10.         mCollapsingTextHelper.setExpandedTextSize(mEditText.getTextSize());  
  11.         mCollapsingTextHelper.setExpandedTextGravity(mEditText.getGravity());  

如果TextInputLayout內已經有了一個EditText,再添加就會報錯,使用CollapsingTextHelper把傳進來的editText的相關屬性取出進行設置

[java] view plain copy
  1. mEditText.addTextChangedListener(new TextWatcher() {  
  2.            @Override  
  3.            public void afterTextChanged(Editable s) {  
  4.                updateLabelVisibility(true)<pre name="code" class="java">  


然後給EditText設置文本變化的監聽,在文本改變之前,正在改變的時候都可以做相應的邏輯處理,往下看有更改EditText的Margin的方法

[java] view plain copy
  1. private LayoutParams updateEditTextMargin(ViewGroup.LayoutParams lp) {  
  2.         // Create/update the LayoutParams so that we can add enough top margin  
  3.         // to the EditText so make room for the label  
  4.         LayoutParams llp = lp instanceof LayoutParams ? (LayoutParams) lp : new LayoutParams(lp);  
  5.   
  6.         if (mTmpPaint == null) {  
  7.             mTmpPaint = new Paint();  
  8.         }  
  9.         mTmpPaint.setTypeface(mCollapsingTextHelper.getTypeface());  
  10.         mTmpPaint.setTextSize(mCollapsingTextHelper.getCollapsedTextSize());  
  11.         llp.topMargin = (int) -mTmpPaint.ascent();  
  12.   
  13.         return llp;  
  14. }  

設置提示文字的樣式

[java] view plain copy
  1. public void setHintTextAppearance(@StyleRes int resId) {  
  2.         mCollapsingTextHelper.setCollapsedTextAppearance(resId);  
  3.         mFocusedTextColor = ColorStateList.valueOf(mCollapsingTextHelper.getCollapsedTextColor());  
  4.   
  5.         if (mEditText != null) {  
  6.             updateLabelVisibility(false);  
  7.   
  8.             // Text size might have changed so update the top margin  
  9.             LayoutParams lp = updateEditTextMargin(mEditText.getLayoutParams());  
  10.             mEditText.setLayoutParams(lp);  
  11.             mEditText.requestLayout();  
  12.         }  
  13. }  

設置錯誤提示開啓和關閉的方法

[java] view plain copy
  1. public void setErrorEnabled(boolean enabled) {  
  2.         if (mErrorEnabled != enabled) {  
  3.             if (mErrorView != null) {  
  4.                 ViewCompat.animate(mErrorView).cancel();  
  5.             }  
  6.   
  7.             if (enabled) {  
  8.                 mErrorView = new TextView(getContext());  
  9.                 mErrorView.setTextAppearance(getContext(), mErrorTextAppearance);  
  10.                 mErrorView.setVisibility(INVISIBLE);  
  11.                 addView(mErrorView);  
  12.   
  13.                 if (mEditText != null) {  
  14.                     // Add some start/end padding to the error so that it matches the EditText  
  15.                     ViewCompat.setPaddingRelative(mErrorView, ViewCompat.getPaddingStart(mEditText),  
  16.                             0, ViewCompat.getPaddingEnd(mEditText), mEditText.getPaddingBottom());  
  17.                 }  
  18.             } else {  
  19.                 removeView(mErrorView);  
  20.                 mErrorView = null;  
  21.             }  
  22.             mErrorEnabled = enabled;  
  23.         }  
  24. }  

如果enabled爲true的時候,這裏會new一個TextView,給TextView設置文本信息和設爲可見,然後使用addView(mErrorView)方法,將其添加到TextInputLayout之中,還記得前面我們提過TextInputLayout之中肯定應該會有一個添加錯誤提示信息的方法,在這裏我們找到了,同時這裏的代碼也是值得我們進行學習的,只有當用戶設置錯誤提示爲真的時候,纔會new一個TextView,這樣是比較省性能的,接下來是setError方法,設置錯誤提示的文本信息,裏面是一些判斷和動畫的設置

[java] view plain copy
  1. public void setError(@Nullable CharSequence error) {  
  2.         if (!mErrorEnabled) {  
  3.             if (TextUtils.isEmpty(error)) {  
  4.                 // If error isn't enabled, and the error is empty, just return  
  5.                 return;  
  6.             }  
  7.             // Else, we'll assume that they want to enable the error functionality  
  8.             setErrorEnabled(true);  
  9.         }  
  10.   
  11.         if (!TextUtils.isEmpty(error)) {  
  12.             ViewCompat.setAlpha(mErrorView, 0f);  
  13.             mErrorView.setText(error);  
  14.             ViewCompat.animate(mErrorView)  
  15.                     .alpha(1f)  
  16.                     .setDuration(ANIMATION_DURATION)  
  17.                     .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)  
  18.                     .setListener(new ViewPropertyAnimatorListenerAdapter() {  
  19.                         @Override  
  20.                         public void onAnimationStart(View view) {  
  21.                             view.setVisibility(VISIBLE);  
  22.                         }  
  23.                     })  
  24.                     .start();  
  25.   
  26.             // Set the EditText's background tint to the error color  
  27.             ViewCompat.setBackgroundTintList(mEditText,  
  28.                     ColorStateList.valueOf(mErrorView.getCurrentTextColor()));  
  29.         } else {  
  30.             if (mErrorView.getVisibility() == VISIBLE) {  
  31.                 ViewCompat.animate(mErrorView)  
  32.                         .alpha(0f)  
  33.                         .setDuration(ANIMATION_DURATION)  
  34.                         .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)  
  35.                         .setListener(new ViewPropertyAnimatorListenerAdapter() {  
  36.                             @Override  
  37.                             public void onAnimationEnd(View view) {  
  38.                                 view.setVisibility(INVISIBLE);  
  39.                             }  
  40.                         }).start();  
  41.   
  42.                 // Restore the 'original' tint, using colorControlNormal and colorControlActivated  
  43.                 final TintManager tintManager = TintManager.get(getContext());  
  44.                 ViewCompat.setBackgroundTintList(mEditText,  
  45.                         tintManager.getTintList(R.drawable.abc_edit_text_material));  
  46.             }  
  47.         }  
  48.   
  49.         sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);  
  50. }  

接下來是draw方法,調用了mCollapsingTextHelper的draw方法,說明這個TextInputLayout是畫出來

[java] view plain copy
  1. <pre name="code" class="java">public void draw(Canvas canvas) {  
  2.         super.draw(canvas);  
  3.         mCollapsingTextHelper.draw(canvas);  
  4. }  

onLayout方法,會拿到EditText的Left,Right等屬性,然後使用mCollapsingTextHelper 來setExpandedBounds,設置一個Bound區域

[java] view plain copy
  1. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  2.         super.onLayout(changed, left, top, right, bottom);  
  3.   
  4.         if (mEditText != null) {  
  5.             final int l = mEditText.getLeft() + mEditText.getCompoundPaddingLeft();  
  6.             final int r = mEditText.getRight() - mEditText.getCompoundPaddingRight();  
  7.   
  8.             mCollapsingTextHelper.setExpandedBounds(l,  
  9.                     mEditText.getTop() + mEditText.getCompoundPaddingTop(),  
  10.                     r, mEditText.getBottom() - mEditText.getCompoundPaddingBottom());  
  11.   
  12.             // Set the collapsed bounds to be the the full height (minus padding) to match the  
  13.             // EditText's editable area  
  14.             mCollapsingTextHelper.setCollapsedBounds(l, getPaddingTop(),  
  15.                     r, bottom - top - getPaddingBottom());  
  16.   
  17.             mCollapsingTextHelper.recalculate();  
  18.         }  
  19. }  

上面的有一句註釋還是很重要的:設置摺疊的bounds去匹配EditText可編輯區域的高,接下來我們查看CollapsingTextHelper這個非常重要的類的代碼

[java] view plain copy
  1. public CollapsingTextHelper(View view) {  
  2.       mView = view;  
  3.   
  4.       mTextPaint = new TextPaint();  
  5.       mTextPaint.setAntiAlias(true);  
  6.   
  7.       mCollapsedBounds = new Rect();  
  8.       mExpandedBounds = new Rect();  
  9.       mCurrentBounds = new RectF();  

構造函數中會把view傳進來,而這個view就是TextInputLayout,同時new了一個TextPaint來進行文本的繪製,然後是new出來3個矩形區域,mCollapsedBounds:輸入框處於摺疊狀態下的矩形區域,mExpandedBounds:提示框獲得焦點,提示文字向上展開的矩形區域,mCurrentBounds:當前狀態下的矩形區域;往下是一大堆set方法,然後有一個setExpandedBounds方法

[java] view plain copy
  1. void setExpandedBounds(int left, int top, int right, int bottom) {  
  2.         if (!rectEquals(mExpandedBounds, left, top, right, bottom)) {  
  3.             mExpandedBounds.set(left, top, right, bottom);  
  4.             mBoundsChanged = true;  
  5.             onBoundsChanged();  
  6.         }  
  7. }  

setCollapsedBounds方法

[java] view plain copy
  1. void setCollapsedBounds(int left, int top, int right, int bottom) {  
  2.       if (!rectEquals(mCollapsedBounds, left, top, right, bottom)) {  
  3.           mCollapsedBounds.set(left, top, right, bottom);  
  4.           mBoundsChanged = true;  
  5.           onBoundsChanged();  
  6.       }  
  7.   }  

其實也沒有什麼,就是設置left,top, right, bottom,然後調用onBoundsChanged方法進行更新,接下來有setCollapsedTextAppearance方法,設置摺疊時候文字的樣式

[java] view plain copy
  1. void setCollapsedTextAppearance(int resId) {  
  2.         TypedArray a = mView.getContext().obtainStyledAttributes(resId, R.styleable.TextAppearance);  
  3.         if (a.hasValue(R.styleable.TextAppearance_android_textColor)) {  
  4.             mCollapsedTextColor = a.getColor(  
  5.                     R.styleable.TextAppearance_android_textColor, mCollapsedTextColor);  
  6.         }  
  7.         if (a.hasValue(R.styleable.TextAppearance_android_textSize)) {  
  8.             mCollapsedTextSize = a.getDimensionPixelSize(  
  9.                     R.styleable.TextAppearance_android_textSize, (int) mCollapsedTextSize);  
  10.         }  
  11.         a.recycle();  
  12.   
  13.         recalculate();  
  14. }  

setExpandedTextAppearance:設置展開狀態時文字的樣式

[java] view plain copy
  1.  void setExpandedTextAppearance(int resId) {  
  2.         TypedArray a = mView.getContext().obtainStyledAttributes(resId, R.styleable.TextAppearance);  
  3.         if (a.hasValue(R.styleable.TextAppearance_android_textColor)) {  
  4.             mExpandedTextColor = a.getColor(  
  5.                     R.styleable.TextAppearance_android_textColor, mExpandedTextColor);  
  6.         }  
  7.         if (a.hasValue(R.styleable.TextAppearance_android_textSize)) {  
  8.             mExpandedTextSize = a.getDimensionPixelSize(  
  9.                     R.styleable.TextAppearance_android_textSize, (int) mExpandedTextSize);  
  10.         }  
  11.         a.recycle();  
  12.   
  13.         recalculate();  
  14. }  

都是取出mView(傳進來的TextInputLayout)的屬性取出來進行設置,我們會發現各個方法裏都調用了recalculate()方法,也就是重新計算,我們查看一下這個方法

[java] view plain copy
  1. public void recalculate() {  
  2.         if (mView.getHeight() > 0 && mView.getWidth() > 0) {  
  3.             // If we've already been laid out, calculate everything now otherwise we'll wait  
  4.             // until a layout  
  5.             calculateBaseOffsets();  
  6.             calculateCurrentOffsets();  
  7.         }  
  8.     }  

判斷條件是當TextInputLayout的Height與Width都大於0的時候會調用calculateBaseOffsets()與calculateCurrentOffsets()方法,註釋的內容:如果TextInputLayout已經被laid out的時候,會去重新計算現在佈局的一切,否則就等待。calculateBaseOffsets()方法,用於計算基本的偏移量,注意註釋的內容:在計算摺疊狀態下的文字大小,也使用同樣的邏輯

[java] view plain copy
  1. final int collapsedAbsGravity = GravityCompat.getAbsoluteGravity(mCollapsedTextGravity,  
  2.                 mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);  
  3.         switch (collapsedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {  
  4.             case Gravity.BOTTOM:  
  5.                 mCollapsedDrawY = mCollapsedBounds.bottom;  
  6.                 break;  
  7.             case Gravity.TOP:  
  8.                 mCollapsedDrawY = mCollapsedBounds.top - mTextPaint.ascent();  
  9.                 break;  
  10.             case Gravity.CENTER_VERTICAL:  
  11.             default:  
  12.                 float textHeight = mTextPaint.descent() - mTextPaint.ascent();  
  13.                 float textOffset = (textHeight / 2) - mTextPaint.descent();  
  14.                 mCollapsedDrawY = mCollapsedBounds.centerY() + textOffset;  
  15.                 break;  
  16.         }  
  17.         switch (collapsedAbsGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {  
  18.             case Gravity.CENTER_HORIZONTAL:  
  19.                 mCollapsedDrawX = mCollapsedBounds.centerX() - (width / 2);  
  20.                 break;  
  21.             case Gravity.RIGHT:  
  22.                 mCollapsedDrawX = mCollapsedBounds.right - width;  
  23.                 break;  
  24.             case Gravity.LEFT:  
  25.             default:  
  26.                 mCollapsedDrawX = mCollapsedBounds.left;  
  27.                 break;  
  28.         }  

取出collapsedAbsGravity的值,然後和各種Gravity進行比較,然後確定mCollapsedDrawY 和mCollapsedDrawX的值

[java] view plain copy
  1. mTextPaint.setTextSize(mExpandedTextSize);  
  2.         width = mTextToDraw != null  
  3.                 ? mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0;  
  4.         final int expandedAbsGravity = GravityCompat.getAbsoluteGravity(mExpandedTextGravity,  
  5.                 mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);  
  6.         switch (expandedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {  
  7.             case Gravity.BOTTOM:  
  8.                 mExpandedDrawY = mExpandedBounds.bottom;  
  9.                 break;  
  10.             case Gravity.TOP:  
  11.                 mExpandedDrawY = mExpandedBounds.top - mTextPaint.ascent();  
  12.                 break;  
  13.             case Gravity.CENTER_VERTICAL:  
  14.             default:  
  15.                 float textHeight = mTextPaint.descent() - mTextPaint.ascent();  
  16.                 float textOffset = (textHeight / 2) - mTextPaint.descent();  
  17.                 mExpandedDrawY = mExpandedBounds.centerY() + textOffset;  
  18.                 break;  
  19.         }  
  20.         switch (expandedAbsGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {  
  21.             case Gravity.CENTER_HORIZONTAL:  
  22.                 mExpandedDrawX = mExpandedBounds.centerX() - (width / 2);  
  23.                 break;  
  24.             case Gravity.RIGHT:  
  25.                 mExpandedDrawX = mExpandedBounds.right - width;  
  26.                 break;  
  27.             case Gravity.LEFT:  
  28.             default:  
  29.                 mExpandedDrawX = mExpandedBounds.left;  
  30.                 break;  
  31.         }  

取出expandedAbsGravity的值然後和各種Gravity進行比較,然後確定mCollapsedDrawY 和mCollapsedDrawX的值,最後調用clearTexture()方法清空texture(紋理的意思)

[java] view plain copy
  1. clearTexture();  

calculateCurrentOffsets方法,通過lerp方法獲取mCurrentDrawX與mCurrentDrawY的值,如果mCollapsedTextColor != mExpandedTextColor,給mTextPaint設置顏色,而這個顏色會通過blendColors方法將mCollapsedTextColor與mExpandedTextColor進行混合,然後採用ViewCompat.postInvalidateOnAnimation方法進行刷新

[java] view plain copy
  1. private void calculateCurrentOffsets() {  
  2.         final float fraction = mExpandedFraction;  
  3.   
  4.         interpolateBounds(fraction);  
  5.         mCurrentDrawX = lerp(mExpandedDrawX, mCollapsedDrawX, fraction,  
  6.                 mPositionInterpolator);  
  7.         mCurrentDrawY = lerp(mExpandedDrawY, mCollapsedDrawY, fraction,  
  8.                 mPositionInterpolator);  
  9.   
  10.         setInterpolatedTextSize(lerp(mExpandedTextSize, mCollapsedTextSize,  
  11.                 fraction, mTextSizeInterpolator));  
  12.   
  13.         if (mCollapsedTextColor != mExpandedTextColor) {  
  14.             // If the collapsed and expanded text colors are different, blend them based on the  
  15.             // fraction  
  16.             mTextPaint.setColor(blendColors(mExpandedTextColor, mCollapsedTextColor, fraction));  
  17.         } else {  
  18.             mTextPaint.setColor(mCollapsedTextColor);  
  19.         }  
  20.   
  21.         ViewCompat.postInvalidateOnAnimation(mView);  
  22. }  

再看一下blendColors內部,也就是通過一個ratio對顏色進行計算,分別計算出ARGB

[java] view plain copy
  1. private static int blendColors(int color1, int color2, float ratio) {  
  2.         final float inverseRatio = 1f - ratio;  
  3.         float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio);  
  4.         float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio);  
  5.         float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio);  
  6.         float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio);  
  7.         return Color.argb((int) a, (int) r, (int) g, (int) b);  
  8.     }  

再看一下draw方法

[java] view plain copy
  1.  public void draw(Canvas canvas) {  
  2.         final int saveCount = canvas.save();  
  3.   
  4.         if (mTextToDraw != null && mDrawTitle) {  
  5.             float x = mCurrentDrawX;  
  6.             float y = mCurrentDrawY;  
  7.   
  8.             final boolean drawTexture = mUseTexture && mExpandedTitleTexture != null;  
  9.   
  10.             final float ascent;  
  11.             final float descent;  
  12.   
  13.             // Update the TextPaint to the current text size  
  14.             mTextPaint.setTextSize(mCurrentTextSize);  
  15.   
  16.             if (drawTexture) {  
  17.                 ascent = mTextureAscent * mScale;  
  18.                 descent = mTextureDescent * mScale;  
  19.             } else {  
  20.                 ascent = mTextPaint.ascent() * mScale;  
  21.                 descent = mTextPaint.descent() * mScale;  
  22.             }  
  23.   
  24.             if (DEBUG_DRAW) {  
  25.                 // Just a debug tool, which drawn a Magneta rect in the text bounds  
  26.                 canvas.drawRect(mCurrentBounds.left, y + ascent, mCurrentBounds.right, y + descent,  
  27.                         DEBUG_DRAW_PAINT);  
  28.             }  
  29.   
  30.             if (drawTexture) {  
  31.                 y += ascent;  
  32.             }  
  33.   
  34.             if (mScale != 1f) {  
  35.                 canvas.scale(mScale, mScale, x, y);  
  36.             }  
  37.   
  38.             if (drawTexture) {  
  39.                 // If we should use a texture, draw it instead of text  
  40.                 canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint);  
  41.             } else {  
  42.                 canvas.drawText(mTextToDraw, 0, mTextToDraw.length(), x, y, mTextPaint);  
  43.             }  
  44.         }  
  45.   
  46.         canvas.restoreToCount(saveCount);  
  47. }  

給TextPaint設置當前文字的大小,並給X,Y賦值

[java] view plain copy
  1. float x = mCurrentDrawX;  
  2.             float y = mCurrentDrawY;  
  3.   
  4.             final boolean drawTexture = mUseTexture && mExpandedTitleTexture != null;  
  5.   
  6.             final float ascent;  
  7.             final float descent;  
  8.   
  9.             // Update the TextPaint to the current text size  
  10.             mTextPaint.setTextSize(mCurrentTextSize);  

如果需要繪製紋理,則調用canvas的drawBitmap方法,否則canvas 的drawText方法,繪製文字

[java] view plain copy
  1. if (drawTexture) {  
  2.                 // If we should use a texture, draw it instead of text  
  3.                 canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint);  
  4.             } else {  
  5.                 canvas.drawText(mTextToDraw, 0, mTextToDraw.length(), x, y, mTextPaint);  
  6.             }  

還有一個calculateIsRtl方法,從右向左計算,是專門給左撇子設計的

[java] view plain copy
  1. private boolean calculateIsRtl(CharSequence text) {  
  2.         final boolean defaultIsRtl = ViewCompat.getLayoutDirection(mView)  
  3.                 == ViewCompat.LAYOUT_DIRECTION_RTL;  
  4.         return (defaultIsRtl  
  5.                 ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL  
  6.                 : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR).isRtl(text, 0, text.length());  
  7. }  

到此,源碼就基本分析完畢了。

轉自:http://blog.csdn.net/u012124438/article/details/51778845





發佈了37 篇原創文章 · 獲贊 32 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章