标签:attribute wpa try contex 技术 int must viewgroup 渐变
最近在做项目,涉及到类似QQ的页面的滑动。但是却遇到了侧滑和ViewPager冲突的问题,头疼了很长时间,最后在网上发现了这篇博客,转载过来供自己学习参考(写这篇博客的原创作者,因为我发现这篇博客的地方也是别人转载的,所以具体是有哪位大牛完成的我也不得而知。转载文章不能标出原创作者和出处,请见谅)
QQ5.0的侧滑效果有多种实现方式,
如http://blog.csdn.net/lmj623565791/article/details/39257409 就是利用HorizontalScrollView实现的,简单实用;
如http://blog.csdn.net/manoel/article/details/39013095/ 通过改造SlidingMenu实现,没有改变原有SlidingMenu功能,屏幕边缘侧滑也可以....
相对来说ViewDragHelper实现方式最为复杂,但灵活性也更高,可以应对各种需求,毕竟google的DrawerLayout也是用ViewDragHelper实现的。
先看下效果:

分析:主面板有个ViewPager,需要注意的是viewpager的滑动肯定与侧滑相互冲突,一般我们让viewpager的第一页是可以向右拖出侧滑菜单,其他页则响应viewpager的滑动。
代码:
自定义侧滑控件DragLayout ,代码量不少,不过关键地方都加了注释。
package com.liujing.draglayoutdemo;/**
* 通过ViewDragHelper实现的侧滑控件
* @author liujing
*
*/
public class DragLayout extends FrameLayout {
private View mLeftContent;
private View mMainContent;
private int mWidth;
private int mDragRange;
private ViewDragHelper mDragHelper;
private int mMainLeft;
private int mHeight;
private Status mStatus = Status.Close;
private GestureDetectorCompat mDetectorCompat;
public DragLayout(Context context) {
this(context, null);
}
public DragLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DragLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//ViewDragHelper.create(forParent, sensitivity, cb);
//对应参数:父布局、敏感度、回调
mDragHelper = ViewDragHelper.create(this, mCallBack);
mDetectorCompat = new GestureDetectorCompat(getContext(),
mGestureListener);
}
private boolean isDrag = true;
public void setDrag(boolean isDrag) {
this.isDrag = isDrag;
if(isDrag){
//这里有个Bug,当isDrag从false变为true是,mDragHelper的mCallBack在
//首次滑动时不响应,再次滑动才响应,只好在此调用下,让mDragHelper恢复下状态
mDragHelper.abort();
}
}
SimpleOnGestureListener mGestureListener = new SimpleOnGestureListener() {
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if((Math.abs(distanceX) > Math.abs(distanceY))&&distanceX<0&&isDrag!=false&&mStatus==Status.Close){
return true;
}else if((Math.abs(distanceX) > Math.abs(distanceY))&&distanceX>0&&isDrag!=false&&mStatus==Status.Open){
return true;
}else {
return false;
}
};
};
ViewDragHelper.Callback mCallBack = new ViewDragHelper.Callback() {
public void onEdgeTouched(int edgeFlags, int pointerId) {
};
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
mDragHelper.captureChildView(mMainContent, pointerId);
};
// 决定child是否可被拖拽。返回true则进行拖拽。
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mMainContent || child == mLeftContent;
}
// 当capturedChild被拖拽时
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
// 横向拖拽的范围,大于0时可拖拽,等于0无法拖拽
// 此方法只用于计算如view释放速度,敏感度等
// 实际拖拽范围由clampViewPositionHorizontal方法设置
@Override
public int getViewHorizontalDragRange(View child) {
return mDragRange;
}
// 此处设置view的拖拽范围。(实际移动还未发生)
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
// 拖动前oldLeft + 变化量dx == left
if (mMainLeft + dx < 0) {
return 0;
} else if (mMainLeft + dx > mDragRange) {
return mDragRange;
}
return left;
}
// 决定了当View位置改变时,希望发生的其他事情。(此时移动已经发生)
// 高频实时的调用,在这里设置左右面板的联动
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
//如果拖动的是主面板
if (changedView == mMainContent) {
mMainLeft = left;
} else {
mMainLeft += dx;
}
// 进行值的修正
if (mMainLeft < 0) {
mMainLeft = 0;
} else if (mMainLeft > mDragRange) {
mMainLeft = mDragRange;
}
// 如果拖拽的是左面板,强制在指定位置绘制Content
if (changedView == mLeftContent) {
layoutContent();
}
dispatchDragEvent(mMainLeft);
}
// View被释放时,侧滑打开或恢复
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (xvel > 0) {
open();
} else if (xvel == 0 && mMainLeft > mDragRange * 0.5f) {
open();
} else {
close();
}
}
//当拖拽状态改变的时,IDLE/DRAGGING/SETTLING
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
}
};
private void layoutContent() {
mMainContent.layout(mMainLeft, 0, mMainLeft + mWidth, mHeight);
mLeftContent.layout(0, 0, mWidth, mHeight);
}
/**
* 每次更新都会调用 根据当前执行的位置计算百分比percent
*/
protected void dispatchDragEvent(int mainLeft) {
float percent = mainLeft / (float) mDragRange;
animViews(percent);
if (mListener != null) {
mListener.onDraging(percent);
}
Status lastStatus = mStatus;
if (updateStatus(mainLeft) != lastStatus) {
if (mListener == null) {
return;
}
if (lastStatus == Status.Draging) {
if (mStatus == Status.Close) {
mListener.onClose();
} else if (mStatus == Status.Open) {
mListener.onOpen();
}
}
}
}
public static interface OnLayoutDragingListener {
void onOpen();
void onClose();
void onDraging(float percent);
}
private OnLayoutDragingListener mListener;
public void setOnLayoutDragingListener(OnLayoutDragingListener l) {
mListener = l;
}
private Status updateStatus(int mainLeft) {
if (mainLeft == 0) {
mStatus = Status.Close;
} else if (mainLeft == mDragRange) {
mStatus = Status.Open;
} else {
mStatus = Status.Draging;
}
return mStatus;
}
public static enum Status {
Open, Close, Draging
}
public Status getStatus() {
return mStatus;
}
public void setStatus(Status mStatus) {
this.mStatus = mStatus;
}
/**
* 伴随动画:
* @param percent
*/
private void animViews(float percent) {
// 主面板:缩放
float inverse = 1 - percent * 0.2f;
ViewHelper.setScaleX(mMainContent, inverse);
ViewHelper.setScaleY(mMainContent, inverse);
// 左面板:缩放、平移、透明度
ViewHelper.setScaleX(mLeftContent, 0.5f + 0.5f * percent);
ViewHelper.setScaleY(mLeftContent, 0.5f + 0.5f * percent);
ViewHelper.setTranslationX(mLeftContent, -mWidth / 2.0f + mWidth / 2.0f
* percent);
ViewHelper.setAlpha(mLeftContent, percent);
// 背景:颜色渐变
getBackground().setColorFilter(
evaluate(percent, Color.BLACK, Color.TRANSPARENT),
PorterDuff.Mode.SRC_OVER);
}
private int evaluate(float fraction, int startValue, int endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int) ((startA + (int) (fraction * (endA - startA))) << 24)
| (int) ((startR + (int) (fraction * (endR - startR))) << 16)
| (int) ((startG + (int) (fraction * (endG - startG))) << 8)
| (int) ((startB + (int) (fraction * (endB - startB))));
}
@Override
public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
boolean onTouchEvent = mDetectorCompat.onTouchEvent(ev);
//将Touch事件传递给ViewDragHelper
return mDragHelper.shouldInterceptTouchEvent(ev) & onTouchEvent;
};
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
//将Touch事件传递给ViewDragHelper
mDragHelper.processTouchEvent(event);
} catch (Exception e) {
}
return true;
}
public void close() {
close(true);
};
public void open() {
open(true);
}
public void close(boolean isSmooth) {
mMainLeft = 0;
if (isSmooth) {
// 执行动画,返回true代表有未完成的动画, 需要继续执行
if (mDragHelper.smoothSlideViewTo(mMainContent, mMainLeft, 0)) {
// 注意:参数传递根ViewGroup
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
layoutContent();
}
}
public void open(boolean isSmooth) {
mMainLeft = mDragRange;
if (isSmooth) {
if (mDragHelper.smoothSlideViewTo(mMainContent, mMainLeft, 0)) {
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
layoutContent();
}
}
@Override
public void computeScroll() {
// 高频率调用,决定是否有下一个变动等待执行
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
mMainContent.layout(mMainLeft, 0, mMainLeft + mWidth, mHeight);
mLeftContent.layout(0, 0, mWidth, mHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//拿到宽高
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
//设置拖动范围
mDragRange = (int) (mWidth * 0.6f);
}
/**
* 填充结束时获得两个子布局的引用
*/
@Override
protected void onFinishInflate() {
int childCount = getChildCount();
// 必要的检验
if (childCount < 2) {
throw new IllegalStateException(
"You need two childrens in your content");
}
if (!(getChildAt(0) instanceof ViewGroup)
|| !(getChildAt(1) instanceof ViewGroup)) {
throw new IllegalArgumentException(
"Your childrens must be an instance of ViewGroup");
}
mLeftContent = getChildAt(0);
mMainContent = getChildAt(1);
}
}
我设置了一个isDrag的标签来控制是否允许侧滑;
在SimpleOnGestureListener的onScroll方法中判断,如果是横向向右滑动,且侧滑是关闭状态,且isDrag的tag为true时,让ViewDragHelper响应对应滑动事件(滑出),
如果是横向向左滑动,且侧滑是开启状态,且isDrag的tag为true时,让ViewDragHelper响应对应滑动事件(滑入),
其余情况,都不处理;
在onViewPositionChanged里通过dispatchDragEvent方法,计算移动百分比,据此执行相应的伴随动画,同时也将该值通过回调传递到外面,执行动画用了nineoldandroids来兼容之前版本。
MainContentLayout是为了处理当侧滑菜单打开后,主面板便不再响应内部的Touch事件了。
package com.liujing.draglayoutdemo;public class MainContentLayout extends RelativeLayout {
private DragLayout mDragLayout;
public MainContentLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MainContentLayout(Context context) {
super(context);
}
public void setDragLayout(DragLayout mDragLayout) {
this.mDragLayout = mDragLayout;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(mDragLayout.getStatus() == Status.Close){
return super.onInterceptTouchEvent(ev);
}else {
return true;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(mDragLayout.getStatus() == Status.Close){
return super.onTouchEvent(event);
}else {
if(MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_UP){
mDragLayout.close();
}
return true;
}
}
}
DragLayout设置回调监听:
mDragLayout.setOnLayoutDragingListener(new OnLayoutDragingListener() {
@Override
public void onOpen() {
//打开
}
@Override
public void onDraging(float percent) {
//滑动中
}
@Override
public void onClose() {
//关闭
}
});
当ViewPager切换时,只要给DragLayout设置是否允许侧滑即可
public void onPageSelected(int postion) {
switch (postion) {
case 0:
mDragLayout.setDrag(true);
break;
case 1:
mDragLayout.setDrag(false);
break;
case 2:
mDragLayout.setDrag(false);
break;
}
}
布局示例:
<com.liujing.draglayoutdemo.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/dl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/img_frame_background" >
<LinearLayout
android:id="@+id/fl_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="40dp"
android:paddingLeft="10dp"
android:paddingTop="50dp" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="18sp"
android:text="这是左面板" >
</TextView>
</LinearLayout>
<com.liujing.draglayoutdemo.MainContentLayout
android:id="@+id/mainContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/tab_bg"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="@+id/pager_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/topbar" />
</com.liujing.draglayoutdemo.MainContentLayout>
</com.liujing.draglayoutdemo.DragLayout>
(转)ViewDragHelper实现QQ5.0侧滑并处理与ViewPager的滑动冲突
标签:attribute wpa try contex 技术 int must viewgroup 渐变
原文地址:http://www.cnblogs.com/1925yiyi/p/7501234.html