在此例子中DragViewGroup继承于LinearLayout
第一步 初始化ViewDragHelper,在构造方法中进行
//通过静态工厂方法创建
viewDragHelper=ViewDragHelper.create(this,callback);//this指parentView,在这里就是自定义ViewGroup;callback是使用ViewDragHelper的核心
第二步 拦截事件,此步骤必不可少
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//事件拦截给viewDragHelper
return viewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//将触摸事件传递给viewDragHelper,此操作必不可少
viewDragHelper.processTouchEvent(event);
return true;
}
第三步 处理computScroll方法,和使用Scroller一样,因为ViewDragHelper内部就是使用了Scroller来实现的
这里一般情况下可以套用以下模板
@Override
public void computeScroll() {
super.computeScroll();
if(viewDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);//刷新带有动画
}
}
- ViewCompat.postInvalidateOnAnimation(this)相当于使用scroller的时候必须要调用invalidate刷新页面,并且不断循环调用
第四步 也是最关键的一步,处理回调Callback,在他的内部有很多方法,我们重写可以实现很多效果
- 首先会重写tryCaptureView方法 ,此方法可以指定哪个View可以被拖动
- 重写clampViewPositionHorizontal、clampViewPositionVertical方法可以实现水平和垂直的滑动,默认返回为0,可以限制滑动区域
- 重写onViewCaptured方法可以实现手指触摸到View时的效果
- 重写onViewReleased方法可以实现手指离开屏幕时的一些功能
- 重写onViewPositionChanged方法在事件位置变化时可以实现一些动态效果
- 重写onViewDragStateChanged方法在拖拽状态变化时调用
private ViewDragHelper.Callback callback=new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return content==child;//判断是否滑动的子View是内容区域
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {//水平滑动,left表示垂直方向上的位置,dx表示较前次的偏移量
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {//垂直滑动,top表示垂直方向上的位置,dy表示较前次的偏移量
return super.clampViewPositionVertical(child, top, dy);//默认为0
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {//手指离开屏幕
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起后缓慢移动到指定位置
if(content.getLeft()<menu.getWidth()/2){
//关闭菜单
//相当于scroller的startScroll方法
viewDragHelper.smoothSlideViewTo(content,0,0);
//刷新带有动画
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}else{
//打开菜单
//相当于scroller的startScroll方法
viewDragHelper.smoothSlideViewTo(content,menu.getWidth(),0);
//刷新带有动画
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
在onFinishInflate方法中按顺序定义定义menu,content
在onSizeChanged方法中获取子View的宽
最终效果就能实现抽屉式的侧滑菜单
完整代码如下
public class DragViewGroup extends LinearLayout {
private ViewDragHelper viewDragHelper;
private ViewGroup menu,content;//菜单和内容
private int mScreenWidth;
//dp
private int menuRightMargin;
private boolean once;
private ViewDragHelper.Callback callback=new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return content==child;//判断是否滑动的子View是内容区域
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {//水平滑动
return Math.min(Math.max(0,left),mScreenWidth-menuRightMargin);//限制滑动区域在屏幕左边距到menu右边距
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {//垂直滑动
return super.clampViewPositionVertical(child, top, dy);//默认为0
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {//手指离开屏幕
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起后缓慢移动到指定位置
if(content.getLeft()<menu.getWidth()/2){
//关闭菜单
//相当于scroller的startScroll方法
viewDragHelper.smoothSlideViewTo(content,0,0);
//刷新带有动画
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}else{
//打开菜单
//相当于scroller的startScroll方法
viewDragHelper.smoothSlideViewTo(content,menu.getWidth(),0);
//刷新带有动画
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
public DragViewGroup(Context context) {
this(context,null);
}
public DragViewGroup(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public DragViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始化viewDragHelper
viewDragHelper=ViewDragHelper.create(this,callback);
WindowManager wm= (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics=new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth=outMetrics.widthPixels;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DragViewGroup);
menuRightMargin= (int) ta.getDimension(R.styleable.DragViewGroup_menuRightMargin,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50,getResources().getDisplayMetrics()));
ta.recycle();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
menu= (ViewGroup) getChildAt(0);
content= (ViewGroup) getChildAt(1);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
menu.getLayoutParams().width=mScreenWidth-menuRightMargin;
content.getLayoutParams().width=mScreenWidth;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
content.layout(0,0,content.getWidth(),content.getHeight());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return viewDragHelper.shouldInterceptTouchEvent(ev);//事件拦截给viewDragHelper
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//将触摸事件传递给viewDragHelper,此操作必不可少
viewDragHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if(viewDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);//刷新带有动画
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LinearLayout.LayoutParams(getContext(),attrs);
}
}
menu
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/img1"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#ffffff"
android:text="第1个item"
android:layout_toRightOf="@id/img1"
android:layout_centerVertical="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/img2"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#ffffff"
android:text="第2个item"
android:layout_toRightOf="@id/img2"
android:layout_centerVertical="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/img3"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#ffffff"
android:text="第3个item"
android:layout_toRightOf="@id/img3"
android:layout_centerVertical="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/img4"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#ffffff"
android:text="第4个item"
android:layout_toRightOf="@id/img4"
android:layout_centerVertical="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/img5"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#ffffff"
android:text="第5个item"
android:layout_toRightOf="@id/img5"
android:layout_centerVertical="true"/>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
activity_main
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.scrollerdemo.MainActivity">
<com.scrollerdemo.DragViewGroup
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent"
app:menuRightMargin="100dp">
<include layout="@layout/menu" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:gravity="center"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:text="helloworld"
android:textSize="30sp"/>
</LinearLayout>
</com.scrollerdemo.DragViewGroup>
</RelativeLayout>
自定义属性
<declare-styleable name="DragViewGroup">
<attr name="menuRightMargin" format="dimension"/>
</declare-styleable>
继续进阶,为侧滑菜单增加动画效果
只需要在callback内部重写onViewPositionChanged方法
代码如下
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
float scale = left * 1f / menu.getWidth();//0 ~ 1
float contentScale = 1 - scale * 0.2f;//1 ~ 0.8
float menuScale = 0.7f + scale * 0.3f;//0.7 ~ 1
float menuAlpha = 0.6f + scale * 0.4f;//0.6 ~ 1
float menuTransX = 0.3f - 0.3f * scale;//0.3 ~ 0
//content
ViewHelper.setPivotX(content, 0);
ViewHelper.setPivotY(content, content.getHeight() / 2);
ViewHelper.setScaleX(content, contentScale);
ViewHelper.setScaleY(content, contentScale);
//menu
ViewHelper.setTranslationX(menu, -menuTransX * menu.getWidth());
ViewHelper.setAlpha(menu, menuAlpha);
ViewHelper.setScaleX(menu, menuScale);
ViewHelper.setScaleY(menu, menuScale);
}
效果如下