ViewPage實(shí)現(xiàn)圖片和視頻輪播

最近要寫一個(gè)廣告頁(yè)展示前硫,開(kāi)始只要圖片胞得,可現(xiàn)在要視頻也能輪播,所以就去網(wǎng)上找屹电。 想著能Copy堅(jiān)決不手寫的原則阶剑,經(jīng)過(guò)一天苦戰(zhàn),終于決定自己去實(shí)現(xiàn)了危号。

話不多說(shuō)牧愁,直接開(kāi)始上實(shí)現(xiàn)方式。
寫功能之前外莲,先引入第三方的框架

implementation 'com.shuyu:GSYVideoPlayer:7.1.3'
implementation 'com.github.bumptech.glide:glide:4.11.0'

GSYVideoPlayer 是一個(gè)基于IJKPlayer(兼容系統(tǒng)MediaPlayer與EXOPlayer2)猪半,實(shí)現(xiàn)了多功能的視頻播放器,想了解更多的可以去百度下偷线,我用這個(gè)的目的就是它里面自帶緩存磨确。Glide不多說(shuō)了。

首先想好思路声邦,要實(shí)現(xiàn)Video和Image 乏奥,肯定要分不同的界面布局,所以用ViewPage和Fragment來(lái)實(shí)現(xiàn)亥曹。Vp2沒(méi)有用邓了,大家可以自己去替換著試試。

ImageFragment 媳瞪,代碼里面有注釋

public class ImageFragment extends Fragment {

private String url;  // 傳過(guò)來(lái)的路徑
private PagerListener finishListener;  //回調(diào)監(jiān)聽(tīng)
private ImageView imageView;
private static final int IMAGETIME = 4000; // 默認(rèn)圖片4s切換

public ImageFragment(String imageRes, PagerListener listener) {
    this.url = imageRes;
    this.finishListener = listener;
}
public void imgStart(){
    if (imageView!=null) {
        finishListener.currentItem(IMAGETIME);
    }
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.layout_image, null);
    view.setOnClickListener(v->finishListener.onClick());
    imageView = view.findViewById(R.id.image);
    Glide.with(getActivity()).load(url).centerCrop()
            .diskCacheStrategy(DiskCacheStrategy.ALL).into(imageView);
    imgStart();
    return view;
}

@Override
public void onDestroy() {
    super.onDestroy();

}

}

VideoFragment 骗炉,代碼里面有注釋

public class VideoFragment extends Fragment {
private  StandardGSYVideoPlayer player;
private String url;  // 傳過(guò)來(lái)的路徑
private PagerListener finishListener; //回調(diào)監(jiān)聽(tīng)

public VideoFragment(String url, PagerListener listener) {
    this.url = url;
    this.finishListener = listener;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

/**
 * 開(kāi)始播放
 */
public void startVideo() {
    if (player != null) {
        player.startPlayLogic();

        // 這塊用的是GSYVideoPlayer 里面的方法 ,下面做判斷是因?yàn)橛械囊曨l時(shí)間太短蛇受,不餓能準(zhǔn)確的回調(diào)
        player.setGSYVideoProgressListener((progress, secProgress, currentPosition, duration) -> {
            if (duration < 30*1000) {//總時(shí)長(zhǎng)小于40S
                if (progress == 96){
                    finishListener.currentItem(0);
                }
            }else if (duration < 50*1000) {//總時(shí)長(zhǎng)小于40S
                if (progress == 97){
                    finishListener.currentItem(0);
                }
            }else if (duration < 90*1000){//總時(shí)長(zhǎng)小于90S
                if (progress == 98){
                    finishListener.currentItem(0);
                }
            }else {
                if (progress == 99){
                    finishListener.currentItem(0);
                }
            }
        });

    }
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.layout_video, null);
    player = view.findViewById(R.id.video_item_player);
    player.setUp(url, true, "");
    player.getTitleTextView().setVisibility(View.GONE);
    player.getBackButton().setOnClickListener(v -> finishListener.onClick());
    player.getFullscreenButton().setVisibility(View.GONE);
    player.getStartButton().setVisibility(View.GONE);
    ImageView imageView = new ImageView(getContext());
    imageView.setImageResource(R.drawable.image8);
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    player.setThumbImageView(imageView);

    startVideo();
    return view;
}

@Override
public void onPause() {
    super.onPause();
}

@Override
public void onResume() {
    super.onResume();
}

@Override
public void onDestroy() {
    super.onDestroy();
    GSYVideoManager.releaseAllVideos();
}

}

PagerListener很簡(jiǎn)單句葵,還是放上吧

interface PagerListener {

// 監(jiān)聽(tīng) 這里可以回調(diào)一些數(shù)據(jù)進(jìn)行處理
void onClick();

// 切換,同樣的根據(jù)需求回調(diào)fragment數(shù)據(jù)
void currentItem(int time);
}

DataBean是數(shù)據(jù) 龙巨,里面看下就好了笼呆,來(lái)自http://www.reibang.com/p/9db0fd1579b6

public class DataBean {
public Integer imageRes;
public String imageUrl;
public String title;
public int viewType;

public DataBean(Integer imageRes, String title, int viewType) {
    this.imageRes = imageRes;
    this.title = title;
    this.viewType = viewType;
}

public DataBean(String imageUrl, String title, int viewType) {
    this.imageUrl = imageUrl;
    this.title = title;
    this.viewType = viewType;
}


public static List<DataBean> MainActivity() {
    List<DataBean> list = new ArrayList<>();
    list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/18/mp4/190318231014076505.mp4", "", 2));
    list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/12/mp4/190312083533415853.mp4", "", 2));
    list.add(new DataBean("https://img.zcool.cn/community/011ad05e27a173a801216518a5c505.jpg", null, 1));
    list.add(new DataBean("https://img.zcool.cn/community/0148fc5e27a173a8012165184aad81.jpg", null, 1));
    list.add(new DataBean("https://img.zcool.cn/community/013c7d5e27a174a80121651816e521.jpg", null, 1));
    list.add(new DataBean("https://img.zcool.cn/community/01b8ac5e27a173a80120a895be4d85.jpg", null, 1));
    list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/09/mp4/190309153658147087.mp4", "", 2));
    list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/19/mp4/190319222227698228.mp4", "", 2));
    list.add(new DataBean("https://img.zcool.cn/community/01a85d5e27a174a80120a895111b2c.jpg", null, 1));
    list.add(new DataBean("https://img.zcool.cn/community/01085d5e27a174a80120a8958791c4.jpg", null, 1));
    list.add(new DataBean("https://img.zcool.cn/community/01f8735e27a174a8012165188aa959.jpg", null, 1));
    return list;
}

public static List<String> getColors(int size) {
    List<String> list = new ArrayList<>();
    for(int i = 0; i < size; i++) {
        list.add(getRandColor());
    }
    return list;
}
}

下面就是實(shí)現(xiàn)功能了,因?yàn)殚_(kāi)始代碼感覺(jué)不會(huì)太多旨别,所以我都寫在了一頁(yè),感覺(jué)不夠漂亮的可以自己去分下汗茄。

public class MainActivity extends AppCompatActivity implements PagerListener {

// 指示器相關(guān)的
private List<ImageView> mImgList;
private int img_select;
private int img_unSelect;
private static final int IMAGESIZE = 15;

// 界面相關(guān)的
private ViewPager viewpager;
private LinearLayout linearLayout;
private List<DataBean> dataBeans;
private List<Fragment> fragments;
private MyFragmentAdapter adapter;

private boolean mIsChanged = false;
private int mCurrentPagePosition = FIRST_ITEM_INDEX;
private static final int FIRST_ITEM_INDEX = 1;
// 需要的可以在這塊設(shè)置成靜態(tài)內(nèi)部類秸弛,避免內(nèi)存泄漏
private Handler handler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        viewpager.setCurrentItem(mCurrentPagePosition + 1);
    }
};

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_vpw);

    viewpager = findViewById(R.id.viewpager);
    linearLayout = findViewById(R.id.dot_horizontal);
    dataBeans = DataBean.MainActivity();
    fragments = new ArrayList<>();

    int size = dataBeans.size();

    mImgList = new ArrayList<>();
    img_select = R.drawable.dot_select;
    img_unSelect = R.drawable.dot_unselect;

    for (int i = 0; i < size; i++) {
        ImageView imageView = new ImageView(this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        //為小圓點(diǎn)左右添加間距
        params.leftMargin = 10;
        params.rightMargin = 10;
        //給小圓點(diǎn)一個(gè)默認(rèn)大小
        params.height = IMAGESIZE;
        params.width = IMAGESIZE;
        // 默認(rèn)第一個(gè)開(kāi)始
        if (i == 0) {
            imageView.setBackgroundResource(img_select);
        } else {
            imageView.setBackgroundResource(img_unSelect);
        }
        //為L(zhǎng)inearLayout添加ImageView
        linearLayout.addView(imageView, params);
        mImgList.add(imageView);
    }


    // 為了實(shí)現(xiàn)無(wú)限輪播  在fragment的第一個(gè)加入dataBeans的最后一個(gè)
    if (dataBeans.get(size - 1).viewType == 1) {
        fragments.add(new ImageFragment(dataBeans.get(size - 1).imageUrl, MainActivity.this));
    } else {
        fragments.add(new VideoFragment(dataBeans.get(size - 1).imageUrl, MainActivity.this));
    }

    // 這里是正常創(chuàng)建
    for (DataBean dataBean : dataBeans) {
        if (dataBean.viewType == 2) {
            fragments.add(new VideoFragment(dataBean.imageUrl, MainActivity.this));
        } else {
            fragments.add(new ImageFragment(dataBean.imageUrl, MainActivity.this));
        }
    }

    // 為了實(shí)現(xiàn)無(wú)限輪播  在fragment的末尾一個(gè)加入dataBeans的第一個(gè)
    if (dataBeans.get(0).viewType == 1) {
        fragments.add(new ImageFragment(dataBeans.get(0).imageUrl, MainActivity.this));
    } else {
        fragments.add(new VideoFragment(dataBeans.get(0).imageUrl, MainActivity.this));
    }

    adapter = new MyFragmentAdapter(getSupportFragmentManager(), fragments);
    viewpager.setAdapter(adapter);
    viewpager.setCurrentItem(FIRST_ITEM_INDEX, false);
    viewpager.setOffscreenPageLimit(3); //設(shè)置預(yù)加載的個(gè)數(shù)
    viewpager.setPageTransformer(false, new ScaleTransformer());
    viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

        @Override
        public void onPageSelected(int position) {
            handler.removeCallbacksAndMessages(null);
            mIsChanged = true;
            if (position >= fragments.size() - 1) {// 末位之后,下次切換跳轉(zhuǎn)到首位(1)
                mCurrentPagePosition = FIRST_ITEM_INDEX;
            } else if (position <= FIRST_ITEM_INDEX) {// 首位之前,下次切換跳轉(zhuǎn)到末尾(N)
                mCurrentPagePosition = fragments.size() - 1;
            } else {
                mCurrentPagePosition = position;
            }

            // 開(kāi)啟自動(dòng)輪播
            Fragment fragment = fragments.get(position);
            if (fragment instanceof VideoFragment) {
                //視頻自動(dòng)播放
                VideoFragment videoFragment = (VideoFragment) fragment;
                videoFragment.startVideo();
            } else {
                //圖片自動(dòng)倒計(jì)時(shí)
                ImageFragment imageFragment = (ImageFragment) fragment;
                imageFragment.imgStart();
            }

            for (int i = 0; i < size; i++) {
                //選中的頁(yè)面改變小圓點(diǎn)為選中狀態(tài)递览,反之為未選中
                if (i == mCurrentPagePosition-1) {
                    (mImgList.get(i)).setBackgroundResource(img_select);
                } else {
                    (mImgList.get(i)).setBackgroundResource(img_unSelect);
                }
            }
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            if (ViewPager.SCROLL_STATE_IDLE == state) {
                if (mIsChanged) {
                    mIsChanged = false;
                    viewpager.setCurrentItem(mCurrentPagePosition, false);
                }
            }
        }
    });

    // 每次開(kāi)始先切換一次叼屠,保障fragment里面的監(jiān)聽(tīng)回調(diào)獲取到,這塊可以去了解一下viewpage + fragment切換時(shí) fragment的生命周期
    currentItem(0);
}

/**
 * 切換viewpage
 */
Runnable runnable = () -> handler.sendEmptyMessage(0);


@Override
public void currentItem(int time) {
    handler.postDelayed(runnable, time);
}

@Override
public void onClick() {
    // 點(diǎn)擊處理
    Log.e("TAG", "onClick: 點(diǎn)擊了界面" );
}


/**
 * Adapter 簡(jiǎn)單使用
 */
public static class MyFragmentAdapter extends FragmentPagerAdapter {
    private List<Fragment> list;

    public MyFragmentAdapter(FragmentManager fm, List<Fragment> list) {
        super(fm);
        this.list = list;
    }

    @NonNull
    @Override
    public Fragment getItem(int arg0) {
        return list.get(arg0);
    }

    @Override
    public int getCount() {
        return list.size();
    }


}

/**
 * ViewPager 切換效果
 */
public static class ScaleTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.70f;
    private static final float MIN_ALPHA = 0.5f;

    @Override
    public void transformPage(View page, float position) {
        if (position < -1 || position > 1) {
            page.setAlpha(MIN_ALPHA);
            page.setScaleX(MIN_SCALE);
            page.setScaleY(MIN_SCALE);
        } else if (position <= 1) { // [-1,1]
            float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
            if (position < 0) {
                float scaleX = 1 + 0.3f * position;
                Log.d("google_lenve_fb", "transformPage: scaleX:" + scaleX);
                page.setScaleX(scaleX);
                page.setScaleY(scaleX);
            } else {
                float scaleX = 1 - 0.3f * position;
                page.setScaleX(scaleX);
                page.setScaleY(scaleX);
            }
            page.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
        }
    }
}
}

好了绞铃,這樣功能就能跑起來(lái)了镜雨。效果圖就不上了,懶得動(dòng)的直接取源碼獲取運(yùn)行
我的源碼

布局在下面
activity_vpw

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <LinearLayout
        android:id="@+id/dot_horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginBottom="32dp"
        android:gravity="center"
        android:orientation="horizontal" />
</FrameLayout>
</LinearLayout>

layout_image

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
    android:id="@+id/image"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

layout_video

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_ll"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
    android:id="@+id/video_item_player"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</LinearLayout>

這只是一個(gè)Demo儿捧,在實(shí)際應(yīng)用時(shí)還需要完善荚坞,里面還有一些小問(wèn)題,需要根據(jù)自己的需求去更改菲盾。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末颓影,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子懒鉴,更是在濱河造成了極大的恐慌诡挂,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件临谱,死亡現(xiàn)場(chǎng)離奇詭異璃俗,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)悉默,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門旧找,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人麦牺,你說(shuō)我怎么就攤上這事钮蛛。” “怎么了剖膳?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵魏颓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我吱晒,道長(zhǎng)甸饱,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任仑濒,我火速辦了婚禮叹话,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘墩瞳。我一直安慰自己驼壶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布喉酌。 她就那樣靜靜地躺著热凹,像睡著了一般泵喘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上般妙,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天纪铺,我揣著相機(jī)與錄音,去河邊找鬼碟渺。 笑死鲜锚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的苫拍。 我是一名探鬼主播芜繁,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼怯疤!你這毒婦竟也來(lái)了浆洗?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤集峦,失蹤者是張志新(化名)和其女友劉穎伏社,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體塔淤,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摘昌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了高蜂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聪黎。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖备恤,靈堂內(nèi)的尸體忽然破棺而出稿饰,到底是詐尸還是另有隱情,我是刑警寧澤露泊,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布喉镰,位于F島的核電站,受9級(jí)特大地震影響惭笑,放射性物質(zhì)發(fā)生泄漏侣姆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一沉噩、第九天 我趴在偏房一處隱蔽的房頂上張望捺宗。 院中可真熱鬧,春花似錦川蒙、人聲如沸蚜厉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)弯囊。三九已至痰哨,卻和暖如春胶果,著一層夾襖步出監(jiān)牢的瞬間匾嘱,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工早抠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霎烙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓蕊连,卻偏偏與公主長(zhǎng)得像悬垃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甘苍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348