Android 详解实现全局右滑返回 仿IOS(带demo)

和自甴很熟 提交于 2019-12-02 12:39:07

前言

虽然现在好多手机都有了全面屏手势,但还有很大一部分用户的Android设备没有全面屏手势的,而IOS的端一直以来都有一个备受好评的功能 全局右滑返回 。而且很多时候开一个项目,会有Android和IOS版本的同时进行,而设计师一般的原型都会根据IOS设备来做,而这个时候,这种IOS固有的操作逻辑,会使得用户在Android端产生不适应感。

所以,这里有一份帮助你所有项目直接改造成全局右滑返回的Demo,你受的了吗
表情包-是个狼灭

最终效果

全局右滑返回
用到的控件是我上一个博客内容里用到的自定义控件:
android 右滑缩小页面(带阴影背景)侧边栏逐渐变大
是这个控件做的延伸,所以也能刚好适合一些需要在首页做划动侧边栏的项目;

实现过程

一、自定义控件

国际惯例,把这个自定义控件拉出来走一遍:

PageEnabledSlidingPaneLayout 由于在侧划的主页经常还需要放置其他一些需要滚动的控件或者翻页的viewpage,所以需要这个侧划布局还可以避免划动冲突。

import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.SlidingPaneLayout;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;

/**
 * 页面划动Layout
 * 继承SlidingPaneLayout
 * 能够兼容别的控件的划动事件
 * 避免冲突
 * @author  dlong
 * created at 2019/3/12 11:29 AM
 */
public class PageEnabledSlidingPaneLayout extends SlidingPaneLayout {
    private float mInitialMotionX;
    private float mInitialMotionY;
    /** 最小触发事件的划动距离 */
    private float mEdgeSlop;//手滑动的距离

    public PageEnabledSlidingPaneLayout(Context context) {
        this(context, null);
    }

    public PageEnabledSlidingPaneLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PageEnabledSlidingPaneLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        //--------  ViewConfiguration 滑动参数设置类 --------//
        ViewConfiguration config = ViewConfiguration.get(context);
        //--------  它获得的是触发移动事件的最短距离,如果小于这个距离就不触发移动控件  --------//
        mEdgeSlop = config.getScaledEdgeSlop();
    }

    /**
     * 拦截手势事件
     * 避免划动冲突
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (MotionEventCompat.getActionMasked(ev)) {
            case MotionEvent.ACTION_DOWN: {
                // 屏幕检测到第一个触点按下
                mInitialMotionX = ev.getX();
                mInitialMotionY = ev.getY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                // 触点在屏幕上移动
                final float x = ev.getX();
                final float y = ev.getY();
                // The user should always be able to "close" the pane, so we only check
                // for child scrollability if the pane is currently closed.
                // 避免与其他划动控件冲突
                if (mInitialMotionX > mEdgeSlop && !isOpen() && canScroll(this, false,
                        Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) {
                    // How do we set super.mIsUnableToDrag = true?
                    // send the parent a cancel event
                    MotionEvent cancelEvent = MotionEvent.obtain(ev);
                    cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
                    return super.onInterceptTouchEvent(cancelEvent);
                }
            }
        }
        return super.onInterceptTouchEvent(ev);
    }
}

二、写一个BaseActivity

直接上代码:

import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.widget.SlidingPaneLayout;
import android.view.View;
import android.view.ViewGroup;

import com.dlong.rep.swiperightbacktest.view.PageEnabledSlidingPaneLayout;

import java.lang.reflect.Field;

/**
 * s所有活动都要继承这个基础活动即可实现右划返回的功能
 * @author  dlong
 * created at 2019/3/12 4:18 PM
 */
public class BaseActivity extends FragmentActivity implements PageEnabledSlidingPaneLayout.PanelSlideListener{


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化划动返回
        initSwipeBackFinish();
    }

    /**
     * 初始化滑动返回
     */
    private void initSwipeBackFinish() {
        if (isSupportSwipeBack()) {
            PageEnabledSlidingPaneLayout slidingPaneLayout = new PageEnabledSlidingPaneLayout(this);
            // 通过反射改变mOverhangSize的值为0,这个mOverhangSize值为菜单到右边屏幕的最短距离,默认
            // 是32dp,现在给它改成0
            try {
                // 属性
                Field f_overHang = SlidingPaneLayout.class.getDeclaredField("mOverhangSize");
                f_overHang.setAccessible(true);
                f_overHang.set(slidingPaneLayout, 0);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 设置划动监听器
            slidingPaneLayout.setPanelSlideListener(this);
            // slidingPaneLayout.setSliderFadeColor(getResources().getColor(android.R.color.transparent));
            // 这里可以自行设置显示或消失时的前景颜色
            slidingPaneLayout.setSliderFadeColor(Color.GRAY);
            slidingPaneLayout.setCoveredFadeColor(Color.GRAY);

            View leftView = new View(this);
            leftView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
            slidingPaneLayout.addView(leftView, 0);

            ViewGroup decor = (ViewGroup) getWindow().getDecorView();
            ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);
            decorChild.setBackgroundColor(getResources().getColor(android.R.color.white));
            decor.removeView(decorChild);
            decor.addView(slidingPaneLayout);
            slidingPaneLayout.addView(decorChild, 1);
        }
    }

    /**
     * 是否支持滑动返回
     * 默认支持
     * 如果想不支持,例如app的首页,只需要复写这个,return false;即可
     * @return
     */
    protected boolean isSupportSwipeBack() {
        return true;
    }

    @Override
    public void onPanelSlide(@NonNull View view, float v) {

    }

    @Override
    public void onPanelOpened(@NonNull View view) {
        // 结束活动
        finish();
        // 添加动画
        this.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_right);
    }

    @Override
    public void onPanelClosed(@NonNull View view) {

    }
}

三、Activity继承

接下来,把每个Activity继承BaseActivity就好了

MainActivity 首页一般不用划动返回,所以要取消掉

public class MainActivity extends BaseActivity {
    private Context mContext = this;
    private Intent mIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected boolean isSupportSwipeBack() {
        return false;  //取消这个页面的划动返回
    }

    public void GoToMenu(View view){
        mIntent = new Intent();
        mIntent.setClass(mContext,MenuActivity.class);
        mContext.startActivity(mIntent);
    }

    public void GoToAdd(View view){
        mIntent = new Intent();
        mIntent.setClass(mContext,AddActivity.class);
        mContext.startActivity(mIntent);
    }
}

MenuActivity 其他页面需要划动返回的不需要做其他处理

public class MenuActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_menu);
    }

    public void Back(View view){
        finish();
    }
}

四、配置Style

差点忘了这事,要配置了style才能用的;方法如下:
styles.xml 文件里写:

<resources xmlns:tools="http://schemas.android.com/tools">

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="NoTitleTheme" parent="AppTheme"> <!--无标题非全屏-->
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="JK.SwipeBack.Transparent.Theme" parent="NoTitleTheme"> <!--无标题非全屏左侧划动返回-->
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">false</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowAnimationStyle">@style/JK.Animation.SlidingBack</item>
        <item name="android:actionBarStyle">@style/JKActionBar.Custom</item>
    </style>

    <style name="JKActionBar.Custom" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse">
        <item name="displayOptions">showCustom</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="background">@android:color/transparent</item>
        <item name="android:displayOptions" tools:ignore="NewApi">showCustom</item>
        <item name="android:height">?actionBarSize</item>
    </style>

    <style name="JK.Animation.SlidingBack" parent="@android:style/Animation.Activity">
        <item name="android:activityOpenEnterAnimation">@anim/slide_in_right</item>
        <item name="android:activityOpenExitAnimation">@anim/slide_out_right</item>
        <item name="android:activityCloseEnterAnimation">@anim/slide_in_right</item>
        <item name="android:activityCloseExitAnimation">@anim/slide_out_right</item>
        <item name="android:wallpaperOpenEnterAnimation">@anim/slide_in_right</item>
        <item name="android:wallpaperOpenExitAnimation">@anim/slide_out_right</item>
        <item name="android:wallpaperCloseEnterAnimation">@anim/slide_in_right</item>
        <item name="android:wallpaperCloseExitAnimation">@anim/slide_out_right</item>
        <item name="android:wallpaperIntraOpenEnterAnimation">@anim/slide_in_right</item>
        <item name="android:wallpaperIntraOpenExitAnimation">@anim/slide_out_right</item>
        <item name="android:wallpaperIntraCloseEnterAnimation">@anim/slide_in_right</item>
        <item name="android:wallpaperIntraCloseExitAnimation">@anim/slide_out_right</item>
    </style>

</resources>

里面用到两个动画, 一进一出。。。
不困
emmmmmm…
res 文件夹下再新建一个文件夹 anim
新建两个 xml文件
第一个:slide_in_right.xml

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false"
    android:zAdjustment="top">
    <translate
        android:duration="200"
        android:fromXDelta="100.0%p"
        android:toXDelta="0.0" />
</set>

第二个:slide_out_right.xml

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false"
    android:zAdjustment="top">
    <translate
        android:duration="200"
        android:fromXDelta="0.0"
        android:toXDelta="100.0%p" />
</set>

当然,建立了style也要用起来才行,用法也简单;
打开 AndroidManifest.xml 再activity填上

<activity
            android:name=".MenuActivity"
            android:theme="@style/JK.SwipeBack.Transparent.Theme" />

记得所有需要右滑返回的活动都要添加
android:theme="@style/JK.SwipeBack.Transparent.Theme"

GitHub

源码:SwipeRightBackTest

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!