参考: 吴小龙:Android Design Support Library使用
参考:徐宜生:Android Design Support Library使用详解
最近看到一个介绍的也挺好的Meterial Design开发者文档
1 CoordinatorLayout #
效果图:
布局图:
可以实现滑到顶部固定的效果
需要满足的条件
- A必须设置:app:layout_scrollFlags=”scroll|enterAlways”,B不设置。
- C必须设置:app:layout_behavior=”@string/appbar_scrolling_view_behavior”
- AB必须在AppBarLayout中
- ABC必须在CoodinatorLayout中
- C不能是ListView,一般是RecyclerView,或者ViewPager,ScrollView
含义:
现在,当用户滚动RecyclerView,AppBarLayout可以这样响应滚动事件:根据子view的滚动标志(scroll flag)来控制它们如何进入(滚入屏幕)与退出(滚出屏幕)。
Flag包括:
scroll: 所有想滚动出屏幕的view都需要设置这个flag- 没有设置这个flag的view将被固定在屏幕顶部。
enterAlways: 这个flag让任意向下的滚动都会导致该view变为可见,启用快速“返回模式”。
enterAlwaysCollapsed: 顾名思义,这个flag定义的是何时进入(已经消失之后何时再次显示)。
假设你定义了一个最小高度(minHeight)同时enterAlways也定义了,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。
exitUntilCollapsed: 同样顾名思义,这个flag时定义何时退出,当你定义了一个minHeight,这个view将在滚动到达这个最小高度的时候消失。
布局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.cqc.coordinatorlayoutdemo.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolBar"
android:layout_width="match_parent"
android:layout_height="30dp"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIcon="@mipmap/ic_launcher"
app:subtitle="标题"/>
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:dividerHeight="2dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v7.widget.RecyclerView>
</android.support.design.widget.CoordinatorLayout>
代码:
package com.cqc.coordinatorlayoutdemo;
import android.support.design.widget.TabLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* 主要是实现顶部固定的效果,其他的都没做处理
* 效果:屏幕从上到下是:A-->B-->C,
* 手指往上滑动控件C,会导致控件A移除屏幕,控件B顶部固定;
* 手指往下滑动控件C,控件A再次出现
* 要求:
* 1 划出屏幕的控件必须设置app:layout_scrollFlags="scroll|enterAlways"
* 2 顶部固定的控件不用设置app:layout_scrollFlags="scroll|enterAlways"
* 3 上述的2个控件必须在AppBarLayout中
* 4 滑动控件 必须设置app:layout_behavior="@string/appbar_scrolling_view_behavior"
* 5 滑动的那个控件不能是listView
* 6 A B C 3个控件必须在CoodinatorLayout中
*
*
* */
public class MainActivity extends AppCompatActivity {
private List<String> list = new ArrayList<>();
private Toolbar toolBar;
private TabLayout tabLayout;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
findViews();
initViews();
}
private void initViews() {
initToolbar();
initTabLayout();
initRecyclerView();
}
private void initToolbar() {
}
private void initTabLayout() {
//设置mode
tabLayout.setTabMode(TabLayout.MODE_FIXED);
//添加每个tab的文字
tabLayout.addTab(tabLayout.newTab().setText("tab1"), );
tabLayout.addTab(tabLayout.newTab().setText("tab2"), );
tabLayout.addTab(tabLayout.newTab().setText("tab3"), );
}
private void initRecyclerView() {
initData();
recyclerView.setItemAnimator(new DefaultItemAnimator());//item动画有默认的
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this,LinearLayoutManager.VERTICAL,false));
MyAdapter adapter = new MyAdapter();
recyclerView.setAdapter(adapter);
}
private void findViews() {
toolBar = (Toolbar) findViewById(R.id.toolBar);
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
}
private void initData() {
list.clear();
for (int i = ; i < ; i++) {
list.add("text" + i);
}
}
public class MyAdapter extends RecyclerView.Adapter<MyHolder> {
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView tv = new TextView(MainActivity.this);
MyHolder holder = new MyHolder(tv);
return holder;
}
@Override
public void onBindViewHolder(MyHolder holder, int position) {
holder.tv.setText(list.get(position));
holder.tv.setTextSize();
}
@Override
public int getItemCount() {
return list.size();
}
}
public class MyHolder extends RecyclerView.ViewHolder {
public TextView tv;
public MyHolder(View itemView) {
super(itemView);
tv = (TextView) itemView;
}
}
}
位于CoordinatorLayout的底部
子View位于CoordinatorLayout的底部,应该用属性
layout_gravity
用
layout_alignParentBottom
无效
源码:CoordinatorLayout
2 NavigationView
DrawerLayout的侧栏栏布局控件,xml中有2个重要的属性 headerLayout + menu
,分别是侧拉栏的头像布局和主布局。
当然可以使用java填充布局:
nagivationView.inflateHeaderView(R.layout.layout_main_nav);
View headerView = mNavView.getHeaderView();//可能有多个header
效果图
xml中的使用
<!--有滚动效果scrollbar-->
<android.support.design.widget.NavigationView
android:id="@+id/navigationView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="left"
android:fitsSystemWindows="true"
app:headerLayout="@layout/header_layout_navigation_view"
app:menu="@menu/menu_naviagtion_view_main"/>
headerLayout:header_layout_navigation_view.xml比较简单,只是放置了1个imageView+2个TextView.
menu:menu_naviagtion_view_main.xml
menu节点包含group+item,group包含item。
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/camera"
android:icon="@android:drawable/ic_menu_camera"
android:title="camera"/>
<item
android:id="@+id/gallery"
android:icon="@android:drawable/ic_menu_gallery"
android:title="gallery"/>
<item
android:id="@+id/slideshow"
android:icon="@android:drawable/ic_menu_slideshow"
android:title="slideshow"/>
<item
android:id="@+id/manage"
android:icon="@android:drawable/ic_menu_manage"
android:title="manage"/>
</group>
<item android:title="其他">
<menu>
<item
android:id="@+id/share"
android:checkable="true"
android:icon="@android:drawable/ic_menu_share"
android:title="share"/>
<item
android:id="@+id/send"
android:checkable="true"
android:icon="@android:drawable/ic_menu_send"
android:title="send"/>
</menu>
</item>
</menu>
代码
navigationView.setNavigationItemSelectedListener(this);
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener
2种方式填充HeadView和Menu
第一种:上面说的xml填充
第二种:代码填充
mNavigationView.inflateMenu(R.menu.nav_menu);
mNavigationView.inflateHeaderView(R.layout.nav_header_main);
怎么让默认选中第一个MenuItem
Menu menu = mNavigationView.getMenu();
menu.getItem().setChecked(true);
3 FloatingActionButton
悬浮控件,可以设置摆放的位置。
当然悬浮控件还可以通过
来实现
relativeLayout(根布局) = layout +button;
效果图
xml
<!--红色是前景色,所有android:background="@android:drawable/ic_dialog_email"无效,不显示-->
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="20dp"
android:src="@android:drawable/ic_dialog_email"
/>
代码
点击FloatingActionButton,弹出Toast
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "action", Toast.LENGTH_SHORT).show();
}
});
上滑隐藏FAB,下滑显示TAB
通过自定义behavior来实现:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0407/4126.html,并在xml中定义beh。
下面2种方法,测试上滑FAB会消失,但是下滑FAB没有出现,网络的解决方法如下,但是测试无效。http://stackoverflow.com/questions/31381474/menu-and-autohide-floatingactionbutton-of-android-design-support-library
<android.support.design.widget.FloatingActionButton
...
android:src="@android:drawable/sym_action_chat"
app:layout_behavior="com.paireach.bw.view.ScrollAwareFABBehavior"/>
第一种:ScrollAwareFABBehaviorDefault
public class ScrollAwareFABBehaviorDefault extends FloatingActionButton.Behavior {
public ScrollAwareFABBehaviorDefault(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View directTargetChild, final View target, final int nestedScrollAxes) {
// Ensure we react to vertical scrolling
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
|| super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View target, final int dxConsumed, final int dyConsumed,
final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > && child.getVisibility() == View.VISIBLE) {
// User scrolled down and the FAB is currently visible -> hide the FAB
child.hide();
} else if (dyConsumed < && child.getVisibility() != View.VISIBLE) {
// User scrolled up and the FAB is currently not visible -> show the FAB
child.show();
}
}
}
第二种:ScrollAwareFABBehavior
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
private boolean mIsAnimatingOut = false;
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View directTargetChild, final View target, final int nestedScrollAxes) {
// Ensure we react to vertical scrolling
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
|| super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View target, final int dxConsumed, final int dyConsumed,
final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > && !this.mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {
// User scrolled down and the FAB is currently visible -> hide the FAB
animateOut(child);
} else if (dyConsumed < && child.getVisibility() != View.VISIBLE) {
// User scrolled up and the FAB is currently not visible -> show the FAB
animateIn(child);
}
}
// Same animation that FloatingActionButton.Behavior uses to hide the FAB when the AppBarLayout exits
private void animateOut(final FloatingActionButton button) {
if (Build.VERSION.SDK_INT >= ) {
ViewCompat.animate(button).translationY(button.getHeight() + getMarginBottom(button)).setInterpolator(INTERPOLATOR).withLayer()
.setListener(new ViewPropertyAnimatorListener() {
public void onAnimationStart(View view) {
ScrollAwareFABBehavior.this.mIsAnimatingOut = true;
}
public void onAnimationCancel(View view) {
ScrollAwareFABBehavior.this.mIsAnimatingOut = false;
}
public void onAnimationEnd(View view) {
ScrollAwareFABBehavior.this.mIsAnimatingOut = false;
view.setVisibility(View.GONE);
}
}).start();
} else {
}
}
// Same animation that FloatingActionButton.Behavior uses to show the FAB when the AppBarLayout enters
private void animateIn(FloatingActionButton button) {
button.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= ) {
ViewCompat.animate(button).translationY()
.setInterpolator(INTERPOLATOR).withLayer().setListener(null)
.start();
} else {
}
}
private int getMarginBottom(View v) {
int marginBottom = ;
final ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
marginBottom = ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin;
}
return marginBottom;
}
}
4 Snackbar
类似Toast,只不过Snackbar实在屏幕顶部弹出,而且可以设置点击事件。同Toast一样,不需要再xml中胚子。
效果图
代码
点击Snackbar,弹出Toast
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Snacker和Toast差不多,只是Snacker底部的弹出。setAction(...)可以给Snacker设置点击事件
Snackbar.make(view, "fab", Snackbar.LENGTH_SHORT).setAction("action", new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "action", Toast.LENGTH_SHORT).show();
}
}).show();
}
});
//无点击事件
Snackbar.make(view, "fab", Snackbar.LENGTH_SHORT).setAction("action", null).show();
//有点击事件
Snackbar.make(view, "fab", Snackbar.LENGTH_SHORT).setAction("action", new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "action", Toast.LENGTH_SHORT).show();
}
}).show();
源码:https://git.oschina.net/MaterialDisign/NavigationViewDemo02
Metarial Design:主要演示 NavigationView + Snackbar + FloagtingActionButton 效果,还有CoordinatorLayout + AppbarLayout + Toolbar。
5 TabLayout
详看:Android Design:原生TabLayout+viewpaper+fragment实现滑动效果
TabLayout一般配合ViewPager使用。设置title要在viewpager的adapter调用 getPageTitle(int position)
方法
xml
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#03A9F4"
app:tabGravity="fill"
app:tabIndicatorColor="@color/colorPrimary"
app:tabMode="fixed"
app:tabIndicatorHeight="2dp"
app:tabSelectedTextColor="@color/colorAccent"
app:tabTextAppearance="@style/TabLayoutTextStyle"
app:tabTextColor="@android:color/black">
</android.support.design.widget.TabLayout>
代码:
//注意这行代码的顺序:viewpaper要先设置adapter,才可以让 tablayout绑定,否则报错:viewpager没有setAdapter()
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem();
TabLayoutDemo
下面介绍的内容是:TextInputLayout + BottomNavigationView + SearchView + 白天夜间模式切换
源码:https://git.oschina.net/MaterialDisign/DesignDemo02
效果图:
6 TextInputLayout
其它:android TextInputLayout组件的使用
注意:
design:25.0.0
有显示密码这个小眼睛
,
design:25.0.1
就没有这个小眼睛。
显示小眼睛需满足2个条件:
android:inputType="textPassword"
app:passwordToggleEnabled="true"
里面只可以包含一个EditView,常用的2个 方法是
setErrorEnabled(boolean)
(不可以提示错误信息) 和
setError(CharSequence)
(提示错误信息)
app:counterEnabled=”true”//设置是否可以开启计数器,默认是false
app:counterOverflowTextAppearance=”“ 计算器越位后的文字颜色和大小
app:counterMaxLength=”“计算器的最大字数限制
app:errorEnabled=”true” 是否允许错误提示
app:errorTextAppearance=”“ 错误提示的文字大小和颜色
app:passwordToggleEnabled=”true”显示小眼睛
app:passwordToggleTint=”@color/colorAccent” 给小眼睛上色
app:passwordToggleTintMode=”multiply”小眼睛的显示方式
app:passwordToggleDrawable=”@mipmap/ic_launcher”用另一张图片代替小眼睛
需要注意的是:如果想要显示小眼睛,就需要在
或者 EditText 中设置 为密码格式。比如:
TextInputEditText
android:inputType="textPassword"
如果在xml中给
TextInputLayout
设置
app:errorEnabled="true"
,那么会扩大它的高,显得不美观,所以在xml中不配置此属性。
效果图
xml
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/textInputLayout1"
android:layout_marginTop="10dp">
<EditText
android:id="@+id/et_pwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密码"
android:imeActionId="@+id/login"
android:imeActionLabel="登录"
android:imeOptions="actionDone"
android:inputType="textPassword"/>
</android.support.design.widget.TextInputLayout>
ime是软键盘(输入法)的意思,是软键盘右下角按钮的id,
android:imeActionId=
是动作意图,
android:imeOptions="actionDone"
是显示的文字(以
android:imeActionLabel=
为主)。详情android EditText inputType 及 android:imeOptions=”actionDone”
imeOptions
代码
//刚开始不显示错误信息,只有判断之后才会提示错误信息
textInputLayout1.setErrorEnabled(false);
textInputLayout2.setErrorEnabled(false);
String phone = et_phone.getText().toString().trim();
String pwd = et_pwd.getText().toString().trim();
if (TextUtils.isEmpty(phone)) {
textInputLayout1.setError("手机号不可为空");
} else if (phone.length() != ) {
textInputLayout1.setError("请输入正确的手机号");
} else if (TextUtils.isEmpty(pwd)) {
textInputLayout2.setError("密码不可为空");
} else if (pwd.length() < ) {
textInputLayout2.setError("密码不少于6位");
} else {
toast.setText("登录成功");
toast.show();
}
可以给软键盘的enter见设置点击事件,调用EditText的setOnEditorActionListener(…)方法
//设置软键盘的点击事件
et_pwd.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (id == R.id.login || id == EditorInfo.IME_ACTION_DONE) {
loginApp();//执行登录操作
return true;//事件被消耗
}
return false;
}
});
7 TextInputLayout
它的
etName.setError("账号不可为空!");
效果图是这样的
如果
TextInputEditText
的父布局是
TextInputLayout
,这会导致一个问题,就是当没有输入任何内容+调用方法
TextInputEditText.setError("")
时,图片小眼睛和错误提示框(圆)重叠在一起。他们都在
TextInputEditText
控件的最右边。所以一般只使用
TextInputLayout.setError("密码不少于6位")
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/et_pwd"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="@string/login_input_pwd"
android:inputType="textPassword"/>
</android.support.design.widget.TextInputLayout>
8 BottomNavigationView
可以作为底部导航栏,自带 动画效果,当只有3个tab时,显示顶部文字;档有4-5个tab时,只显示当前tab的文字。
效果图:
xml
最重要的属性是 app:menu=
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#039BE5"
app:menu="@menu/bottom_navigation_view"
/>
res/menu/bottom_navigation_view.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/tab1"
android:icon="@android:drawable/ic_menu_add"
android:title="tab1"/>
<item
android:id="@+id/tab2"
android:icon="@android:drawable/ic_menu_search"
android:title="tab2"/>
<item
android:id="@+id/tab3"
android:icon="@android:drawable/ic_menu_share"
android:title="tab3"/>
<item
android:id="@+id/tab4"
android:icon="@android:drawable/ic_menu_camera"
android:title="tab4"/>
<item
android:id="@+id/tab5"
android:icon="@android:drawable/ic_menu_week"
android:title="tab5"/>
</menu>
代码
bottonNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.tab1:
toast.setText("tab1");
toast.show();
break;
case R.id.tab2:
toast.setText("tab2");
toast.show();
break;
...
}
return true;
}
});
注意:
onNavigationItemSelected(...)
必须返回
return true
,否则没有动画效果。
BottomNavigationView
继承自
FrameLayou
,所以可以节点下添加其它控件。下图添加了分割线。
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:backgroundTint="@color/colorAccent"
android:backgroundTintMode="src_atop"
app:itemTextColor="#000000"
app:menu="@menu/bottom_main_menu">
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#00ff00"/>
</android.support.design.widget.BottomNavigationView>
BottomNavigationView 怎么默认选中第一个item?
系统默认选中第一个,但是系统选中第一个不执行
onNavigationItemSelected(MenuItem)
。方法未找到相关方法,用下面的代替
HomeFrag homeFrag = new HomeFrag();
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout_main, homeFrag, "HomeFrag").commit();
今天看博客的时候发现一个方法
当时该方法必须在
bottomView.setOnNavigationItemSelectedListener(...)
之后,否则无效。
下面的方法是无效的:
MenuItem tab1 = bottomView.getMenu().getItem();
tab1.setChecked(true);
或 都是无效的。
NavigationMenuItemView tab1 = (NavigationMenuItemView) bottomView.findViewById(R.id.tab1);
tab1.performClick();
怎么解决BottomNavigationView +ViewPager结合使用导致的bug?
bug描述:当viewpager滑动时,底部的menu被选中后无法恢复正常状态。
bug图:
方法:
第一步:ViewPager的监听中加入下面代码:
@Override
public void onPageSelected(int position) {
if (menuItem != null) {
menuItem.setChecked(false);
} else {
bottomNavigationView.getMenu().getItem().setChecked(false);
}
menuItem = bottomNavigationView.getMenu().getItem(position);
menuItem.setChecked(true);
}
此时的效果图:
第二步:BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
BottomNavigationViewHelper是自定义的类
// 利用反射,改变 item 中 mShiftingMode 的值
public class BottomNavigationViewHelper {
public static void disableShiftMode(BottomNavigationView navigationView) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.getChildAt();
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = ; i < menuView.getChildCount(); i++) {
BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
itemView.setShiftingMode(false);
itemView.setChecked(itemView.getItemData().isChecked());
}
} catch (Exception e) {//NoSuchFieldException | IllegalAccessException e
e.printStackTrace();
}
}
}
解决后的效果图:
相关的库:
NavBar
AHBottomNavigation
BottomNavigationBar
BottomNavigationViewEx
BottomBar
BottomNavigation
9 CollapsingToolbarLayout
包裹Toolbar + 其他控件,其他控件作为背景,这里已ImageView为例
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/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.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/iv_activity_new"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/show_img2"/>
<!--layout_height=wrap_content 不显示title,也就没有了title字体变小的动画效果-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_collapseMode="pin"
app:title="Toolbar"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/show_text"/>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
CollapsingToolbarLayout
中的child view 都可以使用属性
app:layout_collapseMode=""
。有3种值: pin+parallax+none.“pin”:固定模式,在折叠的时候最后固定在顶端,保留左边的三条横线,显示的部分是该view的bottom部分;“parallax”:视差模式,在折叠的时候会有个视差折叠的效果,显示的是该View中间部分。
toolbar = (Toolbar) findViewById(R.id.toolbar);
drawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
toggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,, );
toggle.syncState();
drawerLayout.addDrawerListener(toggle);
demo: https://git.oschina.net/MaterialDisign/DrawerLayoutCollpsingToolbarLayout.git
下面的效果
demo: https://git.oschina.net/MaterialDisign/DrawerLayoutCollpsingToolbarLayout2
要实现这个效果要注意:
1.
TabLayout
往上滑动的时候,他的top就是toolBar高度的大约1/2处(开发者模式–>显示布局边界,滑动的时候看一看到,大约是1/2处),所以要想完美显示,注意调整ToolBar的height.
2. 给
TabLayout
设置
android:layout_gravity="bottom"
,否则默认是top
3. title文字要想显示完美,注意调整参数
android:gravity="top"app:titleMarginTop="10dp"
4.
collapsingToolBarLayout.setTitleEnabled(false);
CollapsingToolbarLayout collapsingToolBarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolBarLayout);
collapsingToolBarLayout.setTitleEnabled(false);
<?xml version="1.0" encoding="utf-8"?>
<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.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolBarLayout"
android:layout_width="match_parent"
android:layout_height="250dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/show_img2"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="top"
app:titleMarginTop="10dp"
app:layout_collapseMode="pin"
app:navigationIcon="@mipmap/ic_nav_back_nor"
app:title="Title"/>
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="bottom"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
下面介绍些其他的内容,不属于Material Design,包含SearchView + 夜间模式 + 过度动画
1 SearchView
<android.support.v7.widget.SearchView
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/btn_login"
android:layout_margin="10dp"
android:background="@color/colorPrimary"
app:queryHint="搜索中..."
/>
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
//点击软键盘输入法中的搜索按钮调用这个方法
@Override
public boolean onQueryTextSubmit(String query) {
toast.setText(query);
toast.show();
return true;
}
//newText:搜索框内的整体值
@Override
public boolean onQueryTextChange(String newText) {
toast.setText(newText);
toast.show();
return true;
}
});
2 白天模式 + 夜间模式
用到的类:AppCompatDelegate 方法setDefaultNightMode(int mode) + recreate();
需要创建res/values-night/style.xml,主题继承自
Theme.AppCompat.DayNight
style.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
...
</style>
</resources>
代码;
btn_day.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
recreate();
}
});
btn_night.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
recreate();
}
});
3 过度动画
用到的类:ActivityOptionCompat + ViewCompat,用到的的方法 makeSceneTransitionAnimation(...), ViewCompat.setTransitionName(iv, "transition");
在MAinActivity中给ImageView设置点击事件,并设置过度动画,在新打开的NewActivity中设置控件
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//ActivityOptionsCompat必须放在点击事件中
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, iv, "transition");
startActivity(new Intent(MainActivity.this, NewActivity.class), optionsCompat.toBundle());
}
});