Android使用ViewDragHelper实现QQ6.X最新版本侧滑界面效果实例代码

(一).前言:

这两天QQ进行了重大更新(6.X)尤其在UI风格上面由之前的蓝色换成了白色居多了,侧滑效果也发生了一些变化,那我们今天来模仿实现一个QQ6.X版本的侧滑界面效果。今天我们还是采用神器ViewDragHelper来实现.

本次实例具体代码已经上传到下面的项目中,欢迎各位去star和fork一下。

https://github.com/jiangqqlmj/DragHelper4QQ
FastDev4Android框架项目地址:https://github.com/jiangqqlmj/FastDev4Android

(二).ViewGragHelper的基本使用

前面我们学习ViewGragHelper的基本使用方法,同时也知道了里边的若干个方法的用途,下面我们还是把基本的使用步骤温习一下。要使用ViewGragHelper实现子View拖拽移动的步骤如下:

创建ViewGragHelper实例(传入Callback)

重写事件拦截处理方法onInterceptTouch和onTouchEvent

实现Callback,实现其中的相关方法tryCaptureView以及水平或者垂直方向移动的距离方法

更加具体分析大家可以看前一篇博客,或者我们今天这边会通过具体实例讲解一下。

(三).QQ5.X侧滑效果实现分析:

在正式版本QQ中的侧滑效果如下:

观察上面我们可以理解为两个View,一个是底部的相当于左侧功能View,另外一个是上层主功能内容View,我们在上面进行拖拽上层View或者左右滑动的时候,上层和下层的View相应进行滑动以及View大小变化,同时加入相关的动画。当然我们点击上层的View可以进行打开或者关闭侧滑菜单。

(四).侧滑效果自定义组件实现

1.首先我们这边集成自FrameLayout创建一个自定义View DragLayout。内部的定义的一些变量如下(主要包括一些配置类,手势,ViewDragHelper实例,屏幕宽高,拖拽的子视图View等)

//是否带有阴影效果
private boolean isShowShadow = true;
//手势处理类
private GestureDetectorCompat gestureDetector;
//视图拖拽移动帮助类
private ViewDragHelper dragHelper;
//滑动监听器
private DragListener dragListener;
//水平拖拽的距离
private int range;
//宽度
private int width;
//高度
private int height;
//main视图距离在ViewGroup距离左边的距离
private int mainLeft;
private Context context;
private ImageView iv_shadow;
//左侧布局
private RelativeLayout vg_left;
//右侧(主界面布局)
private CustomRelativeLayout vg_main; 

然后在内部还定义了一个回调接口主要处理拖拽过程中的一些页面打开,关闭以及滑动中的事件回调:

/**
* 滑动相关回调接口
*/
public interface DragListener {
//界面打开
public void onOpen();
//界面关闭
public void onClose();
//界面滑动过程中
public void onDrag(float percent);
}

2.开始创建ViewDragHelper实例,依然在自定义View DragLayout初始化的时候创建,使用ViewGragHelper的静态方法:

public DragLayout(Context context,AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
gestureDetector = new GestureDetectorCompat(context, new YScrollDetector());
dragHelper =ViewDragHelper.create(this, dragHelperCallback);
} 

其中create()方法创建的时候传入了一个dragHelperCallBack回调类,将会在第四点中讲到。

3.接着需要重写ViewGroup中事件方法,拦截触摸事件给ViewGragHelper内部进行处理,这样达到拖拽移动子View视图的目的;

/**
* 拦截触摸事件
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return dragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev);
}
/**
* 将拦截的到事件给ViewDragHelper进行处理
* @param e
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent e){
try {
dragHelper.processTouchEvent(e);
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
} 

这边我们在onInterceptTouchEvent拦截让事件从父控件往子View中转移,然后在onTouchEvent方法中拦截让ViewDragHelper进行消费处理。

4.开始自定义创建ViewDragHelper.Callback的实例dragHelperCallback分别实现一个抽象方法tryCaptureView以及重写以下若干个方法来实现侧滑功能,下面一个个来看一下。

/**
* 拦截所有的子View
* @param child Child the user is attempting to capture
* @param pointerId ID of the pointer attempting the capture
* @return
*/
@Override
public boolean tryCaptureView(Viewchild, int pointerId) {
return true;
} 

该进行拦截ViewGroup(本例中为:DragLayout)中所有的子View,直接返回true,表示所有的子View都可以进行拖拽移动。

/**
* 水平方向移动
* @param child Child view beingdragged
* @param left Attempted motion alongthe X axis
* @param dx Proposed change inposition for left
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (mainLeft + dx < 0) {
return 0;
} else if (mainLeft + dx >range) {
return range;
} else {
return left;
}
}

实现该方法表示水平方向滑动,同时方法中会进行判断边界值,例如当上面的main view已经向左移动边界之外了,直接返回0,表示向左最左边只能x=0;然后向右移动会判断向右最变得距离range,至于range的初始化后边会讲到。除了这两种情况之外,就是直接返回left即可。

/**
* 设置水平方向滑动的最远距离
*@param child Child view to check 屏幕宽度
* @return
*/
@Override
public int getViewHorizontalDragRange(View child) {
return width;
} 

该方法有必要实现,因为该方法在Callback内部默认返回0,也就是说,如果的view的click事件为true,那么会出现整个子View没法拖拽移动的情况了。那么这边直接返回left view宽度了,表示水平方向滑动的最远距离了。

/**
* 当拖拽的子View,手势释放的时候回调的方法, 然后根据左滑或者右滑的距离进行判断打开或者关闭
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild,xvel, yvel);
if (xvel > 0) {
open();
} else if (xvel < 0) {
close();
} else if (releasedChild == vg_main&& mainLeft > range * 0.3) {
open();
} else if (releasedChild == vg_left&& mainLeft > range * 0.7) {
open();
} else {
close();
}
} 

该方法在拖拽子View移动手指释放的时候被调用,这是会判断移动向左,向右的意图,进行打开或者关闭man view(上层视图)。下面是实现的最后一个方法:onViewPositionChanged

/**
* 子View被拖拽 移动的时候回调的方法
* @param changedView View whoseposition changed
* @param left New X coordinate of theleft edge of the view
* @param top New Y coordinate of thetop edge of the view
* @param dx Change in X position fromthe last call
* @param dy Change in Y position fromthe last call
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
if (changedView == vg_main) {
mainLeft = left;
} else {
mainLeft = mainLeft + left;
}
if (mainLeft < 0) {
mainLeft = 0;
} else if (mainLeft > range) {
mainLeft = range;
}
if (isShowShadow) {
iv_shadow.layout(mainLeft, 0,mainLeft + width, height);
}
if (changedView == vg_left) {
vg_left.layout(0, 0, width,height);
vg_main.layout(mainLeft, 0,mainLeft + width, height);
}
dispatchDragEvent(mainLeft);
}
}; 

该方法是在我们进行拖拽移动子View的过程中进行回调,根据移动坐标位置,然后进行重新定义left view和main view。同时调用dispathDragEvent()方法进行拖拽事件相关处理分发同时根据状态来回调接口:

/**
* 进行处理拖拽事件
* @param mainLeft
*/
private void dispatchDragEvent(int mainLeft) {
if (dragListener == null) {
return;
}
float percent = mainLeft / (float)range;
//根据滑动的距离的比例
animateView(percent);
//进行回调滑动的百分比
dragListener.onDrag(percent);
Status lastStatus = status;
if (lastStatus != getStatus()&& status == Status.Close) {
dragListener.onClose();
} else if (lastStatus != getStatus()&& status == Status.Open) {
dragListener.onOpen();
}
} 

该方法中有一行代码float percent=mainLeft/(float)range;算到一个百分比后面会用到

5.至于子View布局的获取初始化以及宽高和水平滑动距离的大小设置方法:

/**
* 布局加载完成回调
* 做一些初始化的操作
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (isShowShadow) {
iv_shadow = new ImageView(context);
iv_shadow.setImageResource(R.mipmap.shadow);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(iv_shadow, 1, lp);
}
//左侧界面
vg_left = (RelativeLayout)getChildAt(0);
//右侧(主)界面
vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1);
vg_main.setDragLayout(this);
vg_left.setClickable(true);
vg_main.setClickable(true);
} 

以及控件大小发生变化回调的方法:

@Override
protected void onSizeChanged(int w, int h,int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = vg_left.getMeasuredWidth();
height = vg_left.getMeasuredHeight();
//可以水平拖拽滑动的距离 一共为屏幕宽度的80%
range = (int) (width *0.8f);
} 

在该方法中我们可以实时获取宽和高以及拖拽水平距离。

6.上面的所有核心代码都为使用ViewDragHelper实现子控件View拖拽移动的方法,但是根据我们这边侧滑效果还需要实现动画以及滑动过程中View的缩放效果,所以我们这边引入了一个动画开源库:

然后根据前面算出来的百分比来缩放View视图:

/**
* 根据滑动的距离的比例,进行平移动画
* @param percent
*/
private void animateView(float percent) {
float f1 = 1 - percent * 0.5f;
ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent);
if (isShowShadow) {
//阴影效果视图大小进行缩放
ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f));
ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f));
}
} 

7.当然除了上面这些还缺少一个效果就是,当我们滑动过程中假如我们手指释放,按照常理来讲view就不会在进行移动了,那么这边我们需要一个加速度当我们释放之后,还能保持一定的速度,该怎么样实现呢?答案就是实现computeScroll()方法。

/**
* 有加速度,当我们停止滑动的时候,该不会立即停止动画效果
*/
@Override
public void computeScroll() {
if (dragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
} 

OK上面关于DragLayout的核心代码就差不多这么多了,下面是使用DragLayout类来实现侧滑效果啦!

(五).侧滑效果组件使用

1.首先使用的布局文件如下:

<com.chinaztt.widget.DragLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
>
<!--下层 左边的布局-->
<includelayoutincludelayout="@layout/left_view_layout"/>
<!--上层 右边的主布局-->
<com.chinaztt.widget.CustomRelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<RelativeLayout
android:id="@+id/rl_title"
android:layout_width="match_parent"
android:layout_height="49dp"
android:gravity="bottom"
android:background="@android:color/holo_orange_light"
>
<includelayoutincludelayout="@layout/common_top_bar_layout"/>
</RelativeLayout>
<!--中间内容后面放入Fragment-->
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<fragment
android:id="@+id/main_info_fragment"
class="com.chinaztt.fragment.OneFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
</LinearLayout>
</com.chinaztt.widget.CustomRelativeLayout>
</com.chinaztt.widget.DragLayout> 

该布局文件中父层View就是DragLayout,然后内部有两个RelativeLayout布局,分别充当下一层布局和上一层主布局。

2.下面我们来看一下下层菜单布局,这边我专门写了一个left_view_layout.xml文件,其中主要分为三块,第一块顶部为头像个人基本信息布局,中间为功能入口列表,底部是设置等功能,具体布局代码如下:

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="70dp"
android:background="@drawable/sidebar_bg"
>
<LinearLayout
android:id="@+id/ll1"
android:paddingLeft="30dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!--头像,昵称信息-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="70dp"
android:orientation="horizontal"
android:gravity="center_vertical"
>
<com.chinaztt.widget.RoundAngleImageView
android:id="@+id/iv_bottom"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitXY"
android:src="@drawable/icon_logo"
app:roundWidth="25dp"
app:roundHeight="25dp"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"
android:text="名字:jiangqqlmj"
android:textColor="@android:color/black"
android:textSize="15sp" />
<ImageButton
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="100dp"
android:layout_width="22dp"
android:layout_height="22dp"
android:background="@drawable/qrcode_selector"/>
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="15dp"
android:text="QQ:781931404"
android:textColor="@android:color/black"
android:textSize="13sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="17dp"
android:layout_height="17dp"
android:scaleType="fitXY"
android:src="@drawable/sidebar_signature_nor"/>
<TextView
android:layout_marginLeft="5dp"
android:textSize="13sp"
android:textColor="#676767"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="用心做产品!"/>
</LinearLayout>
</LinearLayout>
<!--底部功能条-->
<includelayoutincludelayout="@layout/left_view_bottom_layout"
android:id="@+id/bottom_view"
/>
<!--中间列表-->
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/bottom_view"
android:layout_below="@id/ll1"
android:layout_marginBottom="30dp"
android:layout_marginTop="70dp"
android:cacheColorHint="#00000000"
android:listSelector="@drawable/lv_click_selector"
android:divider="@null"
android:scrollbars="none"
android:textColor="#ffffff"/>
</RelativeLayout> 

该布局还是比较简单的,对于上层主内容布局这边就放一个顶部导航栏和中的Fragment内容信息,留着后期大家功能扩展即可。

3.主Activity使用如下:

public class MainActivity extends BaseActivity {
private DragLayout dl;
private ListView lv;
private ImageView iv_icon, iv_bottom;
private QuickAdapter<ItemBean> quickAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setStatusBar();
initDragLayout();
initView();
}
private void initDragLayout() {
dl = (DragLayout) findViewById(R.id.dl);
dl.setDragListener(new DragLayout.DragListener() {
//界面打开的时候
@Override
public void onOpen() {
}
//界面关闭的时候
@Override
public void onClose() {
}
//界面滑动的时候
@Override
public void onDrag(float percent) {
ViewHelper.setAlpha(iv_icon, 1 - percent);
}
});
}
private void initView() {
iv_icon = (ImageView) findViewById(R.id.iv_icon);
iv_bottom = (ImageView) findViewById(R.id.iv_bottom);
lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(quickAdapter=new QuickAdapter<ItemBean>(this,R.layout.item_left_layout, ItemDataUtils.getItemBeans()) {
@Override
protected void convert(BaseAdapterHelper helper, ItemBean item) {
helper.setImageResource(R.id.item_img,item.getImg())
.setText(R.id.item_tv,item.getTitle());
}
});
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
Toast.makeText(MainActivity.this,"Click Item "+position,Toast.LENGTH_SHORT).show();
}
});
iv_icon.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
dl.open();
}
});
}
} 

初始化控件,设置滑动监听器,以及左侧菜单功能列表设置即可了,不过上面大家应该看了QuickAdapter的使用,该为BaseAdapterHelper框架使用,我们需要在项目build.gradle中作如下配置:

具体关于BaseAdapter的使用讲解博客地址如下:

4.正式运行效果如下:

5.因为这边底层需要ViewDragHelper类,所以大家在使用的时候需要导入V4包的,不过我这边直接把ViewGragHelper类的源代码复制到项目中了。

(六).DragLayout源代码带注释

上面主要分析DragLayout的具体实现,不过我这边也贴一下DragLayout带有注释的全部源代码让大家可以更好的了解DragLayout的具体实现代码:

/**
*使用ViewRragHelper实现侧滑效果功能
*/
publicclass DragLayout extends FrameLayout {
private boolean isShowShadow = true;
//手势处理类
private GestureDetectorCompat gestureDetector;
//视图拖拽移动帮助类
private ViewDragHelper dragHelper;
//滑动监听器
private DragListener dragListener;
//水平拖拽的距离
private int range;
//宽度
private int width;
//高度
private int height;
//main视图距离在ViewGroup距离左边的距离
private int mainLeft;
private Context context;
private ImageView iv_shadow;
//左侧布局
private RelativeLayout vg_left;
//右侧(主界面布局)
private CustomRelativeLayout vg_main;
//页面状态 默认为关闭
private Status status = Status.Close;
public DragLayout(Context context) {
this(context, null);
}
public DragLayout(Context context,AttributeSet attrs) {
this(context, attrs, 0);
this.context = context;
}
public DragLayout(Context context,AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
gestureDetector = new GestureDetectorCompat(context, new YScrollDetector());
dragHelper =ViewDragHelper.create(this, dragHelperCallback);
}
class YScrollDetector extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1,MotionEvent e2, float dx, float dy) {
return Math.abs(dy) <=Math.abs(dx);
}
}
/**
* 实现子View的拖拽滑动,实现Callback当中相关的方法
*/
private ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() {
/**
* 水平方向移动
* @param child Child view beingdragged
* @param left Attempted motion alongthe X axis
* @param dx Proposed change inposition for left
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (mainLeft + dx < 0) {
return 0;
} else if (mainLeft + dx >range) {
return range;
} else {
return left;
}
}
/**
* 拦截所有的子View
* @param child Child the user isattempting to capture
* @param pointerId ID of the pointerattempting the capture
* @return
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
/**
* 设置水平方向滑动的最远距离
*@param child Child view to check 屏幕宽度
* @return
*/
@Override
public int getViewHorizontalDragRange(View child) {
return width;
}
/**
* 当拖拽的子View,手势释放的时候回调的方法, 然后根据左滑或者右滑的距离进行判断打开或者关闭
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild,xvel, yvel);
if (xvel > 0) {
open();
} else if (xvel < 0) {
close();
} else if (releasedChild == vg_main&& mainLeft > range * 0.3) {
open();
} else if (releasedChild == vg_left&& mainLeft > range * 0.7) {
open();
} else {
close();
}
}
/**
* 子View被拖拽 移动的时候回调的方法
* @param changedView View whoseposition changed
* @param left New X coordinate of theleft edge of the view
* @param top New Y coordinate of thetop edge of the view
* @param dx Change in X position fromthe last call
* @param dy Change in Y position fromthe last call
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
if (changedView == vg_main) {
mainLeft = left;
} else {
mainLeft = mainLeft + left;
}
if (mainLeft < 0) {
mainLeft = 0;
} else if (mainLeft > range) {
mainLeft = range;
}
if (isShowShadow) {
iv_shadow.layout(mainLeft, 0,mainLeft + width, height);
}
if (changedView == vg_left) {
vg_left.layout(0, 0, width,height);
vg_main.layout(mainLeft, 0,mainLeft + width, height);
}
dispatchDragEvent(mainLeft);
}
};
/**
* 滑动相关回调接口
*/
public interface DragListener {
//界面打开
public void onOpen();
//界面关闭
public void onClose();
//界面滑动过程中
public void onDrag(float percent);
}
public void setDragListener(DragListener dragListener) {
this.dragListener = dragListener;
}
/**
* 布局加载完成回调
* 做一些初始化的操作
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (isShowShadow) {
iv_shadow = new ImageView(context);
iv_shadow.setImageResource(R.mipmap.shadow);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(iv_shadow, 1, lp);
}
//左侧界面
vg_left = (RelativeLayout)getChildAt(0);
//右侧(主)界面
vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1);
vg_main.setDragLayout(this);
vg_left.setClickable(true);
vg_main.setClickable(true);
}
public ViewGroup getVg_main() {
return vg_main;
}
public ViewGroup getVg_left() {
return vg_left;
}
@Override
protected void onSizeChanged(int w, int h,int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = vg_left.getMeasuredWidth();
height = vg_left.getMeasuredHeight();
//可以水平拖拽滑动的距离 一共为屏幕宽度的80%
range = (int) (width * 0.8f);
}
/**
* 调用进行left和main 视图进行位置布局
* @param changed
* @param left
* @param top
* @param right
* @param bottom
*/
@Override
protected void onLayout(boolean changed,int left, int top, int right, int bottom) {
vg_left.layout(0, 0, width, height);
vg_main.layout(mainLeft, 0, mainLeft +width, height);
}
/**
* 拦截触摸事件
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
returndragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev);
}
/**
* 将拦截的到事件给ViewDragHelper进行处理
* @param e
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent e){
try {
dragHelper.processTouchEvent(e);
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
/**
* 进行处理拖拽事件
* @param mainLeft
*/
private void dispatchDragEvent(intmainLeft) {
if (dragListener == null) {
return;
}
float percent = mainLeft / (float)range;
//滑动动画效果
animateView(percent);
//进行回调滑动的百分比
dragListener.onDrag(percent);
Status lastStatus = status;
if (lastStatus != getStatus()&& status == Status.Close) {
dragListener.onClose();
} else if (lastStatus != getStatus()&& status == Status.Open) {
dragListener.onOpen();
}
}
/**
* 根据滑动的距离的比例,进行平移动画
* @param percent
*/
private void animateView(float percent) {
float f1 = 1 - percent * 0.5f;
ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent);
if (isShowShadow) {
//阴影效果视图大小进行缩放
ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f));
ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f));
}
}
/**
* 有加速度,当我们停止滑动的时候,该不会立即停止动画效果
*/
@Override
public void computeScroll() {
if (dragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
/**
* 页面状态(滑动,打开,关闭)
*/
public enum Status {
Drag, Open, Close
}
/**
* 页面状态设置
* @return
*/
public Status getStatus() {
if (mainLeft == 0) {
status = Status.Close;
} else if (mainLeft == range) {
status = Status.Open;
} else {
status = Status.Drag;
}
return status;
}
public void open() {
open(true);
}
public void open(boolean animate) {
if (animate) {
//继续滑动
if(dragHelper.smoothSlideViewTo(vg_main, range, 0)) {
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
vg_main.layout(range, 0, range * 2,height);
dispatchDragEvent(range);
}
}
public void close() {
close(true);
}
public void close(boolean animate) {
if (animate) {
//继续滑动
if(dragHelper.smoothSlideViewTo(vg_main, 0, 0)) {
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
vg_main.layout(0, 0, width,height);
dispatchDragEvent(0);
}
}
} 

(七).最后总结

今天我们实现打造QQ最新版本QQ6.X效果,同时里边用到了ViewDragHelper,BaseAdapterHelper的运用,具体该知识点的使用方法,我已经在我的博客中更新讲解的文章,欢迎大家查看。

(0)

相关推荐

  • ViewDragHelper实现QQ侧滑效果

    前言 侧滑的实现方式有很多方式来实现,这次总结的ViewDragHelper就是其中一种方式,ViewDragHelper是2013年谷歌I/O大会发布的新的控件,为了解决界面控件拖拽问题.下面就是自己学习写的一个实现类似于QQ侧滑效果的实现. activity_main.xml: <com.yctc.drag.DragLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=&qu

  • Android使用ViewDragHelper实现仿QQ6.0侧滑界面(一)

    QQ是大家离不开的聊天工具,方便既实用,自从qq更新至6.0之后,侧滑由原来的划出后主面板缩小变成了左右平滑,在外观上有了很大的提升,于是我就是尝试理解下里面的各种逻辑,结合相关资料,研究研究. 知道这里面的一个主要类是ViewDragHelper,那么首先我们要先来了解一下这个ViewDragHelper类,正所谓打蛇打七寸,我们就先来看看官方文档怎么介绍的,有什么奇特的功能. 首先继承: java.lang.Object android.support.v4.widget.ViewDragH

  • Android基于ViewDragHelper仿QQ5.0侧滑界面效果

    QQ5.0侧滑效果实现方案有很多方式,今天我们使用ViewDragHelper来实现一下. 先上效果图: ①自定义控件SlidingMenu继承FrameLayout,放在FrameLayout上面的布局一层叠着者一层,通过getChildAt()可以很方便的获取到任意一层,进而控制此布局的变化. public class SlidingMenu extends FrameLayout { private ViewDragHelper mViewDragHelper; private int m

  • Android使用ViewDragHelper实现QQ6.X最新版本侧滑界面效果实例代码

    (一).前言: 这两天QQ进行了重大更新(6.X)尤其在UI风格上面由之前的蓝色换成了白色居多了,侧滑效果也发生了一些变化,那我们今天来模仿实现一个QQ6.X版本的侧滑界面效果.今天我们还是采用神器ViewDragHelper来实现. 本次实例具体代码已经上传到下面的项目中,欢迎各位去star和fork一下. https://github.com/jiangqqlmj/DragHelper4QQ FastDev4Android框架项目地址:https://github.com/jiangqqlm

  • Android 自定义 HorizontalScrollView 打造多图片OOM 的横向滑动效果(实例代码)

    自从Gallery被谷歌废弃以后,Google推荐使用ViewPager和HorizontalScrollView来实现Gallery的效果.的确HorizontalScrollView可以实现Gallery的效果,但是HorizontalScrollView存在一个很大的问题,如果你仅是用来展示少量的图片,应该是没问题的,但是如果我希望HorizontalScrollView可以想ViewPager一样,既可以绑定数据集(动态改变图片),还能做到,不管多少图片都不会OOM(ViewPager内

  • Android自定义view系列之99.99%实现QQ侧滑删除效果实例代码详解

    首先声明本文是基于GitHub上"baoyongzhang"的SwipeMenuListView修改而来,该项目地址: https://github.com/baoyongzhang/SwipeMenuListView 可以说这个侧滑删除效果是我见过效果最好且比较灵活的项目,没有之一!!! 但是在使用它之前需要给大家提两点注意事项: 1,该项目支持Gradle dependence,但是目前作者提供的依赖地址对应的项目不是最新的项目,依赖过后的代码与demo中使用的不一致,会提示没有B

  • python3 自动打印出最新版本执行的mysql2redis实例

    我就废话不多说了,直接看代码吧! #!/usr/bin/env python3 # -*- coding: utf-8 -*- # 输出 mysql2redis 命令 # __author__ = caozhi # create_time 2018-11-12,update_time 2019-12-12 # version = 2.0 import os import re import sys import time import fcntl import subprocess import

  • Android自定义手机界面状态栏实例代码

    前言 我们知道IOS上的应用,状态栏的颜色总能与应用标题栏颜色保持一致,用户体验很不错,那安卓是否可以呢?若是在安卓4.4之前,答案是否定的,但在4.4之后,谷歌允许开发者自定义状态栏背景颜色啦,这是个不错的体验!若你手机上安装有最新版的qq,并且你的安卓SDK版本是4.4及以上,你可以看下它的效果: 实现这个效果有两个方法: 1.在xml中设置主题或自定义style: Theme.Holo.Light.NoActionBar.TranslucentDecor Theme.Holo.NoActi

  • Android 使用Fragment模仿微信界面的实例代码

    什么是Fragment 自从Android 3.0中引入fragments 的概念,根据词海的翻译可以译为:碎片.片段.其目的是为了解决不同屏幕分辩率的动态和灵活UI设计.大屏幕如平板小屏幕如手机,平板电脑的设计使得其有更多的空间来放更多的UI组件,而多出来的空间存放UI使其会产生更多的交互,从而诞生了fragments . fragments 的设计不需要你来亲自管理view hierarchy 的复杂变化,通过将Activity 的布局分散到frament 中,可以在运行时修改activit

  • Android中RecyclerView上拉下拉,分割线,多条目的实例代码

    //activity的xml <?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:id="@+id/activity

  • Android界面上拉下拉的回弹效果实例代码

    废话不多说,具体代码如下所示: public class MyScrollView extends ScrollView { private View childView; public MyScrollView(Context context) { super(context); } public MyScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public MyScrollView(Co

  • Android 中按home键和跳转到主界面的实例代码

    //home Intent intent= new Intent(Intent.ACTION_MAIN); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //如果是服务里调用,必须加入new task标识 intent.addCategory(Intent.CATEGORY_HOME); startActivity(intent); //主界面 Intent intent = new Intent(Intent.ACTION_MAIN,null)

  • Android实现桌面悬浮窗、蒙板效果实例代码

    现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作. 今天这篇文章,就是介绍如何实现桌面悬浮窗效果的. 首先,看一下效果图. 悬浮窗一共分为两个部分,一个是平常显示的小窗口,另外一个是点击小窗口显示出来的二级悬浮窗口. 首先,先看一下这个项目的目录结构. 最关键的就是红框内的四个类. 首先,FloatWindowService是一个后台的服务类,主要负责在后台不断的刷新桌面上的小悬浮窗口,否则会导致更换界面之后,悬浮窗口也会随

随机推荐