手势滑动结束 Activity(一)基本功能的实现
喜欢听音乐的朋友可能都看过天天动听这款 app, 这款 app 有一个亮点就是在切换页面(Fragment)的时候能够通过手势滑动来结束当前页面。这里先说一下,我为什么会这么关心这个功能呢,由于前两天 PM说我们即将開始做的这款app 也要实现页面能通过手势滑动来结束的功能,所以我就拿着这款 app 滑了一上午;可是我要实现的跟天天动听这款 app又有点不同,细心观察的朋友可能会发现。天天动听是 Fragment 之间的切换,而我这里要实现的是 Activity 之间的切换,只是,无论是哪种,终于效果都是一样,就是页面能随着手势的滑动而滑动。终于达到某个特定条件,结束此页面。
要实现这个功能事实上也不是特别难。这里我把这个功能的实现分为了下面两个步骤:
1、识别手势滑动自己定义ViewGroup 的实现
2、实现自己定义 ViewGroup 和 Activity 绑定
依据以上两个步骤,我们发现,这当中涉及到的知识点有:Android 事件处理机制、自己定义 View(ViewGroup)的实现,Activity Window的知识,在开发的过程中还涉及到Activity 主题的配置。
Android 事件处理和自己定义 View 都在我前面的 blog 中有讲到,假设不了解的朋友能够去看看。下面開始按步骤来实现功能
一、自己定义 ViewGroup
这个 ViewGroup 的功能仅仅要是对事件的拦截,能够实现手势滑动效果;显示 Activity 的内容包含 ActionBar 和内容区。
1、实现測量和布局
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*获取默认的宽度*/
int width = getDefaultSize(0, widthMeasureSpec);
/*获取默认的高度*/
int height = getDefaultSize(0, heightMeasureSpec);
/*设置ViewGroup 的宽高*/
setMeasuredDimension(width, height);
/*获取子 View 的宽度*/
final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);
/*获取子View 的高度*/
final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);
/*设置子View 的大小*/
mContent.measure(contentWidth, contentHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = r - l;
final int height = b - t;
mContent.layout(0, 0, width, height);
}
由于每一个 Activity 都仅仅有一个 Layout,所以这里仅仅有一个子 View,布局和測量就显得非常easy。
2、事件拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isEnable) {
return false;
}
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP
|| action != MotionEvent.ACTION_DOWN && mIsUnableToDrag) {
/*结束手势的滑动,不拦截*/
endToDrag();
return false;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
/*计算 x。y 的距离*/
int index = MotionEventCompat.getActionIndex(ev);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);
mLastMotionY = MotionEventCompat.getY(ev, index);
/*这里判读。假设这个触摸区域是同意滑动拦截的,则拦截事件*/
if (thisTouchAllowed(ev)) {
mIsBeingDragged = false;
mIsUnableToDrag = false;
} else {
mIsUnableToDrag = true;
}
break;
case MotionEvent.ACTION_MOVE:
/*继续推断是否须要拦截*/
determineDrag(ev);
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_UP:
/*这里做了对多点触摸的处理,当有多个手指触摸的时候依旧能正确的滑动*/
onSecondaryPointerUp(ev);
break;
}
if (!mIsBeingDragged) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
}
return mIsBeingDragged;
}
事件拦截,是拦截而是其不会向子 View 分发。直接运行本级 View的 onTouchEvent方法;
3、事件处理
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnable) {
return false;
}
if (!mIsBeingDragged && !thisTouchAllowed(event))
return false;
final int action = event.getAction();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
/*按下则结束滚动*/
completeScroll();
int index = MotionEventCompat.getActionIndex(event);
mActivePointerId = MotionEventCompat.getPointerId(event, index);
mLastMotionX = mInitialMotionX = event.getX();
break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
/*有多个点按下的时候,取最后一个按下的点为有效点*/
final int indexx = MotionEventCompat.getActionIndex(event);
mLastMotionX = MotionEventCompat.getX(event, indexx);
mActivePointerId = MotionEventCompat.getPointerId(event, indexx);
break;
}
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
determineDrag(event);
if (mIsUnableToDrag)
return false;
}
/*假设已经是滑动状态,则依据手势滑动。而改变View 的位置*/
if (mIsBeingDragged) {
// 下面代码用来推断和运行View 的滑动
final int activePointerIndex = getPointerIndex(event, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
final float x = MotionEventCompat.getX(event, activePointerIndex);
final float deltaX = mLastMotionX - x;
mLastMotionX = x;
float oldScrollX = getScrollX();
float scrollX = oldScrollX + deltaX;
final float leftBound = getLeftBound();
final float rightBound = getRightBound();
if (scrollX < leftBound) {
scrollX = leftBound;
} else if (scrollX > rightBound) {
scrollX = rightBound;
}
mLastMotionX += scrollX - (int) scrollX;
scrollTo((int) scrollX, getScrollY());
}
break;
case MotionEvent.ACTION_UP:
/*假设已经是滑动状态,抬起手指,须要推断滚动的位置*/
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaxMunVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
velocityTracker, mActivePointerId);
final int scrollX = getScrollX();
final float pageOffset = (float) (-scrollX) / getContentWidth();
final int activePointerIndex = getPointerIndex(event, mActivePointerId);
if (mActivePointerId != INVALID_POINTER) {
final float x = MotionEventCompat.getX(event, activePointerIndex);
final int totalDelta = (int) (x - mInitialMotionX);
/*这里推断是否滚动到下一页。还是滚回原位置*/
int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);
setCurrentItemInternal(nextPage, true, initialVelocity);
} else {
setCurrentItemInternal(mCurItem, true, initialVelocity);
}
mActivePointerId = INVALID_POINTER;
endToDrag();
} else {
// setCurrentItemInternal(0, true, 0);
endToDrag();
}
break;
case MotionEventCompat.ACTION_POINTER_UP:
/*这里有事多点处理*/
onSecondaryPointerUp(event);
int pointerIndex = getPointerIndex(event, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionX = MotionEventCompat.getX(event, pointerIndex);
break;
}
return true;
}
由于这里增加了多点控制,所以代码看起来有点复杂。事实上原理非常easy。就是不断的推断是否符合滑动的条件。其它就不细讲了。来看看这个自己定义 ViewGroup 的效果
能够看到。这里我们已经实现了手势识别的 ViewGroup,事实上这个ViewGroup假设发挥想象,它能实现非常多效果。不单单是我今天要讲的效果,还能够用作側拉菜单。或者是做 QQ5.0版本号側滑效果都能够实现的。
二、側滑 View绑定 Activity
这里为了代码的简洁。还是通过一个 ViewGroup 来封装了一层。
/**
* Created by moon.zhong on 2015/3/13.
*/
public class SlidingLayout extends FrameLayout {
/*側滑View*/
private SlidingView mSlidingView ;
/*须要側滑结束的Activity*/
private Activity mActivity ;
public SlidingLayout(Context context) {
this(context, null);
}
public SlidingLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mSlidingView = new SlidingView(context) ;
addView(mSlidingView);
mSlidingView.setOnPageChangeListener(new SlidingView.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 1){ Log.v("zgy","========position=========") ;
mActivity.finish();
}
}
@Override
public void onPageSelected(int position) {
}
});
mActivity = (Activity) context;
bindActivity(mActivity) ;
}
/**
* 側滑View 和Activity 绑定
* @param activity
*/
private void bindActivity(Activity activity){
/*获取Activity 的最顶级ViewGroup*/
ViewGroup root = (ViewGroup) activity.getWindow().getDecorView();
/*获取Activity 显示内容区域的ViewGroup。包行ActionBar*/
ViewGroup child = (ViewGroup) root.getChildAt(0);
root.removeView(child);
mSlidingView.setContent(child);
root.addView(this);
}
}
測试 Activity 这事就变的非常easy了
public class SecondActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
/*绑定Activity*/
new SlidingLayout(this) ;
}
}
来看看效果怎么样:
咦!能滑动结束页面,但为什么边滑走的同一时候看不到第一个 Acitivity,而是要等结束了才干看到呢?我们推測,应该是滑动的时候,这个 Activity 还有哪里把第一个 Activity 覆盖了,每一个 Activity 都是附在一个 Window 上面,所以这里就涉及到一个 Activity 的 window背景颜色问题, OK,把第二个 Activity 的 window 背景设为透明
<style name="TranslucentTheme" parent="AppTheme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<activity android:name=".SecondActivity"
android:label="SecondActivity"
android:screenOrientation="portrait"
android:theme="@style/TranslucentTheme"
/>
再来看看效果,效果图:
完美实现!
好了,今天就到这里,下期文章就是对这个功能的进一步优化和改善,假设感兴趣,能够继续关注我。
手势滑动结束 Activity(一)基本功能的实现的更多相关文章
- Android-通过SlidingPaneLayout高仿微信6.2最新版手势滑动返回(一)
近期更新了微信版本号到6.2.发现里面有个很好的体验,就是在第二个页面Activity能手势向右滑动返回,在手势滑动的过程中能看到第一个页面,这样的体验很赞,这里高仿了一下. 这里使用的是v4包里面的 ...
- css 的通用样式 设置 和倒计时功能 移动轮播图的手势滑动的功能
body{ line-height:1.4; color:#333; font-family:arial; font-size: 12px; background:white; } input,tex ...
- 再谈iOS 7的手势滑动返回功能
本文转载至 http://blog.csdn.net/jasonblog/article/details/28282147 之前随手写过一篇<使用UIScreenEdgePanGestureR ...
- iOS之手势滑动返回功能-b
iOS中如果不自定义UINavigationBar,通过手势向右滑是可以实现返回的,这时左边的标题文字提示的是上一个ViewController的标题,如果需要把文字改为简约风格,例如弄过箭头返回啥的 ...
- 禁用ios7 手势滑动返回功能
禁用ios7 手势滑动返回功能 版权声明:本文为博主原创文章,未经博主允许不得转载. 在有的时候,我们不需要手势返回功能,那么可以在页面中添加以下代码: - (void)viewDidAppear:( ...
- iOS之手势滑动返回功能
iOS中如果不自定义UINavigationBar,通过手势向右滑是可以实现返回的,这时左边的标题文字提示的是上一个ViewController的标题,如果需要把文字改为简约风格,例如弄过箭头返回啥的 ...
- Android-通过SlidingMenu高仿微信6.2最新版手势滑动返回(二)
转载请标明出处: http://blog.csdn.net/hanhailong726188/article/details/46453627 本文出自:[海龙的博客] 一.概述 在上一篇博文中,博文 ...
- iOS 7的手势滑动返回
如今使用默认模板创建的iOS App都支持手势返回功能,假设导航栏的返回button是自己定义的那么则会失效,也能够參考这里手动设置无效. if ([self.navigationController ...
- 滑动关闭activity
// 手指上下滑动时的最小速度 private static final int YSPEED_MIN = 1000; // 手指向右滑动时的最小距离 private static final int ...
随机推荐
- Spring Boot Memory Performance
The Performance Zone is brought to you in partnership with New Relic. Quickly learn how to use Docke ...
- java csv 文件 操作类
一个CSV文件操作类,功能比较齐全: package tool; import java.io.BufferedReader; import java.io.BufferedWriter; impor ...
- SSH 配置日记
1 注意struts2-spring-plugin.jar的导入. Unable to load configuration. - action 异常.需要导入这个包 2 很久都跑不通的 ...
- APUE学习之---------------进程
离职了,交接期也有足够的时间了,可以在好好的再看一下APUE,想想上次详细的看还是在两年之前,虽然中间也偶尔会翻出来看看,但是由于工作上交集相对比较少一直没有去细读一下.现在正好是一段空挡期可以好好看 ...
- 表格java代码的相关知识积累
本文主要收集各大博客中的java表格 用JSP创建一个表格模板 . 项目中要用到一些展示信息的表格,表头不固定,表格内容是即时从后台取的:考虑到复用性,笔者用jsp编写了一个表格模板,可以从reque ...
- [BZOJ2324][ZJOI2011][最小费用最大流]营救皮卡丘
[Problem Description] 皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路. 火箭队 ...
- mahout贝叶斯算法开发思路(拓展篇)2
如果想直接下面算法调用包,可以直接在mahout贝叶斯算法拓展下载,该算法调用的方式如下: $HADOOP_HOME/bin hadoop jar mahout.jar mahout.fansy.ba ...
- Android API 中文(76)——AdapterView.OnItemLongClickListener
前言 本章内容是android.widget.AdapterView.OnItemLongClickListener,版本为Android 2.3 r1,翻译来自"cnmahj", ...
- 使用C#对MongoDB中的数据进行查询,改动等操作
首先,使用的是官方提供的C#訪问组件https://github.com/mongodb/mongo-csharp-driver 然后.编译后引用MongoDB.Bson.dll及MongoDB.Dr ...
- Live Writer Test
测试下LiveWriter写CNblog: 1.Source code plug-in: @Override public List getAll(String orgid,String start, ...