【Android 進(jìn)階】仿抖音系列之翻頁(yè)上下滑切換視頻(一)

前言

最近公司在做個(gè)短視頻的項(xiàng)目酥馍,其中借鑒了很多抖音的設(shè)計(jì),其中就有抖音的上下滑切換視頻。

思路

    1. ViewPager

這里用重寫了ViewPageronInterceptTouchEventonTouchEvent方法周崭,使其可以上下滑動(dòng)切換視圖。
代碼如下:

/**
 * 作者: ch
 * 時(shí)間: 2018/7/30 0030-下午 2:53
 * 描述:
 * 來(lái)源:
 */


public class VerticalViewPager extends ViewPager {

    private boolean isVertical = false;

    public VerticalViewPager(@NonNull Context context) {
        super(context);
    }

    public VerticalViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    private void init() {
        // The majority of the magic happens here
        setPageTransformer(true, new HorizontalVerticalPageTransformer());
        // The easiest way to get rid of the over scroll drawing that happens on the left and right
        setOverScrollMode(OVER_SCROLL_NEVER);
    }


    public boolean isVertical() {
        return isVertical;
    }

    public void setVertical(boolean vertical) {
        isVertical = vertical;
        init();
    }

    private class HorizontalVerticalPageTransformer implements PageTransformer {

        private static final float MIN_SCALE = 0.75f;

        @Override
        public void transformPage(View page, float position) {
            if (isVertical) {

                if (position < -1) {
                    page.setAlpha(0);
                } else if (position <= 1) {
                    page.setAlpha(1);

                    // Counteract the default slide transition
                    float xPosition = page.getWidth() * -position;
                    page.setTranslationX(xPosition);

                    //set Y position to swipe in from top
                    float yPosition = position * page.getHeight();
                    page.setTranslationY(yPosition);
                } else {
                    page.setAlpha(0);
                }
            } else {
                int pageWidth = page.getWidth();

                if (position < -1) { // [-Infinity,-1)
                    // This page is way off-screen to the left.
                    page.setAlpha(0);

                } else if (position <= 0) { // [-1,0]
                    // Use the default slide transition when moving to the left page
                    page.setAlpha(1);
                    page.setTranslationX(0);
                    page.setScaleX(1);
                    page.setScaleY(1);

                } else if (position <= 1) { // (0,1]
                    // Fade the page out.
                    page.setAlpha(1 - position);

                    // Counteract the default slide transition
                    page.setTranslationX(pageWidth * -position);
                    page.setTranslationY(0);

                    // Scale the page down (between MIN_SCALE and 1)
                    float scaleFactor = MIN_SCALE
                            + (1 - MIN_SCALE) * (1 - Math.abs(position));
                    page.setScaleX(scaleFactor);
                    page.setScaleY(scaleFactor);

                } else { // (1,+Infinity]
                    // This page is way off-screen to the right.
                    page.setAlpha(0);
                }
            }
        }
    }

    /**
     * Swaps the X and Y coordinates of your touch event.
     */
    private MotionEvent swapXY(MotionEvent ev) {
        float width = getWidth();
        float height = getHeight();

        float newX = (ev.getY() / height) * width;
        float newY = (ev.getX() / width) * height;

        ev.setLocation(newX, newY);

        return ev;
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (isVertical) {
            boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
            swapXY(ev); // return touch coordinates to original reference frame for any child views
            return intercepted;
        } else {
            return super.onInterceptTouchEvent(ev);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (isVertical) {
            return super.onTouchEvent(swapXY(ev));
        } else {
            return super.onTouchEvent(ev);
        }
    }

}

在上下滑的時(shí)候喳张,不可見(jiàn)時(shí)续镇,要暫停視頻,可見(jiàn)時(shí)重新播放销部,這里使用的是fragment磨取,通過(guò)其生命周期來(lái)控制視頻的播放與暫停。

       private void initView() {
        VerticalViewPagerAdapter pagerAdapter = new VerticalViewPagerAdapter(getSupportFragmentManager());
        vvpBackPlay.setVertical(true);
        //設(shè)置viewpager 緩存數(shù)柴墩,可以根據(jù)需要調(diào)整
        vvpBackPlay.setOffscreenPageLimit(10);
        pagerAdapter.setUrlList(urlList);
        vvpBackPlay.setAdapter(pagerAdapter);
    }
public class VerticalViewPagerAdapter extends PagerAdapter {
    private FragmentManager fragmentManager;
    private FragmentTransaction mCurTransaction;
    private Fragment mCurrentPrimaryItem = null;
    private List<String> urlList;

    public void setUrlList(List<String> urlList) {
        this.urlList = urlList;
    }


    public VerticalViewPagerAdapter(FragmentManager fm) {
        this.fragmentManager = fm;
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {

        if (mCurTransaction == null) {
            mCurTransaction = fragmentManager.beginTransaction();
        }

        VideoFragment fragment = new VideoFragment();
        if (urlList != null && urlList.size() > 0) {
            Bundle bundle = new Bundle();
            if (position >= urlList.size()) {
                bundle.putString(VideoFragment.URL, urlList.get(position % urlList.size()));
            } else {
                bundle.putString(VideoFragment.URL, urlList.get(position));
            }
            fragment.setArguments(bundle);
        }


        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), position));
        fragment.setUserVisibleHint(false);

        return fragment;
    }


    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (mCurTransaction == null) {
            mCurTransaction = fragmentManager.beginTransaction();
        }
        mCurTransaction.detach((Fragment) object);
        mCurTransaction.remove((Fragment) object);
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return ((Fragment) object).getView() == view;
    }

    private String makeFragmentName(int viewId, int position) {
        return "android:switcher:" + viewId + position;
    }

    @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitNowAllowingStateLoss();
            mCurTransaction = null;
        }
    }
}

通過(guò)setUserVisibleHint忙厌、onResumeonPause江咳、onDestroy 4個(gè)方法處理視頻的播放逢净、暫停、重新播放歼指、銷毀邏輯

tip: 這里使用的是 JiaoZiVideoPlayer ,可以替換成其他播放器爹土。

public class VideoFragment extends BaseFragment {
    @BindView(R.id.txv_video)
    MyVideoPlayer txvVideo;
    @BindView(R.id.rl_back_right)
    RelativeLayout rlBackRight;
    @BindView(R.id.dl_back_play)
    DrawerLayout dlBackPlay;
    private String url;
    public static final String URL = "URL";

    @Override
    protected int getLayoutId() {
        return R.layout.fm_video;
    }

    @Override
    protected void initView() {

        url = getArguments().getString(URL);
        Glide.with(context)
                .load(url)
                .into(txvVideo.thumbImageView);
        txvVideo.rl_touch_help.setVisibility(View.GONE);
        txvVideo.setUp(url, url);

    }

    @Override
    protected void loadData() {
        txvVideo.startVideo();
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (txvVideo == null) {
            return;
        }
        if (isVisibleToUser) {
            txvVideo.goOnPlayOnResume();
        } else {
            txvVideo.goOnPlayOnPause();
        }

    }

    @Override
    public void onResume() {

        super.onResume();
        if (txvVideo != null) {
            txvVideo.goOnPlayOnResume();
        }

    }

    @Override
    public void onPause() {
        super.onPause();
        if (txvVideo != null) {
            txvVideo.goOnPlayOnPause();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (txvVideo != null) {
            txvVideo.releaseAllVideos();
        }
    }

}

缺陷

自定義的ViewPager滑動(dòng)不太順滑,推薦使用 VerticalViewPager

    1. RecyclerView + PagerSnapHelper

詳情請(qǐng)移步另外一篇博客
【Android 進(jìn)階】仿抖音系列之翻頁(yè)上下滑切換視頻(四)

問(wèn)題

  • 1.如果要實(shí)現(xiàn)抖音那種可以無(wú)限上滑的踩身,可以在ViewPageronPageSelected 中判斷position
  mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                if (position == list.size() - 2) {
                    //倒數(shù)第2個(gè) 加載數(shù)據(jù)
                    page++;
                    addData();

                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });
  • 2.關(guān)于在大屏手機(jī)上滑動(dòng)不靈敏的問(wèn)題胀茵,可以嘗試將MIN_SCALE設(shè)置為0.5,或者更低挟阻。
  • 3.建議使用RecyclerView + PagerSnapHelper來(lái)實(shí)現(xiàn)琼娘。

最后是源碼Github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市附鸽,隨后出現(xiàn)的幾起案子脱拼,更是在濱河造成了極大的恐慌,老刑警劉巖坷备,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熄浓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡省撑,警方通過(guò)查閱死者的電腦和手機(jī)赌蔑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門俯在,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人娃惯,你說(shuō)我怎么就攤上這事跷乐。” “怎么了石景?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵劈猿,是天一觀的道長(zhǎng)拙吉。 經(jīng)常有香客問(wèn)我潮孽,道長(zhǎng),這世上最難降的妖魔是什么筷黔? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任往史,我火速辦了婚禮,結(jié)果婚禮上佛舱,老公的妹妹穿的比我還像新娘椎例。我一直安慰自己,他們只是感情好请祖,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布订歪。 她就那樣靜靜地躺著,像睡著了一般肆捕。 火紅的嫁衣襯著肌膚如雪刷晋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天慎陵,我揣著相機(jī)與錄音眼虱,去河邊找鬼。 笑死席纽,一個(gè)胖子當(dāng)著我的面吹牛捏悬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播润梯,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼过牙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了纺铭?” 一聲冷哼從身側(cè)響起抒和,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎彤蔽,沒(méi)想到半個(gè)月后摧莽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顿痪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年镊辕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了油够。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡征懈,死狀恐怖石咬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卖哎,我是刑警寧澤鬼悠,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站亏娜,受9級(jí)特大地震影響焕窝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜维贺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一它掂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溯泣,春花似錦虐秋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至肢簿,卻和暖如春靶剑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背译仗。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工抬虽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纵菌。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓阐污,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親咱圆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笛辟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容