SlideCloseLayout—仿头条多图预览的页面关闭效果
Posted xingxing_yan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SlideCloseLayout—仿头条多图预览的页面关闭效果相关的知识,希望对你有一定的参考价值。
序
最近同事问头条多图预览界面的关闭效果怎么实现,之前没有怎么注意,查看之后,顿时发现这个关闭效果还挺有意思,于是决定弄上一弄!那就撸起袖子开始吧!
注:此项目是参考SwipableLayout 控件做的,如有侵权,请联系我(1547740082)
一. 功能概述
- 在上滑或者下滑时,随着手指的移动,图片区域跟随移动,并且activity的背景逐渐变的透明
- 在滑动距离不超过一段范围时,会有回弹效果。
- 在滑动超过设置的范围时,放开手指,页面自动滑动消失
- 在按返回键后,页面结束动画跟手指下滑一样。
效果图
二.SlideCloseLayout的实现
1. 拦截触摸事件
我们知道,View的触摸事件默认从最上层往最下层去传的,当垂直滑动SlideCloseLayout时,需要移动其自身的位置,所以在垂直滑动时,需要拦截触摸事件,不传给子View,而是自己处理。
public boolean onInterceptTouchEvent(MotionEvent ev)
if (isLocked)
return false;
else
final int y = (int) ev.getRawY();
final int x = (int) ev.getRawX();
switch (ev.getAction())
case MotionEvent.ACTION_DOWN:
previousX = x;
previousY = y;
break;
case MotionEvent.ACTION_MOVE:
int diffY = y - previousY;
int diffX = x - previousX;
if (Math.abs(diffX) + 50 < Math.abs(diffY))
return true;
break;
return false;
先解释一下isLocked的作用,当isLocked为true时,就是禁止SlideCloseLayout有滑动效果。我们在移动时判断,如果y方向的滑动距离大于x+50,则认为就是垂直方向的滑动,此时返回true,拦截事件。
2. 处理触摸事件
(1) 当事件被拦截后,就需要SlideCloseLayout在onTouchEvent中去处理,先看移动时怎么处理,如下:
public boolean onTouchEvent(@NonNull MotionEvent ev)
if (!isLocked)
...
switch (ev.getAction())
...
case MotionEvent.ACTION_MOVE:
int diffY = y - previousY;
int diffX = x - previousX;
//判断方向
if (direction == Direction.NONE)
if (Math.abs(diffX) > Math.abs(diffY))
direction = Direction.LEFT_RIGHT;
else if (Math.abs(diffX) < Math.abs(diffY))
direction = Direction.UP_DOWN;
else
direction = Direction.NONE;
//当方向为垂直方向时,移动布局并改变透明度
if (direction == Direction.UP_DOWN)
isScrollingUp = diffY <= 0;
this.setTranslationY(diffY);
if (mBackground != null)
int alpha = (int) (255 * Math.abs(diffY * 1f)) / getHeight();
mBackground.setAlpha(255 - alpha);
return true;
break;
case MotionEvent.ACTION_UP:
...
return true;
return false;
首先,如果SlideCloseLayout被锁定,则不做任何操作。在ACTION_MOVE中,根据水平和垂直的滑动距离判断滑动的方向。当方向为direction == Direction.UP_DOWN时,通过setTranslationY()方法设置控件的移动距离,并根据滑动的距离计算背景透明度的变化。透明度如何计算?根据滑动距离diffY和控件高度height的比例,算出0-255之间的透明度alpha,由于背景是从不透明到透明,所以还需要用255-alpha,设置给mBackground。
(2) 当滑动一定距离,手指松开时,我们需要做两个处理,判断距离是否大于我们设置的阈值(高度的1/7),如果大于,则退出,否则恢复。如下:
case MotionEvent.ACTION_UP:
if (direction == Direction.UP_DOWN)
int height = this.getHeight();
//判断滑动距离是否大于height/7
if (Math.abs(getTranslationY()) > (height / 7))
//执行退出动画
layoutExitAnim(600, true);
else
//执行恢复动画
layoutRecoverAnim();
direction = Direction.NONE;
return true;
退出动画
/**
* 退出布局的动画
* @param duration 动画时长
* @param isFingerScroll 是否手指滑动触发
*/
public void layoutExitAnim(long duration, boolean isFingerScroll)
ObjectAnimator exitAnim;
if (isFingerScroll)
exitAnim = ObjectAnimator.ofFloat(this, "translationY", getTranslationY(), isScrollingUp ? -getHeight() : getHeight());
else
exitAnim = ObjectAnimator.ofFloat(this, "translationY", 0, getHeight());
exitAnim.addListener(new AnimatorListenerAdapter()
@Override
public void onAnimationEnd(Animator animation)
if (mBackground != null)
mBackground.setAlpha(0);
if (mScrollListener != null)
mScrollListener.onLayoutClosed();
);
exitAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
@Override
public void onAnimationUpdate(ValueAnimator animation)
if (mBackground != null)
int alpha = (int) (255 * Math.abs(getTranslationY() * 1f)) / getHeight();
mBackground.setAlpha(255 - alpha);
);
exitAnim.setDuration(duration);
exitAnim.start();
判断是手指滑动退出还是按返回键退出,以此来设置动画需要移动的距离,同时根据isScrollingUp设置动画的方向。动画执行中,背景的透明度还是在继续变化的,所有需要给exitAnim设置AnimatorUpdateListener监听,在每次变化后让透明度跟着变化。当动画执行完后,先设置背景的透明度为0,然后调用回调监听LayoutScrollListener中的onLayoutClosed()方法,在Activity中,实现此接口,在onLayoutColsed()中关闭Activity。
恢复动画
/**
* 恢复动画
*/
private void layoutRecoverAnim()
ObjectAnimator recoverAnim = ObjectAnimator.ofFloat(this, "translationY", this.getTranslationY(), 0);
recoverAnim.setDuration(100);
recoverAnim.start();
if (mBackground != null)
mBackground.setAlpha(255);
如果滑动的距离没有大于设置的阈值,则SlideCloseLayout需要恢复到初始位置,同时透明度也需要恢复到不透明,所以mBackground.setAlpha(255)。
到此,SlideCloseLayout控件就实现完成了,接下来就去使用吧。
三. 使用
- 布局文件
<?xml version="1.0" encoding="utf-8"?>
<com.yyx.library.SlideCloseLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.yyx.slidecloselayout.widget.TouchViewPager
android:id="@+id/scl_pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.yyx.slidecloselayout.widget.TouchViewPager>
</com.yyx.library.SlideCloseLayout>
2 SlideCloseLayoutActivity:
public class SlideCloseLayoutActivity extends AppCompatActivity
private SlideCloseLayout mSlideCloseLayout;
private ViewPager mPager;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slide_close_layout);
//设置activity的背景为黑色
getWindow().getDecorView().setBackgroundColor(Color.BLACK);
mSlideCloseLayout = (SlideCloseLayout) findViewById(R.id.scl);
mPager = (ViewPager) findViewById(R.id.scl_pager);
//给控件设置需要渐变的背景。如果没有设置这个,则背景不会变化
mSlideCloseLayout.setGradualBackground(getWindow().getDecorView().getBackground());
//设置监听,滑动一定距离后让Activity结束
mSlideCloseLayout.setLayoutScrollListener(new SlideCloseLayout.LayoutScrollListener()
@Override
public void onLayoutClosed()
onBackPressed();
);
CustomPagerAdapter adapter = new CustomPagerAdapter(this);
mPager.setAdapter(adapter);
...
先给Activity设置黑色背景,然后件Activity的背景传给控件, mSlideCloseLayout.setGradualBackground(getWindow().getDecorView().getBackground()),这个其实就是给SlideCloseLayout中的mBackground赋值,如果不传则没有背景的变化效果。然后设置LayoutScrollListener监听,并在onLayoutClosed()中执行onBackPressed(),退出Activity。
3 返回键的处理
返回键我并没有像头条那样覆盖Activity的退出动画,在style中直接设置activityCloseExitAnimation这个属性退出时动画不会起作用,必须重写Activity的finish()方法,然后在super.finish()之后执行overridePendingTransition()方法才会起作用,感觉好烦的样子。我的实现方式是直接调用SlideCloseLayout中的退出动画方法layoutExitAnim(),只需要把参数isFingerScroll传为false即可,不用额外的去设置Activity的退出动画。如下:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
if (keyCode == KeyEvent.KEYCODE_BACK)
mSlideCloseLayout.layoutExitAnim(1000, false);
return true;
else
return super.onKeyDown(keyCode, event);
4 Activity样式设置:
<style name="SlideCloseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>
AndroidManifest.xml:
<activity android:name=".activity.SlideCloseLayoutActivity"
android:theme="@style/SlideCloseTheme" />
至此,整个SlideCloseLayout的实现和使用就介绍完,有不好的地方欢迎大神指正!
源码:https://github.com/xingxing-yan/SlideCloseLayout
以上是关于SlideCloseLayout—仿头条多图预览的页面关闭效果的主要内容,如果未能解决你的问题,请参考以下文章