一篇博客让你了解Material Design的使用

更新时间:2017-08-25 11:41:55 点击次数:1879次

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不行。

详细使用请参考 《Material Design系列探究之LinearLayoutCompat》

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());

本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

回到顶部
嘿,我来帮您!