美文网首页多媒体科技
Android 资讯类App项目实战 第五章 视频模块

Android 资讯类App项目实战 第五章 视频模块

作者: Huigesi | 来源:发表于2018-06-11 16:05 被阅读328次

    前言:

    正在做一个资讯类app,打算一边做一边整理,供自己学习与巩固。用到的知识复杂度不高,仅适于新手。经验不多,如果写出来的代码有不好的地方欢迎讨论。

    以往的内容

    第一章 滑动顶部导航栏

    第二章 retrofit获取网络数据

    第三章 新闻模块

    第四章 电影模块

    第五章 视频模块

    本章内容最终效果:

    视频模块效果.gif

    知识点:

    MVP,RxJava,RecyclerView,JZVideoPlayerStandard

    学习目标:

    1、MVP模式的使用

    2、使用RxJava处理复杂请求过程。

    3、使用RecyclerView显示视频列表数据。

    视频模块的视频播放方面主要用了第三方视频库JiaoZiVideoPlayer。除了网络视频播放,本章还运用RxJava解决复杂的网络请求(嵌套请求和循环请求)。

    项目实战:

    注意

    本章用到的drawable资源、values资源皆存放在百度网盘

    (请将values文件夹中的style.xml或color.xml更新一致后再运行,如有后续更新自行修改)

    1.1 项目结构

    image.png

    需导入的库:
    导入JiaoZiVideoPlayer。

    compile 'cn.jzvd:jiaozivideoplayer:6.2.10'
    
    image.png

    用到的Api:
    http://is.snssdk.com/api/news/feed/v51/?category=video
    http://ib.365yg.com/video/urls/v/1/toutiao/mp4/v02004f00000bbpbk3l2v325q7lmkds0?r=6781281688452415&s=2734808831

    1.2 属性类

    本章分别用到3个属性类:TodayBean,TodayContentBean和VideoUrlBean。TodayBean是头条视频信息的数据,但因为返回的数据里最重要的content数据是个字符串,需要用我们将它解析成Json对象,所以诞生了TodayContentBean。VideoUrlBean则是我们获取视频地址需要用到的数据。

    TodayBean的内容大家通过api:http://is.snssdk.com/api/news/feed/v51/?category=video返回数据,再复制用GsonFormat生成就好。
    VideoUrlBean同理,用api:
    http://ib.365yg.com/video/urls/v/1/toutiao/mp4/v02004f00000bbpbk3l2v325q7lmkds0?r=6781281688452415&s=2734808831

    这里TodayContentBean的生成比较麻烦,我直接提供给大家content部分的json解析,大家复制后用GsonFormat生成:

    {
    "abstract":"视频讲述: 现在相亲都不问房子和车了, 都开始问这个问题, 结局亮了。","action_extra":"{\"channel_id\": 3431225546}","action_list":[{"action":1,"desc":"","extra":{}},{"action":3,"desc":"","extra":{}},{"action":7,"desc":"","extra":{}},{"action":9,"desc":"","extra":{}}],"aggr_type":1,"allow_download":false,"article_sub_type":0,"article_type":0,"article_url":"http://toutiao.com/group/6561954781336699400/","ban_comment":0,"ban_danmaku":false,"behot_time":1528701219,"bury_count":2250,"cell_flag":262155,"cell_layout_style":1,"cell_type":0,"comment_count":82,"content_decoration":"","cursor":1528701219999,"danmaku_count":0,"digg_count":1805,"display_url":"http://toutiao.com/group/6561954781336699400/","filter_words":[{"id":"8:0","is_selected":false,"name":"看过了"},{"id":"9:1","is_selected":false,"name":"内容太水"},{"id":"5:2074939231","is_selected":false,"name":"拉黑作者:小军生活圈"},{"id":"6:16087","is_selected":false,"name":"不想看:美女"}],"forward_info":{"forward_count":7},"group_flags":32832,"group_id":6561954781336699400,"has_m3u8_video":false,"has_mp4_video":0,"has_video":true,"hot":0,"ignore_web_transform":1,"interaction_data":"","is_subject":false,"item_id":6561954781336699400,"item_version":0,"keywords":"视频,美女","large_image_list":[{"height":326,"uri":"video1609/896f00091aee6992e724","url":"http://p1.pstatp.com/video1609/896f00091aee6992e724","url_list":[{"url":"http://p1.pstatp.com/video1609/896f00091aee6992e724"},{"url":"http://pb3.pstatp.com/video1609/896f00091aee6992e724"},{"url":"http://pb9.pstatp.com/video1609/896f00091aee6992e724"}],"width":580}],"level":0,"log_pb":{"impr_id":"20180611151339010008061137517240"},"media_info":{"avatar_url":"http://p9.pstatp.com/large/46f800012fe8ec43d9d9","follow":false,"is_star_user":false,"media_id":1584581706610701,"name":"小军生活圈","recommend_reason":"","recommend_type":0,"user_id":68698278295,"user_verified":true,"verified_content":""},"media_name":"小军生活圈","middle_image":{"height":360,"uri":"list/896f00091aee6992e724","url":"http://p1.pstatp.com/list/300x196/896f00091aee6992e724.webp","url_list":[{"url":"http://p1.pstatp.com/list/300x196/896f00091aee6992e724.webp"},{"url":"http://pb3.pstatp.com/list/300x196/896f00091aee6992e724.webp"},{"url":"http://pb9.pstatp.com/list/300x196/896f00091aee6992e724.webp"}],"width":640},"need_client_impr_recycle":1,"publish_time":1527904800,"read_count":566989,"repin_count":179,"rid":"20180611151339010008061137517240","share_count":6043,"share_info":{"cover_image":null,"description":null,"share_type":{"pyq":2,"qq":0,"qzone":0,"wx":0},"share_url":"http://m.toutiaoimg.cn/a6561954781336699400/?iid=0\u0026app=news_article","title":"美女相亲玩套路,看小伙如何整治美女?","token_type":1,"weixin_cover_image":{"height":1034,"uri":"large/pgc-image/15281038563343f512fdb88","url":"http://p3.pstatp.com/large/pgc-image/15281038563343f512fdb88","url_list":[{"url":"http://p3.pstatp.com/large/pgc-image/15281038563343f512fdb88"},{"url":"http://pb9.pstatp.com/large/pgc-image/15281038563343f512fdb88"},{"url":"http://pb1.pstatp.com/large/pgc-image/15281038563343f512fdb88"}],"width":1280}},"share_type":2,"share_url":"http://m.toutiaoimg.cn/a6561954781336699400/?iid=0\u0026app=news_article","show_dislike":true,"show_portrait":false,"show_portrait_article":false,"source":"小军生活圈","source_icon_style":1,"source_open_url":"sslocal://profile?refer=video\u0026uid=68698278295","tag":"video_movie","tag_id":6561954781336699400,"tip":0,"title":"美女相亲玩套路,看小伙如何整治美女?","ugc_recommend":{"activity":"","reason":"头条视频原创作者"},"url":"http://toutiao.com/group/6561954781336699400/","user_info":{"avatar_url":"http://p3.pstatp.com/thumb/46f800012fe8ec43d9d9","description":"每天推送原创搞笑视频,高端黑","follow":false,"follower_count":0,"name":"小军生活圈","user_auth_info":"{\"auth_type\": \"0\", \"other_auth\": {\"pgc\": \"头条视频原创作者\"}, \"auth_info\": \"头条视频原创作者\"}","user_id":68698278295,"user_verified":true,"verified_content":"头条视频原创作者"},"user_repin":0,"user_verified":1,"verified_content":"头条视频原创作者","video_detail_info":{"detail_video_large_image":{"height":326,"uri":"video1609/896f00091aee6992e724","url":"http://p1.pstatp.com/video1609/896f00091aee6992e724","url_list":[{"url":"http://p1.pstatp.com/video1609/896f00091aee6992e724"},{"url":"http://pb3.pstatp.com/video1609/896f00091aee6992e724"},{"url":"http://pb9.pstatp.com/video1609/896f00091aee6992e724"}],"width":580},"direct_play":1,"group_flags":32832,"show_pgc_subscribe":1,"video_id":"v02004c20000bc8bkmdqg5b4ln25l570","video_preloading_flag":1,"video_type":0,"video_watch_count":1499723,"video_watching_count":0},"video_duration":126,"video_id":"v02004c20000bc8bkmdqg5b4ln25l570","video_style":3
    }
    

    1.3 Retrofit

    打开RetrofitService.java
    增加两个需要用到的网络请求注解:

    RetrofitService.java

    然后到RetrofitHelper.java中写上Get方法

    RetrofitHelper.java

    到Api.java类中添加头条的host

    Api.java

    2.1 Model层

    Model层的内容:


    Model

    状态监听接口IVideoLoadListener:

    IVideoLoadListener

    VideoModel的接口IVideoModel:

    IVideoModel.java

    发送请求的VideoModel类:

    VideoModel.java

    这里的视频播放列表地址的获取经历以下过程:
    发送今日头条请求(getToday)获取到content数组 ==》 把content数组里的字符串用Gson转换为Json数据 ==》把json数据里的video_id取出来,做一系列加密和拼接操作得到获取视频播放地址的api ==》发送获取视频播放地址的网络请求(getVideoUrl) ==》取出单个videoUrlBean,把他们放进对象数组里。

    如果用正常的网络获取的话,需要写一个嵌套请求,里面还得有一个循环。
    用RxJava写的话,具体的操作就是各种flatMap的转换就行了。

    2.2 View层

    View层的内容只有一个IVideoView接口:

    image.png

    2.3 Presenter层

    Presenter层的内容:


    Presenter

    IVideoPresenter接口:


    IVideoPresenter

    VideoPresenter:

    public class VideoPresenter implements IVideoPresenter, IVideoLoadListener {
    
    private IVideoModel iVideoModel;
    private IVideoView iVideoView;
    
    public VideoPresenter(IVideoView iVideoView) {
        this.iVideoView = iVideoView;
        this.iVideoModel = new VideoModel();
    }
    
    @Override
    public void loadVideo() {
        iVideoView.showDialog();
        iVideoModel.loadVideo("video", this);
    }
    
    @Override
    public void videoUrlSuccess(List<VideoUrlBean> mainUrlBeans, List<TodayContentBean> contentBeans) {
        List<String> videoList = new ArrayList<>();
        iVideoView.hideDialog();
        for (int i = 0; i < mainUrlBeans.size(); i++) {
            String mainUrl = mainUrlBeans.get(i).getData().getVideo_list().getVideo_1().getMain_url();
            final String url1 = (new String(Base64.decode(mainUrl.getBytes(), Base64.DEFAULT)));
            videoList.add(url1);
        }
        iVideoView.showVideo(contentBeans, videoList);
    }
    
    @Override
    public void fail(Throwable throwable) {
        iVideoView.hideDialog();
        iVideoView.showErrorMsg(throwable);
    }
    
    public static String getVideoContentApi(String videoid) {
        String VIDEO_HOST = "http://ib.365yg.com";
        String VIDEO_URL = "/video/urls/v/1/toutiao/mp4/%s?r=%s";
        String r = getRandom();
        String s = String.format(VIDEO_URL, videoid, r);
        CRC32 crc32 = new CRC32();
        crc32.update(s.getBytes());
        String crcString = crc32.getValue() + "";
        String url = VIDEO_HOST + s + "&s=" + crcString;
        return url;
    }
    
    public static String getRandom() {
        Random random = new Random();
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            result.append(random.nextInt(10));
        }
        return result.toString();
    }
    
    public static TodayContentBean getTodayContentBean(String content) {
        Gson gson = new Gson();
        TodayContentBean bean = gson.fromJson(content, TodayContentBean.class);
        return bean;
    }
    }
    

    3.1 item_video

    由于我们的视频需要列表显示,所以还是得用到RecyclerView
    而每一个item我们用的是基于MediaPlayerIJKplayer,和ExoPlayer的第三方视频库JiaoZiVideoPlayer

    首先新建一个布局文件,命名为item_video

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:layout_margin="4dp"
    app:cardCornerRadius="5dp">
    
    <cn.jzvd.JZVideoPlayerStandard
        android:id="@+id/video_player"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorWhite"></cn.jzvd.JZVideoPlayerStandard>
    </android.support.v7.widget.CardView>
    

    再到fg_video.xml文件中,把RecyclerView加上去:

    <?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">
    
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl_video"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_video"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        </android.support.v7.widget.RecyclerView>
    </android.support.v4.widget.SwipeRefreshLayout>
    
    </LinearLayout>
    

    3.2 ItemVideoAdapter

    为我们刚刚写的item写一个适配器:

    新建一个java文件,命名为ItemVideoAdapter

    ItemVideoAdapter

    3.3 Fragment

    前面做了那么多,最终还是要在Fragment上设置才能让他们显示出来

    FgVideoFragment.java
    public class FgVideoFragment extends Fragment  implements IVideoView{
    
    private IVideoPresenter iVideoPresenter;
    private RecyclerView rv_video;
    private ItemVideoAdapter itemVideoAdapter;
    private SwipeRefreshLayout srl_video;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fg_video,container,false);
    }
    
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        iVideoPresenter = new VideoPresenter(this);
        rv_video = view.findViewById(R.id.rv_video);
        srl_video = view.findViewById(R.id.srl_video);
        srl_video.setColorSchemeColors(Color.parseColor("#ffce3d3a"));
        iVideoPresenter.loadVideo();
        srl_video.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                iVideoPresenter.loadVideo();
            }
        });
        itemVideoAdapter = new ItemVideoAdapter(getActivity());
    }
    
    @Override
    public void showVideo(List<TodayContentBean> todayContentBeans, List<String> videoList) {
        itemVideoAdapter.setData(todayContentBeans, videoList);
        rv_video.setLayoutManager(new LinearLayoutManager(getActivity(),
                LinearLayoutManager.VERTICAL, false));
        rv_video.setAdapter(itemVideoAdapter);
    }
    
    @Override
    public void hideDialog() {
        srl_video.setRefreshing(false);
    }
    
    @Override
    public void showDialog() {
        srl_video.setRefreshing(true);
    }
    
    @Override
    public void showErrorMsg(Throwable throwable) {
        Toast.makeText(getContext(), "加载出错:"+throwable.getMessage(), Toast.LENGTH_SHORT).show();
    }
    }
    

    最终效果:

    视频模块效果2.gif

    学习任务

    根据提供的城市代码数组,通过以下Api,写一个RxJava的网络请求,把相应的城市情况Log出来。
    数组:

    Integer[] city={101280101,101280102,101280103,101280104,101280105, 101280201,101280202,101280203,101280204,101280205,101280206, 101280207,101280208,101280501};
    

    api:

    http://wthrcdn.etouch.cn/weather_mini?citykey=101010100

    效果:


    log

    项目源码:https://github.com/Huigesi/IdleReaderDemo

    上一章:
    第四章 电影模块

    相关文章

      网友评论

        本文标题:Android 资讯类App项目实战 第五章 视频模块

        本文链接:https://www.haomeiwen.com/subject/vghheftx.html