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的登陸頁面都是這樣設計的,今天小編就詳細使用以下此控件,來看運行結果。
首先:
新建一個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中,重要的屬性有以下幾個:
- counterEnabled:是否啓用計數器
- counterMaxLength:啓用計數器時,最大字數限制(僅僅用做顯示)
- counterOverflowTextAppearance:當字數超出計數器的最大限制時的字體格式
- hintAnimationEnabled:是否啓用hint動畫效果
- errorEnabled:是否顯示錯誤信息
- errorTextAppearance:錯誤信息的字體格式
- 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來調用不同的事件,這裏通過 TextInputLayout 的 setError 方法來設置錯誤的提示信息,通過 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;
}
}
此時的運行結果如下圖:
上圖就是運行的結果,看到上面的結果有以下幾個疑問:
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的特點:
- 當它顯示一段時間後或用戶與屏幕交互時它會自動消失。
- 可以自定義action-可選操作。
- swiping it off the screen可以讓其消失
- 它是context sensitive message(自己理解吧),所以這些消息是UI screen的一部分並且它是顯示在所有屏幕其它元素之上(屏幕最頂層),並不是像Toast一樣覆蓋在屏幕上。
- 同一時間只能顯示一個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);
}
}
歡迎關注我的個人技術公衆號,快速查看我的最新文章。