Android实现向右滑动关闭界面

Activity 向右滑动,滑动超过屏幕的一半,就关闭,否则,恢复原来的状态。
解决了滑动冲突。(ViewPager/ListView/RecyclerView)

1.配置透明主题
要想 Activity 滑出屏幕后不遮挡下层 Activity ,需设置透明主题

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

<style name="AppTheme.Slide" parent="@style/AppTheme">
    <!--Required-->
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@style/AppTheme.Slide.Animation</item>
</style>

<style name="AppTheme.Slide.Animation" parent="@android:style/Animation.Activity">
    <item name="android:activityOpenEnterAnimation">@anim/anim_slide_in</item>
    <item name="android:activityOpenExitAnimation">@anim/anim_slide_out</item>
    <item name="android:activityCloseEnterAnimation">@anim/anim_slide_in</item>
    <item name="android:activityCloseExitAnimation">@anim/anim_slide_out</item>
</style>

如果需要滑动关闭则指定 Activity 的 theme 为 AppTheme.Slide ,否则使用 AppTheme 。
这里也添加了 Activity 切换动画,增强体验。

  <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme.Slide">
            <activity
                android:name=".MainActivity"
                android:theme="@style/AppTheme">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity
                android:name=".ViewPagerActivity"/>
            <activity
                android:name=".ListViewActivity" />
            <activity
                android:name=".CheckListActivity"/>
        </application>

2.SlideLayout
继承自 FrameLayout ,主要是处理滑动逻辑和滑动冲突。

public class SlidingLayout extends FrameLayout {
// 页面边缘阴影的宽度默认值
private static final int SHADOW_WIDTH = 16;
private Activity mActivity;
private Scroller mScroller;
// 页面边缘的阴影图
private Drawable mLeftShadow;
// 页面边缘阴影的宽度
private int mShadowWidth;
private int mInterceptDownX;
private int mLastInterceptX;
private int mLastInterceptY;
private int mTouchDownX;
private int mLastTouchX;
private int mLastTouchY;
private boolean isConsumed = false;

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);
    initView(context);
}

private void initView(Context context) {
    mScroller = new Scroller(context);
    mLeftShadow = getResources().getDrawable(R.drawable.left_shadow);
    int density = (int) getResources().getDisplayMetrics().density;
    mShadowWidth = SHADOW_WIDTH * density;
}

/**
 * 绑定Activity
 */
public void bindActivity(Activity activity) {
    mActivity = activity;
    ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();
    View child = decorView.getChildAt(0);
    decorView.removeView(child);
    addView(child);
    decorView.addView(this);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean intercept = false;
    int x = (int) ev.getX();
    int y = (int) ev.getY();
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            intercept = false;
            mInterceptDownX = x;
            mLastInterceptX = x;
            mLastInterceptY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            int deltaX = x - mLastInterceptX;
            int deltaY = y - mLastInterceptY;
            // 手指处于屏幕边缘,且横向滑动距离大于纵向滑动距离时,拦截事件
            if (mInterceptDownX < (getWidth() / 10) && Math.abs(deltaX) > Math.abs(deltaY)) {
                intercept = true;
            } else {
                intercept = false;
            }
            mLastInterceptX = x;
            mLastInterceptY = y;
            break;
        case MotionEvent.ACTION_UP:
            intercept = false;
            mInterceptDownX = mLastInterceptX = mLastInterceptY = 0;
            break;
    }
    return intercept;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    int x = (int) ev.getX();
    int y = (int) ev.getY();
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mTouchDownX = x;
            mLastTouchX = x;
            mLastTouchY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            int deltaX = x - mLastTouchX;
            int deltaY = y - mLastTouchY;

            if (!isConsumed && mTouchDownX < (getWidth() / 10) && Math.abs(deltaX) > Math.abs(deltaY)) {
                isConsumed = true;
            }

            if (isConsumed) {
                int rightMovedX = mLastTouchX - (int) ev.getX();
                // 左侧即将滑出屏幕
                if (getScrollX() + rightMovedX >= 0) {
                    scrollTo(0, 0);
                } else {
                    scrollBy(rightMovedX, 0);
                }
            }
            mLastTouchX = x;
            mLastTouchY = y;
            break;
        case MotionEvent.ACTION_UP:
            isConsumed = false;
            mTouchDownX = mLastTouchX = mLastTouchY = 0;
            // 根据手指释放时的位置决定回弹还是关闭
            if (-getScrollX() < getWidth() / 2) {
                scrollBack();
            } else {
                scrollClose();
            }
            break;
    }
    return true;
}

/**
 * 滑动返回
 */
private void scrollBack() {
    int startX = getScrollX();
    int dx = -getScrollX();
    mScroller.startScroll(startX, 0, dx, 0, 300);
    invalidate();
}

/**
 * 滑动关闭
 */
private void scrollClose() {
    int startX = getScrollX();
    int dx = -getScrollX() - getWidth();
    mScroller.startScroll(startX, 0, dx, 0, 300);
    invalidate();
}

@Override
public void computeScroll() {
    if (mScroller.computeScrollOffset()) {
        scrollTo(mScroller.getCurrX(), 0);
        postInvalidate();
    } else if (-getScrollX() >= getWidth()) {
        mActivity.finish();
    }
}

@Override
protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    drawShadow(canvas);
}

/**
 * 绘制边缘的阴影
 */
private void drawShadow(Canvas canvas) {
    mLeftShadow.setBounds(0, 0, mShadowWidth, getHeight());
    canvas.save();
    canvas.translate(-mShadowWidth, 0);
    mLeftShadow.draw(canvas);
    canvas.restore();
} }

重写 onInterceptTouchEvent 和 onTouchEvent 处理滑动逻辑和滑动冲突。

  • 当有子 View 消费 Touch 事件时,事件会经过 SlidingLayout 的 onInterceptTouchEvent
    。当手指在屏幕边缘按下(mTouchDownX < (getWidth() /
    10)),且横向滑动距离大于纵向滑动距离,则拦截事件,交由 onTouchEvent 处理。
  • 当没有子 View 消费 Touch 事件时,事件会直接回传到 SlidingLayout 的 onTouchEvent 中,这时需要在
    onTouchEvent 中判断是否需要消费该事件,条件同上。
  • 滑动过程中如果 View 即将滑出屏幕左侧,则直接把 View 滑动到 (0,0) 位置。
  • 手指释放后,如果滑动距离超过屏幕的一半,则关闭 Activity ,否则,恢复原来状态。
  • 使用 Scroller 来处理手指释放后的滑动操作。
  • 在 dispatchDraw 中绘制 View 左侧的阴影,增加层次感。

3.使用(BaseActivity)

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(enClicble()){
            SlidingLayout slidingLayout = new SlidingLayout(this);
            slidingLayout.bindActivity(this);
        }
    }
    private boolean enClicble(){
        return true;
    }
}

附上两个anim 一个drawable:

anim_slide_in.xml:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="100%p"
        android:toXDelta="0" />
</set>

anim_slide_out:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="0"
        android:toXDelta="100%p" />
</set>

left_shadow.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <!--颜色渐变范围-->
    <gradient
        android:endColor="#50000000"
        android:startColor="#00000000" />
</shape>
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值