想做一个能够实现多种类型布局放在一个RecyclerView中的页面,Demo最终的效果是下面这个样子:
可以看到,整个列表中,有视频布局,纯文本的笑话布局,长图片布局,动态图片布局;最后两个可以合并在一个布局里去展示。
所以首先,要显示上面的东西需要用到下面这些控件:
1.节操播放器(大概是很久以前的名字了,这个控件经常改名字)
github地址:https://github.com/lipangit/JiaoZiVideoPlayer
使用方法是添加依赖:
compile 'cn.jzvd:jiaozivideoplayer:6.1.2'
2.Glide,这个就不谈了:
compile 'com.github.bumptech.glide:glide:3.7.0'
3.RecyclerView,这个更不谈了,反正和V7库什么的一起添加依赖吧。还有联网权限就不说了。
之前是使用的ListView去实现这个效果的,不过现在RecyclerView才是大势所趋呀(我是看《第二行代码》里面郭神说的),反正ListView能做的,RecyclerView都可以。
虽然使用过多次RecyclerView,但是具体怎么去实现这么一个效果还是有点小麻烦的,所以就上网看到了下面这两篇大佬(人人皆大佬,唯我一小生)的文章,简洁明了,蛮容易看懂:
RecyclerView用法(一)——展示多种类型Item数据
http://blog.csdn.net/cxc19890214/article/details/49226743
RecyclerView实现多种item布局
http://blog.csdn.net/zhumintao/article/details/53023920
想快速上手的小伙伴建议先去看上面的就OK了,我只是想记录一下我的学习成果,所以写的这篇文章。
如果是在ListView中去实现这样的效果,需要多写两个方法
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
@Override
public int getViewTypeCount() {
return super.getViewTypeCount();
}
一个是获取item的类型,另外一个是获取所有类型的数量,如果有5种类型,下面那个方法就返回5。
但是在RecyclerView中,只能看得到第一个方法,而第二个方法就相当于onCreateViewHolder(ViewGroup parent, int viewType)中的第二个参数。
所以创建一个RecyclerView的适配器TypesNewsRecyclerAdapter;
public class TypesNewsRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{}
之前只有一种数据的时候,上面的ViewHolder就是这种数据的ViewHolder,不过现在有多种数据了,ViewHolder自然不能写成某一种,而是需要写成RecyclerView.ViewHolder,然后同样让所有类型的ViewHolder的继承RecyclerView.ViewHolder。
然后我需要三种类型的ViewHolder,分别是视频,文本,图片,所以首先创建三个标识符:
private static final int TYPE_VIDEO = 0; //视频类型
private static final int TYPE_IMAGE = 1; //图片类型
private static final int TYPE_TEXT = 2; //文本类型
接着在getItemViewType中分别给从网络返回的type数据赋值:
@Override
public int getItemViewType(int position) {
ManyTypesNewsBean.ShowapiResBodyBean.PagebeanBean.ContentlistBean data = dataList.get(position);
String type = data.getType();
int itemViewType = -1;
if ("41".equals(type)) {
itemViewType = TYPE_VIDEO;
} else if ("10".equals(type)) {
itemViewType = TYPE_IMAGE;
} else if ("29".equals(type)) {
itemViewType = TYPE_TEXT;
}
return itemViewType;
}
然后为每种类型创建对应的ViewHolder:
class VideoViewHolder extends RecyclerView.ViewHolder{
public VideoViewHolder(View itemView) {
super(itemView);
}
}
class ImageViewHolder extends RecyclerView.ViewHolder{
public ImageViewHolder(View itemView) {
super(itemView);
}
}
class TextViewHolder extends RecyclerView.ViewHolder{
public TextViewHolder(View itemView) {
super(itemView);
}
}
可以从最上面的最终效果看到,每一种布局都有相同的部分,把这一部分的实例写在同一个类中,然后初始化布局写在同一个方法中,比如下面这样:
//公共视图
static class CommonView{
...
//用户信息
ImageView iv_headpic;
TextView tv_name;
...
}
//初始化方法
private void initCommonView(View itemView, CommonView commonView) {
...
commonView.iv_headpic = (ImageView) itemView.findViewById(R.id.iv_headpic);
commonView.tv_name = (TextView) itemView.findViewById(R.id.tv_name);
...
}
接着非常简单的,在之前创建的各类型对应的ViewHolder把这些公共部分添加进去,同时初始化该类型特有的控件:
class VideoViewHolder extends RecyclerView.ViewHolder{
CommonView commonView = new CommonView();
JZVideoPlayerStandard jcv_videoplayer; // 节操播放器
public VideoViewHolder(View itemView) {
super(itemView);
//在这里实例化特有的
jcv_videoplayer = (JZVideoPlayerStandard) itemView.findViewById(R.id.jcv_videoplayer);
initCommonView(itemView, commonView);
}
}
class ImageViewHolder extends RecyclerView.ViewHolder{
CommonView commonView = new CommonView();
ImageView iv_image_icon;
public ImageViewHolder(View itemView) {
super(itemView);
iv_image_icon = (ImageView) itemView.findViewById(R.id.iv_image_icon);
initCommonView(itemView, commonView);
}
}
class TextViewHolder extends RecyclerView.ViewHolder{
CommonView commonView = new CommonView();
public TextViewHolder(View itemView) {
super(itemView);
initCommonView(itemView, commonView);
}
}
初始化布局基本上就完成了,接下来就是要把从网络获取的数据放到布局上,首先还是关于公共部分的数据获取,把所有公共视图的数据获取放在一个方法中,也可以避免很多重复的代码,比如:
private void bindCommonData(CommonView view, ManyTypesNewsBean.ShowapiResBodyBean.PagebeanBean.ContentlistBean data) {
if (data.getProfile_image() != null){
Glide.with(context).load(data.getProfile_image())
.diskCacheStrategy(DiskCacheStrategy.ALL)
.skipMemoryCache(false)
.error(R.drawable.user)
.into(view.iv_headpic);
}
if (data.getName() != null){
view.tv_name.setText(data.getName());
}
view.tv_time_refresh.setText(data.getCreate_time());
...
}
之后就是剩下的各类ViewHolder中的数据获取了,在onBindViewHolder方法中:
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ManyTypesNewsBean.ShowapiResBodyBean.PagebeanBean.ContentlistBean data = dataList.get(position);
if (holder instanceof VideoViewHolder){
VideoViewHolder videoViewHolder = (VideoViewHolder) holder;
bindCommonData(videoViewHolder.commonView, data);
videoViewHolder.jcv_videoplayer.setUp(data.getVideo_uri(), JZVideoPlayerStandard.SCREEN_WINDOW_NORMAL, "");
} else if (holder instanceof ImageViewHolder){
ImageViewHolder imageViewHolder = (ImageViewHolder) holder;
bindCommonData(imageViewHolder.commonView, data);
imageViewHolder.iv_image_icon.setImageResource(R.drawable.bg_item);
if(data.getImage0() != null){
Glide.with(context).load(data.getImage0()).placeholder(R.drawable.bg_item).error(R.drawable.bg_item).diskCacheStrategy(DiskCacheStrategy.ALL).into(imageViewHolder.iv_image_icon);
}
} else if (holder instanceof TextViewHolder){
TextViewHolder textViewHolder = (TextViewHolder) holder;
bindCommonData(textViewHolder.commonView, data);
}
}
需要注意到,上面节操播放器的用法,setUp这个方法接受三个参数,第一个是视频地址,第二个一种播放窗口的模式,第三个是视频的标题,没有就填”“,不能填null,不然会报错,节操播放器的控件在布局中差不多是这么用:
.jzvd.JZVideoPlayerStandard
android:id="@+id/jcv_videoplayer"
android:layout_width="match_parent"
android:layout_height="220dp" />
最后是RecyclerView适配器的完整代码,关于布局的代码就不写了,因为非常非常多~~~:
public class TypesNewsRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_VIDEO = 0; //视频类型
private static final int TYPE_IMAGE = 1; //图片类型
private static final int TYPE_TEXT = 2; //文本类型
private static final int TYPE_SOUND = 3; //声音类型
private Context context;
private List dataList;
public TypesNewsRecyclerAdapter(Context context, List dataList) {
this.context = context;
this.dataList = dataList;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (context != null){
this.context = parent.getContext();
}
View view;
if(viewType == TYPE_VIDEO){
view = View.inflate(context, R.layout.all_video_item, null);
return new VideoViewHolder(view);
} else if(viewType == TYPE_IMAGE){
view = View.inflate(context, R.layout.all_image_item, null);
return new ImageViewHolder(view);
} else {
view =View.inflate(context, R.layout.all_text_item, null);
return new TextViewHolder(view);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ManyTypesNewsBean.ShowapiResBodyBean.PagebeanBean.ContentlistBean data = dataList.get(position);
if (holder instanceof VideoViewHolder){
VideoViewHolder videoViewHolder = (VideoViewHolder) holder;
bindCommonData(videoViewHolder.commonView, data);
videoViewHolder.jcv_videoplayer.setUp(data.getVideo_uri(), JZVideoPlayerStandard.SCREEN_WINDOW_NORMAL, "");
} else if (holder instanceof ImageViewHolder){
ImageViewHolder imageViewHolder = (ImageViewHolder) holder;
bindCommonData(imageViewHolder.commonView, data);
imageViewHolder.iv_image_icon.setImageResource(R.drawable.bg_item);
if(data.getImage0() != null){
Glide.with(context).load(data.getImage0()).placeholder(R.drawable.bg_item).error(R.drawable.bg_item).diskCacheStrategy(DiskCacheStrategy.ALL).into(imageViewHolder.iv_image_icon);
}
} else if (holder instanceof TextViewHolder){
TextViewHolder textViewHolder = (TextViewHolder) holder;
bindCommonData(textViewHolder.commonView, data);
}
}
private void bindCommonData(CommonView view, ManyTypesNewsBean.ShowapiResBodyBean.PagebeanBean.ContentlistBean data) {
if (data.getProfile_image() != null){
Glide.with(context).load(data.getProfile_image())
.diskCacheStrategy(DiskCacheStrategy.ALL)
.skipMemoryCache(false)
.error(R.drawable.user)
.into(view.iv_headpic);
}
if (data.getName() != null){
view.tv_name.setText(data.getName());
}
view.tv_time_refresh.setText(data.getCreate_time());
view.tv_shenhe_ding_number.setText(data.getLove());
view.tv_shenhe_cai_number.setText(data.getHate());
view.tv_context.setText(data.getText());
}
@Override
public int getItemViewType(int position) {
ManyTypesNewsBean.ShowapiResBodyBean.PagebeanBean.ContentlistBean data = dataList.get(position);
String type = data.getType();
int itemViewType = -1;
if ("41".equals(type)) {
itemViewType = TYPE_VIDEO;
} else if ("10".equals(type)) {
itemViewType = TYPE_IMAGE;
} else if ("29".equals(type)) {
itemViewType = TYPE_TEXT;
}
// else if ("31".equals(type)) {
// itemViewType = TYPE_SOUND;
// }
return itemViewType;
}
@Override
public int getItemCount() {
return dataList.size();
}
class VideoViewHolder extends RecyclerView.ViewHolder{
CommonView commonView = new CommonView();
JZVideoPlayerStandard jcv_videoplayer;
public VideoViewHolder(View itemView) {
super(itemView);
//在这里实例化特有的
jcv_videoplayer = (JZVideoPlayerStandard) itemView.findViewById(R.id.jcv_videoplayer);
initCommonView(itemView, commonView);
}
}
class ImageViewHolder extends RecyclerView.ViewHolder{
CommonView commonView = new CommonView();
ImageView iv_image_icon;
public ImageViewHolder(View itemView) {
super(itemView);
iv_image_icon = (ImageView) itemView.findViewById(R.id.iv_image_icon);
initCommonView(itemView, commonView);
}
}
class TextViewHolder extends RecyclerView.ViewHolder{
CommonView commonView = new CommonView();
public TextViewHolder(View itemView) {
super(itemView);
initCommonView(itemView, commonView);
}
}
//公共视图
static class CommonView{
//用户信息
ImageView iv_headpic;
TextView tv_name;
TextView tv_time_refresh;
ImageView iv_right_more;
//顶和赞
TextView tv_shenhe_ding_number;
TextView tv_shenhe_cai_number;
// //下载栏
// LinearLayout ll_download;
//中间公共部分 -所有的都有
TextView tv_context;
}
private void initCommonView(View itemView, CommonView commonView) {
commonView.tv_context = (TextView) itemView.findViewById(R.id.tv_context);
commonView.iv_headpic = (ImageView) itemView.findViewById(R.id.iv_headpic);
commonView.tv_name = (TextView) itemView.findViewById(R.id.tv_name);
commonView.tv_time_refresh = (TextView) itemView.findViewById(R.id.tv_time_refresh);
commonView.iv_right_more = (ImageView) itemView.findViewById(R.id.iv_right_more);
commonView.tv_shenhe_ding_number = (TextView) itemView.findViewById(R.id.tv_shenhe_ding_number);
commonView.tv_shenhe_cai_number = (TextView) itemView.findViewById(R.id.tv_shenhe_cai_number);
// commonView.ll_download = (LinearLayout) itemView.findViewById(R.id.ll_download);
}
}
这就用到了自定义Dialog,在网上看到了一篇非常不错的文章,如下:
PhotoView+Glide+Dialog+ViewPager:打造轻量级的图片浏览方案
http://www.jianshu.com/p/629300228921
我照着文章做了一个结合PhotoView显示长图的Dialog,首先自定义Dialog的显示效果,在style中添加:
--全屏背景半透明 dialog-->
然后自定义Dialog的布局文件:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingTop="5dp">
<uk.co.senab.photoview.PhotoView
android:id="@+id/pv_long"
android:scaleType="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="15dp"
android:layout_marginTop="15dp"/>
<TextView
android:id="@+id/tv_image_index"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="@android:color/white"/>
FrameLayout>
接着是自定义创建一个Config类,用于设置Dialog的宽和高:
public class Config {
public static int EXACT_SCREEN_HEIGHT;
public static int EXACT_SCREEN_WIDTH;
}
接下来开始自定义Dialog:
public class ShowImagesDialog extends Dialog {
private Context context;
private View view ;
private PhotoView photoView;
String url;
public ShowImagesDialog(@NonNull Context context, String url) {
super(context, R.style.transparentBgDialog); //这里用到了自定义Dialog的样式
this.context = context;
this.url = url;
initView();
initData();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(view);
Window window = getWindow();
WindowManager.LayoutParams wl = window.getAttributes();
wl.x = 0;
wl.y = 0;
wl.height = Config.EXACT_SCREEN_HEIGHT;
wl.width = Config.EXACT_SCREEN_WIDTH;
wl.gravity = Gravity.CENTER;
window.setAttributes(wl);
}
private void initView() {
view = View.inflate(context, R.layout.dialog_image, null);
photoView = (PhotoView) view.findViewById(R.id.pv_long);
}
private void initData(){
PhotoViewAttacher.OnPhotoTapListener listener = new PhotoViewAttacher.OnPhotoTapListener() {
@Override
public void onPhotoTap(View view, float x, float y) {
dismiss();
}
};
photoView.setOnPhotoTapListener(listener);
Glide.with(context)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.bg_item)
.error(R.drawable.bg_item)
.into(new SimpleTarget() { //这个灰常重要,作用是让图片清晰地显示
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation super GlideDrawable> glideAnimation) {
photoView.setImageDrawable(resource);
}
});
}
}
最后在TypesNewsRecyclerAdapter的onBindViewHolder方法中添加:
else if (holder instanceof ImageViewHolder){
ImageViewHolder imageViewHolder = (ImageViewHolder) holder;
bindCommonData(imageViewHolder.commonView, data);
imageViewHolder.iv_image_icon.setImageResource(R.drawable.bg_item);
if(data.getImage0() != null){
Glide.with(context).load(data.getImage0()).placeholder(R.drawable.bg_item).error(R.drawable.bg_item).diskCacheStrategy(DiskCacheStrategy.RESULT).into(imageViewHolder.iv_image_icon);
getDeviceDensity();
if (data.getImage0().indexOf("gif") == -1){
imageViewHolder.iv_image_icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new ShowImagesDialog(context, data.getImage0()).show();
}
});
}
}
}
看一下效果吧!