1.先导包
compile 'com.android.support:recyclerview-v7:25.3.0'
2.主布局
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="exp.rusan.secondarylistdemo.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
3.item布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="8dp"
>
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="item"
android:textSize="18sp"
/>
</LinearLayout>
4.给Recyclerview添加每个item的分割线 RvDividerItemDecoration
public class RvDividerItemDecoration extends RecyclerView.ItemDecoration{
public final String TAG = this.getClass().getSimpleName();
private static final int[] ATTRS = new int[] {
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable divider;
private int orientation;
public RvDividerItemDecoration(Context context, int orientation) {
final TypedArray typedArray = context.obtainStyledAttributes(ATTRS);
divider = typedArray.getDrawable(0);
typedArray.recycle();
setOrientation(orientation);
}
public void setOrientation(int pOrientation) {
if (pOrientation != HORIZONTAL_LIST && pOrientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
orientation = pOrientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// Log.v(TAG, "onDraw");
if (orientation == VERTICAL_LIST) {
drawForVertical(c, parent);
} else {
drawForHorizontal(c, parent);
}
}
//获取Item的位置,然后为每个item定位画线
private void drawForVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for ( int i = 0; i < childCount; i++ ) {
final View child = parent.getChildAt(i);
RecyclerView recyclerView = new RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + divider.getIntrinsicHeight();
divider.setBounds(left, top, right, bottom);
divider.draw(c);
}
}
private void drawForHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for ( int i = 0; i < childCount; i++ ) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + divider.getIntrinsicHeight();
divider.setBounds(left, top, right, bottom);
divider.draw(c);
}
}
@Override
//可以通过outRect()为每个Item设置一定的偏移量。
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (orientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, divider.getIntrinsicHeight());
} else {
outRect.set(0, 0, divider.getIntrinsicWidth(), 0);
}
}
}
5.给Recyclerview创建一个适配器 RecyclerAdapter
public class RecyclerAdapter extends SecondaryListAdapter<RecyclerAdapter.GroupItemViewHolder, RecyclerAdapter.SubItemViewHolder> {
private Context context;
private List<DataTree<String, String>> dts = new ArrayList<>();
public RecyclerAdapter(Context context) {
this.context = context;
}
public void setData(List datas) {
dts = datas;
notifyNewData(dts);
}
@Override
public RecyclerView.ViewHolder groupItemViewHolder(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new GroupItemViewHolder(v);
}
@Override
public RecyclerView.ViewHolder subItemViewHolder(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new SubItemViewHolder(v);
}
@Override
public void onGroupItemBindViewHolder(RecyclerView.ViewHolder holder, int groupItemIndex) {
((GroupItemViewHolder) holder).tvGroup.setText(dts.get(groupItemIndex).getGroupItem());
}
@Override
public void onSubItemBindViewHolder(RecyclerView.ViewHolder holder, int groupItemIndex, int subItemIndex) {
((SubItemViewHolder) holder).tvSub.setText(dts.get(groupItemIndex).getSubItems().get(subItemIndex));
}
@Override
public void onGroupItemClick(Boolean isExpand, GroupItemViewHolder holder, int groupItemIndex) {
Toast.makeText(context, "group item " + String.valueOf(groupItemIndex) + " is expand " +
String.valueOf(isExpand), Toast.LENGTH_SHORT).show();
}
@Override
public void onSubItemClick(SubItemViewHolder holder, int groupItemIndex, int subItemIndex) {
Toast.makeText(context, "sub item " + String.valueOf(subItemIndex) + " in group item " +
String.valueOf(groupItemIndex), Toast.LENGTH_SHORT).show();
}
public static class GroupItemViewHolder extends RecyclerView.ViewHolder {
TextView tvGroup;
public GroupItemViewHolder(View itemView) {
super(itemView);
tvGroup = (TextView) itemView.findViewById(R.id.tv);
}
}
public static class SubItemViewHolder extends RecyclerView.ViewHolder {
TextView tvSub;
public SubItemViewHolder(View itemView) {
super(itemView);
tvSub = (TextView) itemView.findViewById(R.id.tv);
}
}
}
6.再创建一个点击下拉列表的适配器 SecondaryListAdapter
public abstract class SecondaryListAdapter<GVH, SVH extends RecyclerView.ViewHolder> extends RecyclerView
.Adapter<RecyclerView.ViewHolder> {
private List<Boolean> groupItemStatus = new ArrayList<>();
private List<DataTree> dataTrees = new ArrayList<>();
/**
* Set new data for adapter to show. It must be called when set new data.
*
* @param data New data
*
*/
public void notifyNewData(List data) {
setDataTrees(data);
}
/**
* Set new data for adapter and notify changing.
*
* @param dt New data
*
*/
private final void setDataTrees(List dt) {
this.dataTrees = dt;
initGroupItemStatus(groupItemStatus);
notifyDataSetChanged();
}
/**
* Initialize the list to false.
*
* @param l The list need to initialize
*
*/
private void initGroupItemStatus(List l) {
for (int i = 0; i < dataTrees.size(); i++) {
l.add(false);
}
}
/**
* Create group item view holder for onCreateViewHolder.
*
* @param parent Provided by onCreateViewHolder.
*
*/
public abstract RecyclerView.ViewHolder groupItemViewHolder(ViewGroup parent);
/**
* Create subitem view holder for onCreateViewHolder.
*
* @param parent Provided by onCreateViewHolder.
*
*/
public abstract RecyclerView.ViewHolder subItemViewHolder(ViewGroup parent);
@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
if (viewType == ItemStatus.VIEW_TYPE_GROUPITEM) {
viewHolder = groupItemViewHolder(parent);
} else if (viewType == ItemStatus.VIEW_TYPE_SUBITEM) {
viewHolder = subItemViewHolder(parent);
}
return viewHolder;
}
/**
* Update the content of specified group item. The method will called by onBindViewHolder.
*
* @param holder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
*
* @param groupItemIndex The index of the group item.
*
*/
public abstract void onGroupItemBindViewHolder(RecyclerView.ViewHolder holder, int
groupItemIndex);
/**
* Update the content of specified subitem. The method will called by onBindViewHolder.
*
* @param holder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
* @param subItemIndex The index of the subitem.
*
*/
public abstract void onSubItemBindViewHolder(RecyclerView.ViewHolder holder, int
groupItemIndex, int subItemIndex);
/**
* The method will be called when the group item clicked.
*
* @param isExpand whether is expanded or no the group item clicked.
* @param holder The holder' s item view clicked.
* @param groupItemIndex The index of the group item clicked.
*
*/
public abstract void onGroupItemClick(Boolean isExpand, GVH holder, int groupItemIndex);
/**
* The method will be called when the subitem clicked.
*
* @param holder The holder' s item view clicked.
* @param subItemIndex The index of the subitem clicked.
*
*/
public abstract void onSubItemClick(SVH holder, int groupItemIndex, int subItemIndex);
@Override
public final void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
final ItemStatus itemStatus = getItemStatusByPosition(position);
final DataTree dt = dataTrees.get(itemStatus.getGroupItemIndex());
if ( itemStatus.getViewType() == ItemStatus.VIEW_TYPE_GROUPITEM ) {
onGroupItemBindViewHolder(holder, itemStatus.getGroupItemIndex());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int groupItemIndex = itemStatus.getGroupItemIndex();
if ( !groupItemStatus.get(groupItemIndex) ) {
onGroupItemClick(false, (GVH) holder, groupItemIndex);
groupItemStatus.set(groupItemIndex, true);
notifyItemRangeInserted(holder.getAdapterPosition() + 1, dt.getSubItems
().size());
} else {
onGroupItemClick(true, (GVH) holder, groupItemIndex);
groupItemStatus.set(groupItemIndex, false);
notifyItemRangeRemoved(holder.getAdapterPosition() + 1, dt.getSubItems
().size());
}
}
});
} else if (itemStatus.getViewType() == ItemStatus.VIEW_TYPE_SUBITEM) {
onSubItemBindViewHolder(holder, itemStatus.getGroupItemIndex(), itemStatus
.getSubItemIndex());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onSubItemClick((SVH) holder, itemStatus.getGroupItemIndex(), itemStatus.getSubItemIndex());
}
});
}
}
@Override
public final int getItemCount() {
int itemCount = 0;
if (groupItemStatus.size() == 0) {
return 0;
}
for (int i = 0; i < dataTrees.size(); i++) {
if (groupItemStatus.get(i)) {
itemCount += dataTrees.get(i).getSubItems().size() + 1;
} else {
itemCount++;
}
}
return itemCount;
}
@Override
public final int getItemViewType(int position) {
return getItemStatusByPosition(position).getViewType();
}
/**
* Get item' s status include view type, group item index and subitem index.
*
* @param position Position
*
*/
private ItemStatus getItemStatusByPosition(int position) {
ItemStatus itemStatus = new ItemStatus();
int count = 0;
int i = 0;
for (i = 0; i < groupItemStatus.size(); i++ ) {
if (count == position) {
itemStatus.setViewType(ItemStatus.VIEW_TYPE_GROUPITEM);
itemStatus.setGroupItemIndex(i);
break;
} else if (count > position) {
itemStatus.setViewType(ItemStatus.VIEW_TYPE_SUBITEM);
itemStatus.setGroupItemIndex(i - 1);
itemStatus.setSubItemIndex(position - ( count - dataTrees.get(i - 1).getSubItems
().size() ) );
break;
}
count++;
if (groupItemStatus.get(i)) {
count += dataTrees.get(i).getSubItems().size();
}
}
if (i >= groupItemStatus.size()) {
itemStatus.setGroupItemIndex(i - 1);
itemStatus.setViewType(ItemStatus.VIEW_TYPE_SUBITEM);
itemStatus.setSubItemIndex(position - ( count - dataTrees.get(i - 1).getSubItems().size
() ) );
}
return itemStatus;
}
private static class ItemStatus {
public static final int VIEW_TYPE_GROUPITEM = 0;
public static final int VIEW_TYPE_SUBITEM = 1;
private int viewType;
private int groupItemIndex = 0;
private int subItemIndex = -1;
public ItemStatus() {
}
public int getViewType() {
return viewType;
}
public void setViewType(int viewType) {
this.viewType = viewType;
}
public int getGroupItemIndex() {
return groupItemIndex;
}
public void setGroupItemIndex(int groupItemIndex) {
this.groupItemIndex = groupItemIndex;
}
public int getSubItemIndex() {
return subItemIndex;
}
public void setSubItemIndex(int subItemIndex) {
this.subItemIndex = subItemIndex;
}
}
/**
* Created by Rusan on 2017/4/12.
*/
public final static class DataTree<K, V> {
private K groupItem;
private List<V> subItems;
public DataTree(K groupItem, List<V> subItems) {
this.groupItem = groupItem;
this.subItems = subItems;
}
public K getGroupItem() {
return groupItem;
}
public List<V> getSubItems() {
return subItems;
}
}
}
7.主函数
public class MainActivity extends AppCompatActivity {
private List<SecondaryListAdapter.DataTree<String, String>> datas = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setHasFixedSize(true);
rv.addItemDecoration(new RvDividerItemDecoration(this, LinearLayoutManager.VERTICAL));
RecyclerAdapter adapter = new RecyclerAdapter(this);
adapter.setData(datas);
rv.setAdapter(adapter);
}
{
List<String> group = new ArrayList<>();
for (int i = 0; i < 10; i++) {
datas.add(new SecondaryListAdapter.DataTree<String, String>(String.valueOf(i), new
ArrayList<String>(){{add("sub 0"); add("sub 1"); add("sub 2");}}));
}
}
}
8.最终效果如下图