热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

android自定义视频列表,Android仿京东天猫列表页播视频看这一篇就足够了

阅读本文解决什么问题?解决android滑动列表页自动播视频中的一些技术难点。助力更好的实现类似需求。不涉及到播放器的具体编解码技术,因为各家用的播放器

阅读本文解决什么问题?

解决android 滑动列表页自动播视频中的一些技术难点。助力更好的实现类似需求。不涉及到播放器的具体编解码技术,因为各家用的播放器可能都不一样(其实是我不会~)

何时播视频最合适?

建议在滑动停止的时候播视频,还在滑动的时候建议不播。目前大厂的app基本上都是基于这个思路来做。 这么做的主要原因有两点:

就如同我们平时做listview或者RecyclerView列表页优化的时候,考虑到图片的加载涉及到io,还有少部分的decode操作,所以我们都会在滑动停止或者是滑动速度变低的时候才加载图片,降低cpu的负载和内存抖动来达到提高页面滑动流畅性的目的,所以播视频也是一样的,在列表页停止滑动的时候才播放视频是可以大大提高页面流畅性的。

即使你的播放器是自研的,但是依旧不建议在列表页滑动的时候进行视频播放,考虑如下场景:A视频正在播放,

此时进行滑动,B视频到了播放区域开始播B视频,此时A从播视频的状态切到停止播放视频的视频封面图状态,B

视频从视频封面图状态切到播放状态(甚至还有黑底的loading的状态),可以想象滑动速度一旦变快,视觉上的效果

就会非常难受,那种一顿顿的效果,因为你列表页中的item几乎全在同一时刻切换图片状态。

所以要在哪个地方开始播视频?

以RecyclerView 为例:

9494f5ea8331633467b865e61c064861.png

所以我们其实只要在这个回调中进行

50f00f99d16167a726c95f0c6ddd470e.png

这个case 的条件 就代表列表页滑动停止。 这就是我们播视频的时机了

如何合理规划播视频的区域?

我们以最流行的做法:符合人类视觉感知的,选择 从上到下列表页中第一个完整展示视频区域的 item 进行播放

有人说我们直接用findFirstCompletelyVisibleItemPosition方法不就行了?其实这样是不完美的,因为我们列表页中的item 并不只是一个单一的视频播放区域,他还有标题,描述,阅读数等等**。你的视频播放区域其实只占你整个item的一部分**

所以我们就要计算出来 视频区域完整显示 这个条件。 代码如下:

case SCROLL_STATE_IDLE:

//取第一个展示出来的item

int firstPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();

//最后一个展示出来的item

int lastPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();

//开始遍历

for (int i = firstPosition; i <= lastPosition; i++) {

//取出这个列表页中的item

View itemView = recyclerView.getLayoutManager().findViewByPosition(i);

//一定要先判空 因为有时上面这个函数真的会返回null

//然后接着判断 是不是有视频播放组件 比如有的列表页除了有视频item还有纯图片的item

//所以要判定这个条件

if (itemView != null && itemView.findViewById(R.id.video_player) != null) {

VideoPlayer videoPlayer = itemView.findViewById(R.id.video_player);

if (isCanBePlayedByRect(videoPlayer)) {

safePlayVideoMethod(videoPlayer);

break;

}

}

}

private boolean isCanBePlayedByRect(VideoPlayer videoPlayer) {

Rect rect = new Rect();

//这里就可以取出来视频播放区域的坐标轴了

vivoSpaceVideoPlayer.getLocalVisibleRect(rect);

int videoHeight = vivoSpaceVideoPlayer.getHeight();

//符合这个条件就意味着 整个视频播放区域 都是完整的呈现在视频中的

final boolean playFlag = (rect.top == 0 && rect.bottom == videoHeight)

return playFlag;

}

达成这个条件 我们基本的算法就写好了,但是这样仍旧不完美。

还有其他条件需要判定吗?

考虑另外一种复杂的情况,比如说看下面这张图:

6e4468b720c803cb4c4d81fa7210205d.png

我们可以看到列表页中的最后2个item其实 是已经完全露出屏幕的,他是符合我们上一小节的判定条件的。

但是视觉上我们仍然是觉得 这2个视频播放区域是不完整的,因为被底部的tab 遮盖住了。

甚至有的用户会把虚拟键给调出来。如下图74a49683380732ba57d664b04dfd2c96.png

这样我们的item展示的区域被遮盖的就更多了。用前面一小节的算法已经不够了。

此时我们需要更进一步,针对前面的算法做一定的修改。有人会说,我们直接用底部tab的高度减一下不就行了么?

这样其实也可以,但是这样做 不太优雅,原因有2:

如果这个tab以后高度修改了,你能保证每次别人的修改你都知道么?

除了有底部tab还有虚拟键的高度啊,这样一算 不是太复杂了么?

我们这里可以用另外一种比较好的方法规避上述的问题:

直接取底部tab的top坐标(针对整个坐标轴而言)。

我们可以在activity的第一帧绘制完毕以后 取一下这个坐标:

if (mTabWidget != null && MAIN_PAGE_BOTTOM_TAB_RECT_TOP == 0) {

Rect rect = new Rect();

//注意 我们这里 使用的是getGlobalVisibleRect 而不是getLocal了

//区别就是 global取得就是 针对全屏幕的坐标轴

mTabWidget.getGlobalVisibleRect(rect);

MAIN_PAGE_BOTTOM_TAB_RECT_TOP = rect.top;

}

到这里我们就拿到了 底部tab 这个view的 top的坐标值了。所以剩下的条件我们只要增加一条

视频播放组件的 全屏bottom的值 要大于 我们这个tab的top的值。 只有这样才能保证 我们视频的view 是没有被遮盖的

private boolean isCanBePlayedByRect(VideoPlayer videoPlayer) {

Rect rect = new Rect();

vivoSpaceVideoPlayer.getLocalVisibleRect(rect);

Rect gRect = new Rect();

//把视频view的全局 bottom坐标 也取出来 与tab的top坐标做对比

vivoSpaceVideoPlayer.getGlobalVisibleRect(gRect);

int videoHeight = vivoSpaceVideoPlayer.getHeight();

final boolean playFlag = (rect.top == 0 && rect.bottom == videoHeight && gRect.bottom

return playFlag;

}

什么时候停止播放?

有人说 我们用4ac13f1293e481dd95e41ccb4f9debd9.png

这个回调 会简单一些?

其实这样也是不完美的,还是前面那个问题,有些item 他就是有其他要素要展示, 就会出现 视频区域已经看不到了,被滑走了,

此时理应停止播放,但是因为还有比如视频阅读数等东西恰好在列表可是区域范围之内 导致这个item无法进入这个回调。

为了解决这个问题,我们使用如下方案:

在列表页滑动的时候,判定视频区域是否全部在列表页之外,人已经看不到了,符合这个条件就停止播放视频

代码如下:

int position = 0;

if (dy > 0) {

//dy>0 代表 item 从下往上 被划出屏幕的 所以我们只需要判定最上面一个可视的item即可

position = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition();

} else if (dy <0) {

//dy>0 代表 item 从上往下 被划出屏幕的 所以我们只需要判定最下面一个可视的item即可

position = ((LinearLayoutManager) mRecyclerView.getLayoutManager()).findLastVisibleItemPosition();

}

View itemView = mRecyclerView.getLayoutManager().findViewByPosition(position);

if (itemView != null) {

VideoPlayer videoPlayer = itemView.findViewById(R.id.video_player);

if (videoPlayer != null) {

Rect rect = new Rect();

Rect gRect = new Rect();

vivoSpaceVideoPlayer.getLocalVisibleRect(rect);

vivoSpaceVideoPlayer.getGlobalVisibleRect(gRect);

//item 从下往上划出屏幕 的判定条件

final boolean stopFlagForDownToUp = (dy > 0 && rect.bottom - rect.top <= STOP_FLAG_PX_VALUE);

//item 从上往下划出屏幕的 判定条件 ----有底部tab

final boolean stopFlagForUptoDown1 = (dy <0 && gRect.top > (MAIN_PAGE_BOTTOM_TAB_RECT_TOP - STOP_FLAG_PX_VALUE));

//item 从上往下划出屏幕的 判定条件 ----没有底部tab

final boolean stopFlagForUptoDown2 = (dy <0 && rect.top != 0);

if (stopFlagForDownToUp || stopFlagForUptoDownHasParent || stopFlagForUptoDownNoParent) {

videoPlayer.release();

}

}

}

这里的算法比较简单,我就不一一解释了,唯一要说一下的就是这个

//定义个阈值 停止播放判定使用

private static final int STOP_FLAG_PX_VALUE = 5;

为什么要加一个这个?因为针对全屏坐标而言,有时候滑动快了并不会经过某一个值,所以我们要有一定的余量。

例如 我们条件是 当我们的视频区域的top坐标(滑动的时候会从例如 1900开始递增) 大于 tab的top坐标(假设这个值是2100)的时候 就判定被遮盖了 停止滑动,

并不代表视频区域的top坐标是 1900 1901 1902 这样1px 1px的递增到2100的。 有时候他会忽略掉,比如某些手机经常出现1997 1998 1999 2102 等等 情况,所以这里我们要一定的余量。

确保 视频一定会被停止播放。

怎么确保列表页第一次被渲染的时候就自动播放?

当我们网络数据回来以后 ,我们会把值set到RecyclerView 里面,然后notify,此时界面就有展示了,但是这个时候

如果用户没有滑动,又恰好第一屏的item有一个是视频,那我们怎么触发这种条件的自动播放呢?

其实RecyclerView有个特性就是 第一次渲染完毕以后 onScrolled会被执行一次,所以我们只要在这里做个标志位

然后去播放一次 就行了。

@Override

public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {

if (!mRvInitFlag) {

playFirstVideoView();

mRvInitFlag = true;

return;

}

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[Android仿京东天猫列表页播视频看这一篇就足够了]http://www.zyiz.net/tech/detail-113729.html



推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
author-avatar
Coco李可儿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有