Android控件RecyclerView实现混排效果仿网易云音乐

前言

最近在使用网易云音乐的时候,看到如下图的排版效果图,自己也想实现一个

这里采用网上用法最多的方式,而且是比较简单的方式实现的,想要做项目的同学也可以快速入手搞定首页界面,可以在最快的时间内模仿出来,且效果达到90%以上的相似

效果演示

至于图片的加载你们可以根据网上的Api获取相应的图片加载到对应的位置,这里只是采用本地图片来演示

实现分析

这里是采用RecyclerView的GridLayoutManager的一个SpanSize这么一个东西,从下图很容易知道其意思

项目结构

项目结构可能对初学者感觉很庞大,不用担心,这里我会按照下面的包名划分,从最简单的开始分析

引入依赖

首先是在Gradle中引入对RecyclerView的依赖

compile 'com.android.support:recyclerview-v7:25.3.1'

View包

由于项目用到的图片是有规格限定的,所以需要对ImageView覆写,得到我们想要尺寸的图片

SquareImageView:正方形图片
RectImageView:长方形图片

public class SquareImageView extends android.support.v7.widget.AppCompatImageView {

  public SquareImageView(Context context) {
    this(context,null);
  }

  public SquareImageView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs,0);
  }

  public SquareImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    setScaleType(ScaleType.FIT_XY);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, widthMeasureSpec);
  }
}
public class RectImageView extends android.support.v7.widget.AppCompatImageView {

  public RectImageView(Context context) {
    this(context, null);
  }

  public RectImageView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public RectImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    setScaleType(ScaleType.FIT_XY);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int width = MeasureSpec.getSize(widthMeasureSpec);
    // 设置大小为宽度的三分之二
    int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width / 3 * 2, widthMode);
    super.onMeasure(widthMeasureSpec, halfWidthMeasureSpec);
  }
}

Music包

这里是我们存储实体的地方,其中四种类型的划分,分别对应项目展示中的前三个模块的划分,其中还有一个标题也算是一种类型,所以共四种

public class Music {

  public int type;
  public String title;
  // 后期可加入Glide加载网络图片Url
  public int imageId;

  public interface TYPE {
    int TYPE_GRID_THREE = 0x01;
    int TYPE_GRID_TWO = 0x02;
    int TYPE_LIST = 0x03;
    int TYPE_TITLE = 0x04;
  }
}

Listener包

由于RecyclerView自身是没有点击事件的,所以这个包是RecyclerView的点击事件接口

public interface OnItemClickListener {
  void OnItemClick(int position);
}

Decoration包

由于RecyclerView是不提供分割线的,所以这个包是自定义的分割线

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {

  private int space;

  public SpacesItemDecoration(int space) {
    this.space = space;
  }

  @Override
  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    outRect.left = space;
    outRect.right = space;
    outRect.bottom = space;
    outRect.top = space;
  }
}

ViewHolder

这里存储的是我们混排效果的控件,标题可能会有点区别,其他是一样的效果,为了后期方便拓展,我们就把他们分开,不代码复用

public class GridThreeViewHolder extends RecyclerView.ViewHolder {

  public SquareImageView iv_icon;
  public TextView tv_content;

  public GridThreeViewHolder(View itemView) {
    super(itemView);
    iv_icon = (SquareImageView) itemView.findViewById(R.id.iv_icon);
    tv_content = (TextView) itemView.findViewById(R.id.tv_content);
  }
}
public class GridTwoViewHolder extends RecyclerView.ViewHolder {

  public RectImageView iv_icon;
  public TextView tv_content;

  public GridTwoViewHolder(View itemView) {
    super(itemView);
    iv_icon = (RectImageView) itemView.findViewById(R.id.iv_icon);
    tv_content = (TextView) itemView.findViewById(R.id.tv_content);
  }
}
public class ListViewHolder extends RecyclerView.ViewHolder {

  public RectImageView iv_icon;
  public TextView tv_content;

  public ListViewHolder(View itemView) {
    super(itemView);
    iv_icon = (RectImageView) itemView.findViewById(R.id.iv_icon);
    tv_content = (TextView) itemView.findViewById(R.id.tv_content);
  }
}
public class TitleViewHolder extends RecyclerView.ViewHolder {

  public TextView tv_content;

  public TitleViewHolder(View itemView) {
    super(itemView);
    tv_content = (TextView) itemView.findViewById(R.id.tv_content);
  }
}

Adapter包

这里就是对所有ViewHolder的控制器,然而这里并不是混排效果实现的最终地方,只不过是填充数据的地方

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {

  private List<Music> mList;
  private Context mContext;
  private LayoutInflater mInflater;

  /**
   * 点击事件
   */
  private OnItemClickListener mOnItemClickListener;

  public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
    this.mOnItemClickListener = onItemClickListener;
  }

  public RecyclerAdapter(Context context, List<Music> list) {
    this.mList = list;
    this.mContext = context;
    this.mInflater = LayoutInflater.from(context);
  }

  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view;
    RecyclerView.ViewHolder mViewHolder = null;
    if (viewType == Music.TYPE.TYPE_GRID_THREE) {
      view = mInflater.inflate(R.layout.item_grid_three, parent, false);
      mViewHolder = new GridThreeViewHolder(view);
    } else if (viewType == Music.TYPE.TYPE_GRID_TWO) {
      view = mInflater.inflate(R.layout.item_grid_two, parent, false);
      mViewHolder = new GridTwoViewHolder(view);
    } else if (viewType == Music.TYPE.TYPE_LIST) {
      view = mInflater.inflate(R.layout.item_list, parent, false);
      mViewHolder = new ListViewHolder(view);
    } else if (viewType == Music.TYPE.TYPE_TITLE) {
      view = mInflater.inflate(R.layout.item_title, parent, false);
      mViewHolder = new TitleViewHolder(view);
    }
    return mViewHolder;
  }

  @Override
  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    switch (getItemViewType(position)) {
      case Music.TYPE.TYPE_GRID_THREE:
        GridThreeViewHolder gHolder_three = (GridThreeViewHolder) holder;
        gHolder_three.tv_content.setText(mList.get(position).title);
        gHolder_three.iv_icon.setImageResource(mList.get(position).imageId);
        //点击事件
        gHolder_three.itemView.setOnClickListener(this);
        gHolder_three.itemView.setTag(position);
        break;
      case Music.TYPE.TYPE_GRID_TWO:
        GridTwoViewHolder gHolder_two = (GridTwoViewHolder) holder;
        gHolder_two.tv_content.setText(mList.get(position).title);
        gHolder_two.iv_icon.setImageResource(mList.get(position).imageId);
        //点击事件
        gHolder_two.itemView.setOnClickListener(this);
        gHolder_two.itemView.setTag(position);
        break;
      case Music.TYPE.TYPE_LIST:
        ListViewHolder lHolder = (ListViewHolder) holder;
        lHolder.tv_content.setText(mList.get(position).title);
        lHolder.iv_icon.setImageResource(mList.get(position).imageId);
        //点击事件
        lHolder.itemView.setOnClickListener(this);
        lHolder.itemView.setTag(position);
        break;
      case Music.TYPE.TYPE_TITLE:
        TitleViewHolder tHolder = (TitleViewHolder) holder;
        tHolder.tv_content.setText(mList.get(position).title);
        //点击事件
        tHolder.itemView.setOnClickListener(this);
        tHolder.itemView.setTag(position);
        break;
    }
  }

  @Override
  public int getItemViewType(int position) {
    return mList.get(position).type;
  }

  @Override
  public int getItemCount() {
    return mList.size();
  }

  @Override
  public void onClick(View v) {
    if (mOnItemClickListener != null) {
      int position = (int) v.getTag();
      mOnItemClickListener.OnItemClick(position);
    }
  }
}

Activity

这里就是我们实现混排效果的关键,我们会根据不同类型的数据,对RecyclerView的SpanSize的进行设置

public class MainActivity extends AppCompatActivity implements OnItemClickListener {

  private RecyclerView ry;
  private GridLayoutManager layoutManager;
  private RecyclerAdapter mAdapter;
  private static List<Music> mList;

  /**
   * 模拟本地数据
   */
  static {
    mList = new ArrayList<>();

    for (int i = 0; i < 1; i++) {
      Music music = new Music();
      music.type = Music.TYPE.TYPE_TITLE;
      music.imageId = R.drawable.ic_cover;
      music.title = "推荐歌单";
      mList.add(music);
    }

    for (int i = 0; i < 6; i++) {
      Music music = new Music();
      music.type = Music.TYPE.TYPE_GRID_THREE;
      music.imageId = R.drawable.ic_cover;
      music.title = "先不要降温!我没钱买衣服";
      mList.add(music);
    }

    for (int i = 0; i < 1; i++) {
      Music music = new Music();
      music.type = Music.TYPE.TYPE_TITLE;
      music.imageId = R.drawable.ic_cover;
      music.title = "推荐MV";
      mList.add(music);
    }

    for (int i = 0; i < 4; i++) {
      Music music = new Music();
      music.type = Music.TYPE.TYPE_GRID_TWO;
      music.imageId = R.drawable.ic_cover;
      music.title = "Perfect Day";
      mList.add(music);
    }

    for (int i = 0; i < 1; i++) {
      Music music = new Music();
      music.type = Music.TYPE.TYPE_TITLE;
      music.imageId = R.drawable.ic_cover;
      music.title = "精选专栏";
      mList.add(music);
    }

    for (int i = 0; i < 3; i++) {
      Music music = new Music();
      music.type = Music.TYPE.TYPE_LIST;
      music.imageId = R.drawable.ic_cover;
      music.title = "去看《银翼杀手2049》前,你应该知道的三件事";
      mList.add(music);
    }

    for (int i = 0; i < 1; i++) {
      Music music = new Music();
      music.type = Music.TYPE.TYPE_TITLE;
      music.imageId = R.drawable.ic_cover;
      music.title = "最新音乐";
      mList.add(music);
    }

    for (int i = 0; i < 6; i++) {
      Music music = new Music();
      music.type = Music.TYPE.TYPE_GRID_THREE;
      music.imageId = R.drawable.ic_cover;
      music.title = "[BGM]一定听过的神级背景配乐";
      mList.add(music);
    }
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ry = (RecyclerView) findViewById(R.id.ry);
    layoutManager = new GridLayoutManager(this, 6);
    layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
      @Override
      public int getSpanSize(int position) {
        int type = mList.get(position).type;
        if (type == Music.TYPE.TYPE_GRID_THREE) {
          return 2;
        } else if (type == Music.TYPE.TYPE_GRID_TWO) {
          return 3;
        } else if (type == Music.TYPE.TYPE_LIST) {
          return 6;
        } else if (type == Music.TYPE.TYPE_TITLE) {
          return 6;
        }
        return 0;
      }
    });
    ry.setLayoutManager(layoutManager);
    ry.addItemDecoration(new SpacesItemDecoration(2));

    // 填充数据
    mAdapter = new RecyclerAdapter(this, mList);
    mAdapter.setOnItemClickListener(this);
    ry.setAdapter(mAdapter);

  }

  @Override
  public void OnItemClick(int position) {
    String title = mList.get(position).title;
    Toast.makeText(this, title, Toast.LENGTH_SHORT).show();
  }
}

layout布局文件

这里的布局很简单,比如用到我们的正方形图片,长方形图片等,这里就不做代码贴出,详细可以查看源码

源码下载

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android中RecyclerView实现横向滑动代码

    RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍Android中RecyclerView实现横向滑动代码,一起看看吧. android.support.v7.widget.RecyclerView 功能:RecyclerView横向滑动 控件:<android.support.v7.widget.RecyclerView /> Java类:RecyclerView.GalleryAdap

  • Android项目实战之仿网易新闻的页面(RecyclerView )

    本文实例实现一个仿网易新闻的页面,上面是轮播的图片,下面是 RecyclerView 显示新闻列表,具体内容如下 错误方法 <?xml version="1.0" encoding="utf-8"?> <LinearLayout ...> <ViewPager ... /> <android.support.v7.widget.RecyclerView .../> </LinearLayout> 这样布局

  • Android RecyclerView网格布局(支持多种分割线)详解(2)

    上篇Android RecyclerView 详解(1)-线性布局 记录了下RecyclerView的使用方法,并且讲述了线性布局列表的使用方法,在此基础上加上了万能分割线,支持颜色分割线和图片分割线,同时支持对分割线设置线宽. 这篇是总结一下网格布局的使用,同样也支持两种分割线和线宽的设置. 主要的相关类: 1. RecyclerView.Adapter 2. GridLayoutManager 网格布局管理器 3. RecycleView.ItemDecoration 分割线 下面就直接通过

  • Android中使用RecyclerView实现下拉刷新和上拉加载

    推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

  • Android中RecyclerView嵌套滑动冲突解决的代码片段

    在纵向RecyclerView嵌套横向RecyclerView时,如果纵向RecyclerView有下拉刷新功能,那么内部的横向RecyclerView的横向滑动体验会很差.(只有纯横向滑动时,才能滑动内部的横向RecyclerView,否则滑动事件就会影响到下拉刷新),添加拦截判断. public class MySwipeRefreshLayout extends SwipeRefreshLayout { private boolean mIsVpDragger; private final

  • Android使用RecyclerView实现水平滚动控件

    前言 相信大家都知道Android滚动控件的实现方式有很多, 使用RecyclerView也比较简单. 做了一个简单的年龄滚动控件, 让我们来看看RecyclerView的使用方式, 主要有以下几点: (1) 对齐控件中心位置. (2) 计算滚动距离. (3) 高亮中心视图. (4) 实时显示中心数据. (5) 停止时自动对齐. (6) 滚动时, 设置按钮状态开关. 效果 1. 框架 主要关注RecyclerView部分逻辑. /** * 初始化年龄滑动条 */ private void ini

  • Android中RecyclerView点击Item设置事件

    在上一篇Android RecylerView入门教程中提到,RecyclerView不再负责Item视图的布局及显示,所以RecyclerView也没有为Item开放OnItemClick等点击事件,这就需要开发者自己实现.博客最下面有Demo程序运行动画. 奉上Demo的Github链接. 在调研过程中,发现有同学修改RecyclerView源码来实现Item的点击监听,但认为这不是一个优雅的解决方案,最终决定在RecyclerView.ViewHolder上做文章. 思路是:因为ViewH

  • Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法

    RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看: 使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用RecycleVie

  • Android RecyclerView实现下拉刷新和上拉加载

    RecyclerView已经出来很久了,许许多多的项目都开始从ListView转战RecyclerView,那么,上拉加载和下拉刷新是一件很有必要的事情. 在ListView上,我们可以通过自己添加addHeadView和addFootView去添加头布局和底部局实现自定义的上拉和下拉,或者使用一些第三方库来简单的集成,例如Android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定义更强,但需要自己实现上拉加载. 而在下面我们将用两种方式

  • Android中RecyclerView布局代替GridView实现类似支付宝的界面

    单纯使用GridView 通用的两种给GridView 添加分割线的方法:http://stackoverflow.com/questions/7132030/android-gridview-draw-dividers 给Gridview 添加分割线,也就是实现网格布局,不清楚谷歌为什么没有给Gridview 添加一个类似 ListView 的Divider 属性,因此就需要我们自己去添加分割线, 目前两种方法,第一种是 利用GridView 的  android:horizontalSpac

随机推荐