仿抖音上下滑动分页视频(1),2024年最新蚂蚁测试开发三面

page.setAlpha(1 - position);
// Counteract the default slide transition
page.setTranslationX(pageWidth * -position);
page.setTranslationY(0);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
page.setAlpha(0);
}
}
}
}

/**

  • 交换x轴和y轴的移动距离
  • @param event 获取事件类型的封装类MotionEvent
    */
    private MotionEvent swapXY(MotionEvent event) {
    //获取宽高
    float width = getWidth();
    float height = getHeight();
    //将Y轴的移动距离转变成X轴的移动距离
    float swappedX = (event.getY() / height) * width;
    //将X轴的移动距离转变成Y轴的移动距离
    float swappedY = (event.getX() / width) * height;
    //重设event的位置
    event.setLocation(swappedX, swappedY);
    return event;
    }

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mRecentTouchTime = System.currentTimeMillis();
if (getCurrentItem() == 0 && getChildCount() == 0) {
return false;
}
if (isVertical) {
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev);
// return touch coordinates to original reference frame for any child views
return intercepted;
} else {
return super.onInterceptTouchEvent(ev);
}
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
if (getCurrentItem() == 0 && getChildCount() == 0) {
return false;
}
if (isVertical) {
return super.onTouchEvent(swapXY(ev));
} else {
return super.onTouchEvent(ev);
}
}
}

3.2 ViewPager和Fragment
  • 采用了ViewPager+FragmentStatePagerAdapter+Fragment来处理。为何选择使用FragmentStatePagerAdapter,主要是因为使用 FragmentStatePagerAdapter更省内存,但是销毁后新建也是需要时间的。一般情况下,如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。关于PagerAdapter的深度解析,可以我这篇文章:PagerAdapter深度解析和实践优化

  • 在activity中的代码如下所示

private void initViewPager() {
List list = new ArrayList<>();
ArrayList fragments = new ArrayList<>();
for (int a = 0; a< DataProvider.VideoPlayerList.length ; a++){
Video video = new Video(DataProvider.VideoPlayerTitle[a],
10,“”,DataProvider.VideoPlayerList[a]);
list.add(video);
fragments.add(VideoFragment.newInstant(DataProvider.VideoPlayerList[a]));
}
vp.setOffscreenPageLimit(1);
vp.setCurrentItem(0);
vp.setOrientation(DirectionalViewPager.VERTICAL);
FragmentManager supportFragmentManager = getSupportFragmentManager();
MyPagerAdapter myPagerAdapter = new MyPagerAdapter(fragments, supportFragmentManager);
vp.setAdapter(myPagerAdapter);
}

class MyPagerAdapter extends FragmentStatePagerAdapter{

private ArrayList list;

public MyPagerAdapter(ArrayList list , FragmentManager fm){
super(fm);
this.list = list;
}

@Override
public Fragment getItem(int i) {
return list.get(i);
}

@Override
public int getCount() {
return list!=null ? list.size() : 0;
}
}

  • 那么在fragment中如何处理呢?关于视频播放器,这里可以看我封装的库,视频lib

public class VideoFragment extends Fragment{

public VideoPlayer videoPlayer;
private String url;
private int index;

@Override
public void onStop() {
super.onStop();
VideoPlayerManager.instance().releaseVideoPlayer();
}

public static Fragment newInstant(String url){
VideoFragment videoFragment = new VideoFragment();
Bundle bundle = new Bundle();
bundle.putString(“url”,url);
videoFragment.setArguments(bundle);
return videoFragment;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle arguments = getArguments();
if (arguments != null) {
url = arguments.getString(“url”);
}
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_video, container, false);
return view;
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
videoPlayer = view.findViewById(R.id.video_player);
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(“初始化操作”,“------”+index++);
VideoPlayerController controller = new VideoPlayerController(getActivity());
videoPlayer.setUp(url,null);
videoPlayer.setPlayerType(ConstantKeys.IjkPlayerType.TYPE_IJK);
videoPlayer.setController(controller);
ImageUtils.loadImgByPicasso(getActivity(),“”,
R.drawable.image_default,controller.imageView());
}
}

3.3 修改滑动距离翻页
  • 需求要求必须手动触摸滑动超过1/2的时候松开可以滑动下一页,没有超过1/2返回原页,首先肯定是重写viewpager,只能从源码下手。经过分析,源码滑动的逻辑处理在此处,truncator的属性代表判断的比例值!

  • 这个方法会在切页的时候重定向Page,比如从第一个页面滑动,结果没有滑动到第二个页面,而是又返回到第一个页面,那个这个page会有重定向的功能

private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
int targetPage;
if (Math.abs(deltaX) > this.mFlingDistance && Math.abs(velocity) > this.mMinimumVelocity) {
targetPage = velocity > 0 ? currentPage : currentPage + 1;
} else {
float truncator = currentPage >= this.mCurItem ? 0.4F : 0.6F;
targetPage = currentPage + (int)(pageOffset + truncator);
}

if (this.mItems.size() > 0) {
ViewPager.ItemInfo firstItem = (ViewPager.ItemInfo)this.mItems.get(0);
ViewPager.ItemInfo lastItem = (ViewPager.ItemInfo)this.mItems.get(this.mItems.size() - 1);
targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
}

return targetPage;
}

  • determineTargetPage这个方法就是计算接下来要滑到哪一页。这个方法调用是在MotionEvent.ACTION_UP这个事件下,先说下参数意思:

  • currentPage:当前ViewPager显示的页面

  • pageOffset:用户滑动的页面偏移量

  • velocity: 滑动速率

  • deltaX: X方向移动的距离

  • 进行debug调试之后,发现问题就在0.4f和0.6f这个参数上。分析得出:0.6f表示用户滑动能够翻页的偏移量,所以不难理解,为啥要滑动半屏或者以上了。

  • 也可以修改Touch事件

  • 控制ViewPager的Touch事件,这个基本是万能的,毕竟是从根源上入手的。你可以在onTouchEvent和onInterceptTouchEvent中做逻辑的判断。但是比较麻烦。

3.4 修改滑动速度
  • 使用viewPager进行滑动时,如果通过手指滑动来进行的话,可以根据手指滑动的距离来实现,但是如果通过setCurrentItem函数来实现的话,则会发现直接闪过去的,会出现一下刷屏。想要通过使用setCurrentItem函数来进行viewpager的滑动,并且需要有过度滑动的动画,那么,该如何做呢?

  • 具体可以分析setCurrentItem源码的逻辑,然后会看到scrollToItem方法,这个特别重要,主要是处理滚动过程中的逻辑。最主要关心的也是smoothScrollTo函数,这个函数中,可以看到具体执行滑动的其实就一句话,就是mScroller.startScroll(sx,sy,dx,dy,duration),则可以看到,是mScroller这个对象进行滑动的。那么想要改变它的属性,则可以通过反射来实现。

  • 代码如下所示,如果是手指触摸滑动,则可以加快一点滑动速率,当然滑动持续时间你可以自己设置。通过自己自定义滑动的时间,就可以控制滑动的速度。

@TargetApi(Build.VERSION_CODES.KITKAT)
public void setAnimationDuration(final int during){
try {
// viewPager平移动画事件
Field mField = ViewPager.class.getDeclaredField(“mScroller”);
mField.setAccessible(true);
// 动画效果与ViewPager的一致
Interpolator interpolator = new Interpolator() {
@Override
public float getInterpolation(float t) {
t -= 1.0f;
return t * t * t * t * t + 1.0f;
}
};
Scroller mScroller = new Scroller(getContext(),interpolator){
final int time = 2000;
@Override
public void startScroll(int x, int y, int dx, int dy, int duration) {
// 如果手工滚动,则加速滚动
if (System.currentTimeMillis() - mRecentTouchTime > time) {
duration = during;
} else {
duration /= 2;
}
super.startScroll(x, y, dx, dy, duration);
}

@Override
public void startScroll(int x, int y, int dx, int dy) {
super.startScroll(x, y, dx, dy,during);
}
};
mField.set(this, mScroller);
} catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
e.printStackTrace();
}
}

04.用RecyclerView实现

4.1 自定义LayoutManager
  • 自定义LayoutManager,并且继承LinearLayoutManager,这样就得到一个可以水平排向或者竖向排向的布局策略。如果你接触过SnapHelper应该了解一下LinearSnapHelper和PagerSnapHelper这两个子类类,LinearSnapHelper可以实现让列表的Item居中显示的效果,PagerSnapHelper就可以做到一次滚动一个item显示的效果。

  • 重写onChildViewAttachedToWindow方法,在RecyclerView中,当Item添加进来了调用这个方法。这个方法相当于是把view添加到window时候调用的,也就是说它比draw方法先执行,可以做一些初始化相关的操作。

/**

  • 该方法必须调用
  • @param recyclerView recyclerView
    */
    @Override
    public void onAttachedToWindow(RecyclerView recyclerView) {
    if (recyclerView == null) {
    throw new IllegalArgumentException(“The attach RecycleView must not null!!”);
    }
    super.onAttachedToWindow(recyclerView);
    this.mRecyclerView = recyclerView;
    if (mPagerSnapHelper==null){
    init();
    }
    mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
    mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);
    }
4.2 添加滑动监听
  • 涉及到一次滑动一页视频,那么肯定会有视频初始化和释放的功能。那么思考一下哪里来开始播放视频和在哪里释放视频?不要着急,要监听滑动到哪页,需要我们重写onScrollStateChanged()函数,这里面有三种状态:SCROLL_STATE_IDLE(空闲),SCROLL_STATE_DRAGGING(拖动),SCROLL_STATE_SETTLING(要移动到最后位置时)。

  • 我们需要的就是RecyclerView停止时的状态,我们就可以拿到这个View的Position,注意这里还有一个问题,当你通过这个position去拿Item会报错,这里涉及到RecyclerView的缓存机制,自己去脑补~~。打印Log,你会发现RecyclerView.getChildCount()一直为1或者会出现为2的情况。来实现一个接口然后通过接口把状态传递出去。

  • 自定义监听listener事件

public interface OnPagerListener {

/**

  • 初始化完成
    */
    void onInitComplete();

/**

  • 释放的监听
  • @param isNext 是否下一个
  • @param position 索引
    */
    void onPageRelease(boolean isNext,int position);

/***

  • 选中的监听以及判断是否滑动到底部

  • @param position 索引

  • @param isBottom 是否到了底部
    */
    void onPageSelected(int position,boolean isBottom);
    }

  • 获取到RecyclerView空闲时选中的Item,重写LinearLayoutManager的onScrollStateChanged方法

/**

  • 滑动状态的改变
  • 缓慢拖拽-> SCROLL_STATE_DRAGGING
  • 快速滚动-> SCROLL_STATE_SETTLING
  • 空闲状态-> SCROLL_STATE_IDLE
  • @param state 状态
    */
    @Override
    public void onScrollStateChanged(int state) {
    switch (state) {
    case RecyclerView.SCROLL_STATE_IDLE:
    View viewIdle = mPagerSnapHelper.findSnapView(this);
    int positionIdle = 0;
    if (viewIdle != null) {
    positionIdle = getPosition(viewIdle);
    }
    if (mOnViewPagerListener != null && getChildCount() == 1) {
    mOnViewPagerListener.onPageSelected(positionIdle,
    positionIdle == getItemCount() - 1);
    }
    break;
    case RecyclerView.SCROLL_STATE_DRAGGING:
    View viewDrag = mPagerSnapHelper.findSnapView(this);
    if (viewDrag != null) {
    int positionDrag = getPosition(viewDrag);
    }
    break;
    case RecyclerView.SCROLL_STATE_SETTLING:
    View viewSettling = mPagerSnapHelper.findSnapView(this);
    if (viewSettling != null) {
    int positionSettling = getPosition(viewSettling);
    }
    break;
    default:
    break;
    }
    }
4.3 监听页面是否滚动
  • 这里有两个方法scrollHorizontallyBy()和scrollVerticallyBy()可以拿到滑动偏移量,可以判断滑动方向。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。

通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
id 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

[外链图片转存中…(img-RQwRvLe9-1712807640367)]

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

[外链图片转存中…(img-wPa5toZG-1712807640368)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-MNiZduaT-1712807640368)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值