zoukankan      html  css  js  c++  java
  • 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.说明一下

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

      所以将这个类封装起来

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



  • 相关阅读:
    Kafka 生产者 自定义分区策略
    同步互斥
    poj 1562 Oil Deposits(dfs)
    poj 2386 Lake Counting(dfs)
    poj 1915 KnightMoves(bfs)
    poj 1664 放苹果(dfs)
    poj 1543 Perfect Cubes (暴搜)
    poj 1166 The Clocks (暴搜)
    poj 3126 Prime Path(bfs)
    处理机调度
  • 原文地址:https://www.cnblogs.com/Jason-Jan/p/8267731.html
Copyright © 2011-2022 走看看