目录
补充:源码地址:https://github.com/JArchie/TiktokDemo
写在前面
各位老铁,我又来啦!既然来了,那肯定又来搞事情啦,话不多说,先上图!
“抖音”都玩过吧,是不是很好玩,我反正是天天刷,作为一个非著名的Android低级攻城狮,虽然技术菜的一匹,但是也经常刷着刷着会思考:咦?这玩意是用哪个控件做的?这个效果是咋实现的啊?由于本人技术水平有限,所以今天咱就先挑个比较简单的来看看是如何实现的,思考再三,我们就拿抖音首页的这个效果来练练手吧,话不多说,开搞!
一、准备工作
我们先不急着写代码,先对抖音的这种效果做一个简单的分析,首先需要明确的是它是可以滑动的,并且可以上滑回去,也可以下滑到下一个,滑动的数量跟随视频的个数而定,到这里其实能实现这种效果的控件就已经被缩小到一个范围内了。初步判定可以使用ViewPager或者是RecyclerView来实现,你细想一下,它实际上就是一个列表啊,每一屏的视频效果就是一个单独的Item,并且它的列表Item的数量可以很大,至少目前你应该没有哪一次是能把抖音滑到底的吧,那最后咱们使用RecyclerView来实现这个效果。
为什么不用ViewPager?我们需要的是每次只加载一屏,ViewPager默认会有预加载机制,并且数据量较大的时候性能表现也是很差的。反之,RecyclerView最好的性能就是只加载一屏幕的Item,并且处理海量数据时性能更优,所以我们选用RecyclerView实现。
基础列表的承载控件我们已经选好了,然后通过上面的效果不难发现,每一屏里面实际上只有一个Item,所以基础的页面布局你应该也知道该怎么做了。然后就是视频播放了,由于这里我们只是仿照实现抖音的主页面效果,最核心的实际上是实现这个RecyclerView滑动的效果,所以代码我这里是尽量考虑简单化,因此视频播放就直接使用的Android原生的VideoView来做的,效果肯定不会多好,如果你对视频播放这块要求比较高的话,可以考虑使用基于ijkplayer实现的一些比较优秀的开源库,再或者能力强的自己基于ffmpeg定制开发播放器。
OK,到这里基本的分析就已经做完了,下面我们就先来实现基础代码吧!
先把需要的图片和视频文件准备好哦,别忘记了,视频这里放在res/raw目录下:
1.1、主页面布局
新建一个TiktokIndexActivity.java,创建布局文件activity_tiktok_layout.xml:
-
<?xml version="1.0" encoding="utf-8"?>
-
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"match_parent">
-
-
<android.support.v7.widget.RecyclerView
-
android:id=
"@+id/mRecycler"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"match_parent"
-
android:background=
"@color/color_01"/>
-
-
<RelativeLayout
-
android:layout_width=
"match_parent"
-
android:layout_height=
"35dp"
-
android:layout_marginTop=
"36dp">
-
-
<LinearLayout
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:layout_centerInParent=
"true"
-
android:orientation=
"horizontal"
-
android:gravity=
"center_vertical">
-
-
<TextView
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:layout_marginRight=
"16dp"
-
android:text=
"南京"
-
android:textColor=
"#f2f2f2"
-
android:textSize=
"18sp"
-
android:textStyle=
"bold" />
-
-
<TextView
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:layout_marginRight=
"16dp"
-
android:text=
"关注"
-
android:textColor=
"#f2f2f2"
-
android:textSize=
"18sp"
-
android:textStyle=
"bold" />
-
-
<TextView
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:text=
"推荐"
-
android:textColor=
"@android:color/white"
-
android:textSize=
"20sp"
-
android:textStyle=
"bold" />
-
-
</LinearLayout>
-
-
<ImageView
-
android:layout_width=
"30dp"
-
android:layout_height=
"30dp"
-
android:layout_alignParentRight=
"true"
-
android:layout_centerVertical=
"true"
-
android:layout_marginRight=
"16dp"
-
android:src=
"@drawable/search_icon"
-
android:tint=
"#f2f2f2" />
-
</RelativeLayout>
-
-
<LinearLayout
-
android:id=
"@+id/mBottomLayout"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"?actionBarSize"
-
android:background=
"@color/color_01"
-
android:layout_alignParentBottom=
"true"
-
android:gravity=
"center_vertical"
-
android:orientation=
"horizontal">
-
-
<TextView
-
android:layout_width=
"0dp"
-
android:layout_height=
"wrap_content"
-
android:layout_weight=
"1"
-
android:gravity=
"center"
-
android:text=
"首页"
-
android:textColor=
"@android:color/white"
-
android:textSize=
"18sp"
-
android:textStyle=
"bold" />
-
-
<TextView
-
android:layout_width=
"0dp"
-
android:layout_height=
"wrap_content"
-
android:layout_weight=
"1"
-
android:gravity=
"center"
-
android:text=
"朋友"
-
android:textColor=
"#f2f2f2"
-
android:textSize=
"17sp"
-
android:textStyle=
"bold" />
-
-
<LinearLayout
-
android:layout_width=
"0dp"
-
android:layout_height=
"match_parent"
-
android:layout_weight=
"1"
-
android:gravity=
"center">
-
<ImageView
-
android:layout_width=
"50dp"
-
android:layout_height=
"30dp"
-
android:scaleType=
"fitCenter"
-
android:src=
"@drawable/icon_add" />
-
</LinearLayout>
-
-
<TextView
-
android:layout_width=
"0dp"
-
android:layout_height=
"wrap_content"
-
android:layout_weight=
"1"
-
android:gravity=
"center"
-
android:text=
"消息"
-
android:textColor=
"#f2f2f2"
-
android:textSize=
"17sp"
-
android:textStyle=
"bold" />
-
-
<TextView
-
android:layout_width=
"0dp"
-
android:layout_height=
"wrap_content"
-
android:layout_weight=
"1"
-
android:gravity=
"center"
-
android:text=
"我"
-
android:textColor=
"#f2f2f2"
-
android:textSize=
"17sp"
-
android:textStyle=
"bold" />
-
</LinearLayout>
-
</RelativeLayout>
1.2、列表Item布局
由于我们的VideoView想要自己设置宽和高,所以这里自定义一个VideoView,重写onMeasure()测量方法:
-
public
class CusVideoView extends VideoView {
-
public CusVideoView(Context context) {
-
super(context);
-
}
-
-
public CusVideoView(Context context, AttributeSet attrs) {
-
super(context, attrs);
-
}
-
-
public CusVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
-
super(context, attrs, defStyleAttr);
-
}
-
-
@Override
-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
int width = getDefaultSize(getWidth(), widthMeasureSpec);
-
int height = getDefaultSize(getHeight(), heightMeasureSpec);
-
setMeasuredDimension(width, height);
-
}
-
}
然后接着来编写每一屏Item的布局文件:item_tiktok_layout.xml
-
<?xml version="1.0" encoding="utf-8"?>
-
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-
xmlns:app=
"http://schemas.android.com/apk/res-auto"
-
xmlns:tools=
"http://schemas.android.com/tools"
-
android:id=
"@+id/mRootView"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"match_parent">
-
-
<com.jarchie.androidui.tiktok.CusVideoView
-
android:id=
"@+id/mVideoView"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"match_parent"
-
android:clickable=
"false"
-
android:focusable=
"false" />
-
-
<ImageView
-
android:id=
"@+id/mThumb"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"match_parent"
-
android:clickable=
"false"
-
android:focusable=
"false"
-
android:scaleType=
"centerCrop"
-
android:visibility=
"visible" />
-
-
<LinearLayout
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:layout_alignParentEnd=
"true"
-
android:layout_centerVertical=
"true"
-
android:layout_marginRight=
"10dp"
-
android:gravity=
"center"
-
android:orientation=
"vertical">
-
-
<RelativeLayout
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content">
-
-
<de.hdodenhof.circleimageview.CircleImageView
-
android:layout_width=
"60dp"
-
android:layout_height=
"60dp"
-
android:layout_alignParentTop=
"true"
-
android:src=
"@drawable/icon_avatar"
-
app:civ_border_color=
"@android:color/white"
-
app:civ_border_width=
"2dp" />
-
-
<ImageView
-
android:layout_width=
"20dp"
-
android:layout_height=
"20dp"
-
android:layout_centerHorizontal=
"true"
-
android:layout_marginTop=
"50dp"
-
android:background=
"@drawable/circle_big_red"
-
android:scaleType=
"centerInside"
-
android:src=
"@drawable/add_icon"
-
android:tint=
"@android:color/white" />
-
</RelativeLayout>
-
-
<TextView
-
android:layout_width=
"50dp"
-
android:layout_height=
"50dp"
-
android:layout_marginTop=
"16dp"
-
android:drawableTop=
"@drawable/heart_icon"
-
android:gravity=
"center"
-
android:text=
"8.88w"
-
android:textColor=
"@android:color/white" />
-
-
<TextView
-
android:layout_width=
"50dp"
-
android:layout_height=
"50dp"
-
android:layout_marginTop=
"16dp"
-
android:drawableTop=
"@drawable/msg_icon"
-
android:gravity=
"center"
-
android:text=
"9.99w"
-
android:textColor=
"@android:color/white" />
-
-
<TextView
-
android:layout_width=
"50dp"
-
android:layout_height=
"50dp"
-
android:layout_marginTop=
"16dp"
-
android:drawableTop=
"@drawable/share_icon"
-
android:gravity=
"center"
-
android:text=
"6.66w"
-
android:textColor=
"@android:color/white" />
-
</LinearLayout>
-
-
<de.hdodenhof.circleimageview.CircleImageView
-
android:layout_width=
"60dp"
-
android:layout_height=
"60dp"
-
android:layout_alignParentEnd=
"true"
-
android:layout_alignParentBottom=
"true"
-
android:layout_marginRight=
"10dp"
-
android:layout_marginBottom=
"60dp"
-
android:src=
"@drawable/header"
-
app:civ_border_color=
"@color/color_01"
-
app:civ_border_width=
"12dp" />
-
-
<LinearLayout
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:layout_alignParentBottom=
"true"
-
android:layout_marginLeft=
"10dp"
-
android:layout_marginBottom=
"60dp"
-
android:orientation=
"vertical">
-
-
<TextView
-
android:id=
"@+id/mTitle"
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:lineSpacingExtra=
"5dp"
-
android:textColor=
"@android:color/white"
-
android:textSize=
"16sp"
-
tools:text=
"测试测试数据哈哈哈哈\n家里几个垃圾了个两个垃圾" />
-
-
<LinearLayout
-
android:layout_width=
"match_parent"
-
android:layout_height=
"wrap_content"
-
android:layout_marginTop=
"10dp"
-
android:orientation=
"horizontal"
-
android:gravity=
"center_vertical">
-
-
<ImageView
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:src=
"@drawable/icon_douyin" />
-
-
<TextView
-
android:id=
"@+id/mMarquee"
-
android:layout_width=
"100dp"
-
android:layout_height=
"wrap_content"
-
android:layout_marginLeft=
"10dp"
-
android:textColor=
"@android:color/white"
-
android:singleLine=
"true"
-
android:ellipsize=
"marquee"
-
android:marqueeRepeatLimit=
"marquee_forever"
-
android:focusable=
"true"
-
android:focusableInTouchMode=
"true"
-
android:textSize=
"14sp"/>
-
</LinearLayout>
-
</LinearLayout>
-
-
<ImageView
-
android:id=
"@+id/mPlay"
-
android:layout_width=
"100dp"
-
android:layout_height=
"100dp"
-
android:layout_centerInParent=
"true"
-
android:alpha=
"0"
-
android:clickable=
"true"
-
android:focusable=
"true"
-
android:src=
"@drawable/play_arrow" />
-
</RelativeLayout>
1.3、列表Item适配器
然后创建列表Item的适配器TiktokAdapter.java:这里视频封面图片我是自己弄了两张图片,效果看上去不大好,有更好的方案的可以留言一起探讨哦!
-
public
class TiktokAdapter extends RecyclerView.Adapter<TiktokAdapter.ViewHolder> {
-
private
int[] videos = {R.raw.v1, R.raw.v2};
-
private
int[] imgs = {R.drawable.fm1, R.drawable.fm2};
-
private List<String> mTitles =
new ArrayList<>();
-
private List<String> mMarqueeList =
new ArrayList<>();
-
private Context mContext;
-
-
public TiktokAdapter(Context context) {
-
this.mContext = context;
-
mTitles.add(
"@乔布奇\nAndroid仿抖音主界面UI效果,\n一起来学习Android开发啊啊啊啊啊\n#Android高级UIAndroid开发");
-
mTitles.add(
"@乔布奇\nAndroid RecyclerView自定义\nLayoutManager的使用方式,仿抖音效果哦");
-
mMarqueeList.add(
"哈哈创作的原声-乔布奇");
-
mMarqueeList.add(
"嘿嘿创作的原声-Jarchie");
-
}
-
-
@NonNull
-
@Override
-
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
-
return
new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_tiktok_layout, viewGroup,
false));
-
}
-
-
@Override
-
public void onBindViewHolder(@NonNull final ViewHolder holder, int pos) {
-
//第一种方式:获取视频第一帧作为封面图片
-
// MediaMetadataRetriever media = new MediaMetadataRetriever();
-
// media.setDataSource(mContext,Uri.parse("android.resource://" + mContext.getPackageName() + "/" + videos[pos % 2]));
-
// holder.mThumb.setImageBitmap(media.getFrameAtTime());
-
//第二种方式:使用固定图片作为封面图片
-
holder.mThumb.setImageResource(imgs[pos %
2]);
-
holder.mVideoView.setVideoURI(Uri.parse(
"android.resource://" + mContext.getPackageName() +
"/" + videos[pos %
2]));
-
holder.mTitle.setText(mTitles.get(pos %
2));
-
holder.mMarquee.setText(mMarqueeList.get(pos %
2));
-
holder.mMarquee.setSelected(
true);
-
}
-
-
@Override
-
public int getItemCount() {
-
return Integer.MAX_VALUE;
-
}
-
-
public
class ViewHolder extends RecyclerView.ViewHolder {
-
RelativeLayout mRootView;
-
ImageView mThumb;
-
ImageView mPlay;
-
TextView mTitle;
-
TextView mMarquee;
-
CusVideoView mVideoView;
-
-
public ViewHolder(@NonNull View itemView) {
-
super(itemView);
-
mRootView = itemView.findViewById(R.id.mRootView);
-
mThumb = itemView.findViewById(R.id.mThumb);
-
mPlay = itemView.findViewById(R.id.mPlay);
-
mVideoView = itemView.findViewById(R.id.mVideoView);
-
mTitle = itemView.findViewById(R.id.mTitle);
-
mMarquee = itemView.findViewById(R.id.mMarquee);
-
}
-
}
-
-
}
二、自定义LayoutManager
我们使用RecyclerView都知道哈,要想让RecylcerView正常工作必须要有两个东西:①、Adapter,负责界面显示适配的,这里我们已经弄好了;②、LayoutManager,告诉列表如何摆放,所以现在想要实现抖音的列表效果的关键就在于这个LayoutManager了,并且普通的LinearLayoutManager是不行的,我们需要自己去实现这个LayoutManger。这里我们取个巧,直接继承LinearLayoutManager来实现自定义布局管理器。
RecyclerView里面有这样一个接口:下面是这个接口的系统源码
-
//这两个方法不是成对出现的,也没有顺序
-
public
interface OnChildAttachStateChangeListener {
-
void onChildViewAttachedToWindow(@NonNull View var1);
-
void onChildViewDetachedFromWindow(@NonNull View var1);
-
}
它里面有两个方法,可以监听列表的Item添加进来和移除出去的两个动作,这是不是就很符合我们现在的使用场景啊,我们的每一屏只有一个Item,并且要在它被添加进来的时候播放视频,在移除时释放掉,所以我们需要实现这个接口。
需要注意的是,这个接口必须在LayoutManager成功进行初始化之后才能监听,所以我们在LayoutManager中重写onAttachedToWindow()方法,在它里面添加这个接口的监听:
-
@Override
-
public void onAttachedToWindow(RecyclerView view) {
-
view.addOnChildAttachStateChangeListener(
this);
-
super.onAttachedToWindow(view);
-
}
完了之后呢,会重写接口中的两个方法,在这两个方法里面我们就可以来实现播放和暂停的操作了。那么这里问题又来了,播放和暂停这两个动作都涉及到一个问题,你是播放上一个视频还是播放下一个视频,因为列表是可以往下滑也可以往上滑的啊,所以我们还得重写另一个监听位移变化的方法:scrollVerticallyBy(),这里dy的值为正数是往上滑,负数是往下滑
-
private
int mDrift;
//位移,用来判断移动方向
-
@Override
-
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
-
this.mDrift = dy;
-
return
super.scrollVerticallyBy(dy, recycler, state);
-
}
OK,这样我们就可以判断是向上滑还是向下滑了,那么上面onChildViewAttachedToWindow()这两个方法就可以写了,在这两个方法中,我们需要把具体的业务逻辑回调到Activity里面去处理,所以这里我们还需要再自定义一个接口OnPageSlideListener :
-
public
interface OnPageSlideListener {
-
//释放的监听
-
void onPageRelease(boolean isNext, int position);
-
-
//选中的监听以及判断是否滑动到底部
-
void onPageSelected(int position, boolean isBottom);
-
}
现在来处理上面的两个回调接口onChildViewAttachedToWindow()和onChildViewDetachedFromWindow():
-
private OnPageSlideListener mOnPageSlideListener;
-
-
//Item添加进来
-
@Override
-
public void onChildViewAttachedToWindow(@NonNull View view) {
-
//播放视频操作,判断将要播放的是上一个视频,还是下一个视频
-
if (mDrift >
0) {
//向上
-
if (mOnPageSlideListener !=
null)
-
mOnPageSlideListener.onPageSelected(getPosition(view),
true);
-
}
else {
//向下
-
if (mOnPageSlideListener !=
null)
-
mOnPageSlideListener.onPageSelected(getPosition(view),
false);
-
}
-
}
-
-
//Item移除出去
-
@Override
-
public void onChildViewDetachedFromWindow(@NonNull View view) {
-
//暂停播放操作
-
if (mDrift >=
0) {
-
if (mOnPageSlideListener !=
null)
-
mOnPageSlideListener.onPageRelease(
true, getPosition(view));
-
}
else {
-
if (mOnPageSlideListener !=
null)
-
mOnPageSlideListener.onPageRelease(
false, getPosition(view));
-
}
-
}
既然这里是通过接口的方式回调到Activity中实现,所以我们还得给它设置一个接口:
-
//接口注入
-
public void setOnPageSlideListener(OnPageSlideListener mOnViewPagerListener) {
-
this.mOnPageSlideListener = mOnViewPagerListener;
-
}
写到这里,当然还不行,此时如果你去跑你的项目,你会发现它还是会像普通的RecyclerView一样随意的滑动,所以我们还需要一个类的帮助才行:PagerSnapHelper,它可以实现让RecyclerView像ViewPager一样的滑动效果,这里我们给它绑定上RecyclerView:
-
private PagerSnapHelper mPagerSnapHelper;
-
-
public CustomLayoutManager(Context context, int orientation, boolean reverseLayout) {
-
super(context, orientation, reverseLayout);
-
mPagerSnapHelper =
new PagerSnapHelper();
-
}
-
-
@Override
-
public void onAttachedToWindow(RecyclerView view) {
-
view.addOnChildAttachStateChangeListener(
this);
-
mPagerSnapHelper.attachToRecyclerView(view);
-
super.onAttachedToWindow(view);
-
}
推荐阅读:让你明明白白的使用RecyclerView——SnapHelper详解
到这里已经可以实现类似ViewPager滑动的效果了,但是我们还需要重写一个方法,不然的话向下滑动播放的时候会有Bug:因为onChildViewAttachedToWindow()和onChildViewDetachedFromWindow()这两个方法并不是成对出现的,它们二者之间也是没有顺序的,因此这里我们再来监听一下滑动状态的改变:判断已经处理完成即手指抬起时的状态
-
@Override
-
public void onScrollStateChanged(int state) {
-
switch (state) {
-
case RecyclerView.SCROLL_STATE_IDLE:
-
View view = mPagerSnapHelper.findSnapView(
this);
//拿到当前进来的View
-
int position = getPosition(view);
-
if (mOnPageSlideListener !=
null) {
-
mOnPageSlideListener.onPageSelected(position, position == getItemCount() -
1);
-
}
-
break;
-
}
-
}
CustomLayoutManager完整代码如下:
-
public
class CustomLayoutManager extends LinearLayoutManager implements RecyclerView.OnChildAttachStateChangeListener {
-
private
int mDrift;
//位移,用来判断移动方向
-
-
private PagerSnapHelper mPagerSnapHelper;
-
private OnPageSlideListener mOnPageSlideListener;
-
-
public CustomLayoutManager(Context context) {
-
super(context);
-
}
-
-
public CustomLayoutManager(Context context, int orientation, boolean reverseLayout) {
-
super(context, orientation, reverseLayout);
-
mPagerSnapHelper =
new PagerSnapHelper();
-
}
-
-
@Override
-
public void onAttachedToWindow(RecyclerView view) {
-
view.addOnChildAttachStateChangeListener(
this);
-
mPagerSnapHelper.attachToRecyclerView(view);
-
super.onAttachedToWindow(view);
-
}
-
-
//Item添加进来
-
@Override
-
public void onChildViewAttachedToWindow(@NonNull View view) {
-
//播放视频操作,判断将要播放的是上一个视频,还是下一个视频
-
if (mDrift >
0) {
//向上
-
if (mOnPageSlideListener !=
null)
-
mOnPageSlideListener.onPageSelected(getPosition(view),
true);
-
}
else {
//向下
-
if (mOnPageSlideListener !=
null)
-
mOnPageSlideListener.onPageSelected(getPosition(view),
false);
-
}
-
}
-
-
//Item移除出去
-
@Override
-
public void onChildViewDetachedFromWindow(@NonNull View view) {
-
//暂停播放操作
-
if (mDrift >=
0) {
-
if (mOnPageSlideListener !=
null)
-
mOnPageSlideListener.onPageRelease(
true, getPosition(view));
-
}
else {
-
if (mOnPageSlideListener !=
null)
-
mOnPageSlideListener.onPageRelease(
false, getPosition(view));
-
}
-
}
-
-
@Override
-
public void onScrollStateChanged(int state) {
//滑动状态监听
-
switch (state) {
-
case RecyclerView.SCROLL_STATE_IDLE:
-
View view = mPagerSnapHelper.findSnapView(
this);
-
int position = getPosition(view);
-
if (mOnPageSlideListener !=
null) {
-
mOnPageSlideListener.onPageSelected(position, position == getItemCount() -
1);
-
}
-
break;
-
}
-
}
-
-
@Override
-
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
-
this.mDrift = dy;
-
return
super.scrollVerticallyBy(dy, recycler, state);
-
}
-
-
//接口注入
-
public void setOnPageSlideListener(OnPageSlideListener mOnViewPagerListener) {
-
this.mOnPageSlideListener = mOnViewPagerListener;
-
}
-
}
三、实现播放
我们接着在Activity中实现播放和停止的方法:
-
//播放
-
private void playVideo() {
-
View itemView = mRecycler.getChildAt(
0);
-
final CusVideoView mVideoView = itemView.findViewById(R.id.mVideoView);
-
final ImageView mPlay = itemView.findViewById(R.id.mPlay);
-
final ImageView mThumb = itemView.findViewById(R.id.mThumb);
-
final MediaPlayer[] mMediaPlayer =
new MediaPlayer[
1];
-
mVideoView.start();
-
-
mVideoView.setOnInfoListener(
new MediaPlayer.OnInfoListener() {
-
@Override
-
public boolean onInfo(MediaPlayer mp, int what, int extra) {
-
mMediaPlayer[
0] = mp;
-
mp.setLooping(
true);
-
mThumb.animate().alpha(
0).setDuration(
200).start();
-
return
false;
-
}
-
});
-
-
//暂停控制
-
mPlay.setOnClickListener(
new View.OnClickListener() {
-
boolean isPlaying =
true;
-
-
@Override
-
public void onClick(View v) {
-
if (mVideoView.isPlaying()) {
-
mPlay.animate().alpha(
1f).start();
-
mVideoView.pause();
-
isPlaying =
false;
-
}
else {
-
mPlay.animate().alpha(
0f).start();
-
mVideoView.start();
-
isPlaying =
true;
-
}
-
}
-
});
-
}
-
-
//释放
-
private void releaseVideo(int index) {
-
View itemView = mRecycler.getChildAt(index);
-
final CusVideoView mVideoView = itemView.findViewById(R.id.mVideoView);
-
final ImageView mThumb = itemView.findViewById(R.id.mThumb);
-
final ImageView mPlay = itemView.findViewById(R.id.mPlay);
-
mVideoView.stopPlayback();
-
mThumb.animate().alpha(
1).start();
-
mPlay.animate().alpha(
0f).start();
-
}
然后处理LayoutManager中回调到Activity中的播放逻辑:
-
mLayoutManager.setOnPageSlideListener(
new OnPageSlideListener() {
-
-
@Override
-
public void onPageRelease(boolean isNext, int position) {
-
int index;
-
if (isNext) {
-
index =
0;
-
}
else {
-
index =
1;
-
}
-
releaseVideo(index);
-
}
-
-
@Override
-
public void onPageSelected(int position, boolean isNext) {
-
playVideo();
-
}
-
});
Activity的完整代码如下:
-
public
class TikTokIndexActivity extends AppCompatActivity {
-
private
static
final String TAG = TikTokIndexActivity.class.getSimpleName();
-
private RecyclerView mRecycler;
-
private CustomLayoutManager mLayoutManager;
-
-
@Override
-
protected void onCreate(@Nullable Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_tiktok_layout);
-
initView();
-
initListener();
-
}
-
-
//初始化监听
-
private void initListener() {
-
mLayoutManager.setOnPageSlideListener(
new OnPageSlideListener() {
-
-
@Override
-
public void onPageRelease(boolean isNext, int position) {
-
int index;
-
if (isNext) {
-
index =
0;
-
}
else {
-
index =
1;
-
}
-
releaseVideo(index);
-
}
-
-
@Override
-
public void onPageSelected(int position, boolean isNext) {
-
playVideo();
-
}
-
});
-
}
-
-
//初始化View
-
private void initView() {
-
mRecycler = findViewById(R.id.mRecycler);
-
mLayoutManager =
new CustomLayoutManager(
this, OrientationHelper.VERTICAL,
false);
-
TiktokAdapter mAdapter =
new TiktokAdapter(
this);
-
mRecycler.setLayoutManager(mLayoutManager);
-
mRecycler.setAdapter(mAdapter);
-
}
-
-
//播放
-
private void playVideo() {
-
//...这里的代码见上方说明
-
}
-
-
//释放
-
private void releaseVideo(int index) {
-
//...这里的代码见上方说明
-
}
-
}
到这里,仿抖音首页播放的效果就简单实现了,OK,咱们下期再会吧!
祝:工作顺利!
转载:https://blog.csdn.net/JArchie520/article/details/110791624