在哔哩哔哩看到侧滑整不出来?侧滑控件仅仅改了几行,让它跑起来
package com.example.fill;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Scroller;
public class SlideMenu extends FrameLayout {
private View menuView, mainView;
private int menuWidth;
private Scroller scroller;
public SlideMenu(Context context) {
super(context);
init();
}
public SlideMenu(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
scroller = new Scroller(getContext());
}
/**
* 当1级子view全部加载完调用,可以用初始化子view引用
* 注意这里无法获取子view的宽高
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
menuView = getChildAt(0);
mainView = getChildAt(1);
}
//使Menu也具有滑动功能
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) ev.getX();
break;
case MotionEvent.ACTION_MOVE:
int deltaX = (int) (ev.getX() - downX);
if (Math.abs(deltaX) > 8) {
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
/**
* s设置两个子view在页面上的布局
*
* @param l:当前子view的左边在父view的坐标系的x坐标
* @param t:当前子view的顶边在父view的坐标系的y坐标
* @param r:当前子view的宽
* @param b:当前子view的高
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
menuWidth = menuView.getLayoutParams().width;
/**
* 众所周知
* {@link android.view.ViewGroup.LayoutParams}
* <P>public static final int FILL_PARENT = -1;</P>
* <P>public static final int MATCH_PARENT = -1;</P>
* <P>public static final int WRAP_CONTENT = -2;</P>
*/
if (menuWidth < 0) {
menuWidth = r - l;
}
menuView.layout(-menuWidth, 0, 0, b);
mainView.layout(0, 0, r, b);
}
/**
* 处理屏幕滑动事件
*/
private int downX;
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
Log.i("TAG", "---------------onTouchEvent------------------ ");
int moveX = (int) event.getX();
Log.i("TAG", "moveX:" + moveX);
int deltaX = moveX - downX;
Log.i("TAG", "deltaX:" + deltaX);
int newScrollX = getScrollX() - deltaX;
Log.i("TAG", "newScrollX:" + newScrollX);
if (newScrollX < -menuWidth) newScrollX = -menuWidth;
Log.i("TAG", "newScrollX:" + newScrollX);
if (newScrollX > 0) newScrollX = 0;
Log.i("TAG", "newScrollX:" + newScrollX);
scrollTo(newScrollX, 0);
downX = moveX;
break;
case MotionEvent.ACTION_UP:
//当滑动距离小于Menu宽度的一半时,平滑滑动到主页面
if (getScrollX() > -menuWidth / 2) {
closeMenu();
} else {
//当滑动距离大于Menu宽度的一半时,平滑滑动到Menu页面
openMenu();
}
break;
}
return true;
}
//关闭menu
private void closeMenu() {
scroller.startScroll(getScrollX(), 0, 0 - getScrollX(), 0, 400);
invalidate();
}
//打开menu
private void openMenu() {
scroller.startScroll(getScrollX(), 0, -menuWidth - getScrollX(), 0, 400);
invalidate();
}
/**
* Scroller不主动去调用这个方法
* 而invalidate()可以调用这个方法
* invalidate->draw->computeScroll
*/
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
//返回true,表示动画没结束
scrollTo(scroller.getCurrX(), 0);
invalidate();
}
}
/**
* 切换菜单的开和关
*/
public void switchMenu() {
if (getScrollX() == 0) {
openMenu();
} else {
closeMenu();
}
}
}
为什么我之前不显示侧滑栏或者滑动没反应?
我们来看一下滑动控制的代码:
int downX = 0;
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();//按下时的地方
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getX();//当前滑动的位置
int deltaX = moveX - downX;//与上一次滑动的距离(这个downX在case最后被赋值了,别被名字骗了
int newScrollX = getScrollX() - deltaX;//上一次的滑动x距离和此次的滑动距离差(即真正需要滑动的距离
if (newScrollX < -menuWidth) newScrollX = -menuWidth;//如果滑动距离居然要比滑动部分的宽度还要大,重置这个大小仅限控件宽度(防止划过头出现破绽
if (newScrollX > 0) newScrollX = 0;//如果你是往右边扒拉,对不起,不动
scrollTo(newScrollX, 0);//开划
downX = moveX;
break;
case MotionEvent.ACTION_UP:
//当滑动距离小于Menu宽度的一半时,平滑滑动到主页面
if (getScrollX() > -menuWidth / 2) {
closeMenu();
} else {
//当滑动距离大于Menu宽度的一半时,平滑滑动到Menu页面
openMenu();
}
break;
}
return true;
}
看完注释你应该会感觉清晰一点:计算滑动距离,防止划过头或者反向滑动。
为什么出现划不动的情况?我们把目光放到这个地方:
@Override
protected void onFinishInflate() {
super.onFinishInflate();
menuView = getChildAt(0);
mainView = getChildAt(1);
menuWidth = menuView.getLayoutParams().width;
}
关注最后一行:menuView.getLayoutParams().width
得到了侧滑栏的宽度。按道理说是没啥问题。但是如果你了解LayoutParams,你大概知道我们经常写的wram_content
,match_parent
在程序内部到底被转换成什么了?
我们瞄一眼ViewGroup.LayoutParams
的部分代码:
public static class LayoutParams {
/**
* Special value for the height or width requested by a View.
* FILL_PARENT means that the view wants to be as big as its parent,
* minus the parent's padding, if any. This value is deprecated
* starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
*/
@SuppressWarnings({"UnusedDeclaration"})
@Deprecated
public static final int FILL_PARENT = -1;
/**
* Special value for the height or width requested by a View.
* MATCH_PARENT means that the view wants to be as big as its parent,
* minus the parent's padding, if any. Introduced in API Level 8.
*/
public static final int MATCH_PARENT = -1;
/**
* Special value for the height or width requested by a View.
* WRAP_CONTENT means that the view wants to be just large enough to fit
* its own internal content, taking its own padding into account.
*/
public static final int WRAP_CONTENT = -2;
}
英语不错的童鞋可以直接看注释。否则你就直接理解成为:
wram_content
在程序当中就是-2
match_parent
在程序当中就是-1
回到刚刚的那几行代码:
@Override
protected void onFinishInflate() {
super.onFinishInflate();
menuView = getChildAt(0);
mainView = getChildAt(1);
menuWidth = menuView.getLayoutParams().width;
}
假如你在不知情的情况下随意把侧滑栏的大小填成了:wram_content
或者 match_parent
恭喜你,掉坑了。
menuView.getLayoutParams().width
将会得到-1
或者是 -2
当程序在执行滑动部分代码的时候:
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getX();
int deltaX = moveX - downX;
int newScrollX = getScrollX() - deltaX;
//当menuWidth为负值,-1或者-2,这个判断将会生效,将会把滑动距离赋值为-1或者-2
if (newScrollX < -menuWidth) newScrollX = -menuWidth;
if (newScrollX > 0) newScrollX = 0;
scrollTo(newScrollX, 0);//开划
downX = moveX;
break;
如上。
当menuWidth为-1或者-2 ,滑动1个像素,几乎和没滑动一样
代码来源:
教学视频来源: