前言:
要问最近什么App最火?那必须是抖音啊!火的不要不要的!抖音的界面在App中算是独树一帜,一进去就是全屏的视频播放界面,上下滑可以切换视频,左滑进入故事相机界面,右滑进入个人中心。这样的效果在Android中应该如何实现呢?
我想到了ViewPager,但是ViewPager只支持左右滑动,上下滑动该怎么实现?我可以不可以把它翻转一下呢?答案是肯定的!OK,我们进入正文。
github地址:https://github.com/kb18519142009/LikeDouyin
大家喜欢的话,就给个star^_^,有问题或者建议,可以直接提issues,也可以在博客下面给我留言。谢谢~
效果图:
正文:
一、实现可以上下滑动的ViewPager
要实现一个可上下滑动的ViewPager,我们需要做两件事:
1、翻转ViewPager
ViewPager中有setPageTransformer(boolean reverseDrawingOrder,@Nullable PageTransformer transformer)这个方法,需要在自定义view的构造中调用。它是我们翻转ViewPager的关键!
接受两个参数,我们将第一个boolean值设置成true,第二个PageTransformer 是一个接口,需要我们自己实现,代码如下:
private class VerticalPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// 当前页的上一页
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
view.setAlpha(1);
// 抵消默认幻灯片过渡
view.setTranslationX(view.getWidth() * -position);
//设置从上滑动到Y位置
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else { // (1,+Infinity]
// 当前页的下一页
view.setAlpha(0);
}
}
}
2、翻转滑动事件
我们通过换算将用户滑动事件的X、Y坐标颠倒,代码如下:
/**
* 交换触摸事件的X和Y坐标
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
然后在onInterceptTouchEvent()和onTouchEvent()中将颠倒后的MotionEvent对象返回即可!
3、代码实现
到这里一个可上下滑动的ViewPager就实现啦,在来看看整个自定义View的代码:
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// 最重要的设置,将viewpager翻转
setPageTransformer(true, new VerticalPageTransformer());
// 设置去掉滑到最左或最右时的滑动效果
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// 当前页的上一页
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
view.setAlpha(1);
// 抵消默认幻灯片过渡
view.setTranslationX(view.getWidth() * -position);
//设置从上滑动到Y位置
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else { // (1,+Infinity]
// 当前页的下一页
view.setAlpha(0);
}
}
}
/**
* 交换触摸事件的X和Y坐标
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev);
return intercepted; //为所有子视图返回触摸的原始坐标
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
二、可左右滑动的ViewPager
大家肯定会说ViewPager本来就是左右滑动的啊!先别着急,由于我想用一个可左右滑动的ViewPager去嵌套一个可上下滑动的ViewPager,在用原生ViewPager嵌套后发现出现事件冲突,左右滑动并不是很流畅,所以就对ViewPager进行了小小的改动,我们直接看代码:
public class HorizontalViewPager extends ViewPager {
private GestureDetector xScrollDetector;
public HorizontalViewPager(Context context) {
super(context);
init();
}
public HorizontalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
xScrollDetector = new GestureDetector(getContext(), new XScrollDetector());
}
private void init() {
setOverScrollMode(OVER_SCROLL_NEVER); //设置去掉滑到最左或最右时的滑动效果
}
private class XScrollDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// 横向滑动大于纵向滑动距离的2.5倍时返回true,更好的解决某些事件冲突问题
return Math.abs(distanceX) > Math.abs(distanceY) * 2.5;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (xScrollDetector.onTouchEvent(ev)) {
super.onInterceptTouchEvent(ev);
return true; //如果是横向滑动事件,就拦截事件,不再向下传递,自己消费事件
}
return super.onInterceptTouchEvent(ev);
}
}
三、给可左右滑动的ViewPager嵌套一个可上下滑动ViewPager
这一步其实就是把横向滑动的ViewPager和纵向滑动的ViewPager组合起来,先在Activity中放一个横向滑动的ViewPager,里边放三个Fragment,左右两边相当于是故事相机和个人中心界面,中间的Fragment放一个纵向滑动的ViewPager,相当于视频播放界面。
Demo下载:https://github.com/kb18519142009/LikeDouyin
Ok!搞定了!就这样我们很轻松的实现了抖音首页的效果!我知道这肯定不是最佳的实现效果,也许还存在一些问题,欢迎大家指出,如果有更好的方式也可以留言告诉我~