详细解析请看:http://blog.youkuaiyun.com/android_tutor/article/details/7193090
onInterceptTouchEvent:
onInterceptTouchEvent是在ViewGroup里面定义的。Android中的layout布局类一般都是继承此类的。onInterceptTouchEvent是用于拦截手势事件的,每个手势事件都会先调用onInterceptTouchEvent。
onTouchEvent:
onTouchEvent同样也是在view中定义的一个方法。处理传递到view 的手势事件。手势事件类型包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL等事件。
其中Layout里的onInterceptTouchEvent默认返回值是false,这样touch事件会传递到View控件,Layout里的onTouch默认返回值是false, View里的onTouch默认返回值是true,当我们手指点击屏幕时候,先调用ACTION_DOWN事件,当onTouch里返回值是true的时候,onTouch回继续调用ACTION_UP事件,如果onTouch里返回值是false,那么onTouch只会调用ACTION_DOWN而不调用ACTION_UP.
先看一下效果:
public class MoveUpLayoutextends LinearLayout {
privatestatic final StringTAG = "TAG";
publicint _currentStatus = STATUS_EXPANDED; // 当前状态
public static final int STATUS_EXPANDED = 1; // 展开状态
public static final int STATUS_COLLAPSED = 2; // 收缩状态
private OnMoveUpTouchListener onMoveUpTouchListener; // 向上滑动监听 事件
private View _headerView; // 头部view
private View _contentView;// 底部view
privateint _originalHeaderViewHeight; // 初始化时(原始)的头部高度
privateint _currentHeaderViewHeight; // 当前头部高度(可变的高度)
private int _touchSlop;// 移动的最小距离
private boolean _isMove =true; // 是否滑动
// 分别记录上次滑动的坐标
private float _lastX = 0;
private float _mLastY = 0;
// 分别记录上次滑动的坐标(onInterceptTouchEvent)
privatefloat _lastXIntercept = 0;
privatefloat _lastYIntercept = 0;
public MoveUpLayout(Context context, AttributeSet attrs,int defStyle) {
super(context, attrs, defStyle);
}
public MoveUpLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MoveUpLayout(Context context) {
super(context);
}
// 当获取到屏幕焦点时,会调用该方法
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasWindowFocus);
// 该方法只在第一显示该视图时,才执行,只执行一次
if (hasWindowFocus && (_headerView ==null || _contentView ==null)) {
this.initHeaderAndContentView();
}
}
private void initHeaderAndContentView(){
int count =this.getChildCount();
if (count > 0 && count <= 2) {
_headerView =this.getChildAt(0);
_contentView =this.getChildAt(1);
_originalHeaderViewHeight =_headerView.getMeasuredHeight();
_currentHeaderViewHeight =_originalHeaderViewHeight;
//初始化移动的最小判断距离
_touchSlop = ViewConfiguration.get(this.getContext()).getScaledTouchSlop();
debug("_originalHeaderViewHeight="+_originalHeaderViewHeight+",_touchSlop="+_touchSlop);
}
}
// 触屏 事件拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean isIntercept =false;
float x = ev.getX();
float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
_lastXIntercept = x;
_lastYIntercept = y;
_lastX = x;
_mLastY = y;
isIntercept = false;
break;
case MotionEvent.ACTION_MOVE:
float distance = y -_lastYIntercept;
debug("onInterceptTouchEvent-ACTION_MOVE:distance="+distance+",=_currentStatus:"+_currentStatus);
// 向上滑动
if (_currentStatus ==STATUS_EXPANDED && distance <= -_touchSlop) {
isIntercept = true;
}elseif (onMoveUpTouchListener !=null) { // 向下滑动
if (onMoveUpTouchListener.moveUpTouchEvent(ev) && distance >=_touchSlop) {
isIntercept = true;
}
}
break;
case MotionEvent.ACTION_UP:
isIntercept = false;
_lastXIntercept = 0;
_lastYIntercept = 0;
break;
}
debug("onInterceptTouchEvent.x="+x+",y="+y+","+ev.getAction()+",isIntercept="+isIntercept);
// 返回true 调用onTouch的ACTION_DOWN、ACTION_UP
// 返回false 调用onTouch 的ACTION_DOWN
return isIntercept &&_isMove; // 默认返回值是false ,不拦截消息
// return false; //不拦截事件
}
// 执行滑动事件
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!_isMove) {
returntrue;
}
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
// float distanceX = x - _lastX;
float distanceY = y -_mLastY;
// 跟随滑动去的距离,动态的改变headerView的大小
//注意: 这行代码很重要,跟随滑动的位置,改变头部View的当前高度
_currentHeaderViewHeight += (int)distanceY;
setHeaderViewHeight(_currentHeaderViewHeight);
break;
case MotionEvent.ACTION_UP:
// 这里做了下判断,当松开手的时候,会自动向两边滑动,具体向哪边滑,要看当前所处的位置
int distance = 0;// 滑动距离
// 当向下滑动时,滑动距离小于头部原高度一半时,还原原来收缩(隐藏)状态
if (_currentHeaderViewHeight <=_originalHeaderViewHeight * 0.5) {
distance = 0;
_currentStatus =STATUS_COLLAPSED;
} else {
// 当向上滑动时,滑动距离小于头部原高度一半时,还原原来扩展(展开)状态
distance = _originalHeaderViewHeight;
_currentStatus =STATUS_EXPANDED;
}
// 慢慢滑向终点
setSmoothHeaderHeight(_currentHeaderViewHeight, distance, 500);
break;
}
// debug("onTouchEvent.x="+x+",y="+y+","+event.getAction());
_lastX = x; // 当前位置设置为点击屏幕的位置
_mLastY = y;
// ViewGroup里的onTouchEvent默认值是false。
// View里的onTouchEvent返回默认值是true
returntrue;
}
/**************
* 设置头部的高度,并更新当前视图
* @param height
*/
private void setHeaderViewHeight(int height){
// 当头部的高度小于零时,等于零
if (height < 0) {
height = 0;
}elseif (height >_originalHeaderViewHeight) {
// 当头部的高度大于原高度时,等于原高度
height = _originalHeaderViewHeight;
}
if (_headerView !=null && _headerView.getLayoutParams() !=null) {
_headerView.getLayoutParams().height = height;// 改变头部View的高度
_headerView.requestLayout();//请求重新加载头部视图
_currentHeaderViewHeight = height;
}
debug("setHeaderView:Height="+height+",_currentHeaderViewHeight="+_currentHeaderViewHeight);
}
/***************
* 快速滑动或滑动到头部高度一半时调用,起平滑过度作用
* @param from 当前头部高度
* @param to 移动的目标高度
* @param duration 过度时长
*/
private void setSmoothHeaderHeight(final int from, final int to, final int duration){
final int frameCount = (int) (duration / 1000f * 30) + 1;// 过度帧数,起平滑过度效果
final float partation = (to - from) / (float) frameCount;// 每帧移动距离
new Thread("Thread") {
@Override
public void run() {
for (int i = 0; i < frameCount; i++) {
final int height;
if (i == frameCount - 1) {// 当为最后一帧时,设置为目标位置
height = to;
} else {// 向 to目标进行过度
height = (int) (from + partation * i);
}
// 更新高度
post(new Runnable() {
public void run() {
setHeaderViewHeight(height);
}
});
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
}
public void setOnMoveUpTouchListener(OnMoveUpTouchListener onMoveUpTouchListener) {
this.onMoveUpTouchListener = onMoveUpTouchListener;
}
public boolean isMove() {
return_isMove;
}
public void setMove(boolean isMove) {
this._isMove = isMove;
}
public void debug(String msg){
Log.i(TAG, msg);
}
public interface OnMoveUpTouchListener{
public boolean moveUpTouchEvent(MotionEvent event);
}
}
在Actiivty中实现OnMoveUpTouchListener接口,这个接口是判断在什么时候,可以向下滑动,展开头的内容
@Override
public boolean moveUpTouchEvent(MotionEvent event) {
Log.i("TAG", "moveUpTouchEvent");
// Listview是否显示的是第一项
if (listview.getFirstVisiblePosition() == 0) {
View view = listview.getChildAt(0);
// 显示第一个项时,可以向下滑动
if (view != null && view.getTop() >= 0) {
return true;
}
};
return false;
}