Android仿抖音主頁效果實(shí)現(xiàn)代碼
寫在前面
各位老鐵,我又來啦!既然來了,那肯定又來搞事情啦,話不多說,先上圖!
“抖音”都玩過吧,是不是很好玩,我反正是天天刷,作為一個(gè)非著名的Android低級(jí)攻城獅,雖然技術(shù)菜的一匹,但是也經(jīng)常刷著刷著會(huì)思考:咦?這玩意是用哪個(gè)控件做的?這個(gè)效果是咋實(shí)現(xiàn)的?。坑捎诒救思夹g(shù)水平有限,所以今天咱就先挑個(gè)比較簡單的來看看是如何實(shí)現(xiàn)的,思考再三,我們就拿抖音首頁的這個(gè)效果來練練手吧,話不多說,開搞!
一、準(zhǔn)備工作
我們先不急著寫代碼,先對(duì)抖音的這種效果做一個(gè)簡單的分析,首先需要明確的是它是可以滑動(dòng)的,并且可以上滑回去,也可以下滑到下一個(gè),滑動(dòng)的數(shù)量跟隨視頻的個(gè)數(shù)而定,到這里其實(shí)能實(shí)現(xiàn)這種效果的控件就已經(jīng)被縮小到一個(gè)范圍內(nèi)了。初步判定可以使用ViewPager或者是RecyclerView來實(shí)現(xiàn),你細(xì)想一下,它實(shí)際上就是一個(gè)列表啊,每一屏的視頻效果就是一個(gè)單獨(dú)的Item,并且它的列表Item的數(shù)量可以很大,至少目前你應(yīng)該沒有哪一次是能把抖音滑到底的吧,那最后咱們使用RecyclerView來實(shí)現(xiàn)這個(gè)效果。
為什么不用ViewPager?我們需要的是每次只加載一屏,ViewPager默認(rèn)會(huì)有預(yù)加載機(jī)制,并且數(shù)據(jù)量較大的時(shí)候性能表現(xiàn)也是很差的。反之,RecyclerView最好的性能就是只加載一屏幕的Item,并且處理海量數(shù)據(jù)時(shí)性能更優(yōu),所以我們選用RecyclerView實(shí)現(xiàn)。
基礎(chǔ)列表的承載控件我們已經(jīng)選好了,然后通過上面的效果不難發(fā)現(xiàn),每一屏里面實(shí)際上只有一個(gè)Item,所以基礎(chǔ)的頁面布局你應(yīng)該也知道該怎么做了。然后就是視頻播放了,由于這里我們只是仿照實(shí)現(xiàn)抖音的主頁面效果,最核心的實(shí)際上是實(shí)現(xiàn)這個(gè)RecyclerView滑動(dòng)的效果,所以代碼我這里是盡量考慮簡單化,因此視頻播放就直接使用的Android原生的VideoView來做的,效果肯定不會(huì)多好,如果你對(duì)視頻播放這塊要求比較高的話,可以考慮使用基于ijkplayer實(shí)現(xiàn)的一些比較優(yōu)秀的開源庫,再或者能力強(qiáng)的自己基于ffmpeg定制開發(fā)播放器。
OK,到這里基本的分析就已經(jīng)做完了,下面我們就先來實(shí)現(xiàn)基礎(chǔ)代碼吧!
先把需要的圖片和視頻文件準(zhǔn)備好哦,別忘記了,視頻這里放在res/raw目錄下:
1.1、主頁面布局
新建一個(gè)TiktokIndexActivity.java,創(chuàng)建布局文件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="關(guān)注" 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想要自己設(shè)置寬和高,所以這里自定義一個(gè)VideoView,重寫onMeasure()測(cè)量方法:
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="測(cè)試測(cè)試數(shù)據(jù)哈哈哈哈\n家里幾個(gè)垃圾了個(gè)兩個(gè)垃圾" /> <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適配器
然后創(chuàng)建列表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一起來學(xué)習(xí)Android開發(fā)啊啊啊啊啊\n#Android高級(jí)UIAndroid開發(fā)"); mTitles.add("@喬布奇\nAndroid RecyclerView自定義\nLayoutManager的使用方式,仿抖音效果哦"); mMarqueeList.add("哈哈創(chuàng)作的原聲-喬布奇"); mMarqueeList.add("嘿嘿創(chuàng)作的原聲-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正常工作必須要有兩個(gè)東西:①、Adapter,負(fù)責(zé)界面顯示適配的,這里我們已經(jīng)弄好了;②、LayoutManager,告訴列表如何擺放,所以現(xiàn)在想要實(shí)現(xiàn)抖音的列表效果的關(guān)鍵就在于這個(gè)LayoutManager了,并且普通的LinearLayoutManager是不行的,我們需要自己去實(shí)現(xiàn)這個(gè)LayoutManger。這里我們?nèi)€(gè)巧,直接繼承LinearLayoutManager來實(shí)現(xiàn)自定義布局管理器。
RecyclerView里面有這樣一個(gè)接口:下面是這個(gè)接口的系統(tǒng)源碼
//這兩個(gè)方法不是成對(duì)出現(xiàn)的,也沒有順序 public interface OnChildAttachStateChangeListener { void onChildViewAttachedToWindow(@NonNull View var1); void onChildViewDetachedFromWindow(@NonNull View var1); }
它里面有兩個(gè)方法,可以監(jiān)聽列表的Item添加進(jìn)來和移除出去的兩個(gè)動(dòng)作,這是不是就很符合我們現(xiàn)在的使用場(chǎng)景啊,我們的每一屏只有一個(gè)Item,并且要在它被添加進(jìn)來的時(shí)候播放視頻,在移除時(shí)釋放掉,所以我們需要實(shí)現(xiàn)這個(gè)接口。
需要注意的是,這個(gè)接口必須在LayoutManager成功進(jìn)行初始化之后才能監(jiān)聽,所以我們?cè)贚ayoutManager中重寫onAttachedToWindow()方法,在它里面添加這個(gè)接口的監(jiān)聽:
@Override public void onAttachedToWindow(RecyclerView view) { view.addOnChildAttachStateChangeListener(this); super.onAttachedToWindow(view); }
完了之后呢,會(huì)重寫接口中的兩個(gè)方法,在這兩個(gè)方法里面我們就可以來實(shí)現(xiàn)播放和暫停的操作了。那么這里問題又來了,播放和暫停這兩個(gè)動(dòng)作都涉及到一個(gè)問題,你是播放上一個(gè)視頻還是播放下一個(gè)視頻,因?yàn)榱斜硎强梢酝禄部梢酝匣陌。晕覀冞€得重寫另一個(gè)監(jiān)聽位移變化的方法:scrollVerticallyBy(),這里dy的值為正數(shù)是往上滑,負(fù)數(shù)是往下滑
private int mDrift;//位移,用來判斷移動(dòng)方向 @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { this.mDrift = dy; return super.scrollVerticallyBy(dy, recycler, state); }
OK,這樣我們就可以判斷是向上滑還是向下滑了,那么上面onChildViewAttachedToWindow()這兩個(gè)方法就可以寫了,在這兩個(gè)方法中,我們需要把具體的業(yè)務(wù)邏輯回調(diào)到Activity里面去處理,所以這里我們還需要再自定義一個(gè)接口OnPageSlideListener :
public interface OnPageSlideListener { //釋放的監(jiān)聽 void onPageRelease(boolean isNext, int position); //選中的監(jiān)聽以及判斷是否滑動(dòng)到底部 void onPageSelected(int position, boolean isBottom); }
現(xiàn)在來處理上面的兩個(gè)回調(diào)接口onChildViewAttachedToWindow()和onChildViewDetachedFromWindow():
private OnPageSlideListener mOnPageSlideListener; //Item添加進(jìn)來 @Override public void onChildViewAttachedToWindow(@NonNull View view) { //播放視頻操作,判斷將要播放的是上一個(gè)視頻,還是下一個(gè)視頻 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)); } }
既然這里是通過接口的方式回調(diào)到Activity中實(shí)現(xiàn),所以我們還得給它設(shè)置一個(gè)接口:
//接口注入 public void setOnPageSlideListener(OnPageSlideListener mOnViewPagerListener) { this.mOnPageSlideListener = mOnViewPagerListener; }
寫到這里,當(dāng)然還不行,此時(shí)如果你去跑你的項(xiàng)目,你會(huì)發(fā)現(xiàn)它還是會(huì)像普通的RecyclerView一樣隨意的滑動(dòng),所以我們還需要一個(gè)類的幫助才行:PagerSnapHelper,它可以實(shí)現(xiàn)讓RecyclerView像ViewPager一樣的滑動(dòng)效果,這里我們給它綁定上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詳解
到這里已經(jīng)可以實(shí)現(xiàn)類似ViewPager滑動(dòng)的效果了,但是我們還需要重寫一個(gè)方法,不然的話向下滑動(dòng)播放的時(shí)候會(huì)有Bug:因?yàn)閛nChildViewAttachedToWindow()和onChildViewDetachedFromWindow()這兩個(gè)方法并不是成對(duì)出現(xiàn)的,它們二者之間也是沒有順序的,因此這里我們?cè)賮肀O(jiān)聽一下滑動(dòng)狀態(tài)的改變:判斷已經(jīng)處理完成即手指抬起時(shí)的狀態(tài)
@Override public void onScrollStateChanged(int state) { switch (state) { case RecyclerView.SCROLL_STATE_IDLE: View view = mPagerSnapHelper.findSnapView(this);//拿到當(dāng)前進(jìn)來的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;//位移,用來判斷移動(dòng)方向 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添加進(jìn)來 @Override public void onChildViewAttachedToWindow(@NonNull View view) { //播放視頻操作,判斷將要播放的是上一個(gè)視頻,還是下一個(gè)視頻 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) { //滑動(dòng)狀態(tài)監(jiān)聽 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; } }
三、實(shí)現(xiàn)播放
我們接著在Activity中實(shí)現(xiàn)播放和停止的方法:
//播放 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中回調(diào)到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(); } //初始化監(jiān)聽 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) { //...這里的代碼見上方說明 } }
到這里,仿抖音首頁播放的效果就簡單實(shí)現(xiàn)了,OK,咱們下期再會(huì)吧!
祝:工作順利!
到此這篇關(guān)于Android仿抖音主頁效果實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Android抖音主頁內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 使用【AIDL】調(diào)用外部服務(wù)的解決方法
本篇文章是對(duì)Android中使用AIDL調(diào)用外部服務(wù)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06flutter實(shí)現(xiàn)一個(gè)列表下拉抽屜的示例代碼
本文主要介紹了flutter實(shí)現(xiàn)一個(gè)列表下拉抽屜的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02Android編程入門之HelloWorld項(xiàng)目目錄結(jié)構(gòu)分析
這篇文章主要介紹了Android編程入門之HelloWorld項(xiàng)目目錄結(jié)構(gòu)分析,較為詳細(xì)的分析了Android項(xiàng)目的目錄結(jié)構(gòu)與具體作用,需要的朋友可以參考下2015-12-12Android編程中號(hào)碼匹配位數(shù)修改的方法
這篇文章主要介紹了Android編程中號(hào)碼匹配位數(shù)修改的方法,涉及Android編程中參數(shù)修改的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Android沉浸式狀態(tài)欄微技巧(帶你真正理解沉浸式模式)
因?yàn)锳ndroid官方從來沒有給出過沉浸式狀態(tài)欄這樣的命名,只有沉浸式模式(Immersive Mode)這種說法.下面通過本文給大家介紹Android沉浸式狀態(tài)欄微技巧,需要的朋友參考下2016-12-12Android 開發(fā)訂單流程view實(shí)例詳解
這篇文章主要介紹了 Android 開發(fā)訂單流程view實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03