先看效果:
这就是我们要实现的嵌套滑动效果,即在同一个事件序列中,先滑动外部控件,当外部控件滑到设定的高度时,滑动内部空间。我们都知道,在传统的时间分发机制中,某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话)并且它的onInterceptTouchEvent不会再被调用。所以上面的效果用传统的事件分发是无法实现的。
这时候就要用到我们的NestedScrolling机制了,原理大概是子view拦截了事件后,但是在滑动之前,将滑动的细节信息dx,dy传递给父View,父View决定是否消耗滑动值dx,dy,然后子View再利用剩余的值来滑动。
上面这种效果我们可以使用NestedScrolling机制来实现,也可以使用相对简单的BottomSheet来实现,其实BottomSheet内部还是使用NestedScrolling机制来实现的,这篇文章我们使用BottomSheet来实现,想使用NestedScrolling机制实现的可以参考下面的文章。
NestedScrolling实现嵌套滑动:https://blog.youkuaiyun.com/XG1057415595/article/details/81155391。
NestedScrolling机制推荐这篇文章:https://www.jianshu.com/p/f09762df81a5
我们开始吧!
BottomSheetBehavior
首先,BottomSheetBehavior需要作为CoordinatorLayout的子View,所以CoordinatorLayout是必不可少的。
这里先看MainActivity的布局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/cl"
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=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/button_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show_Or_Hide"/>
<Button
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="next_page"/>
</LinearLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="200dp"
app:layout_behavior="@string/bottom_sheet_behavior"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"
android:scaleType="fitXY"/>
<ImageView
android:layout_width="@dimen/img_size"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"
android:scaleType="fitXY"/>
<ImageView
android:layout_width="@dimen/img_size"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"
android:scaleType="fitXY"/>
<ImageView
android:layout_width="@dimen/img_size"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"
android:scaleType="fitXY"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
这里CoordinatorLayout有一个LinearLayout和一个NestedScrollView。LinearLayout包含两个Button;NestedScrollview是一个实现了NestedScrollingParent的可以滑动的FrameLayout,NestedScrollingParent是NestedScrolling机制的重要部分。这里不做过多赘述,可以通过上面的文章进行了解。
这里NestedScrollview加了一个app:layout_behavior属性,值为“@string/bottom_sheet_behavior",加了这个属性后,这个布局就会自动移动到页面底部的外边,所以页面上是看不到android:id="@+id/bottom_sheet"这个布局的。我们在bottom_sheet布局里面放了几个ImageView。注意:这里NestedScrollview的layout_height高度就是BottomSheet弹出来的最大高度。
接下来就是Activity中的代码了:
public class MainActivity extends AppCompatActivity {
BottomSheetBehavior behavior;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View bottomSheet = findViewById(R.id.bottom_sheet);
behavior = BottomSheetBehavior.from(bottomSheet);
behavior.setPeekHeight(200);
behavior.setHideable(true);
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View view, int i) {
}
@Override
public void onSlide(@NonNull View view, float v) {
}
});
Button button = findViewById(R.id.button_show);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(behavior.getState() == BottomSheetBehavior.STATE_EXPANDED){
Log.e("clidk", "onClick: collapsed");
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}else{
Log.e("clidk", "onClick: expanded");
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
});
Button next = findViewById(R.id.next);
next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
});
}
}
在onCreate方法中通过BottomSheetBehavior.from方法获取布局的behavior,然后调用behavior.setPeekHeight(200)设置bottom_sheet状态为collapsed时出现在手机屏幕的高度,setHideable方法设置是否可以隐藏,我们还可以为behavior设置监听。接着我们设置按钮的监听,收缩和展开BottomSheet。
就是这么简单,就可以实现文章开头的效果。如果采用NestedScrolling机制实现需要自己写很多逻辑。
BottomSheetDialog
BottomSheetDialog也差不多,我们大概看一下。
先是activity_second布局文件:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/cl"
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=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/button_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show_Or_Hide"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
只有一个按钮,根布局还是CoordinatorLayout
还有BottomSheetDialog的布局文件dialog_layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="100dp"
app:layout_behavior="@string/bottom_sheet_behavior"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"
android:scaleType="fitXY"/>
<ImageView
android:layout_width="@dimen/img_size"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"/>
<ImageView
android:layout_width="@dimen/img_size"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"/>
<ImageView
android:layout_width="@dimen/img_size"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"/>
<ImageView
android:layout_width="@dimen/img_size"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"/>
<ImageView
android:layout_width="@dimen/img_size"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="@dimen/img_size"
android:src="@drawable/ic_launcher_background"
android:scaleType="fitXY"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
同样采用NestedScrollview,内部还是几个ImageView。
注意:这里NestedScrollview的layout_height高度无法指定BottomSheetDialog弹出来的最大高度。BottomSheetDialog的高度默认是内部内容的高度,内容过多会覆盖整个屏幕。后面会讲解怎么设置高度!
接下来看SecondActivity:
public class SecondActivity extends AppCompatActivity {
BottomSheetDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
View view = getLayoutInflater().inflate(R.layout.dialog_layout,null);
dialog = new BottomSheetDialog(this);
dialog.setContentView(view);
View parent = (View) view.getParent();
BottomSheetBehavior behavior = BottomSheetBehavior.from(parent);
behavior.setPeekHeight(300);//首次弹出的高度
dialog.show();
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,600);
dialog.getWindow().setGravity(Gravity.BOTTOM);
Button button = findViewById(R.id.button_show);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(dialog.isShowing()){
Log.e("clidk", "onClick: collapsed");
dialog.dismiss();
}else{
Log.e("clidk", "onClick: expanded");
dialog.show();
}
}
});
}
}
这里通过LayoutInflater获取dialog_layout的布局view,然后创建BottomSheetDialog对象dialog;setContentView设置dialog的布局;
View parent = (View) view.getParent();
BottomSheetBehavior behavior = BottomSheetBehavior.from(parent);
behavior.setPeekHeight(300);//首次弹出的高度
上面三行设置首次弹出的高度(这里和前面BottomSheetBehavior有点不同);
dialog.show();
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,600);
dialog.getWindow().setGravity(Gravity.BOTTOM);
上面三行设置弹出的最大高度,且需要在dialog.show()方法之后调用,否则无效果。
以上就是BottomSheet的实现嵌套滑动的过程啦!喜欢点个赞吧~