Android仿抖音上下滑動切換視頻
?自從各大直播平臺可以滑動切換直播間后,公司就出了一大波需求橙弱,還要配合各種收費(fèi)具伍,各種VIP,很是頭疼(haha 主要是我這個人很懶纵散,不想加班)梳码,后來研究了下 ,也查閱了一些別人寫的demo和一些想法伍掀,也對此有了一些理解掰茶。
- 1 最開始是打算用RecyclerView來實(shí)現(xiàn)的,因為他的復(fù)用性很強(qiáng)蜜笤,用起來也很方便濒蒋,和SnapHelper相結(jié)合便可以實(shí)現(xiàn)滑動分頁的功能。
什么是 SnapHelper
把兔?
SnapHelper是一個抽象類沪伙,官方提供了一個LinearSnapHelper的子類,可以讓RecyclerView滾動停止時相應(yīng)的Item停留中間位置县好。在25.1.0版本中围橡,官方又提供了一個PagerSnapHelper的子類,可以使RecyclerView像ViewPager一樣的效果缕贡,一次只能滑一頁翁授,而且居中顯示拣播。詳細(xì)源碼解讀可以看這里讓你明明白白的使用RecyclerView——SnapHelper詳解,這里我們用到的就是PagerSnapHelper收擦。
如何使用
使用非常簡單贮配,只需要創(chuàng)建對象之后調(diào)用attachToRecyclerView()附著到對應(yīng)的RecyclerView對象上就可以了。
snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(rvPage2);
?設(shè)置Adapter
videoAdapter = new ListVideoAdapter(urlList);
layoutManager = new LinearLayoutManager(Page2Activity.this, LinearLayoutManager.VERTICAL, false);
rvPage2.setLayoutManager(layoutManager);
rvPage2.setAdapter(videoAdapter);
?這樣我們就只需要監(jiān)聽RecyclerView的滾動炬守,然后就可以實(shí)現(xiàn)我們的邏輯了
rvPage2.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE://停止?jié)L動
View view = snapHelper.findSnapView(layoutManager);
//TODO 銷毀所有視頻
RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(view);
if (viewHolder != null && viewHolder instanceof VideoViewHolder) {
//TODO 啟動想要播放的視頻
}
break;
case RecyclerView.SCROLL_STATE_DRAGGING://拖動
break;
case RecyclerView.SCROLL_STATE_SETTLING://慣性滑動
break;
}
}
});
- 2 不過RecyclerView有個問題牧嫉,雖然可以達(dá)到切換到效果,但是如果我是上下切換减途,當(dāng)我左右快速滑動的時候酣藻,也會造成上下切換,當(dāng)然了鳍置,這個可以去監(jiān)聽他的觸摸事件辽剧,只是每一個item里面還有很多事件要處理,沖突性和復(fù)雜性會增加很多税产,就將其設(shè)置為備選方案了怕轿。所以后來接觸到上下切換的VerticalViewPager,就有了其他的方案辟拷。
- A 和V4包的ViewPager使用一樣撞羽,適配FragmentPagerAdapter,加載多個Fragment衫冻,這樣的方式其實(shí)很簡單诀紊,很粗暴,不過性能也是很差的隅俘,不建議使用
- B 適配PagerAdapter邻奠,監(jiān)聽setPageTransformer,加載新的數(shù)據(jù)为居,通過消息傳遞到Fragment碌宴,刷新數(shù)據(jù)
class PagerAdapter extends android.support.v4.view.PagerAdapter {
private List<HnLiveListModel.LiveListBean> list;
public PagerAdapter(List<HnLiveListModel.LiveListBean> list) {
this.list = list;
}
@Override
public int getCount() {
if (list.size() > 1) {
return Integer.MAX_VALUE;
} else {
return 1;
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = LayoutInflater.from(container.getContext()).inflate(R.layout.live_activity_audience_mask_layout, null);
int pos = position % list.size();
HnLiveListModel.LiveListBean data = list.get(pos);
//遮罩層
FrescoImageView mFrescoImageView = (FrescoImageView) view.findViewById(R.id.fiv_mask);
mFrescoImageView.setVisibility(View.VISIBLE);
if (data != null) {
String avator = data.getAvator();
mFrescoImageView.setController(FrescoConfig.getController(avator));
}
view.setId(position);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(container.findViewById(position));
}
}
mVerticalViewPager.setPageTransformer(false, new ViewPager.PageTransformer() {
private float yPosition;
public float getPosition() {
return yPosition;
}
@Override
public void transformPage(View page, float position) {
page.setTranslationX(page.getWidth() * -position);
yPosition = position * page.getHeight();
page.setTranslationY(yPosition);
ViewGroup viewGroup = (ViewGroup) page;
HnLogUtils.i(TAG, "page.id == " + page.getId() + ", position == " + position);
if ((position < 0 && viewGroup.getId() != mCurrentItem)) {
View roomContainer = viewGroup.findViewById(R.id.room_container);
if (roomContainer != null && roomContainer.getParent() != null && roomContainer.getParent() instanceof ViewGroup) {
((ViewGroup) (roomContainer.getParent())).removeView(roomContainer);
}
}
// 滿足此種條件,表明需要加載直播視頻蒙畴,以及聊天室了
if (viewGroup.getId() == mCurrentItem && position == 0 && mCurrentItem != mRoomUid) {
if (mRoomContainer.getParent() != null && mRoomContainer.getParent() instanceof ViewGroup) {
((ViewGroup) (mRoomContainer.getParent())).removeView(mRoomContainer);
}
EventBus.getDefault().post(new HnLiveEvent(0, HnLiveConstants.EventBus.Close_Dialog, 0));
EventBus.getDefault().post(new HnLiveEvent(0, HnLiveConstants.EventBus.Hide_Mask, 0));
loadVideoAndChatRoom(viewGroup, mCurrentItem);
}
}
});
/**
* 加載房間信息
*
* @param viewGroup
* @param mCurrentItem
*/
private void loadVideoAndChatRoom(ViewGroup viewGroup, int mCurrentItem) {
pos = mCurrentItem % list.size();
HnLogUtils.i(TAG, "當(dāng)前加載的位置:" + pos + "--->" + mCurrentItem);
HnLiveListModel.LiveListBean bean = list.get(pos);
//聊天室的fragment只加載一次贰镣,以后復(fù)用
if (!mInit) {
mRoomFragment = HnAudienceRoomFragment.newInstance(bean);
mFragmentManager.beginTransaction().replace(R.id.fragment_container, mRoomFragment).commitAllowingStateLoss();
mInit = true;
} else {
if (mRoomFragment == null) {
mRoomFragment = HnAudienceRoomFragment.newInstance(bean);
mFragmentManager.beginTransaction().replace(R.id.fragment_container, mRoomFragment).commitAllowingStateLoss();
mInit = true;
}
EventBus.getDefault().post(new HnLiveEvent(0, HnLiveConstants.EventBus.Update_Room_Info, bean));
}
viewGroup.addView(mRoomContainer);
this.mRoomUid = mCurrentItem;
}
- C 適配PagerAdapter,初始化每個Item的View 忍抽,以View為數(shù)據(jù)源八孝,適配到adapter中(不過直播中業(yè)務(wù)復(fù)雜,不推薦在直播中使用鸠项,小視頻可以使用(直接一次性將數(shù)據(jù)傳遞過來))干跛,這個方式主要是為了復(fù)用播放器,這樣就不要添加多個播放器了
for (HnChatVideoSwitchEntity item : mList) {
if (mActivity == null) return;
View view = LayoutInflater.from(mActivity).inflate(R.layout.adapter_invite_chat, null);
//TODO 初始化控件
//TODO 設(shè)置點(diǎn)擊事件
//TODO 設(shè)置數(shù)據(jù)
mViews.add(view);
}
adapter里面很簡單
public class HnInviteChatAdapter extends PagerAdapter {
private static final String TAG = "DouYinAdapter";
private List<View> mViews;
public HnInviteChatAdapter(List<View> views) {
this.mViews = views;
}
public void setmViews(List<View> mViews) {
this.mViews = mViews;
}
@Override
public int getCount() {
return mViews.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
Log.d(TAG, "instantiateItem: called");
container.addView(mViews.get(position));
return mViews.get(position);
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Log.d(TAG, "destroyItem: ");
container.removeView(mViews.get(position));
}
}
然后當(dāng)滑動ViewPager時祟绊,重置數(shù)據(jù)就可以了
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
mCurrentItem = position;
//TODO視頻暫停播放
if (mIjkVideoView != null) {
mIjkVideoView.pause();
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (mPlayingPosition == mCurrentItem) return;
if (state == VerticalViewPager.SCROLL_STATE_IDLE) {
//TODO視頻暫停播放
stopPlay();
//TODO 重置一些控件顯示與否
releaseView();
ViewParent parent = mIjkVideoView.getParent();
//TODO移除上個頁面的視頻控件
if (parent != null && parent instanceof FrameLayout) {
((FrameLayout) parent).removeView(mIjkVideoView);
}
getAnchorData(mCurrentItem);
}
}
});
然后就是更新數(shù)據(jù)了
private void getAnchorData(int position) {
mUid = mList.get(position).getUser_id();
mDbean = mList.get(position);
mPlayUrl = mDbean.getUser_video();
mCurrentItem = position;
View view = mViews.get(mCurrentItem);
RelativeLayout mContainer = view.findViewById(R.id.mContainer);
initItemView(view);
//TODO 設(shè)置控件數(shù)據(jù)和狀態(tài)
//添加播放器
ViewGroup parent = (ViewGroup) mIjkVideoView.getParent();
if (parent != null) {
parent.removeAllViews();
}
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
mIjkVideoView.setLayoutParams(params);
mContainer.addView(mIjkVideoView, 0, params);
startPlay();
mPlayingPosition = mCurrentItem;
}