RecyclerView花样布局

来源:互联网 发布:js随机整数的函数 编辑:程序博客网 时间:2024/04/26 04:02
目录:
    1.什么是花样布局?
    2.实现原理与要点分析
    3.RecyclerView多类型item布局实现分类电影展示

  1.什么是花样布局?
        花样布局也就是我们经常说的多布局,一个RecyclerView实现多种布局样式,有时候业务需求或者说是解决UI单调性我们需要不同的布局
来显示不同的数据。

   2.实现原理与要点分析
        实现原理:简单来讲就是通过不同的数据来加载不同的布局显示,说起来简单做起来就不辣么容易了。
        要点分析:假设大家都会简单使用RecyclerView并实现简单的布局,如果不懂可以先看看我之前的文章 Android抢Listview饭碗之RecyclerView浅析
以及RecyclerView分割线  对RecyclerView有个简单的了解。前面两篇文章都是简单的单一布局使用,而并没有用到今天的猪脚getItemViewType(),下面我们来看看源码对他的描述。
/**
* Return the view type of the item at <code>position</code> for the purposes
* of view recycling.
*
* <p>The default implementation of this method returns 0, making the assumption of
* a single view type for the adapter. Unlike ListView adapters, types need not
* be contiguous. Consider using id resources to uniquely identify item view types.
*
* @param position position to query
* @return integer value identifying the type of the view needed to represent the item at
* <code>position</code>. Type codes need not be contiguous.
*/
public int getItemViewType(int position) {
return 0;
}
源码描述该方法的大概意思是说该方法返回指定位置(position)的view类型(viewType)。其实细心的朋友可能看到onCreateViewHolder(ViewGroup parent, int viewType)中有viewType参数,对,你猜的没错,该方法就是根据getItemViewType()方法返回的类型,然后根据不同的类型去选择加载不同的布局,以此来达到显示不同布局的效果。接下来我们来实现一个多类型item布局实现分类电影展示。

3.RecyclerView多类型item布局实现分类电影展示
  先上几张效果图(图片都来自时光网,非商用只做学习所用,应该不犯法吧?)
  
   
我都忍不住夸我寄几,炫酷吧,其实关键还是时光网的设计师NB,必须表个白,说一句我爱你,哈哈哈!

下面贴出代码,注释都在代码里面,废话不多说。
 
  3.1 自定义适配器类MultiRecyclerViewAdapter.java
package com.example.recyclerviewdemo;
 
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
 
import java.util.ArrayList;
import java.util.HashMap;
 
/**
* Created by elimy on 2017-01-10.
*/
public class MultiRecyclerViewAdapter extends RecyclerView.Adapter {
 
private Context context;
/* final int TITLE_LAYOUT=1;*/
final int TITLE_ONE_LAYOUT=1;
final int TITLE_TWO_LAYOUT=4;
final int NEWS_LAYOUT=2;
final int MOVIES_LAYOUT=3;
ArrayList<TitleObject> titleList;
ArrayList<NewsObject> newsList;
ArrayList<MoviesObject> movieList;
ArrayList<Integer> typeArray=new ArrayList<>();
HashMap<Integer,Integer> typePosition=new HashMap<>();
 
public MultiRecyclerViewAdapter(Context context, ArrayList<TitleObject> titleList, ArrayList<NewsObject> newsList, ArrayList<MoviesObject> moviesList) {
this.context=context;
this.titleList=titleList;
this.newsList=newsList;
this.movieList=moviesList;
 
//addTypeListToTypeArray(titleList,TITLE_LAYOUT);
 
//这里我为了把标题拆分开,所以没用直接调用上面这句代码,上一段代码的效果将导致标题占据第一、二条而没有到达我们
// 想要的效果
typePosition.put(TITLE_ONE_LAYOUT,typeArray.size());
typeArray.add(TITLE_ONE_LAYOUT);
Log.d("TAG","size="+typeArray.size());
 
addTypeListToTypeArray(newsList,NEWS_LAYOUT);
Log.d("TAG","size="+typeArray.size());
 
typePosition.put(TITLE_TWO_LAYOUT,typeArray.size());
typeArray.add(TITLE_TWO_LAYOUT);
Log.d("TAG","size="+typeArray.size());
 
addTypeListToTypeArray(moviesList,MOVIES_LAYOUT);
Log.d("TAG","size="+typeArray.size());
 
}
 
/*
* 将item位置与类型对应保存起来,同时保存每一种类型数据的起始位置
* 方便后面绑定数据的时候获取到对应类型的对应位置数据
* */
private void addTypeListToTypeArray(ArrayList list,int type){
//记录每一种类型的初始记录位置
typePosition.put(type,typeArray.size());
//相当于把3类List放入一个统一的List,建立起了typeArray下标和list类型的对应关系
for (int i=0;i<list.size();i++){
typeArray.add(type);
}
 
}
 
/*
* 根据不同的类型加载不同的布局
* */
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=null;
if (viewType==TITLE_ONE_LAYOUT){
view= LayoutInflater.from(context).inflate(R.layout.title_item_layout,parent,false);
return new TitleViewHolder(view);
}else if(viewType==TITLE_TWO_LAYOUT){
view= LayoutInflater.from(context).inflate(R.layout.title_item_layout,parent,false);
return new TitleViewHolder(view);
} else if(viewType==NEWS_LAYOUT){
view=LayoutInflater.from(context).inflate(R.layout.news_item_layout,parent,false);
return new NewsViewHolder(view);
}else{
view=LayoutInflater.from(context).inflate(R.layout.movies_item_layout,parent,false);
return new MovieViewHolder(view);
}
}
 
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
 
//这里的position是等同于在typeArray的位置,实际的position需要减去该类型在typeArray的起始位置值,这样得出
// 的结果才是在自己的List中的位置
int truePosition=position-typePosition.get(getItemViewType(position));
 
if (holder instanceof TitleViewHolder){
 
/*
* 由于我将一个List的数据拆分到不同的位置显示,所以下面看起来有点绕了,实际开发可以把数据直接组装好以后再
* 设置布局显示
* */
if (getItemViewType(position)==TITLE_TWO_LAYOUT){
//这里truePosition+1是为了避免TITLE_TWO_LAYOUT和TITLE_ONE_LAYOUT在TitleList中取到同一个标题
TitleObject titleObject= titleList.get(truePosition+1);
((TitleViewHolder) holder).textView.setText(titleObject.getTitle());
}else {
TitleObject titleObject = titleList.get(truePosition);
((TitleViewHolder) holder).textView.setText(titleObject.getTitle());
}
}else if(holder instanceof NewsViewHolder){
NewsObject newsObject = newsList.get(truePosition);
((NewsViewHolder) holder).newsImages.setImageResource(newsObject.getNewsImage());
((NewsViewHolder) holder).newsTitle.setText(newsObject.getNewsTitle());
((NewsViewHolder) holder).newsDesc.setText(newsObject.getNewsDesc());
}else {
MoviesObject moviesObject=movieList.get(truePosition);
((MovieViewHolder)holder).moviesImage.setImageResource(moviesObject.getMovieImage());
((MovieViewHolder)holder).moviesName.setText(moviesObject.getMovieName());
((MovieViewHolder)holder).moviesType.setText(moviesObject.getMovieType());
((MovieViewHolder)holder).moviesDesc.setText(moviesObject.getMovieDesc());
}
}
 
@Override
public int getItemCount() {
//typeArray的长度就相当于3个list长度总和
return typeArray.size();
}
 
/*
* 这里设置不同的位置返回不同的View类型,以此来达到混合多样式布局的效果
* */
@Override
public int getItemViewType(int position) {
 
//因为之前就是将类型和位置对应起来存的,所以这样就直接根据位置来获取类型
return typeArray.get(position);
}
 
/*
* 标题布局ViewHolder
* */
class TitleViewHolder extends RecyclerView.ViewHolder{
 
TextView textView;
 
public TitleViewHolder(View itemView) {
super(itemView);
textView= (TextView) itemView.findViewById(R.id.title_tv);
}
}
 
/*
* 新闻布局ViewHolder
* */
class NewsViewHolder extends RecyclerView.ViewHolder{
ImageView newsImages;
TextView newsTitle;
TextView newsDesc;
 
public NewsViewHolder(View itemView) {
super(itemView);
newsImages= (ImageView) itemView.findViewById(R.id.news_image_view);
newsTitle= (TextView) itemView.findViewById(R.id.news_title);
newsDesc= (TextView) itemView.findViewById(R.id.news_desc);
}
}
/*
* 电影布局viewHolder
* */
class MovieViewHolder extends RecyclerView.ViewHolder {
ImageView moviesImage;
TextView moviesName;
TextView moviesType;
TextView moviesDesc;
 
public MovieViewHolder(View itemView) {
super(itemView);
moviesImage= (ImageView) itemView.findViewById(R.id.movie_image_view);
moviesName= (TextView) itemView.findViewById(R.id.movie_name_tv);
moviesType= (TextView) itemView.findViewById(R.id.movie_type_tv);
moviesDesc= (TextView) itemView.findViewById(R.id.movie_desc_tv);
 
}
}
}
3.2 主布局java类MultiRecyclerViewActivity.java
package com.example.recyclerviewdemo;
 
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
 
import java.util.ArrayList;
 
/**
* Created by elimy on 2017-01-10.
*/
public class MultiRecyclerViewActivity extends AppCompatActivity {
 
RecyclerView recyclerView;
MultiRecyclerViewAdapter multiAdapter;
 
ArrayList<TitleObject> titleList;
ArrayList<NewsObject> newsList;
ArrayList<MoviesObject> moviesList;
 
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.multi_recyclerview_activity);
 
//绑定布局并初始化
recyclerView= (RecyclerView) findViewById(R.id.multi_recycler_view);
 
//构造数据模型
initTitleData();
initNewsData();
initMoviesData();
 
GridLayoutManager layoutManager= new GridLayoutManager(this,2);
//根据数据类型设置GridLayoutManager的不同显示布局
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
 
/*
* 这里getSpanSize()的返回值,源码注释大概是说,该view在该显示位置的跨度,也就是说它横跨多少个单位
* 前面设置GridLayoutManager布局显示2列,所以如果返回值为2则表示占满整个宽度,如果返回1表示1/2
* */
@Override
public int getSpanSize(int position) {
if (recyclerView.getAdapter().getItemViewType(position)==1){
return 2;
}else if (recyclerView.getAdapter().getItemViewType(position)==2){
return 2;
}else if (recyclerView.getAdapter().getItemViewType(position)==4){
return 2;
} else {
return 1;
}
}
});
//初始化MultiRecyclerViewAdapter
multiAdapter=new MultiRecyclerViewAdapter(this,titleList,newsList,moviesList);
 
//设置布局和适配器
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(multiAdapter);
}
 
/*
* 构造电影数据
* */
private void initMoviesData() {
moviesList=new ArrayList<>();
 
moviesList.add(new MoviesObject(R.drawable.movie01,"情圣","113分钟 - 喜剧","这生活原来不过是一场春梦"));
moviesList.add(new MoviesObject(R.drawable.movie02,"铁道飞虎","123分钟 - 动作 / 喜剧","铁道版《虎口脱险》够搞笑"));
moviesList.add(new MoviesObject(R.drawable.movie03,"长城","104分钟 - 奇幻 / 冒险","来自张艺谋的视觉饕餮盛宴"));
moviesList.add(new MoviesObject(R.drawable.movie04,"猪猪侠之英雄猪少年","90分钟 - 动画","憨萌笨小猪拯救世界"));
moviesList.add(new MoviesObject(R.drawable.movie05,"血战钢锯岭","139分钟 - 剧情 / 历史","加菲演绎铁血柔情"));
moviesList.add(new MoviesObject(R.drawable.movie06,"太空旅客"," 116分钟 - 冒险/剧情","星爵大表姐展开太空冒险"));
moviesList.add(new MoviesObject(R.drawable.movie07,"一万公里的约定","105分钟 - 剧情","极限马拉松上演热血青春"));
moviesList.add(new MoviesObject(R.drawable.movie08,"你的名字。","106分钟 - 动画 / 剧情","极限马拉松上演热血青春"));
moviesList.add(new MoviesObject(R.drawable.movie09,"海洋奇缘","103分钟 - 动画 / 冒险","海水的质感太棒了"));
moviesList.add(new MoviesObject(R.drawable.movie10,"摆渡人","128分钟 - 爱情 / 喜剧","梁朝伟金城武拯救爱情“落水者”"));
 
}
 
/*
* 构造新闻数据
* */
private void initNewsData() {
newsList=new ArrayList<>();
newsList.add(new NewsObject(R.drawable.news01,"《怦然星动》涉嫌抄袭引官司","欢瑞传媒成被告 编剧蔡心" +
"索赔350万"));
newsList.add(new NewsObject(R.drawable.news02,"黄渤小沈阳新片《冰之下》预计年内上映","导演蔡尚君" +
"揭秘幕后故事:黄渤饰演底层“淘金者”"));
newsList.add(new NewsObject(R.drawable.news03,"外媒评选2017年最期待电影Top33","\"迷失Z城\"夺冠" +
" 超级英雄与独立电影平分秋色"));
newsList.add(new NewsObject(R.drawable.news04,"《卧虎藏龙2》女星将演花木兰?","迪士尼否定:" +
"还在选角中 影片或4月在华开拍"));
newsList.add(new NewsObject(R.drawable.news05,"《美国恐怖故事》还要再拍三季","香蕉姐和伊万彼得斯回" +
"归第7季 主题暂时未知"));
newsList.add(new NewsObject(R.drawable.news06,"黑后&钢力士将回归《死侍2》","《牌皇》还在打磨剧本" +
" 查宁塔图姆并未退出"));
newsList.add(new NewsObject(R.drawable.news07,"汤老师签约3部《疯狂的麦克斯》续集","赞乔治米勒是艺" +
"术家 不确定片名是否为\"废土\""));
newsList.add(new NewsObject(R.drawable.news08,"揭秘《侠盗一号》里最萌的机器人K2","代表了高端的特" +
"效技术之一"));
newsList.add(new NewsObject(R.drawable.news09,"美国服装工会奖公布提名名单","\"爱乐之城\"\"夜行" +
"动物\"角逐当代类最佳服装设计"));
newsList.add(new NewsObject(R.drawable.news10,"估值50亿的“开心麻花”拟启动IPO","三年估值翻16倍" +
"或成“话剧第一股”"));
}
 
/*
*构造标题数据
* */
private void initTitleData() {
titleList=new ArrayList<>();
titleList.add(new TitleObject("|娱乐新闻"));
titleList.add(new TitleObject("|热门电影"));
}
 
}
3.3 电影对象类MovieObject.java
package com.example.recyclerviewdemo;
 
import java.io.Serializable;
 
/**
* Created by elimy on 2017-01-10.
*/
public class MoviesObject implements Serializable {
private int movieImage;
private int Type=3;
private String movieName;
private String movieType;
private String movieDesc;
 
public MoviesObject(int movieImage, String movieName, String movieType,String movieDesc) {
this.movieImage = movieImage;
this.movieDesc = movieDesc;
this.movieType = movieType;
this.movieName = movieName;
}
 
public int getType() {
return Type;
}
 
public void setType(int type) {
Type = type;
}
public String getMovieDesc() {
return movieDesc;
}
 
public void setMovieDesc(String movieDesc) {
this.movieDesc = movieDesc;
}
 
public String getMovieType() {
return movieType;
}
 
public void setMovieType(String movieType) {
this.movieType = movieType;
}
 
public String getMovieName() {
return movieName;
}
 
public void setMovieName(String movieName) {
this.movieName = movieName;
}
 
public int getMovieImage() {
return movieImage;
}
 
public void setMovieImage(int movieImage) {
this.movieImage = movieImage;
}
 
}
3.4 新闻对象类NewsObject.java
package com.example.recyclerviewdemo;
 
import java.io.Serializable;
 
/**
* Created by elimy on 2017-01-10.
*/
public class NewsObject implements Serializable {
 
private int Type=2;
private int newsImage;
private String newsTitle;
private String newsDesc;
 
public NewsObject(int newsImage, String newsTitle, String newsDesc) {
this.newsImage = newsImage;
this.newsTitle = newsTitle;
this.newsDesc = newsDesc;
}
 
public String getNewsTitle() {
return newsTitle;
}
 
public int getNewsImage() {
return newsImage;
}
 
public void setNewsImage(int newsImage) {
this.newsImage = newsImage;
}
 
public String getNewsDesc() {
return newsDesc;
}
 
public void setNewsDesc(String newsDesc) {
this.newsDesc = newsDesc;
}
 
public void setNewsTitle(String newsTitle) {
this.newsTitle = newsTitle;
}
public int getType() {
return Type;
}
 
public void setType(int type) {
Type = type;
}
}
3.5 标题对象类TitleObject.java
package com.example.recyclerviewdemo;
 
import java.io.Serializable;
 
/**
* Created by elimy on 2017-01-10.
*/
public class TitleObject implements Serializable {
 
private int Type=1;
private String title;
 
public int getType() {
return Type;
}
 
public void setType(int type) {
Type = type;
}
 
public TitleObject(String title) {
this.title = title;
}
 
public String getTitle() {
return title;
}
 
public void setTitle(String title) {
this.title = title;
}
 
 
}
3.6 电影展示布局movies_item_layout.xml
<?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:orientation="vertical"
android:layout_margin="5dp"
android:layout_width="match_parent"
android:layout_height="150dp">
<android.support.v7.widget.CardView
android:id="@+id/movie_card_view"
android:layout_width="match_parent"
app:cardCornerRadius="5dp"
android:padding="5dp"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/movie_linear_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<ImageView
android:src="@drawable/movie07"
android:scaleType="centerCrop"
android:id="@+id/movie_image_view"
android:layout_height="150dp"
android:layout_width="100dp" />
<LinearLayout
android:id="@+id/movie_desc_linear_layout"
android:orientation="vertical"
android:padding="5dp"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="@+id/movie_name_tv"
android:textSize="18sp"
tools:text="一万公里的约定"
android:maxLines="2"
android:ellipsize="end"
android:textColor="#000"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/movie_type_tv"
android:textSize="14sp"
tools:text="105分钟 - 剧情"
android:ellipsize="end"
android:padding="5dp"
android:singleLine="true"
android:layout_marginBottom="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/movie_desc_tv"
android:textSize="14sp"
tools:text="极限马拉松上演热血青春"
android:textColor="@color/bgColor3"
android:maxLines="2"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
 
</android.support.v7.widget.CardView>
</LinearLayout>
3.7 新闻布局news_item_layout.xml
<?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:orientation="vertical"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="300dp">
<android.support.v7.widget.CardView
android:id="@+id/news_card"
app:cardCornerRadius="15dp"
app:cardPreventCornerOverlap="true"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardElevation="5dp"
app:contentPadding="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/news_linear_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
 
<!--设置新闻图片-->
<ImageView
android:id="@+id/news_image_view"
android:src="@mipmap/ic_launcher"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_weight="5"
android:layout_height="0dp" />
 
<!--设置新闻标题-->
<TextView
android:id="@+id/news_title"
android:textSize="28sp"
tools:text="新闻标题"
android:textColor="#000"
android:ellipsize="end"
android:paddingLeft="10dp"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp" />
 
<!--设置描述文本-->
<TextView
android:id="@+id/news_desc"
android:textSize="18sp"
android:padding="3dp"
android:maxLines="2"
android:ellipsize="end"
android:paddingLeft="10dp"
tools:text="新闻描述一大堆"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
3.8 标题显示布局title_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
tools:text="|实时新闻"
android:id="@+id/title_tv"
android:paddingLeft="20dp"
 
android:gravity="center_vertical"
android:textColor="@color/colorPrimary"
android:textSize="28sp"
android:textStyle="bold"
android:background="#0D0D0D"
android:layout_width="match_parent"
android:layout_height="88dp" />
</LinearLayout>
3.9 最后来个主布局multi_recyclerview_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/multi_recycler_view"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp">
 
</android.support.v7.widget.RecyclerView>
</LinearLayout>
代码粘贴完毕,最后要说的是上面的3个不同类型的List数据是自行组装起来然后根据每一个item类型和位置去和布局绑定的,实际开发中可以单独将数据组装好,可以交给后端处理做也行。如果这样的话就可以直接接收数据根据不同类型去加载不同布局就好了,省去了自己拼装。

最后还是要感谢CCTV MTV,感谢时光网的设计师,感谢慕课网
下面是参考链接:
                        http://www.imooc.com/video/13043
                        http://www.voidcn.com/blog/zhumintao/article/p-6257213.html



0 0
原创粉丝点击