Android初級開發(十一)——(轉載)一篇文章輕鬆掌握Material Design

Material Design的基本概念

Material Design是Google設計的一套視覺語言,將優先的經典的設計原理與科技創新相結合,爲開發者提供一套完成視覺和交互設計規範。移動設備是這套設計語言的基礎對象,讓用戶在不同的平臺、不同尺寸的設備上能保持一致的體驗。 
Material Design強調交互上的即時反饋,即對於用戶的觸控等行爲app需要給出即時的反應。同時Material Design要求應用給用戶帶入感,讓用戶在使用時是沉浸在當前的應用當中。例如Google給出了沉浸式狀態欄等“工具”,希望通過改變StatusBar和NavigationBar來給用戶更強的融入感,專注於應用本身提供的內容。 
Google從動畫、顏色、樣式、觸控反饋、佈局等多個方面給出了Material Design的設計要求。無論是單一的控件還是圖文佈局,Google都給出了明確的設計說明,有興趣的同學可以去上方提到的官方鏈接處做進一步瞭解。

RecyclerView的使用

寫條目佈局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:orientation="vertical">

    <TextView
        android:id="@+id/tv_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

寫Adapter以及其內部類自定義的ViewHolder:

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {

    private List<String> mDatas;
    private Context mContext;

    public MyRecyclerViewAdapter(Context context, List<String> datas) {
        mContext = context;
        mDatas = datas;
    }

    //自定義ViewHolder
    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView tv_item;

        MyViewHolder(View itemView) {
            super(itemView);
            tv_item = (TextView) itemView.findViewById(R.id.tv_item);
        }
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //創建ViewHolder
        View itemView = View.inflate(parent.getContext(), R.layout.item_list, null);
        return new MyViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        //數據綁定
        holder.tv_item.setText(mDatas.get(position));
        //設置點擊監聽
        holder.tv_item.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext, mDatas.get(position), Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        //數據集大小
        return mDatas.size();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

在Activity中的使用,通過設置不同的LayoutManager就可以實現不同的佈局效果:

public class MDRecyclerViewActivity extends AppCompatActivity {

    private RecyclerView rv_list;
    private MyRecyclerViewAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_md_recyclerview);

        rv_list = (RecyclerView) findViewById(R.id.rv_list);

        List<String> datas = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            datas.add("第" + i + "個數據");
        }

        mAdapter = new MyRecyclerViewAdapter(this, datas);
        //豎直線性,不反轉佈局
//        rv_list.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        //表格佈局
//        rv_list.setLayoutManager(new GridLayoutManager(this, 3));
        //瀑布流佈局
        rv_list.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));
        rv_list.setAdapter(mAdapter);

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

Inflate時的注意事項:

在Adapter中的onCreateViewHolder,需要Inflate佈局文件,有三種寫法:

View itemView = View.inflate(parent.getContext(), R.layout.item_list, null);
View itemView = View.inflate(parent.getContext(), R.layout.item_list, parent);
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

寫法一般情況下是沒有問題的,但是當我們在onBindViewHolder中拿到佈局中TextView的LayoutParams的時候,就有可能返回空。 
寫法二直接Crash,因爲ItemView佈局已經有一個Parent了(Inflate的時候把ItemView添加到Recycleview了),不能再添加一個Parent(Recycleview再次添加ItemView)。 
寫法三是一、二的兩種兼容方案,推薦這種寫法。

添加增刪接口 
在Adapter中添加以及刪除的接口:

//條目的增刪
public void addItem(String data, int position) {
    mDatas.add(position, data);
    notifyItemInserted(position);
}

public void removeItem(int position) {
    mDatas.remove(position);
    notifyItemRemoved(position);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注意如果你想使用RecyclerView提供的增刪動畫,那麼就需要使用新增的notify方法。

添加條目點擊監聽 
自定義一個點擊回調接口:

//條目點擊
ItemClickListener mItemClickListener;

public interface ItemClickListener {
    void onclick(int position, String data);
}

public void setItemClickListener(ItemClickListener listener) {
    mItemClickListener = listener;
}

public abstract class ItemClickListenerPosition implements View.OnClickListener {

    private int mPosition;

    public ItemClickListenerPosition(int position) {
        mPosition = position;
    }

    public int getPosition() {
        return mPosition;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

ItemClickListenerPosition是一個自定義的OnClickListener,目的就是爲了把Position和監聽綁定在一起,同時也使用了getLayoutPosition方法。防止了點擊Position錯亂的問題。

(onBindViewHolder() 方法中的位置參數 position 不是實時更新的,例如在我們刪除元素後,item 的 position 並沒有改變。)

然後在onBindViewHolder裏面進行監聽:

@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {

    //數據綁定

    //設置條目監聽
    holder.itemView.setOnClickListener(new ItemClickListenerPosition(holder.getLayoutPosition()) {
        @Override
        public void onClick(View v) {
            if (mItemClickListener != null) {
                mItemClickListener.onclick(getPosition(), mDatas.get(getPosition()));
            }
        }
    });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

想詳細瞭解RecyclerView的使用,請參考《 一篇博客理解Recyclerview的使用》

DrawerLayout+NavigationView

使用DrawerLayout實現側滑: 
定義一個佈局:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#D197F2"
        app:title="我是標題"
        app:titleTextColor="#fff"/>

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="內容"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="200dp"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:background="@android:color/holo_blue_light"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="側滑菜單1"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="側滑菜單2"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="側滑菜單3"/>

        </LinearLayout>

    </android.support.v4.widget.DrawerLayout>


</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

這個佈局側滑菜單包括了菜單部分以及內容部分,用DrawerLayout來包裹起來。其中,菜單部分的根佈局需要添加Android:layout_gravity=”start”,如果是右滑的話,改爲end即可。

這樣就可以完成了一個基本的側滑效果。

DrawerLayout的實現其實是通過ViewDragHelper來實現的,DrawerLayout構造函數的相關代碼如下:

public DrawerLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    mLeftCallback = new ViewDragCallback(Gravity.LEFT);
    mRightCallback = new ViewDragCallback(Gravity.RIGHT);

    mLeftDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mLeftCallback);
    mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
    mLeftDragger.setMinVelocity(minVel);
    mLeftCallback.setDragger(mLeftDragger);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

利用DrawerLayout的監聽實現一些效果 
例如,我們可以實現側滑的時候,Toolbar左上角的按鈕實時變化,我們可以添加一個監聽ActionBarDrawerToggle:

toolbar = (Toolbar) findViewById(R.id.toolbar);
drawer = (DrawerLayout) findViewById(R.id.drawer);

ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
toggle.syncState();

drawer.addDrawerListener(toggle);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

分析一下實現原理: 
其中,ActionBarDrawerToggle實現了DrawerLayout.DrawerListener。並且在滑動的過程中不斷 刷新左上角的Drawerable:

@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
    setPosition(Math.min(1f, Math.max(0, slideOffset)));
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

setPosition的實現如下:

private void setPosition(float position) {
    if (position == 1f) {
        mSlider.setVerticalMirror(true);
    } else if (position == 0f) {
        mSlider.setVerticalMirror(false);
    }
    mSlider.setProgress(position);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

其實就是在滑動的過程中不斷改變mSlider(一個自定義Drawerable對象)的Progress,從而不斷刷新狀態。

因此,我們可以做一些自定義的特效,例如側滑的時候縮放、平移:

drawer.addDrawerListener(new DrawerLayout.DrawerListener() {

    @Override
    public void onDrawerStateChanged(int newState) {
        // 狀態發生改變

    }

    @Override
    public void onDrawerSlide(View drawerView, float slideOffset) {
        // 滑動的過程當中不斷地回調 slideOffset:0~1
        View content = drawer.getChildAt(0);
        float scale = 1 - slideOffset;//1~0
        float leftScale = (float) (1 - 0.3 * scale);
        float rightScale = (float) (0.7f + 0.3 * scale);//0.7~1
        drawerView.setScaleX(leftScale);//1~0.7
        drawerView.setScaleY(leftScale);//1~0.7

        content.setScaleX(rightScale);
        content.setScaleY(rightScale);
        content.setTranslationX(drawerView.getMeasuredWidth() * (1 - scale));//0~width

    }

    @Override
    public void onDrawerOpened(View drawerView) {
        // 打開

    }

    @Override
    public void onDrawerClosed(View drawerView) {
        // 關閉

    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

DrawerLayout+NavigationView實現側滑

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 內容部分 -->
    <FrameLayout
        android:id="@+id/fl"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

    <!-- 菜單部分 -->
    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/navigation_headerlayout"
        app:menu="@menu/navigation_menu"
        />

</android.support.v4.widget.DrawerLayout>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

我們指定了頭部如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_marginTop="20dp"
        android:src="@drawable/icon_people"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="璐寶寶"
        android:textSize="20sp"/>

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

菜單部分如下(menu文件夾下建立),其中菜單可以嵌套:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/action_gallery"
        android:icon="@android:drawable/ic_menu_gallery"
        android:orderInCategory="100"
        android:title="相冊"
        />
    <item
        android:id="@+id/action_details"
        android:icon="@android:drawable/ic_menu_info_details"
        android:orderInCategory="100"
        android:title="詳情"
        />
    <item
        android:id="@+id/action_about"
        android:icon="@android:drawable/ic_menu_help"
        android:orderInCategory="100"
        android:title="關於"
        />
    <item
        android:id="@+id/action_music"
        android:icon="@android:drawable/ic_menu_more"
        android:orderInCategory="100"
        android:title="音樂"
        >
        <menu>
            <item
                android:id="@+id/action_play"
                android:icon="@android:drawable/ic_media_play"
                android:title="播放"/>
            <item
                android:id="@+id/action_pause"
                android:icon="@android:drawable/ic_media_pause"
                android:title="暫停"/>
        </menu>
    </item>

</menu>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

到現在爲止,就可以實現側滑了,最後我們添加上對應的點擊事件,然後關閉菜單:

nav_view = (NavigationView) findViewById(R.id.nav_view);
drawer = (DrawerLayout) findViewById(R.id.drawer);
nav_view.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        Toast.makeText(NavigationViewActivity.this, item.getTitle(), Toast.LENGTH_SHORT).show();
        drawer.closeDrawer(nav_view);
        return false;
    }
});

nav_view.getHeaderView(0).findViewById(R.id.iv_icon).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(NavigationViewActivity.this, "點擊了頭部的圖標", Toast.LENGTH_SHORT).show();
        drawer.closeDrawer(nav_view);
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

SnackBar

//其中View是一個錨點
Snackbar snackbar = Snackbar.make(v, "是否打開XXX模式", Snackbar.LENGTH_SHORT);

//只能設置一個Action
snackbar.setAction("打開", new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.e(TAG, "打開XXX模式");
    }
});
//監聽打開與關閉
snackbar.setCallback(new Snackbar.Callback() {
    @Override
    public void onShown(Snackbar snackbar) {
        super.onShown(snackbar);
        Log.e(TAG, "顯示");
    }

    @Override
    public void onDismissed(Snackbar snackbar, int event) {
        super.onDismissed(snackbar, event);
        Log.e(TAG, "關閉");
    }
});
snackbar.show();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Snackbar的Duration有三種:

Snackbar.LENGTH_SHORT 
Snackbar.LENGTH_LONG 
Snackbar.LENGTH_INDEFINITE—無限長 
make方法傳入的是一個錨點,這裏傳入了一個Button對象。然後還可以設置動作以及回調監聽。

Snackbar的詳細使用參見《輕量級控件SnackBar使用以及源碼分析》

TextInputLayout

佈局:

<android.support.design.widget.TextInputLayout
    android:id="@+id/til_input"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:hintAnimationEnabled="true">

    <EditText
        android:id="@+id/et_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入用戶名"/>

</android.support.design.widget.TextInputLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

hintAnimationEnabled屬性是設置是否開啓Hint的動畫。

需要注意的是,TextInputLayout必須包含一個EditText。

下面是一個基本的例子:

public class TextInputMainActivity extends AppCompatActivity {

    private TextInputLayout til_input;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_text_input);

        til_input = (TextInputLayout) findViewById(R.id.til_input);
        til_input.getEditText().addTextChangedListener(new MaxTextTextWatcher(til_input, "字數不能大於6", 6));

        //開啓計數
        til_input.setCounterEnabled(true);
        til_input.setCounterMaxLength(6);

    }

    class MaxTextTextWatcher implements TextWatcher {

        private TextInputLayout mTextInputLayout;
        private String mErrorString;
        private int maxTextCount;

        public MaxTextTextWatcher(TextInputLayout textInputLayout, String errorString, int maxTextCount) {
            mTextInputLayout = textInputLayout;
            mErrorString = errorString;
            this.maxTextCount = maxTextCount;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            String str = mTextInputLayout.getEditText().getText().toString().trim();
            if (!TextUtils.isEmpty(str)) {
                if (str.length() > maxTextCount) {
                    //顯示錯誤
                    //設置錯誤提示
                    mTextInputLayout.setError(mErrorString);
                    mTextInputLayout.setErrorEnabled(true);
                } else {
                    //關閉錯誤
                    mTextInputLayout.setErrorEnabled(false);
                }
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

在這個例子裏面,我們利用了TextInputLayout的錯誤提示、字數統計功能,基本的使用都比較簡單。

在TextInputLayout可以輕鬆地通過getEditText方法找到它所包裹的EditText。、 
在顯示錯誤的時候,需要先設置錯誤的提示,每次顯示的時候都要設置。 
大部分屬性都可以通過xml的方式設置,這裏通過代碼動態設置只是爲了方便演示。

TextInputLayout詳細使用請參見強大的提示控件TextInputLayout使用以及源碼分析

Toolbar

<android.support.v7.widget.Toolbar
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimary"
    app:logo="@drawable/ic_launcher"
    app:subtitle="子標題"
    app:navigationIcon="@drawable/abc_ic_ab_back_mtrl_am_alpha"
    app:subtitleTextColor="#fff"
    app:title="我是標題"
    app:titleTextColor="#fff"></android.support.v7.widget.Toolbar>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Toolbar是一個ViewGroup,裏面可以放子控件。因此,如果你想標題居中的話,那麼就放入一個TextView吧。

這裏的?attr/colorPrimary是使用了系統的顏色值,當然我們也可以在主題中重寫。

注意:Toolbar需要使用Appcompat的一套東西。

返回監聽:

toolbar.setNavigationOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {
        finish();
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

實現Toolbar隨着界面滑動透明度變化效果 
首先我們需要一個佈局,通過相對佈局把Toolbar壓在ScrollView(或者ListView、RecyclerView)的上面。Toolbar的高度與ScrollView上方內邊距都使用系統的actionBarSize。

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.nan.advancedui.toolbar.MyScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:paddingTop="?attr/actionBarSize">

        <!--這裏是我們的內容佈局-->

    </com.nan.advancedui.toolbar.MyScrollView>

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:title="標題"
        >
    </android.support.v7.widget.Toolbar>

</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

還需要注意給ScrollView設置多兩個屬性,不然的話滑出去以後上內邊距會一直保留:

android:clipToPadding=”false” 該控件的繪製範圍是否不在Padding裏面。false:繪製的時候範圍會考慮padding即會往裏面縮進。 
android:clipChildren=”false” 子控件是否能不超出padding的區域(比如ScrollView上滑動的時候,child可以滑出該區域) 
然後監聽滑動事件,這裏如果是ScrollView的話,需要自定義重寫方法才能監聽:

public class MyScrollView extends ScrollView {

    private OnAlphaListener listener;


    public void setOnAlphaListener(OnAlphaListener listener) {
        this.listener = listener;
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (listener != null) {
            int scrollY = getScrollY();
            int screen_height = getContext().getResources().getDisplayMetrics().heightPixels;
            if (scrollY <= screen_height / 3f) {//0~1f,而透明度應該是1~0f
                listener.onAlpha(1 - scrollY / (screen_height / 3f));//alpha=滑出去的高度/(screen_height/3f)
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

透明度的計算需要根據實際情況來 
自定義一個接口回調,Activity(Fragment)實:

public interface OnAlphaListener {

    void onAlpha(float alpha);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

界面的邏輯如下:

public class ToolbarActivity extends AppCompatActivity implements OnAlphaListener {

    private Toolbar mToolbar;
    private MyScrollView mScrollview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_toolbar);

        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        mScrollview = (MyScrollView) findViewById(R.id.scrollView);

        mScrollview.setOnAlphaListener(this);
    }

    @Override
    public void onAlpha(float alpha) {
        mToolbar.setAlpha(alpha);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

SearchView

SearchView也是V7包的控件,一般也是跟Toolbar中的菜單結合使用。

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.ricky.materialdesign.toolbar.MainActivity"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_search"
        android:orderInCategory="100"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="always"
        android:title="查找"/>
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        app:showAsAction="never"
        android:title="設置"/>
    <item
        android:id="@+id/action_share"
        android:orderInCategory="100"
        app:showAsAction="always"
        android:title="分享"
        android:icon="@android:drawable/ic_menu_share"/>
    <item
        android:id="@+id/action_edit"
        android:orderInCategory="100"
        app:showAsAction="ifRoom"
        android:title="編輯"
        android:icon="@android:drawable/ic_menu_edit"/>

</menu>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

這裏app:actionViewClass=”android.support.v7.widget.SearchView”是指定了菜單的View是一個SearchView。因此我們就可以在代碼中使用了:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);

    //SearchView在Menu裏面,我們通過Item的getActionView就可以找到
    MenuItem item = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
    //設置一出來就直接呈現搜索框---SearchView
    searchView.setIconified(false);

    //進來就呈現搜索框並且不能被隱藏
    //searchView.setIconifiedByDefault(false);

    //有時候我們需要實現自定義擴展效果
    //通過猜想,searchView用到了一個佈局,去appcompat裏面找到abc_search_view.xml,該裏面的控件的屬性
    ImageView icon = (ImageView) searchView.findViewById(R.id.search_go_btn);
    icon.setImageResource(R.drawable.abc_ic_voice_search_api_mtrl_alpha);
    icon.setVisibility(View.VISIBLE);
    searchView.setMaxWidth(200);

    //輸入提示
    SearchView.SearchAutoComplete et = (SearchView.SearchAutoComplete) searchView.findViewById(R.id.search_src_text);
    et.setHint("輸入商品名或首字母");
    et.setHintTextColor(Color.WHITE);

    //設置提交按鈕是否可用(可見)
    searchView.setSubmitButtonEnabled(true);

    //提交按鈕監聽
    icon.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "提交", 1).show();
        }
    });

    //像AutoCompleteTextView一樣使用提示
    //searchView.setSuggestionsAdapter(adapter);

    //監聽焦點改變
    searchView.setOnQueryTextFocusChangeListener(new OnFocusChangeListener() {

        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            // TODO Auto-generated method stub

        }
    });

    //searchView的關閉監聽
    searchView.setOnCloseListener(new OnCloseListener() {

        @Override
        public boolean onClose() {
            // TODO Auto-generated method stub
            return false;
        }
    });

    searchView.setOnSearchClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "提交", 0).show();
        }
    });

    //監聽文本變化,調用查詢
    searchView.setOnQueryTextListener(new OnQueryTextListener() {

        @Override
        public boolean onQueryTextSubmit(String text) {
            //提交文本
            Toast.makeText(MainActivity.this, "提交文本:"+text, 0).show();
            return false;
        }

        @Override
        public boolean onQueryTextChange(String text) {
            // 文本改變的時候回調
            System.out.println("文本變化~~~~~"+text);

            return false;
        }
    });

    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

TabLayout

下面以TabLayout+ViewPager+Fragment爲例,講述TabLayout的基本使用。

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.TabLayout
        android:id="@+id/tablayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabGravity="center"
        app:tabIndicatorColor="#4ce91c"
        app:tabMode="scrollable"
        app:tabSelectedTextColor="#4ce91c"
        app:tabTextColor="#ccc"
        app:tabIndicatorHeight="5dp"
        />

    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

其中,需要關注的屬性有:

app:tabIndicatorColor="@color/colorPrimary_pink"//指示器的顏色
app:tabTextColor="@color/colorPrimary_pink"//tab的文字顏色
app:tabSelectedTextColor="@color/colorPrimary_pinkDark"//選中的tab的文字顏色
app:tabMode="fixed"//scrollable:可滑動;fixed:不能滑動,平分tabLayout寬度
app:tabGravity="center"// fill:tab平均填充整個寬度;center:tab居中顯示
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

需要切換的Fragment,爲了方便,我們重用一個Fragment:

public class NewsDetailFragment extends Fragment {

    @Override
    @Nullable
    public View onCreateView(LayoutInflater inflater,
            @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        TextView tv = new TextView(getContext());
        Bundle bundle = getArguments();
        String title = bundle.getString("title");
        tv.setBackgroundColor(Color.rgb((int)(Math.random()*255), (int)(Math.random()*255), (int)(Math.random()*255)));
        tv.setText(title);
        return tv;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Activity的代碼:

public class TabLayoutActivity extends AppCompatActivity {

    private TabLayout tabLayout;
    private String[] title = {
            "頭條",
            "新聞",
            "娛樂",
            "體育",
            "科技",
            "美女",
            "財經",
            "汽車",
            "房子",
            "頭條"
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_layout);

        final ViewPager viewPager = (ViewPager) findViewById(R.id.vp);
        tabLayout = (TabLayout) findViewById(R.id.tablayout);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());


        //1.TabLayout和Viewpager關聯
//        tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
//
//            @Override
//            public void onTabUnselected(TabLayout.Tab arg0) {
//
//            }
//
//            @Override
//            public void onTabSelected(TabLayout.Tab tab) {
//                // 被選中的時候回調
//                viewPager.setCurrentItem(tab.getPosition(), true);
//            }
//
//            @Override
//            public void onTabReselected(TabLayout.Tab tab) {
//
//            }
//        });
        //2.ViewPager滑動關聯tabLayout
//        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
        //設置tabLayout的標籤來自於PagerAdapter
//        tabLayout.setTabsFromPagerAdapter(adapter);


        //設置tabLayout的標籤來自於PagerAdapter
        tabLayout.setupWithViewPager(viewPager);

        viewPager.setAdapter(adapter);

        //設置Indicator的左右間距(Indicator的寬度)
        setIndicator(this, tabLayout, 15, 15);
    }

    class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return title[position];
        }

        @Override
        public Fragment getItem(int position) {
            Fragment f = new NewsDetailFragment();
            Bundle bundle = new Bundle();
            bundle.putString("title", title[position]);
            f.setArguments(bundle);
            return f;
        }

        @Override
        public int getCount() {
            return title.length;
        }

    }

    //下面三個方法是設置Indicator

    public static void setIndicator(Context context, TabLayout tabs, int leftDip, int rightDip) {
        Class<?> tabLayout = tabs.getClass();
        Field tabStrip = null;
        try {
            tabStrip = tabLayout.getDeclaredField("mTabStrip");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        tabStrip.setAccessible(true);
        LinearLayout ll_tab = null;
        try {
            ll_tab = (LinearLayout) tabStrip.get(tabs);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        int left = (int) (getDisplayMetrics(context).density * leftDip);
        int right = (int) (getDisplayMetrics(context).density * rightDip);

        for (int i = 0; i < ll_tab.getChildCount(); i++) {
            View child = ll_tab.getChildAt(i);
            child.setPadding(0, 0, 0, 0);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
            params.leftMargin = left;
            params.rightMargin = right;
            child.setLayoutParams(params);
            child.invalidate();
        }
    }

    public static DisplayMetrics getDisplayMetrics(Context context) {
        DisplayMetrics metric = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metric);
        return metric;
    }

    public static float getPXfromDP(float value, Context context) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
                context.getResources().getDisplayMetrics());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132

新提供的tabLayout.setupWithViewPager(viewPager);方法代替了註釋中的3個方法了,其實內部做的事都是一樣的。TabLayout默認沒有提供修改Indicator寬度的函數,需要我們通過反射的方式去設置。

用TabLayout實現底部導航(相對於傳統的TabHost,它是可滑動的)

只需要三個步驟: 
1.在佈局中就把TabLayout放在佈局底部 
2。去掉底部的indicator,app:tabIndicatorHeight=”0dp” 
3.實現自己的效果,自定義的標籤佈局 
代碼如下:

for (int i = 0; i < tabLayout.getTabCount(); i++) {
    TabLayout.Tab tab = tabLayout.getTabAt(i);
    tab.setCustomView(view);
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

CardView

CardView就是一個ViewGroup,裏面可以放置子佈局

<android.support.v7.widget.CardView
    android:layout_width="300dp"
    android:layout_height="200dp"
    android:layout_margin="16dp"
    android:clickable="true"
    android:foreground="?attr/selectableItemBackground"
    android:stateListAnimator="@drawable/z_translation"
    app:cardCornerRadius="10dp"
    app:cardElevation="10dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/test"/>

</android.support.v7.widget.CardView>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

其中,cardElevation是設置高度,高度越高,陰影越明顯。foreground屬性是設置點擊水波紋效果。cardCornerRadius是設置圓角的大小。stateListAnimator是設置點擊的動畫效果,點擊以後,往下壓,z_translation如下:

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

    <item
        android:state_pressed="true">
        <objectAnimator
            android:duration="@android:integer/config_shortAnimTime"
            android:propertyName="translationZ"
            android:valueTo="-15dp"
            android:valueType="floatType"/>
    </item>

    <item>
        <objectAnimator
            android:duration="@android:integer/config_shortAnimTime"
            android:propertyName="translationZ"
            android:valueTo="0dp"
            android:valueType="floatType"/>
    </item>

</selector>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

CardView兼容性開發 
創建layout、layout-v21兩套佈局,根據下面的差別寫兩份CardView的佈局文件。其中尤其注意的是stateListAnimator這個屬性,如果最小SDK版本低於21,AS就會警告。 
1.陰影的細微差別 
5.x系統:邊距陰影比較小,需要手動添加邊距16dp,android:layout_margin=”16dp” 
4.x系統:邊距陰影比較大,手動修改邊距0dp(原因:兼容包裏面設置陰影效果自動設置了margin來處理16dp) 
2.圓角效果的細微差別 
5.x系統:圖片和佈局都可以很好的呈現圓角效果,圖片也變圓角了,因此5.x上面不需要設置app:contentPadding

4.x系統:圖不能變成圓角(圖片的直角會頂到CardView的邊上),如果要做成5.x一樣的效果:通過加載圖片的時候自己去處理成圓角(與CardView的圓角大小一樣),因此4.x上面不需要設置app:contentPadding,從而儘量好看一些

3.水波紋效果的差別 
5.x系統:可以通過 
android:foreground=”?attr/selectableItemBackground”實現 
4.x系統:需要自己實現

4.點擊動畫的差別 
5.x系統:可以通過android:stateListAnimator=”@drawable/z_translation”設置動畫 
4.x系統:不能設置上述的動畫,因爲4.x沒有z軸的概念

FloatingActionButton

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_gravity="right|bottom"
    android:onClick="rotate"
    android:src="@drawable/ic_add_white_24dp"
    app:backgroundTint="?attr/colorPrimary"
    app:elevation="10dp"
    app:fabSize="normal"
    app:rippleColor="#f00"
    />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

其中: 
1.src屬性是設置圖標 
2.backgroundTint是設置背景色(圖標是透明背景的) 
3.elevation是設置陰影大小 
4.fabsize是設置圖標的大小,一般爲normal(不用設置) 
5.rippleColor是設置水波紋的顏色 
點擊事件如下(旋轉):

private boolean reverse = false;

public void rotate(View v) {
    float toDegree = reverse ? -180f : 180f;
    ObjectAnimator animator = ObjectAnimator
            .ofFloat(v, "rotation", 0.0f, toDegree)
            .setDuration(400);
    animator.start();
    reverse = !reverse;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

FloatingActionButton動畫 
方案1:列表滑動的時候FloatingActionButton隱藏與顯示,通過自定義OnScrollListener實現

public class FabScrollListener extends OnScrollListener {
    private static final int THRESHOLD = 20;
    private int distance = 0;
    private HideScrollListener hideListener;
    private boolean visible = true;//是否可見

    public FabScrollListener(HideScrollListener hideScrollListener) {
        this.hideListener = hideScrollListener;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        /**
         * dy:Y軸方向的增量
         * 有正和負
         * 當正在執行動畫的時候,就不要再執行了
         */
        if (distance > THRESHOLD && visible) {
            //隱藏動畫
            visible = false;
            hideListener.onHide();
            distance = 0;
        } else if (distance < -THRESHOLD && !visible) {
            //顯示動畫
            visible = true;
            hideListener.onShow();
            distance = 0;
        }
        if (visible && dy > 0 || (!visible && dy < 0)) {
            distance += dy;
        }
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

自定義一個OnScrollListener,重寫onScrolled方法。判斷當前的滾動方向、滾動距離、當前的FloatingActionButton是否顯示來進行相應的邏輯處理。

其中HideScrollListener是一個自定義的監聽接口:

public interface HideScrollListener {
    void onHide();
    void onShow();
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

由Activity實現這個接口:

public class FabAnimActivity extends AppCompatActivity implements HideScrollListener {

    private RecyclerView recyclerview;
    private ImageButton fab;
    private Toolbar toolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //省略一些代碼

        //添加滑動監聽
        recyclerview.addOnScrollListener(new FabScrollListener(this));
    }

    @Override
    public void onHide() {
        // 隱藏動畫--屬性動畫
        toolbar.animate().translationY(-toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(3));
        RelativeLayout.LayoutParams layoutParams = (LayoutParams) fab.getLayoutParams();

        fab.animate().translationY(fab.getHeight() + layoutParams.bottomMargin).setInterpolator(new AccelerateInterpolator(3));
    }

    @Override
    public void onShow() {
        // 顯示動畫--屬性動畫
        toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));

        fab.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

方案2:自定義FloatingActionButton的Behavior實現 
繼承FloatingActionButton的Behavior:

public class FabBehavior extends FloatingActionButton.Behavior {
    private boolean visible = true;//是否可見

    //實例化CoordinatorLayout.LayoutParams時反射生成Behavior實例,這就是爲什麼自定義behavior需要重寫如下的構造函數
    public FabBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
        // 當觀察的View(RecyclerView)發生滑動的開始的時候回調的
        //nestedScrollAxes:滑動關聯軸, 我們現在只關心垂直的滑動。
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        // 當觀察的view滑動的時候回調的
        //根據情況執行動畫
        if (dyConsumed > 0 && visible) {
            //show
            visible = false;
            onHide(child);
        } else if (dyConsumed < 0) {
            //hide
            visible = true;
            onShow(child);
        }

    }

    public void onHide(FloatingActionButton fab) {
        // 隱藏動畫--屬性動畫
//        toolbar.animate().translationY(-toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(3));

        CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
//        fab.animate().translationY(fab.getHeight()+layoutParams.bottomMargin).setInterpolator(new AccelerateInterpolator(3));
        //FAB 縮小
        ViewCompat.animate(fab).scaleX(0f).scaleY(0f).start();
    }

    public void onShow(FloatingActionButton fab) {
        // 顯示動畫--屬性動畫
//        toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));

        CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
//        fab.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
        //FAB放大
        ViewCompat.animate(fab).scaleX(1f).scaleY(1f).start();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

構造方法必須重寫,重寫onStartNestedScroll返回判斷哪個方向的滑動,重寫onNestedScroll進行相應的邏輯處理(FloatingActionButton的屬性動畫顯示與隱藏)。

最後在佈局文件中使用CoordinatorLayout佈局,並且給FloatingActionButton添加自定義的Behavior:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:paddingTop="?attr/actionBarSize"
        />

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:title="Fab動畫"
        app:titleTextColor="#fff"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="58dp"
        android:layout_height="58dp"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:src="@drawable/ic_favorite_outline_white_24dp"
        app:layout_behavior="com.nan.advancedui.fab.anim.behavior.FabBehavior"
        />

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

CoordinatorLayout

CoordinatorLayout是一個繼承於ViewGroup的佈局容器。CoordinatorLayout監聽滑動子控件的滑動通過Behavior反饋到其他子控件並執行一些動畫。簡單來說,就是通過協調並調度裏面的子控件或者佈局來實現觸摸(一般是指滑動)產生一些相關的動畫效果。 
其中,view的Behavior是通信的橋樑,我們可以通過設置view的Behavior來實現觸摸的動畫調度。

注意:滑動控件指的是:RecyclerView/NestedScrollView/ViewPager,意味着ListView、ScrollView不行。

詳細使用請參考 《CoordinatorLayout使用全解析》

MaterialDesign動畫

1.Touch Feedback(觸摸反饋) 
5.0+的手機是自帶的。

通過給控件設置background的屬性值即可實現:

<Button
    android:id="@+id/btn_test"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/selectableItemBackground"
    android:background="?attr/selectableItemBackgroundBorderless"
    android:text="測試"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其中,selectableItemBackground是有邊界的水波紋效果,selectableItemBackgroundBorderless是沒有邊界的水波紋效果。

可以修改背景顏色和水波紋的顏色,並且最好使用AppcompatActivity:

 <item name="colorControlHighlight">@color/colorPrimary_pink</item>
 <item name="colorButtonNormal">@color/material_blue_grey_800</item>
  • 1
  • 2
  • 1
  • 2

如果想改變個別控件的顏色的話,可以通過在外面再嵌套一層佈局實現。 
2.Reveal Effect(揭露效果) 
例子:Activity的揭露出現的效果。主要使用ViewAnimationUtil工具類實現:

//圓形水波紋揭露效果
ViewAnimationUtils.createCircularReveal(
        view, //作用在哪個View上面
        centerX, centerY, //擴散的中心點
        startRadius, //開始擴散初始半徑
        endRadius)//擴散結束半徑
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其中,擴散的半徑通過勾股定理進行計算,例如:

(float) Math.hypot(view.getWidth() / 2, view.getHeight() / 2)
  • 1
  • 1
@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    view_root = (LinearLayoutCompat) findViewById(R.id.llc_test);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Animator animator = ViewAnimationUtils.createCircularReveal(view_root, view_root.getWidth() / 2, view_root.getHeight() / 2, 0f, (float) Math.hypot(view_root.getWidth() / 2, view_root.getHeight() / 2));
        animator.setDuration(1000);
        animator.setInterpolator(new AccelerateInterpolator());
        animator.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

因爲動畫播放是依附在window上面的,而在Activity onCreate方法中調用時Window還未初始化完畢,因此需要在onWindowFocusChanged中執行動畫。

3.Activity transition(Activity轉場動畫效果) 
兩個Activity進行跳轉的時候,轉場動畫。以前我們是通過overridePendingTransition方法實現。

主要使用ActivityOptions類。只支持API21以上的版本。版本判斷會比較麻煩,谷歌很貼心 設計了一個兼容類:ActivityOptionsCompat(v4包中),但是此類在低版本上面並沒有轉場動畫效果,只是解決了我們手動去判斷版本的問題而已。

使用轉換動畫前提:需要給兩個Activity都設置如下,讓其允許使用轉場動畫。

//方法一:
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
//方法二:
修改主題:<item name="android:windowContentTransitions">true</item>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

轉場動畫可以分爲兩大類:共享元素轉換和普通的轉換。 
1)共享元素轉換

單個元素:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(MDAnimActivity.this, iv_test, "test");
    Intent intent = new Intent(MDAnimActivity.this, MDAnimSceneTransitionActivity.class);
    startActivity(intent, options.toBundle());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

多個元素同時轉換:

ActivityOptionsCompat optionsCompat = ActivityOptionsCompat
        .makeSceneTransitionAnimation(this, Pair.create((View)iv1, "iv1"),Pair.create((View)bt, "bt"));
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent, optionsCompat.toBundle());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

頁面返回的時候系統自動實現了,請看FragmentActivity的onBackPressed方法:

@Override
public void onBackPressed() {
    if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
        super.onBackPressed();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.非共享元素的轉換 
只有API 21纔有下面自帶效果,因此使用的時候需要判斷版本號。

三種系統帶的:滑動效果(Slide)、展開效果Explode、漸變顯示隱藏效果Fade。下面以Fade爲例子介紹:

//最好兩個Activity都設置一些,效果會比較好看
Fade fade = new Fade();
fade.setDuration(1000);
getWindow().setExitTransition(fade);//出去的動畫
getWindow().setEnterTransition(fade);//進來的動畫


//如果有共享元素,可以設置共享元素,那麼它就會按照共享元素動畫執行,其他的子view就會按照Fade動畫執行。
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this);
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent, optionsCompat.toBundle());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章