欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-12-10 19:36:10
前言 最近在使用网易云音乐的时候,看到如下图的排版效果图,自己也想实现一个 这里采用网上用法最多的方式,而且是比较简单的方式实现的,想要做项目的同学也可以快速入手搞...

前言

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

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

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

效果演示

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

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

实现分析

这里是采用recyclerview的gridlayoutmanager的一个spansize这么一个东西,从下图很容易知道其意思

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

项目结构

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

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

引入依赖

首先是在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布局文件

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

源码下载

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