Material Design的基本概念
Material Design是Google设计的一套视觉语言,将优先的经典的设计原理与科技创新相结合,为开发者提供一套完成视觉和交互设计规范。移动设备是这套设计语言的基础对象,让用户在不同的平台、不同尺寸的设备上能保持一致的体验。 Material Design强调交互上的即时反馈,即对于用户的触控等行为app需要给出即时的反应。同时Material Design要求应用给用户带入感,让用户在使用时是沉浸在当前的应用当中。例如Google给出了沉浸式状态栏等“工具”,希望通过改变StatusBar和NavigationBar来给用户更强的融入感,专注于应用本身提供的内容。 Google从动画、颜色、样式、触控反馈、布局等多个方面给出了Material Design的设计要求。无论是单一的控件还是图文布局,Google都给出了明确的设计说明,有兴趣的同学可以去上方提到的官方链接处做进一步了解。
RecyclerView的使用
写条目布局:
<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 mDatas;
private Context mContext;
public MyRecyclerViewAdapter(Context context, List datas) {
mContext = context;
mDatas = datas;
}
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) {
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 datas = new ArrayList<>();
for (int i = 0 ; i < 100 ; i++) {
datas.add("第" + i + "个数据" );
}
mAdapter = new MyRecyclerViewAdapter(this , datas);
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)
写法一般情况下是没有问题的,但是当我们在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);
}
注意如果你想使用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实现侧滑: 定义一个布局:
<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)
分析一下实现原理: 其中,ActionBarDrawerToggle实现了DrawerLayout.DrawerListener。并且在滑动的过程中不断 刷新左上角的Drawerable:
@Override
public void onDrawerSlide (View drawerView, float slideOffset) {
setPosition(Math.min(1 f, Math.max(0 , slideOffset)));
}
setPosition的实现如下:
private void setPosition (float position) {
if (position == 1 f) {
mSlider.setVerticalMirror(true );
} else if (position == 0 f) {
mSlider.setVerticalMirror(false );
}
mSlider.setProgress(position);
}
其实就是在滑动的过程中不断改变mSlider(一个自定义Drawerable对象)的Progress,从而不断刷新状态。
因此,我们可以做一些自定义的特效,例如侧滑的时候缩放、平移:
drawer.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerStateChanged (int newState) {
}
@Override
public void onDrawerSlide (View drawerView, float slideOffset) {
View content = drawer.getChildAt(0 );
float scale = 1 - slideOffset;
float leftScale = (float ) (1 - 0.3 * scale);
float rightScale = (float ) (0.7 f + 0.3 * scale);
drawerView.setScaleX(leftScale);
drawerView.setScaleY(leftScale);
content.setScaleX(rightScale);
content.setScaleY(rightScale);
content.setTranslationX(drawerView.getMeasuredWidth() * (1 - scale));
}
@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
我们指定了头部如下:
<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文件夹下建立),其中菜单可以嵌套:
<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
Snackbar snackbar = Snackbar.make(v, "是否打开XXX模式" , Snackbar.LENGTH_SHORT);
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
布局:
.support.design .widget .TextInputLayout
android:id="@+id/til_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintAnimationEnabled="true" >
"@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入用户名" />
.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使用以及源码分析
.support.v 7.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" > .support.v 7.widget .Toolbar >
Toolbar是一个ViewGroup,里面可以放子控件。因此,如果你想标题居中的话,那么就放入一个TextView吧。
这里的?attr/colorPrimary是使用了系统的颜色值,当然我们也可以在主题中重写。
注意:Toolbar需要使用Appcompat的一套东西。
返回监听:
toolbar.setNavigationOnClickListener(new OnClickListener() {
@Override
public void onClick (View v) {
finish();
}
});
实现Toolbar随着界面滑动透明度变化效果 首先我们需要一个布局,通过相对布局把Toolbar压在ScrollView(或者ListView、RecyclerView)的上面。Toolbar的高度与ScrollView上方内边距都使用系统的actionBarSize。
"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 >
.support.v 7.widget .Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:title="标题"
>
.support.v 7.widget .Toolbar >
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 / 3 f) {
listener.onAlpha(1 - scrollY / (screen_height / 3 f));
}
}
}
}
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);
}
界面的逻辑如下:
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中的菜单结合使用。
"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" />
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);
MenuItem item = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setIconified(false );
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();
}
});
searchView.setOnQueryTextFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange (View v, boolean hasFocus) {
}
});
searchView.setOnCloseListener(new OnCloseListener() {
@Override
public boolean onClose () {
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的基本使用。
"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" >
.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"
/>
.support.v 4.view .ViewPager
android:id="@+id/vp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
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"
app:tabSelectedTextColor="@color/colorPrimary_pinkDark"
app:tabMode="fixed"
app:tabGravity="center"
需要切换的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());
tabLayout.setupWithViewPager(viewPager);
viewPager.setAdapter(adapter);
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;
}
}
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);
}
CardView
CardView就是一个ViewGroup,里面可以放置子布局
.support.v 7.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" >
"match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/test" />
.support.v 7.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轴的概念
.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 ? -180 f : 180 f;
ObjectAnimator animator = ObjectAnimator
.ofFloat(v, "rotation" , 0.0 f, toDegree)
.setDuration(400 );
animator.start();
reverse = !reverse;
}
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();
}
由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 ;
public FabBehavior (Context context, AttributeSet attrs) {
super (context, attrs);
}
@Override
public boolean onStartNestedScroll (CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int 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);
if (dyConsumed > 0 && visible) {
visible = false ;
onHide(child);
} else if (dyConsumed < 0 ) {
visible = true ;
onShow(child);
}
}
public void onHide (FloatingActionButton fab) {
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
ViewCompat.animate(fab).scaleX(0 f).scaleY(0 f).start();
}
public void onShow (FloatingActionButton fab) {
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
ViewCompat.animate(fab).scaleX(1 f).scaleY(1 f).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:
.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" >
.support.v 7.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"
/>
.support.v 7.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" />
.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"
/>
.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的属性值即可实现:
id="@+id/btn_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:background="?attr/selectableItemBackgroundBorderless"
android:text ="测试" />
其中,selectableItemBackground是有边界的水波纹效果,selectableItemBackgroundBorderless是没有边界的水波纹效果。
可以修改背景颜色和水波纹的颜色,并且最好使用AppcompatActivity:
<item name ="colorControlHighlight" >@color/colorPrimary_pinkitem >
<item name ="colorButtonNormal" >@color/material_blue_grey_800item >
如果想改变个别控件的颜色的话,可以通过在外面再嵌套一层布局实现。 2.Reveal Effect(揭露效果) 例子:Activity的揭露出现的效果。主要使用ViewAnimationUtil工具类实现:
ViewAnimationUtils.createCircularReveal(
view,
centerX, centerY,
startRadius,
endRadius)
其中,扩散的半径通过勾股定理进行计算,例如:
(float) Math.hypot (view.getWidth () / 2 , view.getHeight () / 2 )
@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 , 0 f, (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);
修改主题:- "android:windowContentTransitions">
true
转场动画可以分为两大类:共享元素转换和普通的转换。 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 ())
}
多个元素同时转换:
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());
页面返回的时候系统自动实现了,请看FragmentActivity的onBackPressed方法:
@Override
public void onBackPressed () {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
super .onBackPressed();
}
}
2.非共享元素的转换 只有API 21才有下面自带效果,因此使用的时候需要判断版本号。
三种系统带的:滑动效果(Slide)、展开效果Explode、渐变显示隐藏效果Fade。下面以Fade为例子介绍:
Fade fade = new Fade();
fade.setDuration(1000 );
getWindow().setExitTransition(fade);
getWindow().setEnterTransition(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