【FastDev4Android框架开发】打造QQ6.X最新版本号側滑界面效果(三十八)
转载请标明出处:
http://blog.csdn.net/developer_jiangqq/article/details/50253925
本文出自:【江清清的博客】
(一).前言:
【好消息】个人站点已经上线执行,后面博客以及技术干货等精彩文章会同步更新。请大家关注收藏:http://www.lcode.org
这两天QQ进行了重大更新(6.X)尤其在UI风格上面由之前的蓝色换成了白色居多了,側滑效果也发生了一些变化,那我们今天来模仿实现一个QQ6.X版本号的側滑界面效果。
今天我们还是採用神器ViewDragHelper来实现。之前我们曾经基于ViewDragHelper的使用和打造QQ5.X效果了。基本用法能够点击以下的连接:
- 神器ViewDragHelper全然解析,妈妈再也不操心我自己定义ViewGroup滑动View操作啦~(三十三)
- 神器ViewDragHelper全然解析之详细解释实现QQ5.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"
>
<!--下层 左边的布局-->
<includelayout="@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"
>
<includelayout="@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> <!--底部功能条-->
<includelayout="@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的运用。详细该知识点的用法,我已经在我的博客中更新解说的文章,欢迎大家查看。
本次详细实例凝视过的全部代码已经上传到Github项目中了。同一时候欢迎大家去Github站点进行clone或者下载浏览:
https://github.com/jiangqqlmj/DragHelper4QQ
同一时候欢迎大家star和fork整个开源高速开发框架项目~
本例所用其它知识点博文地址例如以下:
- 神器ViewDragHelper全然解析,妈妈再也不操心我自己定义ViewGroup滑动View操作啦~(三十三)
- 神器ViewDragHelper全然解析之详细解释实现QQ5.X側滑酷炫效果(三十四)
- BaseAdapterHelper的基本使用介绍,让你摆脱狂写一堆Adapter烦恼(二十四)
- BaseAdapterHelper详细解释源码分析,让你摆脱狂写一堆Adapter烦恼(二十五)
特别致谢DragLayout组件开发人员:https://github.com/BlueMor/DragLayout
尊重原创,转载请注明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵权必究!
关注我的订阅号,每天分享移动开发技术(Android/IOS),项目管理以及博客文章!
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
关注我的微博,能够获得很多其它精彩内容
【FastDev4Android框架开发】打造QQ6.X最新版本号側滑界面效果(三十八)的更多相关文章
- 【FastDev4Android框架开发】RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout(三十一)
转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息] ...
- 应用程序框架实战三十八:项目示例VS解决方案的创建(一)
进行项目开发的第一步,是创建出适合自己团队习惯的VS解决方案,虽然我已经提供了项目示例,但毕竟是我创建的,你直接使用可能并不合适,另外你如果尝试模仿重新创建该示例,中间可能碰到各种障碍,特别是项目间的 ...
- Android项目实战(三十八):2017最新 将AndroidLibrary提交到JCenter仓库(图文教程)
我们经常使用github上的开源项目,使用步骤也很简单 比如: compile 'acffo.xqx.xwaveviewlib:maven:1.0.0' 这里就学习一下如何将自己的类库做出这种可以供他 ...
- Unity 游戏框架搭建 2019 (三十六~三十八) partial与public
在上一篇,我们把菜单的顺序从头到尾整理了一遍.在整理菜单顺序的过程中,记录了一个要做的事情. 要做的事情: (完成) 备份:导出文件,并取一个合理的名字. 整理完菜单顺序后,学习新的知识,解决随着示例 ...
- 网站开发进阶(三十八)Web前端开发规范文档你需要知道的事
Web前端开发规范文档你需要知道的事 规范目的 为提高团队协作效率, 便于后台人员添加功能及前端后期优化维护, 输出高质量的文档, 特制订此文档. 本规范文档一经确认, 前端开发人员必须按本文档规范进 ...
- BizTalk开发系列(三十八)微软BizTalk Server定价和许可[解读]
做BizTalk的项目一段时间了,但是对BizTalk的价格和许可还不是很了解.给客户设计解决方案时大部分产品都是直接按照企业版的功能来设计,很 少考虑到价格和许可方面的因素,以为这个不是我们的事情或 ...
- Java开发笔记(一百三十八)JavaFX的箱子
前面介绍了JavaFX标签控件的用法,其中提到Label文本支持中文字体,那么它到底支持哪些中文字体呢?自然要看当前的操作系统都安装了哪些字体才行,对于中文的Windows系统,默认安装了黑体“Sim ...
- Dynamic CRM 2013学习笔记(三十八)流程1 - 操作(action)开发与配置详解
CRM 2013 里流程有4个类别:操作(action).业务流程(business process flow).对话(dialog)和工作流(workflow).它们都是从 setting –> ...
- Java开发笔记(三十八)利用正则表达式校验字符串
前面多次提到了正则串.正则表达式,那么正则表达式究竟是符合什么定义的字符串呢?正则表达式是编程语言处理字符串格式的一种逻辑式子,它利用若干保留字符定义了形形色色的匹配规则,从而通过一个式子来覆盖满足了 ...
随机推荐
- 【Luogu4389】付公主的背包
题目 传送门 解法 答案显然是\(n\)个形如\(\sum_{i \geq 1} x^{vi}\)的多项式的卷积 然而直接NTT的时间复杂度是\(O(nm\log n)\) 我们可以把每个多项式求\( ...
- [Swift]LeetCode1071.字符串的最大公因子 | Greatest Common Divisor of Strings
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- ccf 201803-4 棋局评估 (对抗搜索)
棋局评估 问题描述 Alice和Bob正在玩井字棋游戏. 井字棋游戏的规则很简单:两人轮流往3*3的棋盘中放棋子,Alice放的是“X”,Bob放的是“O”,Alice执先.当同一种棋子占据一行.一列 ...
- django-2的路由层(URLconf)
URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码 ...
- JavaScript 关于DOM的事件操作
一.JavaScript的组成 JavaScript基础分为三个部分: ECMAscript:JavaScript的标准语法.包括变量,表达式,运算符,函数,if语句,for语句等. DOM:文档对象 ...
- .net core2.0 自定义中间件
一.中间件(Middleware) 中间件是被组装成一个应用程序管道来处理请求和响应的软件组件. 二.编写SimpleMiddleware using Microsoft.AspNetCore.Htt ...
- Java算法——求出两个字符串的最长公共字符串
问题:有两个字符串str1和str2,求出两个字符串中最长公共字符串. 例如:“acbbsdef”和"abbsced"的最长公共字符串是“bbs” 算法思路: 1.把两个字符串分别 ...
- c++ 虚函数,纯虚函数的本质区别
转载博客:https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289202&idx=1&sn=431ffd1fa ...
- Excel 批量出来数据
try { string sheetname = TextBox1.Text.Trim(); HttpPostedFile upLoadPostFile = FileUpload1.PostedFil ...
- Luogu P1365 WJMZBMR打osu! / Easy
概率期望专题首杀-- 毒瘤dp 首先根据数据范围推断出复杂度在O(n)左右 但不管怎么想都是n^2-- 晚上躺在床上吃东西的时候(误)想到之前有几道dp题是通过前缀和优化的 而期望的可加性又似乎为此创 ...