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 视频详情页面的更多相关文章

  1. TouTiao开源项目 分析笔记20 问答详情

    1.效果预览 1.1.效果预览,从问答列表开始 前面实现了从列表到内容. 这里主要讲解从内容到详情. 点击每一个回答内容,进入回答详情页面. 1.2.触发的点击事件 在WendaContentView ...

  2. TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现

    1.预览效果 1.1.首先看一下需要实现的效果. 第一种,文字类型新闻. 第二种,图片类型新闻. 1.2.在NewsArticleTextViewBinder中设置了点击事件 RxView.click ...

  3. TouTiao开源项目 分析笔记2

    1.Constant常量定义类 1.1.源代码 public class Constant { public static final String USER_AGENT_MOBILE = " ...

  4. TouTiao开源项目 分析笔记14 段子评论

    1.段子页面详情 1.1.先看看预览界面吧 左边的页面已经实现了,现在的目的就是要实现点击左侧的每一个item 然后跳转到右边相应的段子详情页面. 1.2.首先肯定有右侧这个活动==>JokeC ...

  5. TouTiao开源项目 分析笔记12 从总体到局部 构建视频主页面

    1.构建视频主列表的整体碎片VideoTabLayout 1.1.首先创建一个VideoTabLayout package com.jasonjan.headnews.module.video; im ...

  6. TouTiao开源项目 分析笔记17 新闻媒体专栏

    1.效果预览 1.1.要实现的效果 1.2.如何调转到新闻媒体专栏 点击右上角的用户图标. 在新闻详情页面的Fragment的菜单点击事件中触发. case R.id.action_open_medi ...

  7. TouTiao开源项目 分析笔记10 实现通用普通文章片段页面

    1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...

  8. TouTiao开源项目 分析笔记6

    1.NewsChannelBean简单类笔记 1.1.Comparable接口的实现和使用 参考文章:Comparable接口的实现和使用. 因为NewsChannelBean实现了Comparabl ...

  9. TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架

    1.效果预览 1.1.如下图所以,到目前为止所有的功能. 2.从InitApp开始->SplashActivity->MainActivity 2.1.InitApp源代码.这是整个项目的 ...

随机推荐

  1. 【Android学习入门】Android中activity的启动模式

    启动模式简单地说就是Activity启动时的策略,在Androidmanifest.xml文件中的标签android:launchMode属性设置,在Android中Activity共有四种启动模式分 ...

  2. ScrollView监听滑动到顶部和底部的方法

    不需要监听滑动位置,只需要重写ScrollView的onOverScrolled和stopNestedScroll方法就可以了 public class ReadScrollView extends ...

  3. android studio gradle统一管理版本

    创建config.gradle ext { android = [ compileSdkVersion : 26, buildToolsVersion : "26.0.2", mi ...

  4. u-boot分析(九)----nand flash初始化|nand flash读写分析

    u-boot分析(九) 上篇博文我们按照210的启动流程,分析到了初始化串口,由于接下来的取消存储保护不是很重要,所以我们今天按照u-boot的启动流程对nand flash初始化进行分析. 今天我们 ...

  5. Java—字符串

    字符串 在java中,字符串被作为String类型的对象处理.String类位于java.lang包中,默认情况下,该包被自动导入所有的程序. 创建String对象的方法: String s1 = & ...

  6. Eclipse导入web项目后,run列表中没有run on server?

    Eclipse导入web项目,没有run列表中run on server? 首先确保正确安装Tomcat和JDK .找到对于web项目的文件夹,打开文件夹下.project文件 <?xml ve ...

  7. 2017年10月31日结束Outlook 2007与Office 365的连接

    2017 年10月31日 ,微软即将推出 Office 365中Exchange Online邮箱将需要Outlook for Windows的连接,即通过HTTP Over MAPI方式,传统使用R ...

  8. ring0 SSDTHook

    SSDT 的全称是 System Services Descriptor Table,系统服务描述符表.这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来. ...

  9. leetcode: 数组

    1. longest-consecutive-sequence Given an unsorted array of integers, find the length of the longest ...

  10. 1.08 在select语句使用条件逻辑

    问题:要在select语句中,对数值执行if-else操作.例如,要产生一个结果集,如果一个员工工资小于等于2000美金,就返回消息”underpaid”:如果大于等于4000美金:就返回消息”ove ...