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>