1. 程式人生 > >Android6.0-新控制元件(一)

Android6.0-新控制元件(一)

前言

谷歌在2015年I/O大會上釋出了Android M的新版本.在這次的I/O大會上,谷歌對Android並沒有做很大的改變,主要完善之前的Android L版本.不過這次谷歌在繼Material Design風格之後,做了很多風格上的相容,並推出了Android Design Support Library庫,全面支援Material Design設計風格的UI效果.該庫包含了FloatingActionButton,TextInputLayout,Snackbar,TabLayout,NavigationView,CoordinatorLayout,AppBarLayout,CollapsingToolbarLayout八個新控制元件.由於篇幅的原因,我們先來學習一下前四個的使用.後續會繼續完成其餘控制元件的使用.

使用

需要匯入Android Design Support Library庫,該庫支援Android 2.1以上裝置.在build.gradle(moudle:app)中新增依賴:compile 'com.android.support:design:24.2.1' 版本號可自己匹配.

FloatingActionButton

效果圖:

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|right"
    android:src="@drawable/ic_favorite"
    android:layout_margin="10dp"
    app:backgroundTint="@color/colorPrimary"
    app:elevation="4dp"
    app:pressedTranslationZ="8dp"
    app:fabSize="normal"
    app:rippleColor="@color/colorPrimaryDark"
    app:useCompatPadding="false" />

注意:

設定FloatingActionButton的背景顏色時使用:app:backgroundTint,預設為 Theme 主題中的 “colorAccent”的顏色;如果使用android:backgroundTint執行時,則會報解析Xml異常.

設定FloatingActionButton的大小,通過app:fabSize屬性來設定,有三種格式:auto(基於視窗的大小而自動適應),normal(正常大小的按鈕),mini(迷你大小的按鈕).這裡設定layout_width和layout_height貌似沒有什麼效果,FloatingActionButton的大小由fabSize屬性來決定.可當你把寬和高設定成match_parent時,FloatingActionButton的大小會充滿父窗體,大小雖然改變了,但這樣的設定在開發中貌似沒一點卵用.

app:elevation :設定 FloatingActionButton 陰影的深度,預設有陰影.

app:pressedTranslationZ :設定 FloatingActionButton 點選時陰影的大小.

app:rippleColor :設定 FloatingActionButton 點選時的顏色,也就是設定上面屬性後,陰影的顏色.

app:useCompatPadding :設定是否啟用compat的填充.

src - 設定FAB的圖示,Google建議符合Design設計的該圖示大小為24dp.

app:layout_anchor - 設定FAB的錨點,即以哪個控制元件為參照點設定位置.

app:layout_anchorGravity - 設定FAB相對錨點的位置,值有 bottom、center、right、left、top等.

以上在Xml佈局中設定的屬性,在程式碼中同樣可以設定.

Snackbar

  • 簡介: Snackbar用於展示一條簡短的訊息給使用者,該訊息在很多的時間內會消失.該訊息只是給使用者一個提示,使用者不需要去操作.例如:當我們傳送一條Email時,告訴使用者該Email傳送的狀態.Snackbar和Toast很像,只不過Toast是在螢幕中心彈出,而Snackbar是在螢幕底部彈出.

  • 使用:程式碼中使用

    //得到Snackbar物件
    final Snackbar snackbar = Snackbar.make(coordinatorLayout, "我是Snackbar...", Snackbar.LENGTH_LONG);
    //設定Snackbar背景
    snackbar.getView().setBackgroundResource(R.color.colorPrimary);
    snackbar.show();
    //顯示帶Action的Snackbar
    snackbar.setAction("取消", new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //關閉Snackbar
            snackbar.dismiss();
        }
    });
    
  • 常用方法:

    • make() 方法,生成Snackbar訊息物件,第一個引數為View物件,Snackbar 會試著尋找一個父 View 來 hold 這個 View。Snackbar 將遍歷整個 View tree 來尋找一個合適的父 View,它可能是一個 coordinatorLayout 也可能是 window decor’s content view,隨便哪一個都行.後面的兩個引數和Toast一致.
    • setDuration() 方法:設定顯示持續時間.
    • setAction() 方法:設定 Action,第一個引數會作為按鈕(Actiong)的文字,第二個引數是按鈕的點選事件.
    • setCallback() 方法:Snackbar 的顯示和消失會回撥 Snackbar.Callback的 onDismissed()和 onShown()方法.
    • getView():獲取 Snackbar 的 View,進而可以定製Snackbar.
  • 注意:

    • 谷歌其實更建議在CoordinatorLayout(不要著急,後面會細說)佈局中使用Snackbar(make()方法中傳入的View),當用戶滑動螢幕時,可以使Snackbar消失.
    • 當然,如果你只是想在底部彈出提示訊息,那麼make()方法中傳入的任意的View即可.
    • 還有就是當你點選FloatingActionButton時,想彈出Snackbar,同時又想避免Snackbar遮蓋住FloatingActionButton,這時,你應該使用CoordinatorLayout來協調各個View之間的動畫效果.

效果圖:設定點選FloatingActionButton的點選事件,點選彈出Snackbar.

TextInputLayout

  • 簡介: TextInputLayout的出現是Google為EditText提供的浮動標籤,使其更符合Material Design的風格.TextInputLayout繼承LinearLayout,它只是一個容器,不過該容器下只接收一個元素,子元素就是一個EditText元素.這樣就為EditText提供了一個帶有動畫效果的提示標籤,同時也可以處理錯誤資訊,將錯誤資訊提示在EditText的下方.

  • 使用:

    • 佈局:

      <android.support.design.widget.TextInputLayout
              android:id="@+id/til_username"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_marginLeft="30dp"
              android:layout_marginRight="30dp"
              android:layout_marginTop="20dp">
          <!--這裡可以直接使用EditText-->
          <android.support.design.widget.TextInputEditText
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:hint="Username" />
      </android.support.design.widget.TextInputLayout>
      
      <android.support.design.widget.TextInputLayout
          android:id="@+id/til_password"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginLeft="30dp"
          android:layout_marginRight="30dp"
          android:layout_marginTop="10dp">
      
              <android.support.design.widget.TextInputEditText
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:hint="Password"
                  android:inputType="textPassword" />
      </android.support.design.widget.TextInputLayout>
      
    • 程式碼:

      //初始化TextInputLayout(程式碼中我使用了ButterKnife註解,這裡就不貼出來了)
      //得到EditText物件
      final EditText userEditText = tilUsername.getEditText();
      final EditText pwdEditText = tilPassword.getEditText();
      //設定hint提示,也可直接在xml中設定(個人感覺如果在佈局中已經設定了hint,程式碼中就不必在設定了.)
      //userEditText.setHint("Username");
      //pwdEditText.setHint("Password");
      
      
      //EditText新增文字變化監聽
      userEditText.addTextChangedListener(new TextWatcher() {
          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
              Log.d(TAG, "beforeTextChanged執行了....s = " + s + "---start = " + start + "---count = " + count + "---after = " + after);
          }
      
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
              Log.d(TAG, "onTextChanged執行了....s = " + s + "---start = " + start + "---count = " + count + "---before = " + before);
              if (s.length() > 7) {
                  tilUsername.setErrorEnabled(true);//設定是否開啟錯誤提示
                  tilUsername.setError("使用者名稱長度不能超過8個");//設定錯誤提示的資訊
              } else {
                  tilUsername.setErrorEnabled(false);
              }
          }
      
          @Override
          public void afterTextChanged(Editable s) {
              Log.d(TAG, "afterTextChanged執行了....s = " + s);
          }
          });
      
      pwdEditText.addTextChangedListener(new TextWatcher() {
          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
      
          }
      
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
              if (s.length() < 6) {
                  tilPassword.setErrorEnabled(true);
                  tilPassword.setError("密碼長度不能小於6個");
              } else {
                  tilPassword.setErrorEnabled(false);
              }
          }
      
          @Override
          public void afterTextChanged(Editable s) {
      
          }
      });
      
  • 注意:程式碼裡註釋已經很詳細了,相信你一看就能明白.這裡提一點:就是浮動標籤字型的顏色預設是你設定主題時的顏色.

    設定主題的顏色就是EditText浮動標籤提示資訊的顏色:<item name="colorAccent">@color/colorAccent</item>

效果圖:這裡模擬登入介面。並對使用者名稱長度是否超過8個,密碼長度是否小於6個做了判斷。

TabLayout

  • 簡介: TabLayout提供了一個水平佈局顯示標籤,主要用來做選顯示卡切換這一類效果的控制元件。例如:網易新聞客戶端的Tab。Github上也有很多類似效果的開源控制元件,只是這次谷歌把它官方化了,更方便開發者使用。
  • 簡單TabLayout效果圖:

  • 使用:

    • xml:

      <android.support.design.widget.TabLayout
          android:id="@+id/tab_layout"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          app:tabIndicatorColor="@android:color/holo_blue_bright"     //Tab指示器下標的顏色
          app:tabSelectedTextColor="@android:color/holo_blue_bright"  //Tab被選中字型的顏色
          app:tabTextColor="@android:color/black">                    //Tab未被選中字型的顏色
      
      </android.support.design.widget.TabLayout>
      
    • 程式碼:

      //新增8個Tab標籤,並設定第一個Tab標籤為選中狀態.
      tabLayout.addTab(tabLayout.newTab().setText("Tab 1"),true);
      tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
      tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
      tabLayout.addTab(tabLayout.newTab().setText("Tab 4"));
      tabLayout.addTab(tabLayout.newTab().setText("Tab 5"));
      tabLayout.addTab(tabLayout.newTab().setText("Tab 6"));
      tabLayout.addTab(tabLayout.newTab().setText("Tab 7"));
      tabLayout.addTab(tabLayout.newTab().setText("Tab 8"));
      //設定Tab的模式為可滑動,當tab標籤超過螢幕寬度時,可以滑動.
      tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
      

    這裡只是簡單使用TabLayout完成Tabs選項卡,一般情況下TabLayout+ViewPager相結合使用,這樣的使用場景會更多一點,也能發揮出TabLayout的優勢。接下來使用TabLayout+ViewPager完成一個小Demo。

  • 效果圖:(自定義TabLayout樣式+ViewPager使用)

  • 使用:首先我們在Style中自定義TabLayout的樣式,在佈局中使用時引入自定義的TabLayout樣式.新增ViewPager,每個頁面是一個Fragment,並且實現聯動效果,下面直接看程式碼:

    • 自定義TabLayout樣式:

      <!-- 自定義 TabLayout 樣式 -->
      <style name="CustomTabLayout" parent="Widget.Design.TabLayout">
          <item name="paddingEnd">10dp</item>
          <item name="paddingStart">10dp</item>
          <item name="tabBackground">@color/colorPrimary</item>
          <item name="tabContentStart">10dp</item>
          <item name="tabGravity">center</item>
          <item name="tabIndicatorColor">#999900</item>
          <item name="tabIndicatorHeight">6dp</item>
          <item name="tabMaxWidth">@dimen/tab_max_width</item>
          <item name="tabMinWidth">@dimen/tab_min_width</item>
          <item name="tabMode">scrollable</item>
          <item name="tabPaddingBottom">2dp</item>
          <item name="tabPaddingEnd">10dp</item>
          <item name="tabPaddingStart">10dp</item>
          <item name="tabPaddingTop">15dp</item>
          <item name="tabSelectedTextColor">#ffcc00</item>
          <item name="tabTextAppearance">@style/CustomTabTextAppearance</item>
          <item name="tabTextColor">#000066</item>
      </style>
      <!-- 自定義 TabText 的外觀 -->
      <style name="CustomTabTextAppearance" parent="TextAppearance.Design.Tab">
          <item name="android:textSize">14sp</item>
          <item name="android:textColor">#006600</item>
          <item name="textAllCaps">false</item>
      </style>
      
    • 頁面佈局:

      <?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:id="@+id/activity_tab_layout_view_pager"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical"
          tools:context="com.listenergao.mytest.activity.TabLayoutViewPagerActivity">
      
          <include layout="@layout/toolbar_layout" />
      
          <android.support.design.widget.TabLayout
              android:id="@+id/tab_layout"
              style="@style/CustomTabLayout"
              android:layout_width="match_parent"
              android:layout_height="wrap_content">
      
          </android.support.design.widget.TabLayout>
      
          <android.support.v4.view.ViewPager
              android:id="@+id/viewpager"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
      
          </android.support.v4.view.ViewPager>
      </LinearLayout>
      
    • 頁面程式碼:(Activity頁面)

      package com.listenergao.mytest.activity;
      
      import android.os.Bundle;
      import android.support.design.widget.TabLayout;
      import android.support.v4.app.Fragment;
      import android.support.v4.view.ViewPager;
      import android.support.v7.widget.Toolbar;
      import android.widget.LinearLayout;
      
      import com.listenergao.mytest.R;
      import com.listenergao.mytest.data.TabFragmentAdapter;
      import com.listenergao.mytest.fragment.TabFragment;
      
      import java.util.ArrayList;
      import java.util.List;
      
      import butterknife.BindView;
      import butterknife.ButterKnife;
      
      /**
       * TabLayout+ViewPager示例
       *
       * @author By ListenerGao
       *         create at 2016/10/14 18:52
       */
      public class TabLayoutViewPagerActivity extends BaseActivity {
      
          @BindView(R.id.toolbar)
          Toolbar toolbar;
          @BindView(R.id.tab_layout)
          TabLayout tabLayout;
          @BindView(R.id.viewpager)
          ViewPager viewpager;
          @BindView(R.id.activity_tab_layout_view_pager)
          LinearLayout activityTabLayoutViewPager;
          private List<String> mTabList;
          private List<Fragment> mTabFragments;
      
          @Override
          protected int getLayoutResId() {
              return R.layout.activity_tab_layout_view_pager;
          }
      
          @Override
          protected void initView() {
              ButterKnife.bind(this);
      
              toolbar.setTitle("TabLayout+ViewPager");
              setSupportActionBar(toolbar);
              getSupportActionBar().setDisplayHomeAsUpEnabled(true);
      
          }
      
          @Override
          protected void initData() {
              mTabList = initTabList();
              initTabLayout(mTabList);
              mTabFragments = initFragments(mTabList);
      
              TabFragmentAdapter adapter = new TabFragmentAdapter(getSupportFragmentManager(),mTabFragments,mTabList);
              viewpager.setAdapter(adapter);
              //將TabLayout與ViewPager關聯起來(注意:該行程式碼需在ViewPager設定Adapter之後呼叫)
              tabLayout.setupWithViewPager(viewpager);
              //為Tabs設定介面卡
              tabLayout.setTabsFromPagerAdapter(adapter);
          }
      
          /**
           * 初始化Tab標籤資料
           *
           * @return
           */
          private List<String> initTabList() {
              List<String> tabList = new ArrayList<>();
              for (int i = 0; i < 8; i++) {
                  tabList.add("TAB " + i);
              }
              return tabList;
          }
      
          /**
           * 初始化TabLayout
           *
           * @param list
           */
          private void initTabLayout(List<String> list) {
              for (int i = 0; i < list.size(); i++) {
                  if (i == 0)
                      tabLayout.addTab(tabLayout.newTab().setText(list.get(i)), true);
                  else
                      tabLayout.addTab(tabLayout.newTab().setText(list.get(i)));
              }
              //設定TabLayout的模式為可滑動
              tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
          }
      
          /**
           * 初始化Tab標籤對應的Fragment
           *
           * @param list 標籤集合資料
           * @return
           */
          public List<Fragment> initFragments(List<String> list) {
              List<Fragment> mTabFragments = new ArrayList<>();
              for (int i = 0; i < list.size(); i++) {
                  Fragment tabFragment = new TabFragment();
                  Bundle bundle = new Bundle();
                  bundle.putString("Content", list.get(i));
                  tabFragment.setArguments(bundle);
                  mTabFragments.add(tabFragment);
              }
              return mTabFragments;
          }
      
      }
      
    • Viewpager介面卡:(這裡繼承的是FragmentStatePagerAdapter,因為可能有很多個Tab標籤頁面(Fragment).這裡不細說,可以自行google.)

      package com.listenergao.mytest.data;
      
      import android.support.v4.app.Fragment;
      import android.support.v4.app.FragmentManager;
      import android.support.v4.app.FragmentStatePagerAdapter;
      
      import java.util.List;
      
      
      /**
       * ViewPager的介面卡
       *
       * @author By ListenerGao
       *         Create at 2016/10/15 16:36
       */
      public class TabFragmentAdapter extends FragmentStatePagerAdapter {
          private List<Fragment> mFragments;
          private List<String> mTabTitles;
          public TabFragmentAdapter(FragmentManager fm, List<Fragment> fragments,List<String> tabTitles) {
              super(fm);
              this.mFragments = fragments;
              this.mTabTitles = tabTitles;
          }
      
          @Override
          public Fragment getItem(int position) {
              return mFragments.get(position);
          }
      
          @Override
          public int getCount() {
              return mFragments.size();
          }
          //注意:需要重寫此方法,從標籤集合中獲取到title,否則標籤上的title則不會顯示.
          @Override
          public CharSequence getPageTitle(int position) {
              return mTabTitles.get(position);
          }
      }
      
    • Tab標籤對應的Fragment頁面:

      package com.listenergao.mytest.fragment;
      
      import android.os.Bundle;
      import android.support.annotation.Nullable;
      import android.support.v4.app.Fragment;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.TextView;
      
      import com.listenergao.mytest.R;
      
      import butterknife.BindView;
      import butterknife.ButterKnife;
      
      
      /**
       * Tab標籤對應的頁面
       *
       * @author By ListenerGao
       *         Create at 2016/10/15 16:53
       */
      public class TabFragment extends Fragment {
          @BindView(R.id.tv_content)
          TextView tvContent;
          private View view;
          private String content;
      
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              if (getArguments() != null) {
                  Bundle bundle = getArguments();
                  content = bundle.getString("Content");
              }
          }
      
          @Override
          public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                   Bundle savedInstanceState) {
              // Inflate the layout for this fragment
              view = inflater.inflate(R.layout.fragment_tab, container, false);
              ButterKnife.bind(this, view);
              return view;
          }
      
          @Override
          public void onActivityCreated(@Nullable Bundle savedInstanceState) {
              super.onActivityCreated(savedInstanceState);
              tvContent.setText(content);
          }
      }
      
  • 常用方法:

    • 1,addTab(TabLayout.Tab tab, int position, boolean setSelected) 增加選項卡到 layout中,這是一個過載方法.第一個引數是Tab物件,通過Tablayout.newTab()建立;第二個引數是插入Tab的位置;最後一個引數是當前Tab是否為選中狀態.
    • 2,newTab() 新建個 tab.
    • 3,setTabMode(),設定 Mode,有兩種值:TabLayout.MODE_SCROLLABLE和TabLayout.MODE_FIXED分別表示當tab的內容超過螢幕寬度是否支援橫向水平滑動,第一種支援滑動,第二種不支援,預設不支援水平滑動.如果你新增的Tab很少的話,你應該把Tab的mode設定成MODE_FIXED,否則可能會出現Tab標籤不會佔滿螢幕的寬度.
    • 4,setOnTabSelectedListener(TabLayout.OnTabSelectedListener onTabSelectedListener) 為每個 tab 增加選擇監聽器.
    • 5,setScrollPosition(int position, float positionOffset, boolean updateSelectedText) 設定Tab滾動到的位置.
    • 6,setTabGravity(int gravity) 設定 Gravity.
    • 7,setTabTextColors(ColorStateList textColor) 設定 tab 中文字的顏色.
    • 8,setTabTextColors(int normalColor, int selectedColor) 設定 tab 中文字的顏色 預設 選中的顏色.
    • 9,setTabsFromPagerAdapter(PagerAdapter adapter) 將TabLayout與ViewPager關聯,當PageAdapter更新時,TabLayout會自動更新.(注意:該方法已過時)
    • 10,setupWithViewPager(ViewPager viewPager) 設定Tablayout和ViewPager實現聯動效果.
    • 11,getTabAt(int index) 得到選項卡.
    • 12,getTabCount() 得到選項卡的總個數.
    • 13,getTabGravity() 得到 tab 的 Gravity.
    • 14,getTabMode() 得到 tab 的模式.
    • 15,getTabTextColors() 得到 tab 中文字的顏色.

由於篇幅原因,NavigationView,CoordinatorLayout,AppBarLayout,CollapsingToolbarLayout會在下篇文章中介紹.

原始碼地址:(該功能是在我一個練手專案中寫的)