SlideCloseLayout—仿头条多图预览的页面关闭效果
来源:互联网 发布:mui vue.js 集成 编辑:程序博客网 时间:2024/06/02 13:16
序
最近同事问头条多图预览界面的关闭效果怎么实现,之前没有怎么注意,查看之后,顿时发现这个关闭效果还挺有意思,于是决定弄上一弄!那就撸起袖子开始吧!
注:此项目是参考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—仿头条多图预览的页面关闭效果
- Android 仿微信侧滑关闭页面效果
- jquery 预览图片的效果
- 实时自定义预览的效果
- 测试链接的预览效果
- IOS 图片的预览效果
- 关闭TV的效果
- JS实现分享页面自动关闭效果
- Android右滑关闭页面效果设计
- html(object)控制ie的打印、刷新、另存、关闭、打开、页面设置、属性、打印预览
- win7 下关闭任务栏的预览窗口
- H5图片预览关闭的优化
- MyEclipse中关闭JSP的预览功能
- MyEclipse中关闭JSP的预览功能
- Eclipse编辑jsp不显示预览效果页面
- js 的简单的图片预览效果
- 上传图片前的预览效果
- Camara 自定义预览效果 PreviewCallBack 的使用
- 单例模式优缺点
- iOS 数组的去重(普通的无序的去重和排序好的去重)
- HttpClient
- 浅谈方正教务模拟登录实现
- 多线程编程
- SlideCloseLayout—仿头条多图预览的页面关闭效果
- 01背包问题
- 363. Max Sum of Rectangle No Larger Than K
- Malaysia Online Casino RM30 Free 50(30 free 50, deposit promotion, free bonus, malaysia online casin
- Java: Integer.toBinaryString(int i) 方法翻译详解
- smtp邮件服务 ipv6 高级网络配置
- [Wondgirl] iOS数组排序
- 1380 没有上司的舞会 树形dp
- 门禁系统