简介

​【GitHub 地址】​​ 自定义 RecyclerView 适配器,用于扩展和折叠组,支持多种视图类型。

效果图

【Android -- UI 开发】RecyclerView 实现二级列表_Android开发

【Android -- UI 开发】RecyclerView 实现二级列表_Android开发_02

【Android -- UI 开发】RecyclerView 实现二级列表_ide_03

【Android -- UI 开发】RecyclerView 实现二级列表_Android开发_04

使用

1. 添加依赖
​​​compile 'com.thoughtbot:expandablerecyclerview:1.3'​​​​compile 'com.thoughtbot:expandablecheckrecyclerview:1.4'​

2. 自定义 ExpandableGroup 类

public class Genre extends ExpandableGroup<Artist> {
private int iconResId;

public Genre(String title, List<Artist> items, int iconResId) {
super(title, items);
this.iconResId = iconResId;
}

public int getIconResId() {
return iconResId;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Genre)) {
return false;
}

Genre genre = (Genre) o;

return getIconResId() == genre.getIconResId();

}

@Override
public int hashCode() {
return getIconResId();
}
}
/**
* Created on 2019/5/15 4:56 PM
*
* @author Kevin
*/
public class Artist implements Parcelable {
private String name;
private boolean isFavorite;

public Artist(String name, boolean isFavorite) {
this.name = name;
this.isFavorite = isFavorite;
}

protected Artist(Parcel in) {
name = in.readString();
}

public String getName() {
return name;
}

public boolean isFavorite() {
return isFavorite;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Artist)) return false;

Artist artist = (Artist) o;

if (isFavorite() != artist.isFavorite()) return false;
return getName() != null ? getName().equals(artist.getName()) : artist.getName() == null;

}

@Override
public int hashCode() {
int result = getName() != null ? getName().hashCode() : 0;
result = 31 * result + (isFavorite() ? 1 : 0);
return result;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}

@Override
public int describeContents() {
return 0;
}

public static final Creator<Artist> CREATOR = new Creator<Artist>() {
@Override
public Artist createFromParcel(Parcel in) {
return new Artist(in);
}

@Override
public Artist[] newArray(int size) {
return new Artist[size];
}
};
}

3. 创建 ChildViewHolder 和 HeaderViewHolder 。

/**
* Created on 2019/5/15 4:52 PM
*
* @author Kevin
*/
public class HeaderViewHolder extends GroupViewHolder {
private TextView genreName;
private ImageView arrow;
private ImageView icon;
public HeaderViewHolder(View itemView) {
super(itemView);

genreName = itemView.findViewById(R.id.list_item_genre_name);
arrow = itemView.findViewById(R.id.list_item_genre_arrow);
icon = itemView.findViewById(R.id.list_item_genre_icon);
}

public void setGenreTitle(ExpandableGroup genre) {
if (genre instanceof Genre) {
genreName.setText(genre.getTitle());
icon.setBackgroundResource(((Genre) genre).getIconResId());
}

}

@Override
public void expand() {
animateExpand();
}

@Override
public void collapse() {
animateCollapse();
}

private void animateExpand() {
RotateAnimation rotate =
new RotateAnimation(360, 180, RELATIVE_TO_SELF, 0.5f, RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(300);
rotate.setFillAfter(true);
arrow.setAnimation(rotate);
}

private void animateCollapse() {
RotateAnimation rotate =
new RotateAnimation(180, 360, RELATIVE_TO_SELF, 0.5f, RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(300);
rotate.setFillAfter(true);
arrow.setAnimation(rotate);
}

}
/**
* Created on 2019/5/15 4:59 PM
*
* @author Kevin
*/
public class ItemViewHolder extends ChildViewHolder {
public TextView childTextView;

public ItemViewHolder(View itemView) {
super(itemView);
childTextView = itemView.findViewById(R.id.list_item_artist_name);
}

public void setArtistName(String name) {
childTextView.setText(name);
}

/**
* 子 item 的点击事件。
* @param position
*/
public void click(final int position) {
childTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TAG", "onClick: " + position);
}
});
}
}

4. 创建 GenreAdapter 类

/**
* Created on 2019/5/15 5:00 PM
*
* @author Kevin
*/
public class GenreAdapter extends ExpandableRecyclerViewAdapter<HeaderViewHolder,ItemViewHolder> {
public GenreAdapter(List<? extends ExpandableGroup> groups) {
super(groups);
}

@Override
public HeaderViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_genre, parent, false);
return new HeaderViewHolder(view);
}

@Override
public ItemViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_artist, parent, false);
return new ItemViewHolder(view);
}

@Override
public void onBindChildViewHolder(ItemViewHolder holder, final int flatPosition,
ExpandableGroup group, final int childIndex) {

final Artist artist = ((Genre) group).getItems().get(childIndex);
holder.setArtistName(artist.getName());

holder.click(childIndex);


}

@Override
public void onBindGroupViewHolder(HeaderViewHolder holder, int flatPosition,
ExpandableGroup group) {

holder.setGenreTitle(group);
}
}

5. 在 Activity 中使用

public class ExpandActivity extends AppCompatActivity {
public GenreAdapter adapter;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_expand);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(getClass().getSimpleName());

RecyclerView recyclerView = findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);

RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof DefaultItemAnimator) {
((DefaultItemAnimator) animator).setSupportsChangeAnimations(false);
}

adapter = new GenreAdapter(makeGenres());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);

Button clear = findViewById(R.id.toggle_button);
clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adapter.toggleGroup(makeJazzGenre());
}
});
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
adapter.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
adapter.onRestoreInstanceState(savedInstanceState);
}
}

6. MainActivity 跳转到 ExpandActivity

private void initView() {
Button expand = (Button) findViewById(R.id.expand_button);
expand.setOnClickListener(navogateTo(ExpandActivity.class));

}

public View.OnClickListener navogateTo(final Class<?> clazz) {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,clazz);
startActivity(intent);
}
};
}

7. 模拟数据的封装

/**
* Created on 2019/5/15 5:14 PM
*
* @author Kevin
*/
public class GenreDataFactory {
public static List<Genre> makeGenres() {
return Arrays.asList(makeRockGenre(),
makeJazzGenre(),
makeClassicGenre());
}

public static Genre makeRockGenre() {
return new Genre("水果", makeRockArtists(), R.drawable.ic_banjo);
}

public static List<Artist> makeRockArtists() {
Artist queen = new Artist("西瓜", true);
Artist styx = new Artist("苹果", false);
Artist reoSpeedwagon = new Artist("香蕉", false);
Artist boston = new Artist("圣女果", true);

return Arrays.asList(queen, styx, reoSpeedwagon, boston);
}

public static Genre makeJazzGenre() {
return new Genre("游戏", makeJazzArtists(), R.drawable.ic_banjo);
}

public static List<Artist> makeJazzArtists() {
Artist milesDavis = new Artist("王者荣耀", true);
Artist ellaFitzgerald = new Artist("英雄联盟", true);
Artist billieHoliday = new Artist("吃鸡", false);

return Arrays.asList(milesDavis, ellaFitzgerald, billieHoliday);
}


public static Genre makeClassicGenre() {
return new Genre("运动", makeClassicArtists(), R.drawable.ic_banjo);
}

public static List<Artist> makeClassicArtists() {
Artist beethoven = new Artist("篮球", false);
Artist bach = new Artist("足球", true);
Artist brahms = new Artist("乒乓球", false);
Artist puccini = new Artist("羽毛球", false);

return Arrays.asList(beethoven, bach, brahms, puccini);
}
}