滑动返回

手机屏幕越来越大,单手操作时,无法点击到左上角的回退按钮。看到很多人都有实现这个功能,但是实现的功能过于强大。所以自己写了一个Layout来实现这个功能。整个Layout实现滑动返回只有180行代码。

实现思想

现在有一个ViewGroup和一个View,在这个ViewGroup和View之间加上一个层Overlay,实现滑动时,Overlay和view像→滑动,ViewGroup不动。

比如,Activity的decorView就是ViewGroup,view就是ActionBarOverlayLayout(decorView.getChildAtIndex(0));

将activty的windowbackground职位null,并将windowbackroundcolor置为透明色即可。

源码

public class SlideOverlayLayout extends FrameLayout {

	private onSlideListener mOnSlideListener;

	private int mTouchSlop;
	private Scroller mScroller;

	private float mLastPositionX;
	private float mLastPositonY;

	private boolean mIsSliding;
	private boolean mIsFinish;

	private View mSlidView;

	public SlideOverlayLayout(Context context) {
		super(context);
		init(context);
	}

	public SlideOverlayLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public SlideOverlayLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	private void init(Context context) {
		mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
		mScroller = new Scroller(context);
	}

	public void setOnSlideListener(onSlideListener onSlideListener) {
		mOnSlideListener = onSlideListener;
	}

	public void slide() {
		if (mOnSlideListener == null) {
			mSlidView = null;
			return;
		}

		mOnSlideListener.insertSlideOverlayLayout(this);

		if (getChildCount() == 0) {
			throw new IllegalArgumentException(
					"slide view at least have one child, which is the slid view.");
		}

		mSlidView = getChildAt(getChildCount() - 1);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent event) {
		if (mOnSlideListener == null) {
			return super.onInterceptTouchEvent(event);
		}

		if (!mOnSlideListener.canSlide(this, event.getRawX(), event.getRawY())) {
			return super.onInterceptTouchEvent(event);
		}

		switch (event.getAction()) {

		case MotionEvent.ACTION_DOWN:
			mIsSliding = true;
			mLastPositionX = event.getRawX();
			mLastPositonY = event.getRawY();
			break;
		case MotionEvent.ACTION_MOVE:
			float positionX = event.getRawX();
			float positionY = event.getRawY();

			float deltaX = positionX - mLastPositionX;
			float deltaY = positionY - mLastPositonY;

			if (Math.abs(deltaY) < mTouchSlop && deltaX > 0) {
				mLastPositionX = positionX;
				mLastPositonY = positionY;
				return mIsSliding = true;
			} else {
				mIsSliding = false;
			}
			break;
		default:
			break;
		}
		return super.onInterceptTouchEvent(event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (mOnSlideListener == null || !mIsSliding) {
			return super.onTouchEvent(event);
		}

		switch (event.getAction()) {
		case MotionEvent.ACTION_MOVE:
			float positionX = event.getRawX();
			float positionY = event.getRawY();

			float deltaX = positionX - mLastPositionX;

			mLastPositionX = positionX;
			mLastPositonY = positionY;

			if (mSlidView.getScrollX() - deltaX > 0) {
				deltaX = mSlidView.getScrollX();
			}

			mSlidView.scrollBy((int) -deltaX, 0);

			mOnSlideListener.onSlide(this, deltaX - mSlidView.getScrollX());
			break;
		case MotionEvent.ACTION_UP:
			mIsSliding = false;
			int dx;
			if (mIsFinish = mOnSlideListener.canFinish(this,
					-mSlidView.getScrollX())) {
				dx = -(getWidth() + mSlidView.getScrollX()) + 1;
			} else {
				dx = -mSlidView.getScrollX();
			}

			mScroller.startScroll(mSlidView.getScrollX(), 0, dx, 0,
					Math.abs(dx));
			postInvalidate();
			break;
		}
		return true;
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			mSlidView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			mOnSlideListener.onSlide(this, - mScroller.getCurrX());
			postInvalidate();
		} else if (mIsFinish) {
			mOnSlideListener.onFinish(this);
		}
	}

	public interface onSlideListener {

		// Insert the slide layout between the container and slid view.
		// Also you can add other views into the layout to let the slid look
		// better.
		// But be sure that the slid view will be the last child to be added in.
		void insertSlideOverlayLayout(SlideOverlayLayout layout);

		// When touching the screen, this will be called with the position where
		// your finger touches.
		boolean canSlide(SlideOverlayLayout layout, float positionX,
				float positionY);

		// This will be called when ever your finger moves with the offset.
		void onSlide(SlideOverlayLayout layout, float offset);

		// When leaving your finger from the screen, this method will be called
		// with the final offset, and return the boolean value which can finish
		// the slid action.
		boolean canFinish(SlideOverlayLayout layout, float offset);

		// when return true by canFinish() method, this will be immediately
		// called when the slide animation overs.
		void onFinish(SlideOverlayLayout layout);
	}
}

如何使用

1.将activity的window置为透明(查看values下的style文件)

 <style name="AppTheme.Translucent">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowIsTranslucent">true</item>
        <!-- Note that we use the base animation style here (that is no
             animations) because we really have no idea how this kind of
             activity will be used. -->
        <item name="android:windowAnimationStyle">@android:style/Animation</item>
    </style>

2.将其应用到AndroidManifest文件的制定Activity上

        <activity
      		android:name="com.demo.slide.activity.SlideActivity"
            android:theme="@style/AppTheme.Translucent" />

3.在activity中使用SlideOverlayLayout

public class SlideActivity extends Activity implements onSlideListener {

//	private View vSlideAlphaView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_slide);

		SlideOverlayLayout slideView = new SlideOverlayLayout(this);
		slideView.setOnSlideListener(this);
		slideView.slide();
	}

	@Override
	public void insertSlideOverlayLayout(SlideOverlayLayout layout) {
		ViewGroup root = (ViewGroup) getWindow().getDecorView();

		View contentView = root.getChildAt(0);

		root.removeView(contentView);

//		vSlideAlphaView = new View(this);
//		vSlideAlphaView.setBackgroundColor(Color.BLACK);
//		vSlideAlphaView.getBackground().setAlpha(255 / 2);
//		layout.addView(vSlideAlphaView);

		layout.addView(contentView);
		root.addView(layout);
	}

	@Override
	public void onSlide(SlideOverlayLayout layout, float offset) {
//		float alpha = 0.5f - offset / vSlideAlphaView.getWidth();
//
//		if (alpha < 0) {
//			alpha = 0;
//		}
//
//		vSlideAlphaView.getBackground().setAlpha((int) (255 * alpha));
	}

	@Override
	public boolean canFinish(SlideOverlayLayout layout, float offset) {
		return offset > layout.getWidth() / 3.0f;
	}

	@Override
	public void onFinish(SlideOverlayLayout layout) {
		finish();
	}

	@Override
	public boolean canSlide(SlideOverlayLayout layout, float positionX,
			float positonY) {
		return true;
	}

}

注:对这个listener方法的说明

// Insert the slide layout between the container and slid view.
		// Also you can add other views into the layout to let the slid look
		// better.
		// But be sure that the slid view will be the last child to be added in.
		void insertSlideOverlayLayout(SlideOverlayLayout layout);

		// When touching the screen, this will be called with the position where
		// your finger touches.
		boolean canSlide(SlideOverlayLayout layout, float positionX,
				float positionY);

		// This will be called when ever your finger moves with the offset.
		void onSlide(SlideOverlayLayout layout, float offset);

		// When leaving your finger from the screen, this method will be called
		// with the final offset, and return the boolean value which can finish
		// the slid action.
		boolean canFinish(SlideOverlayLayout layout, float offset);

		// when return true by canFinish() method, this will be immediately
		// called when the slide animation overs.
		void onFinish(SlideOverlayLayout layout);

源码地址

SlideDemo

时间: 2024-10-13 15:56:11

滑动返回的相关文章

iOS开发——实用技术OC篇&amp;8行代码教你搞定导航控制器全屏滑动返回效果

8行代码教你搞定导航控制器全屏滑动返回效果 前言 此次文章,讲述的是导航控制器全屏滑动返回效果,而且代码量非常少,10行内搞定. 效果如图: 如果喜欢我的文章,可以关注我,也可以来小码哥,了解下我们的iOS培训课程.陆续还会有更新ing.... 一.自定义导航控制器 目的:以后需要使用全屏滑动返回功能,就使用自己定义的导航控制器. 二.分析导航控制器侧滑功能 效果:导航控制器默认自带了侧滑功能,当用户在界面的左边滑动的时候,就会有侧滑功能. 系统自带的侧滑效果: 分析: 1.导航控制器的view

全屏滑动返回

#import "XMGNavigationViewController.h" @interface XMGNavigationViewController ()<UIGestureRecognizerDelegate> @end @implementation XMGNavigationViewController - (void)viewDidLoad { [super viewDidLoad]; UIPanGestureRecognizer *pan = [[UIPa

滑动返回功能被覆盖的解决思路

在OC开发中,导航控制器是一个非常常见的控件,而且在不少使用时候,我们需要自定义导航条NavigationBar.但是这个做法可能带来一些小麻烦,下面是我遇到的问题以及解决方案的思维过程. 在苹果内部,返回功能的实现自带了一个边缘的滑动返回功能.但是一旦使用我们自定义的NavigationBar,子控制器这个功能便会消失.如果我们既要用到自定义,又要保留滑动返回功能,那我们首先要分析消失的原因,再来寻找解决方案. 既然替换NavigationBar会导致滑动返回功能的消失,那我们基本可以确定,在

Activity中 左滑动返回监听

网易新闻中有个比较炫的效果,在QQ进入聊天界面也有这种效果,就是从界面左侧滑动到右侧时,界面退出,其实功能很容易实现: 1) Activity 去实现 implements OnTouchListener 2) 实现其中的onTouch方法: /** * 左滑动返回监听 */ @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACT

iOS7下滑动返回与ScrollView共存二三事

[转载请注明出处] = =不是整篇复制就算注明出处了亲... iOS7下滑动返回与ScrollView共存二三事 [前情回顾] 去年的时候,写了这篇帖子iOS7滑动返回.文中提到,对于多页面结构的应用,可以替换interactivePopGestureRecognizer的delegate以统一管理应用中所有页面滑动返回的开关,比如在UINavigationController的派生类中 1 //我是一个NavigationController的派生类 2 - (id)initWithRootV

iOS之手势滑动返回功能-b

iOS中如果不自定义UINavigationBar,通过手势向右滑是可以实现返回的,这时左边的标题文字提示的是上一个ViewController的标题,如果需要把文字改为简约风格,例如弄过箭头返回啥的,那么你需要自定义UINavigationBar,但当你自定义navigationBar后,这个功能就会自动失效. 屏蔽右滑返回功能代码:   if ([self.navigationController respondsToSelector:@selector(interactivePopGest

一个丝滑的全屏滑动返回手势

一个丝滑的全屏滑动返回手势 全屏返回手势 自 iOS7 之后,Apple 增加了屏幕边缘右划返回交互的支持,再配合上 UINavigationController 的交互式动画,pop 到上一级页面的操作变的非常顺畅和丝滑,从此,我很少再使用点击左上角导航栏上的返回按钮的方式返回了,因为这对单手操作十分不友好:如果一个 App 居然胆敢不支持滑动返回,那离被卸载就不远了. 说到全屏返回手势,首先我感觉这件事本身可能就有问题,毕竟有点反苹果官方的交互,让用户从任意的地方都能够滑 动返回这个交互在国

ios navigationcontroller 滑动返回与webview加载html图片自适应屏幕宽度

1 .ios navigationcontroller 滑动返回 滑动返回是navigationcontroller默认返回按钮自带的功能,如果返回按钮自定义该功能失效, 解决的办法有两个: ① self.navigationItem.backBarButtonItem =   [[UIBarButtonItem alloc]initWithCustomView:button];//这个方法用不了 只能用 self.navigationItem.backBarButtonItem = [ [UI

IOS 自定义 滑动返回 手势

/** 只需要在你自定义的导航控制器中,改成如下代码即可,自定义手势返回 */ #define KEY_WINDOW [[UIApplication sharedApplication] keyWindow] #define kScreenW KEY_WINDOW.bounds.size.width #define kAnimateDuration 0.3 #define kDefaultAlapa 0.5 #define kDefaultScale 0.95 #define iOS7 ([UI