1:Android5.0中TextInputLayout和Snackbar的使用。

Android5.0出來已經老長時間了,但是這裏面好多控件小編使用的還不是很熟練,本篇文章就主要學習TextInputLayout和Snackbar。

TextInputLayout&&Snackbar

1:來看谷歌官方給出的TextInputLayout的定義。

這裏刪除了一些作用不大的代碼,只留下來小編需要說明的。

package android.support.design.widget;

/**
 * Layout which wraps an {@link android.widget.EditText} (or descendant) to show a floating label
 * when the hint is hidden due to the user inputting text.
 */
public class TextInputLayout extends LinearLayout {

    private static final int ANIMATION_DURATION = 200;
    private static final int INVALID_MAX_LENGTH = -1;

    private static final String LOG_TAG = "TextInputLayout";

    private final FrameLayout mInputFrame;

從谷歌的官方程序中可以得到:TextInputLayout繼承自LinearLayout,且在android.support.design包下,因此要想使用此控件必須要在程序中導入design包。如

compile 'com.android.support:design:24+'

根據上面的英文解釋可以知道,TextInputLayout可以在Edittext的提示信息隱藏的時候顯示一個浮動的標籤,現在好多APP的登陸頁面都是這樣設計的,今天小編就詳細使用以下此控件,來看運行結果。

image

首先:

新建一個EmptyActivity的項目,小編這裏命名是TestTextInputLayout,並在build.gradle(Mode app)中添加design包的依賴。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:24.2.1'
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:design:24+'
}
其次:

在activity_main.xml中寫效果圖的佈局文件

最外層佈局需要加上:
xmlns:app=”http://schemas.android.com/apk/res-auto”才能使用TextInputLayout的一些屬性。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:gravity="center"
    android:orientation="vertical"
    tools:context="bruce.chang.testtextinputlayout.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="@drawable/mine_login_editor_bg"
        android:orientation="vertical"
        >

        <android.support.design.widget.TextInputLayout
            android:id="@+id/usernameWrapper"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            >

            <EditText
                android:id="@+id/username"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawableLeft="@mipmap/mine_login_account"
                android:drawablePadding="10dp"
                android:hint="Username"
                android:inputType="textEmailAddress"
                android:textColor="#333333"
                android:textSize="14sp"/>

        </android.support.design.widget.TextInputLayout>

        <android.support.design.widget.TextInputLayout
            android:id="@+id/passwordWrapper"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            app:counterEnabled="false"
            app:counterMaxLength="10"
            app:counterOverflowTextAppearance="@android:style/TextAppearance.DeviceDefault.Small"
            app:hintAnimationEnabled="true"
            app:passwordToggleEnabled="true"
            >

            <EditText
                android:id="@+id/password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:drawableLeft="@mipmap/mine_login_password"
                android:drawablePadding="10dp"
                android:hint="Password"
                android:inputType="textPassword"
                android:textColor="#333333"

                android:textSize="14sp"/>

        </android.support.design.widget.TextInputLayout>


    </LinearLayout>

    <TextView
        android:id="@+id/tvLogin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:padding="10dp"
        android:text="login"
        android:textColor="#ffffff"
        />

</LinearLayout>

這裏在最外層的LinearLayout中通過 android:focusable=”true”
android:focusableInTouchMode=”true”來獲得焦點。每一個 TextInputLayout只能包含一個 EditText 的控件,這裏可以簡單設置一下EditText的屬性。

在TextInputLayout中,重要的屬性有以下幾個:

  1. counterEnabled:是否啓用計數器
  2. counterMaxLength:啓用計數器時,最大字數限制(僅僅用做顯示)
  3. counterOverflowTextAppearance:當字數超出計數器的最大限制時的字體格式
  4. hintAnimationEnabled:是否啓用hint動畫效果
  5. errorEnabled:是否顯示錯誤信息
  6. errorTextAppearance:錯誤信息的字體格式
  7. app:passwordToggleEnabled=”true” 查看密碼開關是否啓用,默認是啓用的

小編雖然在第二個TextInputLayout中添加了這些屬性,但因爲設置了 app:counterEnabled=”false” 這個屬性,所以不會有計數器的效果。這裏的兩個 EditText都沒有設置其background,如果設置了背景顏色,當輸入錯誤的時候將會有不一樣的提示效果。最後還有要給模擬登陸按鈕的 TextView,用以在其點擊事件中處理上面的兩個 EditText

最後:

MainActivity.java代碼參考附件1,下面說明實現過程.

第一步:初始化View,代碼中設置一下提示信息:
usernameWrapper = (TextInputLayout) findViewById(R.id.usernameWrapper);
passwordWrapper = (TextInputLayout) findViewById(R.id.passwordWrapper);
tvLogin = (TextView) findViewById(R.id.tvLogin);
tvLogin.setOnClickListener(this);
activity_main = (LinearLayout) findViewById(R.id.activity_main);
activity_main.setOnClickListener(this);
usernameWrapper.setHint("用戶名");
passwordWrapper.setHint("密碼");
第二步:編寫一個判斷EditText輸入信息的方法,這裏僅僅判斷輸入的字符串的長度,當長度大於5的時候才認定爲合法的字符串。
  public boolean validatePassword(String password) {
        return password.length() > 5;
}
第三步:編寫點擊login時候調用的方法,彈出一個提示信息,Toast的形式我們用的很多了,這裏使用Snackbar來彈出提示信息。
public void doLogin() {
        Snackbar.make(activity_main, "you have login,", Snackbar.LENGTH_SHORT)
                .show();

}
第四步:編寫一個隱藏軟鍵盤的方法,點擊空白處,收起軟鍵盤,這一點沒有太大必要,但是小編個人習慣而已。
private void hideKeyboard() {
        View view = getCurrentFocus();
        if (view != null) {
            ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).
                    hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
        }
}
第五步:在onclick方法中根據id來調用不同的事件,這裏通過 TextInputLayoutsetError 方法來設置錯誤的提示信息,通過 setErrorEnabled 方法來清楚相應的錯誤提示信息。
public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tvLogin:
                hideKeyboard();
                String username = usernameWrapper.getEditText().getText().toString();
                String password = passwordWrapper.getEditText().getText().toString();

                if (!validatePassword(username)) {
                    usernameWrapper.setError("Not a valid email address!");
                    usernameWrapper.getEditText().getText().clear();
                } else if (!validatePassword(password)) {
                    passwordWrapper.setError("Not a valid password!");//可以通過setError()顯示輸入錯誤提示,
                    passwordWrapper.getEditText().getText().clear();//輸入錯誤的時候,清空此EditText
                } else {
                    usernameWrapper.setErrorEnabled(false);//setErrorEnabled(fasle)清除錯誤提示
                    passwordWrapper.setErrorEnabled(false);
                    doLogin();
                    break;

                }
            case R.id.activity_main:

                hideKeyboard();
                break;
        }
}
此時的運行結果如下圖:

image

上圖就是運行的結果,看到上面的結果有以下幾個疑問:
1. 輸入錯誤的時候的提示信息的顏色(A)、輸入時候浮動lable的顏色(B)、以及Edittext默認的背景(即那條線)的顏色(C)爲什麼是紅色的?
2. Snackbar彈出的時候背景爲什麼是黑色的,能否修改這個黑色的背景顏色?
3.如果新建可以側滑的Activity,當彈出Snackbar的時候可以向右滑動關閉,爲什麼這裏不可以?

解決疑問1:

經過在網上查找一番,發現提示信息的顏色與系統所在的Style有關,默認的Style如下:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
</style>

1中ABC的顏色都是默認的colorAccent的顏色,但修改此顏色,BC會發生變化,A是不會發生變化的。
新建一個Style:
<style name="AppThemeA" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">#00ff00</item>
        <item name="colorPrimaryDark">#0000ff</item>
        <item name="colorAccent">#333333</item>
</style>
當時用AppThemeA作爲APP的style的時候,1中顏色B和顏色C變成了#333333的顏色,A的顏色仍然是AppTheme中的colorAccent的顏色,

解決疑問2:

Snackbar默認的顏色就是黑色的,但可以修改成任意的顏色,接下來將會詳細的說明Snackbar的使用,如何修改Snackbar的字體顏色和背景顏色。

解決疑問3:

要想支持Swipe手勢的話,這個view需要是一個CoordinatorLayout,默認的帶側邊欄的Activity中佈局裏面就有CoordinatorLayout,而這裏使用的是LinearLayout所以不支持向右滑動關閉Snackbar。
修改Snackbar的自己顏色和背景顏色

參考文章鏈接

http://www.jcodecraeer.com/plus/view.php?aid=3187

字體顏色設置

對於Action可以通過Snack的bar的公開API
snackbar.setActionTextColor(int color)
設置。

但是使用的時候不太好用,沒有找到設置消息文字顏色的API,但是在查看Snackbar.class的時候找到了一個方法:

 public View getView() {
        return this.mView;
 }

當去查看setActionTextColor(int color)的時候,發現了這個方法也被調用了,用於獲取Snackbar的用於顯示Action的TextView實例。繼續查看,可以發現,每一個Snackbar的內容通過其內部類SnackbarLayout來呈現,其具體聲明如下:

public static class SnackbarLayout extends LinearLayout

而在Snackbar(ViewGroup parent)這個包級聲明的方法中發現上面的getView()返回的mView就是SnackbarLayout實例,這個類的佈局最終是

design_layout_snackbar_include.xml

,這個是Android Support Design Library庫中的佈局文件,具體內容如下:

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
            android:id="@+id/snackbar_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:paddingTop="@dimen/snackbar_padding_vertical"
            android:paddingBottom="@dimen/snackbar_padding_vertical"
            android:paddingLeft="@dimen/snackbar_padding_horizontal"
            android:paddingRight="@dimen/snackbar_padding_horizontal"
            android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
            android:maxLines="@integer/snackbar_text_max_lines"
            android:layout_gravity="center_vertical|left|start"
            android:ellipsize="end"/>

    <TextView
            android:id="@+id/snackbar_action"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/snackbar_extra_spacing_horizontal"
            android:layout_marginStart="@dimen/snackbar_extra_spacing_horizontal"
            android:layout_gravity="center_vertical|right|end"
            android:background="?attr/selectableItemBackground"
            android:paddingTop="@dimen/snackbar_padding_vertical"
            android:paddingBottom="@dimen/snackbar_padding_vertical"
            android:paddingLeft="@dimen/snackbar_padding_horizontal"
            android:paddingRight="@dimen/snackbar_padding_horizontal"
            android:visibility="gone"                    android:textAppearance="@style/TextAppearance.Design.Snackbar.Action"/>
</merge>

其實到這裏,剩下的問題就可以很快解決了。從上面可以看出來,具體Snackbar的內容就是佈局中的兩個TextView,因此只要能夠獲取這兩個個TextView實例,那麼修改屬性就非常簡單了。而上面的getView()則解決了上面這個問題,因此可以寫下面的幫助方法實現設置消息文本的顏色。

public void setSnackbarMessageTextColor(Snackbar snackbar, int color) {
        View view = snackbar.getView();
        ((TextView) view.findViewById(R.id.snackbar_text)).setTextColor(color);
}

使用下面的代碼調用:

Snackbar snackbar = Snackbar.make(container, "SnackbarTest", Snackbar.LENGTH_LONG)
                .setAction("Action", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                       //點擊事件
                    }
                });
        setSnackbarMessageTextColor(snackbar, Color.parseColor("#FFFFFF"));
        snackbar.show();

setAction方法的源代碼:

 /**
     * Set the action to be displayed in this {@link Snackbar}.
     *
     * @param text     Text to display
     * @param listener callback to be invoked when the action is clicked
     */
    @NonNull
    public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
        final TextView tv = mView.getActionView();

        if (TextUtils.isEmpty(text) || listener == null) {
            tv.setVisibility(View.GONE);
            tv.setOnClickListener(null);
        } else {
            tv.setVisibility(View.VISIBLE);
            tv.setText(text);
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    listener.onClick(view);
                    // Now dismiss the Snackbar
                    dispatchDismiss(Callback.DISMISS_EVENT_ACTION);
                }
            });
        }
        return this;
    }

從上面源代碼可以看到,在設置action的點擊事件的時候我們沒有寫任何的事件處理操作,但是從源碼中可以看到默認執行了讓snackBar消失的操作,所以當在使用的過程中才會發現點擊Action的文字,Snackbar將會消失。

如何爲Snackbar添加背景顏色

可以通過getView() 方法獲取Snackbar的核心視圖,然後就可以設置顏色了,當然也可以設置字體的顏色,注意設置字體顏色的時候的id必須是snackbar_text。。

比如:

snackbar.getView().setBackgroundColor(colorId);
snackbar.getView().findViewById(R.id.snackbar_text)).setTextColor(colorId)

下面附件2是ColoredSnackbar類,它封裝了一些方法,可以根據用戶指定的類型顯示不同背景顏色。

如何使用呢?

Snackbar snackbar = Snackbar.make("對應的view", R.string.hello_snackbar, Snackbar.LENGTH_SHORT);
ColoredSnackBar.alert(snackbar).show();
Snackbar的特點:
  1. 當它顯示一段時間後或用戶與屏幕交互時它會自動消失。
  2. 可以自定義action-可選操作。
  3. swiping it off the screen可以讓其消失
  4. 它是context sensitive message(自己理解吧),所以這些消息是UI screen的一部分並且它是顯示在所有屏幕其它元素之上(屏幕最頂層),並不是像Toast一樣覆蓋在屏幕上。
  5. 同一時間只能顯示一個snackbar。

本篇文章,小編主要是使用TextInputLayout和Snackbar,Android7.0都發布很長世間了,作爲開發者不能還停留在5.0之前的控件上,練習使用這些新的控件不僅僅提高自己的技術,更重要可以接觸新的Android知識,開發將會變得更簡單,小編說了這麼多,最重要的還是自己動手操作一下。

附件1:

package bruce.chang.testtextinputlayout;

import android.content.Context;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    TextInputLayout usernameWrapper;
    TextInputLayout passwordWrapper;
    TextView tvLogin;
    LinearLayout activity_main;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        usernameWrapper = (TextInputLayout) findViewById(R.id.usernameWrapper);
        passwordWrapper = (TextInputLayout) findViewById(R.id.passwordWrapper);
        tvLogin = (TextView) findViewById(R.id.tvLogin);
        tvLogin.setOnClickListener(this);
        activity_main = (LinearLayout) findViewById(R.id.activity_main);
        activity_main.setOnClickListener(this);
        usernameWrapper.setHint("用戶名");
        passwordWrapper.setHint("密碼");

    }

    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tvLogin:
                hideKeyboard();
                String username = usernameWrapper.getEditText().getText().toString();
                String password = passwordWrapper.getEditText().getText().toString();

                if (!validatePassword(username)) {
                    usernameWrapper.setError("Not a valid email address!");
                    usernameWrapper.getEditText().getText().clear();
                } else if (!validatePassword(password)) {
                    passwordWrapper.setError("Not a valid password!");//可以通過setError()顯示輸入錯誤提示,
                    passwordWrapper.getEditText().getText().clear();//輸入錯誤的時候,清空此EditText
                } else {
                    usernameWrapper.setErrorEnabled(false);//setErrorEnabled(fasle)清除錯誤提示
                    passwordWrapper.setErrorEnabled(false);
                    doLogin();
                    break;

                }
            case R.id.activity_main:

                hideKeyboard();
                break;
        }
    }

    public void doLogin() {
        Snackbar snackbar = Snackbar.make(usernameWrapper, "OK! I'm performing login.", Snackbar.LENGTH_LONG);
        snackbar.setAction("取消", new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
        snackbar.setActionTextColor(getResources().getColor(R.color.colorAccent));
        setSnackbarMessageTextColor(snackbar, getResources().getColor(R.color.colorAccent));
        snackbar.show();

    }

    private void hideKeyboard() {
        View view = getCurrentFocus();
        if (view != null) {
            ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).
                    hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
        }
    }

    public boolean validatePassword(String password) {
        return password.length() > 5;
    }


    public void setSnackbarMessageTextColor(Snackbar snackbar, int color) {
        View view = snackbar.getView();
        view.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
        ((TextView) view.findViewById(R.id.snackbar_text)).setTextColor(color);
    }
}

附件2:

public class ColoredSnackbar {

    private static final int red = 0xfff44336;
    private static final int green = 0xff4caf50;
    private static final int blue = 0xff2195f3;
    private static final int orange = 0xffffc107;

    private static View getSnackBarLayout(Snackbar snackbar) {
        if (snackbar != null) {
            return snackbar.getView();
        }
        return null;
    }

    private static Snackbar colorSnackBar(Snackbar snackbar, int colorId) {
        View snackBarView = getSnackBarLayout(snackbar);
        if (snackBarView != null) {
            snackBarView.setBackgroundColor(colorId);
        }

        return snackbar;
    }

    public static Snackbar info(Snackbar snackbar) {
        return colorSnackBar(snackbar, blue);
    }

    public static Snackbar warning(Snackbar snackbar) {
        return colorSnackBar(snackbar, orange);
    }

    public static Snackbar alert(Snackbar snackbar) {
        return colorSnackBar(snackbar, red);
    }

    public static Snackbar confirm(Snackbar snackbar) {
        return colorSnackBar(snackbar, green);
    }
}

歡迎關注我的個人技術公衆號,快速查看我的最新文章。

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章