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 + "\n" + 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();
- }
- }
①首先是启动函数,传入一个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.说明一下
因为处理新老数据代码很相似
所以将这个类封装起来
按照不同的参数区分不同的类型。
TouTiao开源项目 分析笔记18 视频详情页面的更多相关文章
- TouTiao开源项目 分析笔记20 问答详情
1.效果预览 1.1.效果预览,从问答列表开始 前面实现了从列表到内容. 这里主要讲解从内容到详情. 点击每一个回答内容,进入回答详情页面. 1.2.触发的点击事件 在WendaContentView ...
- TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现
1.预览效果 1.1.首先看一下需要实现的效果. 第一种,文字类型新闻. 第二种,图片类型新闻. 1.2.在NewsArticleTextViewBinder中设置了点击事件 RxView.click ...
- TouTiao开源项目 分析笔记2
1.Constant常量定义类 1.1.源代码 public class Constant { public static final String USER_AGENT_MOBILE = " ...
- TouTiao开源项目 分析笔记14 段子评论
1.段子页面详情 1.1.先看看预览界面吧 左边的页面已经实现了,现在的目的就是要实现点击左侧的每一个item 然后跳转到右边相应的段子详情页面. 1.2.首先肯定有右侧这个活动==>JokeC ...
- TouTiao开源项目 分析笔记12 从总体到局部 构建视频主页面
1.构建视频主列表的整体碎片VideoTabLayout 1.1.首先创建一个VideoTabLayout package com.jasonjan.headnews.module.video; im ...
- TouTiao开源项目 分析笔记17 新闻媒体专栏
1.效果预览 1.1.要实现的效果 1.2.如何调转到新闻媒体专栏 点击右上角的用户图标. 在新闻详情页面的Fragment的菜单点击事件中触发. case R.id.action_open_medi ...
- TouTiao开源项目 分析笔记10 实现通用普通文章片段页面
1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...
- TouTiao开源项目 分析笔记6
1.NewsChannelBean简单类笔记 1.1.Comparable接口的实现和使用 参考文章:Comparable接口的实现和使用. 因为NewsChannelBean实现了Comparabl ...
- TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架
1.效果预览 1.1.如下图所以,到目前为止所有的功能. 2.从InitApp开始->SplashActivity->MainActivity 2.1.InitApp源代码.这是整个项目的 ...
随机推荐
- Springboot中SpringMvc拦截器配置与应用(实战)
一.什么是拦截器,及其作用 拦截器(Interceptor): 用于在某个方法被访问之前进行拦截,然后在方法执行之前或之后加入某些操作,其实就是AOP的一种实现策略.它通过动态拦截Action调用的对 ...
- Linux命令之查看内存和CPU消耗命令TOP使用
1)输入top ,按enter键,即可查看服务器内存消耗情况 注意:其中PID表示进程号 :%cpu表示cpu消耗情况:%M表示内存消耗情况:通常在做性能测试的时候用到该命令: 默认为实时刷新:按s键 ...
- 修改Linux中发送邮件中附件大小的限制
方法一: 在命令中设定postfix的message_size_limit值 (但系统重启后会失效) postconf -e "message_size_limit = 20480000&q ...
- CRM和C4C product category hierarchy的可编辑性控制逻辑
CRM 从ERP导入到CRM系统的Product Hierarchy,在CRM系统切换成编辑模式时,会收到一条提示信息: Hierarchy XXX may only be changed in th ...
- Fiddler-1 官网下载及安装
1 进入Fiddler官网:http://www.telerik.com/fiddler 点击[Free download]:填写一些信息后就可以下载. 2 双击安装包--下一步dinghanhua下 ...
- QT5中两个窗体之间传递信息(值)
一个窗体A调用另一个窗体B: 1)包含窗体B的头文件#include"B.h" 2)在窗体A中增加slots函数: public slots: void infoRecv(QStr ...
- 【JavaScript 封装库】BETA 4.0 测试版发布!
/* 源码作者: 石不易(Louis Shi) 联系方式: http://www.shibuyi.net =============================================== ...
- IOS 发布程序(打包上传)
• 发布程序的主要步骤 登录开发者主页 生成cer证书:cer是一个跟电脑相关联的证书文件,让电脑具备发布程序的功能 添加App ID:发布哪些app? 生成MobileProvision文件:生成一 ...
- 二叉树遍历,先序序列+中序序列=后序序列,Poj(2255)
这里我参考了JHF大神的写法啦,直接把输出写在了建树的过程中了. 思路: 先根据先序序列找到根节点,在找该节点在中序序列中的位置,这样,左右子树有分开了.这里的细节值得注意一下,不然很容易建树出错.( ...
- vuejs使用组件的细节点
is属性 <div id='root'> <table> <tbody> <row></row> <row></row&g ...