先看图:
最近朋友遇到的需求:
1、底部的布局需要上下拖动,且只能拖动顶部标题栏
2、未展开和展开状态都要能滑动图标列表
3、要能点击后面的黑色区域,因为后面可能放一个图片可以收缩放大等。
遇到的问题:
1、要能点击后面的黑色区域就不能用dialog,如果要用dialog那就要处理能点击到后面activity的逻辑,就要加WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE的配置。
2、BottomSheet的拖动是对应整个布局都可以滑动的,如果只能滑动标题就要把最外层布局设为自定义然后监听按下的y值是否在标题区域内,是就打开滑动,否就关闭滑动。
3、在未展开状态下。列表滑动到最底部未展示最后一条,这和BottomSheet移动的距离有关recycleview被移到屏幕外了,解决办法就是给recycleview一个paddingbottom,然后移动的时候也事实改变这个paddingbottom。
代码简单,就不做细致分析了。记录一下,万一以后用到了呢?
先看activity布局
app:layout_behavior="@string/bottom_sheet_behavior"就能让这个布局滑动起来,有人说布局一定要是带滑动属性的布局才行,其实并不需要。然后我们不需要三级滑动,什么叫三级滑动呢?当我们进入页面布局先是折叠状态然后我们可以拉伸撑满全屏,最后我们往下滑可以让他完全退出到屏幕外,这三种状态就被人称之为三级滑动。这里我们不需要完全退出,它最小也要漏出250dp,所以我们设置
app:behavior_hideable="false"
app:behavior_peekHeight="250dp"
布局里面还有一点需要注意,在折叠状态时,我们滑动列表会发现很卡,我们只需要在recycleview里面加上android:nestedScrollingEnabled="false"就行,CoordinatorLayout里面经常遇到的事。
然后看activity代码
public class DragDialogActivity extends AppCompatActivity implements View.OnClickListener {
private MyRelativeLayout mBottomSheetContent;
private BottomSheetBehavior mBehavior;
private RecyclerView mRecyclerView;
private TestAdapter mAdapter;
private TabLayout mTitleTab;
private int mPaddingBottomValue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_dialog);
initView();
}
private void initView() {
setBottomSheetState();
initRecycleView();
findViewById(R.id.mIv).setOnClickListener(this);
}
private void initRecycleView() {
mRecyclerView = findViewById(R.id.mRecyclerView);
mRecyclerView.setLayoutManager(new GridLayoutManager(this,4));
mAdapter = new TestAdapter();
mRecyclerView.setAdapter(mAdapter);
//解決展示不全的问题
mBottomSheetContent.post(new Runnable() {
@Override
public void run() {
int parentHeight = mBottomSheetContent.getHeight();
mPaddingBottomValue = parentHeight - mBehavior.getPeekHeight();
//计算出mRecycleview需要距离底部的偏移量
mRecyclerView.setPadding(0,0,0,mPaddingBottomValue);
}
});
//标题列表
mTitleTab = findViewById(R.id.mTitleTab);
initTestTitles();
}
private void initTestTitles() {
for (int i = 0; i < 20; i++) {
mTitleTab.addTab(mTitleTab.newTab().setText("标题"+i));
}
}
private void setBottomSheetState() {
mBottomSheetContent = findViewById(R.id.mBottomSheetContent);
mBehavior = BottomSheetBehavior.from(mBottomSheetContent);
mBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
mBottomSheetContent.setBehavior(mBehavior);
mBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
float newPaddingBottomValue = mPaddingBottomValue * (1-slideOffset);
mRecyclerView.setPadding(0,0,0, (int) newPaddingBottomValue);
Log.d("yanjin","newPaddingBottomValue = "+newPaddingBottomValue);
}
});
}
@Override
public void onClick(View view) {
int id = view.getId();
if(id == R.id.mIv){
Toast.makeText(App.getInstance(), "我能点到后面的控件哦", Toast.LENGTH_SHORT).show();
}
}
}
这里就处理了问题列表里面提到的recycleview显示不足的问题,盯着 mRecyclerView.setPadding相关代码看看就明白了。
然后就是我们的外部布局了,底部的布局需要上下拖动,且只能拖动顶部标题栏的话,我们要监听按下的位置是不是标题区域是的话打开滑动,不是的话就关闭。
public class MyRelativeLayout extends RelativeLayout {
private BottomSheetBehavior mBehavior;
private int length;
public MyRelativeLayout(Context context) {
this(context, null);
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
length = Utils.dp2px(50f);//50dp对应着标题的高度
}
public void setBehavior(BottomSheetBehavior mBehavior) {
this.mBehavior = mBehavior;
}
//true就是拦截 false 就是不拦截
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int y = (int) event.getY();
if(length < y){
if (mBehavior != null) {
mBehavior.setDraggable(false);//如果手指按下的Y值在列表里面,就不要dialog移动,这样就会把事件给recycle
}
}
}else if(event.getAction() == MotionEvent.ACTION_UP){
if (mBehavior != null) {
mBehavior.setDraggable(true);//手指抬起时一定要设置回可拖动
}
}
return super.dispatchTouchEvent(event);
}
}
有朋友会问,那我能不能反过来,我先初始的时候设置setDraggable为false,然后在dispatchTouchEvent反向设置?答案是不行,我尝试过了,有兴趣的朋友可以试试,再读读源码。
其余的adapter代码,条目布局就不加上了。