简介
【GitHub 地址】 自定义 RecyclerView 适配器,用于扩展和折叠组,支持多种视图类型。
效果图
使用
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;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Genre)) {
return false;
}
Genre genre = (Genre) o;
return getIconResId() == genre.getIconResId();
}
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;
}
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;
}
public int hashCode() {
int result = getName() != null ? getName().hashCode() : 0;
result = 31 * result + (isFavorite() ? 1 : 0);
return result;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
public int describeContents() {
return 0;
}
public static final Creator<Artist> CREATOR = new Creator<Artist>() {
public Artist createFromParcel(Parcel in) {
return new Artist(in);
}
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());
}
}
public void expand() {
animateExpand();
}
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() {
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);
}
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);
}
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);
}
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);
}
public void onBindGroupViewHolder(HeaderViewHolder holder, int flatPosition,
ExpandableGroup group) {
holder.setGenreTitle(group);
}
}
5. 在 Activity 中使用
public class ExpandActivity extends AppCompatActivity {
public GenreAdapter adapter;
protected void onCreate( 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() {
public void onClick(View v) {
adapter.toggleGroup(makeJazzGenre());
}
});
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
adapter.onSaveInstanceState(outState);
}
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() {
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);
}
}