• TouTiao开源项目 分析笔记18 视频详情页面



    1.效果预览

    1.1.需要做到的真实效果

      

    1.2.触发的点击事件

      在MediaArticleVideoViewBinder的每一个item点击事件中:

      VideoContentActivity.launch(bean);

      在NewsArticleVideoViewViewBinder的每一个item点击事件中:

      VideoContentActivity.launch(item);


    2.视频详情的活动

    2.1.首先看一下第三方库==>视频播放==>jiecaovideoplayer的使用

      github地址:https://github.com/wlsh/JieCaoVideoPlayer/

      参考博客:http://blog.csdn.net/w_l_s/article/details/53132179

      这里就不详细了解了。

      然后这里需要一个节操播放器的一个自定义帮助器 

    public class MyJCVideoPlayerStandard extends JCVideoPlayerStandard {
        public static onClickFullScreenListener onClickFullScreenListener;
    
        public MyJCVideoPlayerStandard(Context context) {
            super(context);
        }
    
        public MyJCVideoPlayerStandard(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public static void setOnClickFullScreenListener(onClickFullScreenListener listener) {
            onClickFullScreenListener = listener;
        }
    
        @Override
        public void onClick(View v) {
            super.onClick(v);
            int id = v.getId();
            if (id == R.id.fullscreen) {
                if (onClickFullScreenListener != null) {
                    onClickFullScreenListener.onClickFullScreen();
                }
            }
        }
    
        public interface onClickFullScreenListener {
            void onClickFullScreen();
        }
    }

      主要工作就是设置了一个全屏监听器。

    2.2.需要实现的底层接口==>IVideoContent.View

    public interface IVideoContent {
        interface View extends INewsComment.View {
    
            /**
             * 设置播放器
             */
            void onSetVideoPlay(String url);
        }
    
        interface Presenter extends INewsComment.Presenter {
    
            /**
             * 请求数据
             */
            void doLoadVideoData(String videoid);
        }
    }

      针对新闻评论页面底层接口的改进。

      其实就是在新闻评论页面接口中添加了额外的一个方法而已。

    2.3.然后就是重头戏,活动的源代码了

    package com.jasonjan.headnews.module.video.content;
    
    import android.content.Intent;
    import android.content.pm.ActivityInfo;
    import android.content.res.ColorStateList;
    import android.graphics.PorterDuff;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.design.widget.FloatingActionButton;
    import android.support.design.widget.Snackbar;
    import android.support.v4.widget.ContentLoadingProgressBar;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.text.TextUtils;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.WindowManager;
    
    import com.jasonjan.headnews.R;
    import com.jasonjan.headnews.adapter.DiffCallback;
    import com.jasonjan.headnews.bean.common.LoadingBean;
    import com.jasonjan.headnews.bean.common.LoadingEndBean;
    import com.jasonjan.headnews.bean.news.MultiNewsArticleDataBean;
    import com.jasonjan.headnews.global.InitApp;
    import com.jasonjan.headnews.main.ErrorAction;
    import com.jasonjan.headnews.main.IntentAction;
    import com.jasonjan.headnews.main.Register;
    import com.jasonjan.headnews.module.base.BaseActivity;
    import com.jasonjan.headnews.module.news.comment.INewsComment;
    import com.jasonjan.headnews.util.ImageLoader;
    import com.jasonjan.headnews.util.OnLoadMoreListener;
    import com.jasonjan.headnews.util.SettingUtil;
    import com.jasonjan.headnews.widget.MyJCVideoPlayerStandard;
    import com.trello.rxlifecycle2.LifecycleTransformer;
    import com.trello.rxlifecycle2.android.ActivityEvent;
    
    import java.util.List;
    
    import fm.jiecao.jcvideoplayer_lib.JCUserAction;
    import fm.jiecao.jcvideoplayer_lib.JCUserActionStandard;
    import fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;
    import fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;
    import me.drakeet.multitype.Items;
    import me.drakeet.multitype.MultiTypeAdapter;
    
    public class VideoContentActivity extends BaseActivity implements IVideoContent.View {
    
        public static final String TAG = "VideoContentActivity";
        protected boolean canLoadMore = false;
        protected MultiTypeAdapter adapter;
        private String groupId;
        private String itemId;
        private String videoId;
        private String videoTitle;
        private String shareUrl;
        private MultiNewsArticleDataBean dataBean;
        private Items oldItems = new Items();
    
        private RecyclerView recyclerView;
        private ContentLoadingProgressBar progressBar;
        private FloatingActionButton fab;
        private MyJCVideoPlayerStandard jcVideo;
        private IVideoContent.Presenter presenter;
        private int currentAction;
        private SwipeRefreshLayout swipeRefreshLayout;
    
        public static void launch(MultiNewsArticleDataBean bean) {
            InitApp.AppContext.startActivity(new Intent(InitApp.AppContext, VideoContentActivity.class)
                    .putExtra(VideoContentActivity.TAG, bean)
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            }
            setContentView(R.layout.fragment_video_content_new);
            presenter = new VideoContentPresenter(this);
            initView();
            initData();
            onLoadData();
        }
    
        private void initData() {
            Intent intent = getIntent();
            try {
                dataBean = intent.getParcelableExtra(TAG);
                if (null != dataBean.getVideo_detail_info()) {
                    if (null != dataBean.getVideo_detail_info().getDetail_video_large_image()) {
                        String image = dataBean.getVideo_detail_info().getDetail_video_large_image().getUrl();
                        if (!TextUtils.isEmpty(image)) {
                            ImageLoader.loadCenterCrop(this, image, jcVideo.thumbImageView, R.color.viewBackground, R.mipmap.error_image);
                        }
                    }
                }
                this.groupId = dataBean.getGroup_id() + "";
                this.itemId = dataBean.getItem_id() + "";
                this.videoId = dataBean.getVideo_id();
                this.videoTitle = dataBean.getTitle();
                this.shareUrl = dataBean.getDisplay_url();
                oldItems.add(dataBean);
            } catch (NullPointerException e) {
                ErrorAction.print(e);
            }
    
        }
    
        private void initView() {
            recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
    
            adapter = new MultiTypeAdapter(oldItems);
            Register.registerVideoContentItem(adapter);
            recyclerView.setAdapter(adapter);
            recyclerView.addOnScrollListener(new OnLoadMoreListener() {
                @Override
                public void onLoadMore() {
                    if (canLoadMore) {
                        canLoadMore = false;
                        presenter.doLoadMoreData();
                    }
                }
            });
    
            MyJCVideoPlayerStandard.setOnClickFullScreenListener(new MyJCVideoPlayerStandard.onClickFullScreenListener() {
                @Override
                public void onClickFullScreen() {
                    if (currentAction == JCUserAction.ON_ENTER_FULLSCREEN && SettingUtil.getInstance().getIsVideoForceLandscape()) {
                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    }
                }
            });
    
            progressBar = (ContentLoadingProgressBar) findViewById(R.id.pb_progress);
            int color = SettingUtil.getInstance().getColor();
            progressBar.getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.MULTIPLY);
            progressBar.show();
    
            swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh_layout);
            swipeRefreshLayout.setColorSchemeColors(SettingUtil.getInstance().getColor());
            swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    swipeRefreshLayout.post(new Runnable() {
                        @Override
                        public void run() {
                            swipeRefreshLayout.setRefreshing(true);
                        }
                    });
                    onLoadData();
                }
            });
    
            fab = (FloatingActionButton) findViewById(R.id.fab);
            fab.setBackgroundTintList(ColorStateList.valueOf(SettingUtil.getInstance().getColor()));
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    IntentAction.send(VideoContentActivity.this, videoTitle + "
    " + shareUrl);
                }
            });
    
            jcVideo = (MyJCVideoPlayerStandard) findViewById(R.id.jc_video);
            jcVideo.thumbImageView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    fab.setVisibility(View.GONE);
                    return false;
                }
            });
        }
    
        @Override
        public void onLoadData() {
            presenter.doLoadData(groupId, itemId);
            presenter.doLoadVideoData(videoId);
        }
    
        @Override
        public void onSetAdapter(final List<?> list) {
            Items newItems = new Items();
            newItems.add(dataBean);
            newItems.addAll(list);
            newItems.add(new LoadingBean());
            DiffCallback.notifyDataSetChanged(newItems, newItems, DiffCallback.NEWS_COMMENT, adapter);
            oldItems.clear();
            oldItems.addAll(newItems);
            canLoadMore = true;
        }
    
        @Override
        public void onShowLoading() {
            progressBar.show();
        }
    
        @Override
        public void onHideLoading() {
            progressBar.hide();
            swipeRefreshLayout.post(new Runnable() {
                @Override
                public void run() {
                    swipeRefreshLayout.setRefreshing(false);
                }
            });
        }
    
        @Override
        public void onShowNetError() {
            Snackbar.make(recyclerView, R.string.network_error, Snackbar.LENGTH_SHORT).show();
        }
    
        @Override
        public void setPresenter(INewsComment.Presenter presenter) {
    
        }
    
        @Override
        public <T> LifecycleTransformer<T> bindToLife() {
            return this.bindUntilEvent(ActivityEvent.DESTROY);
        }
    
        @Override
        public void onShowNoMore() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (oldItems.size() > 1) {
                        Items newItems = new Items(oldItems);
                        newItems.remove(newItems.size() - 1);
                        newItems.add(new LoadingEndBean());
                        adapter.setItems(newItems);
                        adapter.notifyDataSetChanged();
                    } else if (oldItems.size() == 0) {
                        oldItems.add(new LoadingEndBean());
                        adapter.setItems(oldItems);
                        adapter.notifyDataSetChanged();
                    }
                    canLoadMore = false;
                }
            });
        }
    
        @Override
        public void onSetVideoPlay(String urls) {
            jcVideo.setUp(urls, JCVideoPlayerStandard.SCREEN_LAYOUT_NORMAL, videoTitle);
            if (SettingUtil.getInstance().getIsVideoAutoPlay()) {
                jcVideo.startButton.performClick();
                fab.setVisibility(View.GONE);
            }
    
            // 设置监听事件 判断是否进入全屏
            JCVideoPlayer.setJcUserAction(new JCUserAction() {
                @Override
                public void onEvent(int type, String url, int screen, Object... objects) {
                    if (type == JCUserActionStandard.ON_CLICK_START_THUMB ||
                            type == JCUserAction.ON_CLICK_START_ICON ||
                            type == JCUserAction.ON_CLICK_RESUME ||
                            type == JCUserAction.ON_CLICK_START_AUTO_COMPLETE) {
                        fab.setVisibility(View.GONE);
                    }
    
                    if (type == JCUserAction.ON_CLICK_PAUSE || type == JCUserAction.ON_AUTO_COMPLETE) {
                        fab.setVisibility(View.VISIBLE);
                    }
    
                    if (type == JCUserAction.ON_ENTER_FULLSCREEN) {
                        currentAction = JCUserAction.ON_ENTER_FULLSCREEN;
    
                        View decorView = getWindow().getDecorView();
                        int uiOptions = 0;
                        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
                            uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
                        } else {
                            uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
                        }
                        decorView.setSystemUiVisibility(uiOptions);
    
                        if (slidrInterface != null) {
                            slidrInterface.lock();
                        }
                    }
    
                    if (type == JCUserAction.ON_QUIT_FULLSCREEN) {
                        currentAction = JCUserAction.ON_QUIT_FULLSCREEN;
    
                        View decorView = getWindow().getDecorView();
                        decorView.setSystemUiVisibility(0);
    
                        if (slidrInterface != null) {
                            slidrInterface.unlock();
                        }
                    }
                }
            });
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            JCVideoPlayer.releaseAllVideos();
        }
    
        @Override
        public void onBackPressed() {
            if (JCVideoPlayer.backPress()) {
                return;
            }
            super.onBackPressed();
        }
    
    }
    View Code

      ①首先是启动函数,传入一个MultiNewsArticleDataBean类

        因为新闻页面也有视频类型的,这是通用Bean类。

        可以知道是哪一条新闻,有哪些视频链接。

        将数据封装成一个Bean,然后放在意图里面。

      ②然后是一个onCreate函数。

        这里处理一下沉浸式标题。

        加载布局,生成处理器。

        初始化视图,初始化数据。

        加载数据。

      ③然后是初始化视图的函数。

        首先获得recyclerView,设置适配器+recyclerView滑动监听事件。

        然后处理节操播放器的全屏点击事件。

        然后获得progressBar进度条,设置颜色。

        然后获得swipeRefreshLayout,设置颜色+刷新监听事件。

        然后活得分享浮动按钮,设置点击事件。

        然后获得节操播放器的布局,设置点击缩略图事件。

      ④然后是加载数据。

        调用处理器的加载数据函数。

        调用处理器的加载视频数据函数。

      ⑤然后设置适配器函数。

        传入一个List。

        然后处理新老数据。

      ⑥然后重写展示加载函数。

        调用了加载圈的show函数。

      ⑦然后隐藏加载函数。

        先调用加载圈的隐藏函数。

        然后调用了刷新圈的隐藏函数。

      ⑧然后重写网络错误函数。

      ⑨然后重写设置处理器,这是一个空函数。

      ⑩然后重写绑定生命周期。

      ⑪然后是重写没有更多了。

      ⑫然后重写接口函数设置视频播放。  

        这里面设置监听事件,判断是否进入全屏。

        

      ⑬然后重写暂停生命周期,设置释放所有资源。

      ⑭然后重写活动返回。

    2.4.活动需要的布局==>fragment_video_content_new.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        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="match_parent"
        android:background="@color/windowBackground"
        android:fitsSystemWindows="true">
    
        <com.jasonjan.headnews.widget.MyJCVideoPlayerStandard
            android:id="@+id/jc_video"
            android:layout_width="match_parent"
            android:layout_height="220dp"
            android:fitsSystemWindows="true"/>
    
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="196dp">
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </android.support.v4.widget.SwipeRefreshLayout>
    
        <android.support.v4.widget.ContentLoadingProgressBar
            android:id="@+id/pb_progress"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"/>
    
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="56dp"
            android:layout_height="56dp"
            android:layout_margin="16dp"
            app:elevation="16dp"
            app:layout_anchor="@id/jc_video"
            app:layout_anchorGravity="bottom|end"
            app:layout_behavior="com.meiji.toutiao.widget.behavior.ScrollAwareFABBehavior"
            app:srcCompat="@drawable/ic_share_white_24dp"/>
    
    </android.support.design.widget.CoordinatorLayout>

      预览图片:

      

    2.5.活动清单配置 

    <activity
                android:name=".module.video.content.VideoContentActivity"
                android:configChanges="orientation|screenSize|uiMode"
                android:label="@string/title_video_content"
                android:theme="@style/AppTheme.NoActionBar.Slidable" />

    2.6.配置浮动按钮行为

      首先需要新建一个行为类==>ScrollAwareFABBehavior

    public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
    
        public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
                                           FloatingActionButton child,
                                           View directTargetChild,
                                           View target,
                                           int nestedScrollAxes) {
            return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL ||
                    super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
        }
    
        @Override
        public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child,
                                   View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed,
                    dyUnconsumed);
    
            if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
                child.setVisibility(View.INVISIBLE);
            } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
                child.show();
            }
        }
    }

      然后是在布局中配置行为: 

     <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="56dp"
            android:layout_height="56dp"
            android:layout_margin="16dp"
            app:elevation="16dp"
            app:layout_anchor="@id/jc_video"
            app:layout_anchorGravity="bottom|end"
            app:layout_behavior="com.jasonjan.headnews.widget.ScrollAwareFABBehavior"
            app:srcCompat="@drawable/ic_share_white_24dp"/>


    3.视频详情处理器

    3.1.源代码

    public class VideoContentPresenter extends NewsCommentPresenter implements IVideoContent.Presenter {
    
        private static final String TAG = "VideoContentPresenter";
        private IVideoContent.View view;
    
        VideoContentPresenter(IVideoContent.View view) {
            super(view);
            this.view = view;
        }
    
        private 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);
            // 将/video/urls/v/1/toutiao/mp4/{videoid}?r={Math.random()} 进行crc32加密
            CRC32 crc32 = new CRC32();
            crc32.update(s.getBytes());
            String crcString = crc32.getValue() + "";
            String url = VIDEO_HOST + s + "&s=" + crcString;
            return url;
        }
    
        private 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();
        }
    
        @Override
        public void doLoadVideoData(String videoid) {
            String url = getVideoContentApi(videoid);
            RetrofitFactory.getRetrofit().create(IVideoApi.class).getVideoContent(url)
                    .subscribeOn(Schedulers.io())
                    .map(new Function<VideoContentBean, String>() {
                        @Override
                        public String apply(@NonNull VideoContentBean videoContentBean) throws Exception {
                            VideoContentBean.DataBean.VideoListBean videoList = videoContentBean.getData().getVideo_list();
                            if (videoList.getVideo_3() != null) {
                                String base64 = videoList.getVideo_3().getMain_url();
                                String url = (new String(Base64.decode(base64.getBytes(), Base64.DEFAULT)));
                                Log.d(TAG, "getVideoUrls: " + url);
                                return url;
                            }
    
                            if (videoList.getVideo_2() != null) {
                                String base64 = videoList.getVideo_2().getMain_url();
                                String url = (new String(Base64.decode(base64.getBytes(), Base64.DEFAULT)));
                                Log.d(TAG, "getVideoUrls: " + url);
                                return url;
                            }
    
                            if (videoList.getVideo_1() != null) {
                                String base64 = videoList.getVideo_1().getMain_url();
                                String url = (new String(Base64.decode(base64.getBytes(), Base64.DEFAULT)));
                                Log.d(TAG, "getVideoUrls: " + url);
                                return url;
                            }
                            return null;
                        }
                    })
                    .compose(view.<String>bindToLife())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<String>() {
                        @Override
                        public void accept(@NonNull String s) throws Exception {
                            view.onSetVideoPlay(s);
                            view.onHideLoading();
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(@NonNull Throwable throwable) throws Exception {
                            view.onShowNetError();
                            view.onHideLoading();
                            ErrorAction.print(throwable);
                        }
                    });
        }
    }

    3.2.构造函数。传进来一个视图层。

    3.3.一个静态函数,获取视频详情的API。

      传进来一个视频Id,传出去一个url。

    3.4.一个获取随机值得函数。获取API需要加密,然后调用这个随机函数即可。

    3.5.请求视频数据的函数。

      传进来一个视频id。

      调用API获取视频详情,返回一个Observable<VideoContentBean>。

      然后将String转换成自己想要的类。

      然后在subscribe中处理之后的事件,比如视图层设置播放器等。


    4.API请求

    4.1.源代码 

    public interface IVideoApi {
        /**
         * 获取视频标题等信息
         * http://toutiao.com/api/article/recent/?source=2&category=类型&as=A105177907376A5&cp=5797C7865AD54E1&count=20"
         */
        @GET("api/article/recent/?source=2&as=A105177907376A5&cp=5797C7865AD54E1&count=30")
        Observable<ResponseBody> getVideoArticle(
                @Query("category") String category,
                @Query("_") String time);
    
        /**
         * 获取视频信息
         * Api 生成较复杂 详情查看 
         * http://ib.365yg.com/video/urls/v/1/toutiao/mp4/视频ID?r=17位随机数&s=加密结果
         */
        @GET
        Observable<VideoContentBean> getVideoContent(@Url String url); 
    }

      

    4.2.获取视频标题等信息的请求接口。

      传进去一个类型,一个时间。

      传出来一个Observable<ResponseBody>

    4.3.获取视频详情信息。

      传进去一个url。  

      传出来一个Observable<VideoContentBean>。


    5.注册视频详情类型

    5.1.调用的地方

      在视频详情活动页面的initView中 

      Register.registerVideoContentItem(adapter);

    5.2.然后在Register中注册视频详情类型

    /**
         * 注册视频详情类型
         * @param adapter
         */
        public static void registerVideoContentItem(@NonNull MultiTypeAdapter adapter) {
            adapter.register(MultiNewsArticleDataBean.class, new VideoContentHeaderViewBinder());
            adapter.register(NewsCommentBean.DataBean.CommentBean.class, new NewsCommentViewBinder());
            adapter.register(LoadingBean.class, new LoadingViewBinder());
            adapter.register(LoadingEndBean.class, new LoadingEndViewBinder());
        }

    5.3.需要注册一个视频内容头部的绑定器

    public class VideoContentHeaderViewBinder extends ItemViewBinder<MultiNewsArticleDataBean, VideoContentHeaderViewBinder.ViewHolder> {
    
        @NonNull
        @Override
        protected VideoContentHeaderViewBinder.ViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
            View view = inflater.inflate(R.layout.item_video_content_header, parent, false);
            return new ViewHolder(view);
        }
    
        @Override
        protected void onBindViewHolder(@NonNull ViewHolder holder, @NonNull MultiNewsArticleDataBean item) {
            try {
                String media_avatar_url = item.getMedia_info().getAvatar_url();
                if (!TextUtils.isEmpty(media_avatar_url)) {
                    ImageLoader.loadCenterCrop(holder.itemView.getContext(), media_avatar_url, holder.iv_media_avatar_url, R.color.viewBackground);
                }
    
                String title = item.getTitle();
                String abstractX = item.getAbstractX();
                String source = item.getSource();
    
                int video_duration = item.getVideo_duration();
                String min = String.valueOf(video_duration / 60);
                String second = String.valueOf(video_duration % 10);
                if (Integer.parseInt(second) < 10) {
                    second = "0" + second;
                }
                String tv_video_time = min + ":" + second;
                String tv_comment_count = item.getComment_count() + "";
                final String media_id = item.getMedia_info().getMedia_id();
    
                holder.tv_title.setText(title);
                holder.tv_tv_video_duration_str.setText("时长 " + tv_video_time + " | " + tv_comment_count + "评论");
                holder.tv_abstract.setText(abstractX);
                holder.tv_source.setText(source);
    
                RxView.clicks(holder.itemView)
                        .throttleFirst(1, TimeUnit.SECONDS)
                        .subscribe(new Consumer<Object>() {
                            @Override
                            public void accept(@io.reactivex.annotations.NonNull Object o) throws Exception {
                                MediaHomeActivity.launch(media_id);
                            }
                        });
            } catch (Exception e) {
                ErrorAction.print(e);
            }
        }
    
        public class ViewHolder extends RecyclerView.ViewHolder {
    
            private TextView tv_title;
            private TextView tv_tv_video_duration_str;
            private TextView tv_abstract;
            private TextView tv_source;
            private CircleImageView iv_media_avatar_url;
            private LinearLayout media_layout;
    
            public ViewHolder(View itemView) {
                super(itemView);
                this.tv_title = itemView.findViewById(R.id.tv_title);
                this.tv_tv_video_duration_str = itemView.findViewById(R.id.tv_tv_video_duration_str);
                this.tv_abstract = itemView.findViewById(R.id.tv_abstract);
                this.tv_source = itemView.findViewById(R.id.tv_extra);
                this.iv_media_avatar_url = itemView.findViewById(R.id.iv_media_avatar_url);
                this.media_layout = itemView.findViewById(R.id.media_layout);
            }
        }
    }

    5.4.还需要头部每一个item的布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        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:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">
    
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="8dp"
            android:textSize="20sp"
            tools:text="毒舌马丁催泪讲述中国式父亲,母亲曾给他下跪,现场观众感动落泪"/>
    
        <TextView
            android:id="@+id/tv_tv_video_duration_str"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingBottom="8dp"
            android:paddingTop="8dp"
            tools:text="时长 3:35"/>
    
        <TextView
            android:id="@+id/tv_abstract"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            tools:text="97年驻港部队第一次进驻香港,万人空巷欢迎,场面壮观"/>
    
        <LinearLayout
            android:id="@+id/media_layout"
            android:layout_width="wrap_content"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/selectableItemBackground"
            android:foreground="?attr/selectableItemBackground"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:padding="8dp">
    
            <com.jasonjan.headnews.widget.CircleImageView
                android:id="@+id/iv_media_avatar_url"
                android:layout_width="36dp"
                android:layout_height="36dp"
                android:scaleType="centerCrop"
                app:srcCompat="@color/viewBackground"
                tools:ignore="ContentDescription"/>
    
            <TextView
                android:id="@+id/tv_extra"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:maxLines="1"
                android:paddingLeft="8dp"
                android:paddingRight="8dp"
                android:textStyle="bold"
                tools:text="龙猫公社"/>
    
        </LinearLayout>
    
    </LinearLayout>

      效果预览:

      


    6.处理新老数据

    6.1.源代码

    package com.jasonjan.headnews.adapter;
    
    import android.support.v7.util.DiffUtil;
    import android.support.v7.widget.RecyclerView;
    
    import com.jasonjan.headnews.bean.joke.JokeCommentBean;
    import com.jasonjan.headnews.bean.joke.JokeContentBean;
    import com.jasonjan.headnews.bean.media.MediaWendaBean;
    import com.jasonjan.headnews.bean.media.MultiMediaArticleBean;
    import com.jasonjan.headnews.bean.news.MultiNewsArticleDataBean;
    import com.jasonjan.headnews.bean.news.NewsCommentBean;
    import com.jasonjan.headnews.bean.photo.PhotoArticleBean;
    import com.jasonjan.headnews.bean.wenda.WendaArticleDataBean;
    import com.jasonjan.headnews.bean.wenda.WendaContentBean;
    
    import java.util.List;
    
    /**
     * Created by JasonJan on 2017/12/6.
     */
    
    public class DiffCallback extends DiffUtil.Callback {
    
        public static final int JOKE = 1;
        public static final int PHOTO = 2;
        public static final int NEWS_COMMENT = 5;
        public static final int JOKE_COMMENT = 6;
        public static final int MUlTI_NEWS = 7;
        public static final int WENDA_ARTICLE = 8;
        public static final int WENDA_CONTENT = 9;
        public static final int SEARCH = 10;
        public static final int MUlTI_MEDIA = 11;
        public static final int MEDIA_WENDA = 12;
        private List oldList, newList;
        private int type;
    
        public DiffCallback(List oldList, List newList, int type) {
            this.oldList = oldList;
            this.newList = newList;
            this.type = type;
        }
    
        public static void notifyDataSetChanged(List oldList, List newList, int type, RecyclerView.Adapter adapter) {
            DiffCallback diffCallback = new DiffCallback(oldList, newList, type);
            DiffUtil.DiffResult result = DiffUtil.calculateDiff(diffCallback, true);
            result.dispatchUpdatesTo(adapter);
        }
    
        @Override
        public int getOldListSize() {
            return oldList != null ? oldList.size() : 0;
        }
    
        @Override
        public int getNewListSize() {
            return newList != null ? newList.size() : 0;
        }
    
        @Override
        public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
            try {
                switch (type) {
                    case JOKE:
                        return ((JokeContentBean.DataBean.GroupBean) oldList.get(oldItemPosition)).getContent().equals(
                                ((JokeContentBean.DataBean.GroupBean) newList.get(newItemPosition)).getContent());
                    case PHOTO:
                        return ((PhotoArticleBean.DataBean) oldList.get(oldItemPosition)).getTitle().equals(
                                ((PhotoArticleBean.DataBean) newList.get(newItemPosition)).getTitle());
                    case NEWS_COMMENT:
                        return ((NewsCommentBean.DataBean.CommentBean) oldList.get(oldItemPosition)).getText().equals(
                                ((NewsCommentBean.DataBean.CommentBean) newList.get(newItemPosition)).getText());
                    case JOKE_COMMENT:
                        return ((JokeCommentBean.DataBean.RecentCommentsBean) oldList.get(oldItemPosition)).getText().equals(
                                ((JokeCommentBean.DataBean.RecentCommentsBean) newList.get(newItemPosition)).getText());
                    case MUlTI_NEWS:
                        return ((MultiNewsArticleDataBean) oldList.get(oldItemPosition)).getTitle().equals(
                                ((MultiNewsArticleDataBean) newList.get(newItemPosition)).getTitle());
                    case WENDA_ARTICLE:
                        return ((WendaArticleDataBean) oldList.get(oldItemPosition)).getQuestionBean().getTitle().equals(
                                ((WendaArticleDataBean) newList.get(newItemPosition)).getQuestionBean().getTitle());
                    case WENDA_CONTENT:
                        return ((WendaContentBean.AnsListBean) oldList.get(oldItemPosition)).getAnsid().equals(
                                ((WendaContentBean.AnsListBean) newList.get(newItemPosition)).getAnsid());
    
                    case MUlTI_MEDIA:
                        return ((MultiMediaArticleBean.DataBean) oldList.get(oldItemPosition)).getTitle().equals(
                                ((MultiMediaArticleBean.DataBean) newList.get(newItemPosition)).getTitle());
                    case MEDIA_WENDA:
                        return ((MediaWendaBean.AnswerQuestionBean) oldList.get(oldItemPosition)).getQuestion().getTitle().equals(
                                ((MediaWendaBean.AnswerQuestionBean) newList.get(newItemPosition)).getQuestion().getTitle());
                }
            } catch (Exception e) {
    //            ErrorAction.print(e);
            }
            return false;
        }
    
        @Override
        public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
            try {
                switch (type) {
                    case JOKE:
                        return ((JokeContentBean.DataBean.GroupBean) oldList.get(oldItemPosition)).getShare_url().equals(
                                ((JokeContentBean.DataBean.GroupBean) newList.get(newItemPosition)).getShare_url());
                    case PHOTO:
                        return ((PhotoArticleBean.DataBean) oldList.get(oldItemPosition)).getSource_url().equals(
                                ((PhotoArticleBean.DataBean) newList.get(newItemPosition)).getSource_url());
                    case NEWS_COMMENT:
                        return ((NewsCommentBean.DataBean.CommentBean) oldList.get(oldItemPosition)).getUser_name().equals(
                                ((NewsCommentBean.DataBean.CommentBean) newList.get(newItemPosition)).getUser_name());
                    case JOKE_COMMENT:
                        return ((JokeCommentBean.DataBean.RecentCommentsBean) oldList.get(oldItemPosition)).getId() ==
                                ((JokeCommentBean.DataBean.RecentCommentsBean) newList.get(newItemPosition)).getId();
                    case MUlTI_NEWS:
                        return ((MultiNewsArticleDataBean) oldList.get(oldItemPosition)).getItem_id() ==
                                ((MultiNewsArticleDataBean) newList.get(newItemPosition)).getItem_id();
                    case WENDA_ARTICLE:
                        return ((WendaArticleDataBean) oldList.get(oldItemPosition)).getQuestionBean().getContent().equals(
                                ((WendaArticleDataBean) newList.get(newItemPosition)).getQuestionBean().getContent());
                    case WENDA_CONTENT:
                        return ((WendaContentBean.AnsListBean) oldList.get(oldItemPosition)).getAns_url().equals(
                                ((WendaContentBean.AnsListBean) newList.get(newItemPosition)).getAns_url());
    
                    case MUlTI_MEDIA:
                        return ((MultiMediaArticleBean.DataBean) oldList.get(oldItemPosition)).getAbstractX().equals(
                                ((MultiMediaArticleBean.DataBean) newList.get(newItemPosition)).getAbstractX());
                    case MEDIA_WENDA:
                        return ((MediaWendaBean.AnswerQuestionBean) oldList.get(oldItemPosition)).getAnswer().getAnsid().equals(
                                ((MediaWendaBean.AnswerQuestionBean) newList.get(newItemPosition)).getAnswer().getAnsid());
                }
            } catch (Exception e) {
    //            ErrorAction.print(e);
            }
            return false;
        }
    }

    6.2.说明一下

      因为处理新老数据代码很相似

      所以将这个类封装起来

      按照不同的参数区分不同的类型。 



  • 相关阅读:
    jsonp
    web系统中上下移动功能的实现
    重载的目的是什么
    重写和重载
    final和static
    static的应用
    shiro认证
    做阉割版Salesforce难成伟大的TOB企业
    Go语言在国产CPU平台上应用前景的探索与思考
    101 More Security Best Practices for Kubernetes
  • 原文地址:https://www.cnblogs.com/Jason-Jan/p/8267731.html
Copyright © 2020-2023  润新知