仿抖音上下滑动分页视频,2024年阿里Android岗面试必问-程序员宅基地

技术标签: 2024年程序员学习  面试  音视频  android  

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()可以拿到滑动偏移量,可以判断滑动方向。

/**

  • 监听竖直方向的相对偏移量
  • @param dy y轴滚动值
  • @param recycler recycler
  • @param state state滚动状态
  • @return int值
    */
    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
    if (getChildCount() == 0 || dy == 0) {
    return 0;
    }
    this.mDrift = dy;
    return super.scrollVerticallyBy(dy, recycler, state);
    }

/**

  • 监听水平方向的相对偏移量
  • @param dx x轴滚动值
  • @param recycler recycler
  • @param state state滚动状态
  • @return int值
    */
    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
    if (getChildCount() == 0 || dx == 0) {
    return 0;
    }
    this.mDrift = dx;
    return super.scrollHorizontallyBy(dx, recycler, state);
    }
4.4 attach和Detached
  • 列表的选中监听好了,我们就看看什么时候释放视频的资源,第二步中的三种状态,去打印getChildCount()的日志,你会发现getChildCount()在SCROLL_STATE_DRAGGING会为1,SCROLL_STATE_SETTLING为2,SCROLL_STATE_IDLE有时为1,有时为2,还是RecyclerView的缓存机制O(∩∩)O,这里不会去赘述缓存机制,要做的是要知道在什么时候去做释放视频的操作,还要分清是释放上一页还是下一页。

private RecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener =
new RecyclerView.OnChildAttachStateChangeListener() {
/**

  • 第一次进入界面的监听,可以做初始化方面的操作
  • @param view view
    */
    @Override
    public void onChildViewAttachedToWindow(@NonNull View view) {
    if (mOnViewPagerListener != null && getChildCount() == 1) {
    mOnViewPagerListener.onInitComplete();
    }
    }

/**

  • 页面销毁的时候调用该方法,可以做销毁方面的操作

  • @param view view
    */
    @Override
    public void onChildViewDetachedFromWindow(@NonNull View view) {
    if (mDrift >= 0){
    if (mOnViewPagerListener != null) {
    mOnViewPagerListener.onPageRelease(true , getPosition(view));
    }
    }else {
    if (mOnViewPagerListener != null) {
    mOnViewPagerListener.onPageRelease(false , getPosition(view));
    }
    }
    }
    };

  • 哪里添加该listener监听事件,如下所示。这里注意需要在页面销毁的时候移除listener监听事件。

/**

  • attach到window窗口时,该方法必须调用
  • @param recyclerView recyclerView
    */
    @Override
    public void onAttachedToWindow(RecyclerView recyclerView) {
    //这里省略部分代码
    mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);
    }

/**

  • 销毁的时候调用该方法,需要移除监听事件
  • @param view view
  • @param recycler recycler

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

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

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

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

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

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

最后

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

[外链图片转存中…(img-xlboJWlA-1712807670696)]

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

[外链图片转存中…(img-Dm1X3Cxo-1712807670696)]

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/HUAXIAL/article/details/137633535

智能推荐

Extjs多个tab页面——panel层叠_extjs写一个panel,多个ta-程序员宅基地

文章浏览阅读4.8k次。Extjs多个tab页面——panel层叠_extjs写一个panel,多个ta

搭建流媒体推流/拉流服务(RTMP/RTSP/HLS/HTTP-FLV)_rtmp拉流地址-程序员宅基地

文章浏览阅读1.9w次,点赞21次,收藏110次。搭建流媒体服务,将rtsp或者rtmp视频流转换为H5可以直接播放的视频。用在直播、安防监控、视频点播、屏幕画面转播等_rtmp拉流地址

【天梯赛】L2-010. 排座位(并查集)_hive排座位-程序员宅基地

文章浏览阅读292次。布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。输入格式:输入第一行给出3个正整数:N(<= 100),即前来参宴的宾客总人数,则这些人从1到N编号;M为已知两两宾客之间的关系数;K为查询的条数。随后M行,每行给出一对宾客之间的关系,格式为:“宾客..._hive排座位

java中nextToken,Java StringTokenizer nextToken()用法及代碼示例-程序员宅基地

文章浏览阅读1.5k次。StringTokenizer類的nextToken()方法用於從此StringTokenizer依次返回下一個標記。用法:public String nextToken()參數:該方法不帶任何參數。返回值:該方法返回字符串令牌化程序行中存在的下一個令牌。下麵的程序說明StringTokenizer的nextToken()方法的用法:示例1:// Java code to illustrate n..._next token的用法

如何自动校正系统时间_timesync 自动-程序员宅基地

文章浏览阅读268次。网上一些校正系统时间的方法,大多需要手动操作,有没有什么工具可以自动完成相关的操作呢? 可以试试NetTime工具(http://www.timesynctool.com/).安装该工具时会创建一个自启动服务,以后每次启动系统,它都会随之启动并自动校正系统时间。_timesync 自动

CSS:如何清除a标签之间的默认留白间距_css处理手机自动留白安全距离-程序员宅基地

文章浏览阅读2.3k次。即使我们使用了类似 *{margin: 0;padding: 0;} 这样的代码重置了浏览器默认样式,也会发现类似标签这种inline-block元素,它们之间也还存在着间距。demo:默认情况123456789101112131415_css处理手机自动留白安全距离

随便推点

maven打包错误: Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources on-程序员宅基地

文章浏览阅读601次。问题截图:解决方案:在pom.xml文件中添加: <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.4.3</version> _failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.3.0

ArduPilot开源飞控之do_failsafe_action_开源飞控解锁-程序员宅基地

文章浏览阅读310次。之前在[ArduPilot飞控之FAILSAFE机制](https://blog.csdn.net/lida2003/article/details/132191018)中,针对Ardupilot的`FAILSAFE`机制进行了相对完整的介绍。本章节将从代码的角度来看`FAILSAFE`代码触发入口,以及对应的应对策略。_开源飞控解锁

运维工作反思总结_运维工作汇报不足-程序员宅基地

文章浏览阅读3.3k次。软件项目的生命周期分为六个阶段:项目前期、项目启动、项目蓝图、项目实施、项目验收、项目运维。其中项目运维是项目中的重要环节。针对本次客户反馈工作进行反思总结。_运维工作汇报不足

Linux系统中的日志服务管理_linux系统日志服务-程序员宅基地

文章浏览阅读1.1k次。目录1.日志介绍1.1 什么是日志1.2 Linux系统使用的两种系统日志服务1.3 ELK2.rsyslog的管理2.1 系统日志术语1.2 实验1.日志介绍1.1 什么是日志网络设备、系统及服务程序等,在运作时都会产生一个叫log的事件记录;每一行日志都记载着日期、时间、使用者及动作等相关操作的描述日志记录的内容包括: 历史事件:时间,地点,人物,事件 日志级别:事件的关键性程度,Loglevel 1.2 Linux系统使用的两种系统日_linux系统日志服务

linux 上编译arm64,Arch Linux 搭建 arm64/AArch64 交叉编译环境-程序员宅基地

文章浏览阅读3.2k次。Arch Linux 搭建 arm64/AArch64 交叉编译环境0. 系统环境系统:Arch Linux: 成文时间最新工具版本:Binutils:2.34-1GCC:9.2.1+20200130-2GNUMake:4.31. 编译交叉编译工具链此部分参考博客.交叉编译工具链需要用到至少七个工具(库):Binutilshttps://www.gnu.org/software/binutils/..._linux编译arm64的库

Pytorch分布式训练/多卡训练(二) —— Data Parallel并行(DDP)(2.2)(代码示例)(BN同步&主卡保存&梯度累加&多卡测试inference&随机种子seed)_model = ddp(model, device_ids=[local_rank], output-程序员宅基地

文章浏览阅读6.1k次,点赞11次,收藏50次。DDP的使用非常简单,因为它不需要修改你网络的配置。其精髓只有一句话model = DistributedDataPrallel(model, device_ids=[local_rank], output_device=local_rank) 原本的model就是你的PyTorch模型,新得到的model,就是你的DDP模型。最重要的是,后续的模型关于前向传播、后向传播的用法,和原来完全一致!DDP把分布式训练的细节都隐藏起来了,不需要暴露给用户,非常优雅!初..........._model = ddp(model, device_ids=[local_rank], output_device=local_rank)

推荐文章

热门文章

相关标签