很久没更新简书了,感觉能写的东西不是太多,很多网上都能搜到的,感觉也没写的必要。不过最近遇到了个多级列表,而且每级菜单还要吸顶的需求,我这里实现的方式在网上应该比较少或者很难在网上搜到,所以在此记录一下。刚开始在网上我也搜了不少思路,大多都是滑动recycleView/ListView时,在getItemOffsets、onDraw、onDrawOver或者addOnScrollListener方法中,根据recycleview条目的位置,画个固定头部或者造个假固定头部,关键位置滑过来就跟着一起移动。但是这些方法感觉并不太适合我这种场景。于是我就尝试了各种方法,最终决定自己打造一个自定义View(可以让你轻松实现手机联系人的吸顶效果,或者多级菜单(每层菜单都支持吸顶/也可不开吸顶功能)的功能)。思路就不多介绍了,比较复杂,有兴趣的可以自己看一下源码,不过使用上还是比较轻松的。
部分源码
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mContext = parent.getContext();
View view = LayoutInflater.from(mContext).inflate(getLayoutId(viewType), parent, false);//------设置条目布局------
final BaseViewHolder baseViewHolder = new BaseViewHolder(mContext, view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onItemClickListner != null) {
onItemClickListner.onItemClickListner(v, baseViewHolder.getLayoutPosition());
}
if (onItemTriggerListner != null) {
T t = mDatas.get(baseViewHolder.getLayoutPosition());
if (t.isLeaf()) {
onItemTriggerListner.onItemCheckListner(v, baseViewHolder.getLayoutPosition(), t, TreeNodeHelper.getids(t), t.isChecked());
} else {
onItemTriggerListner.onItemExpandListner(v, baseViewHolder.getLayoutPosition(), t, TreeNodeHelper.getids(t), t.isExpand());
}
}
}
});
initView(baseViewHolder);//------初始化控件------
setListener(baseViewHolder, mDatas);//------设置监听器------
return baseViewHolder;
}
@Override
public void onBindViewHolder(BaseViewHolder holder, final int position) {
T t = mDatas.get(position);
initView(holder);//------初始化控件------
bindData(holder, position, t);//------绑定数据------
}
@Override
public int getItemCount() {
return mDatas.size();
}
@Override
public long getItemId(int position) {
return position;
}
/**
* 返回ItemView布局类型---子类实现可以在外部用个变量保存返回值,初始化不同布局控件和点击事件可以直接根据这个返回值判断
*
* @return
*/
protected abstract int setItemViewType(List<T> mDatas, int position);
/**
* 初始化item布局,获取item
*
* @return
*/
protected abstract int getLayoutId(int viewType);
/**
* 初始化控件
*
* @param holder
*/
protected abstract void initView(BaseViewHolder holder);
/**
* 设置监听事件
*/
protected abstract void setListener(BaseViewHolder holder, List<T> mDatas);
/**
* 绑定数据
*/
protected abstract void bindData(BaseViewHolder holder, int position, T t);
protected void createFixView(View view, int position, T t) {
}
/**
* 如果需要实现吸顶功能,需要在子Adapter中实现这个方法绑定view
* 如果需要在内部实现点击事件,可以用(int)view.getTag()方法获取position
*
* @param view
* @param position
* @param t
*/
protected void changeFixViewData(View view, int position, T t) {
}
private void scrollChange() {
ExpandRecycleViewAdapter adapter = (ExpandRecycleViewAdapter) mRecyclerView.getAdapter();
List<TreeNode> mDatas = adapter.mDatas;
List<TreeNode> mExpandDatas = adapter.mExpandDatas;
int scrollY = adapter.getScrollY();
int defalutFixTop = 0;
int level = 1;
for (int i = 0; i < mExpandDatas.size(); i++) {
defalutFixTop = 0;
TreeNode expandTreeNode = mExpandDatas.get(i);
TreeNode nextParentOrBrotherTreeNode = expandTreeNode.getNextParentOrBrotherTreeNode();
TreeNode tempExpandTreeNode = expandTreeNode;
while (tempExpandTreeNode.getParent() != null) {
TreeNode parent = tempExpandTreeNode.getParent();
defalutFixTop += parent.getItemHeight();
tempExpandTreeNode = parent;
}
int expandTreeNodeMarginTop = expandTreeNode.getMarginTop();
int expandTreeNodeItemHeight = expandTreeNode.getItemHeight();
int nextParentOrBrotherTreeNodeMarginTop = 0;
int nextParentOrBrotherTreeNodeItemHeight = 0;
if (nextParentOrBrotherTreeNode != null) {
nextParentOrBrotherTreeNodeMarginTop = nextParentOrBrotherTreeNode.getMarginTop();
nextParentOrBrotherTreeNodeItemHeight = nextParentOrBrotherTreeNode.getItemHeight();
}
View view = getView(adapter, expandTreeNode);
int y = 0;
if (nextParentOrBrotherTreeNode != null) {
//吸顶
if (expandTreeNodeMarginTop - scrollY <= defalutFixTop &&
nextParentOrBrotherTreeNodeMarginTop - scrollY >= expandTreeNodeItemHeight + defalutFixTop) {
// if (expandTreeNode.getLevel() != level) {
// if (expandTreeNode.getLevel() < level) {
// break;
// }
// continue;
// } else {
// level++;
// }
view.setTag(expandTreeNode.getItemPosition());
y = defalutFixTop;
view.setY(y);
view.setVisibility(VISIBLE);
adapter.changeFixViewData(view, expandTreeNode.getItemPosition(), expandTreeNode);
// Log.e(TAG, "scrollChange: 吸顶" + expandTreeNode.getItemPosition());
} else if (nextParentOrBrotherTreeNodeMarginTop - scrollY < expandTreeNodeItemHeight + defalutFixTop &&
nextParentOrBrotherTreeNodeMarginTop - scrollY > defalutFixTop) {//吸附
// if (expandTreeNode.getLevel() != level) {
// if (expandTreeNode.getLevel() < level) {
// break;
// }
// continue;
// } else {
// level++;
// }
view.setTag(expandTreeNode.getItemPosition());
y = nextParentOrBrotherTreeNodeMarginTop - scrollY - expandTreeNodeItemHeight;
view.setY(y);
view.setVisibility(VISIBLE);
adapter.changeFixViewData(view, expandTreeNode.getItemPosition(), expandTreeNode);
// Log.e(TAG, "scrollChange: 吸附" + expandTreeNode.getItemPosition());
} else {//消失
if (view.getTag() != null) {
int itemPosition = (int) view.getTag();
if (itemPosition == expandTreeNode.getItemPosition()) {
view.setVisibility(INVISIBLE);
// Log.e(TAG, "scrollChange: 消失" + expandTreeNode.getItemPosition());
}
}
}
} else {
//吸顶
if (expandTreeNodeMarginTop - scrollY <= defalutFixTop) {
// if (expandTreeNode.getLevel() != level) {
// if (expandTreeNode.getLevel() < level) {
// break;
// }
// continue;
// } else {
// level++;
// }
view.setTag(expandTreeNode.getItemPosition());
y = defalutFixTop;
view.setY(y);
view.setVisibility(VISIBLE);
adapter.changeFixViewData(view, expandTreeNode.getItemPosition(), expandTreeNode);
// Log.e(TAG, "scrollChange: 吸顶-" + expandTreeNode.getItemPosition());
} else {//消失
if (view.getTag() != null) {
int itemPosition = (int) view.getTag();
if (itemPosition == expandTreeNode.getItemPosition()) {
view.setVisibility(INVISIBLE);
// Log.e(TAG, "scrollChange: 消失-" + expandTreeNode.getItemPosition());
}
}
}
}
}
}
使用案例
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_same_height_expand);
initView();
TreeNodeLevelManager.getInstance().clearFreeLevel();
TreeNodeLevelManager.getInstance().setFreeLevel(1);//不会强制关闭,expandOnlyOneGroup
TreeNodeLevelManager.getInstance().clearLevelHightCache();
TreeNodeLevelManager.getInstance().putHeight(1, 50);
TreeNodeLevelManager.getInstance().putHeight(2, 50);
TreeNodeLevelManager.getInstance().putHeight(3, 50);
TreeNodeLevelManager.getInstance().putHeight(4, 50);
List<TreeNode<Title>> treeNodeList = DataHelper.testgetRootExpandTreeNodeList();
ExpandRecycleViewAdapter<TreeNode<Title>> treeNodeExpandRecycleViewAdapter = new ExpandRecycleViewAdapter<TreeNode<Title>>(treeNodeList) {
private TextView tvTitle;
@Override
protected int setItemViewType(List<TreeNode<Title>> mDatas, int position) {
return 0;
}
@Override
protected int getLayoutId(int viewType) {
return R.layout.item_group;
}
@Override
protected void initView(BaseViewHolder holder) {
tvTitle = (TextView) holder.getView(R.id.tv_title);
}
@Override
protected void setListener(BaseViewHolder holder, List<TreeNode<Title>> mDatas) {
}
@Override
protected void bindData(BaseViewHolder holder, int position, TreeNode<Title> titleTreeNode) {
tvTitle.setText(titleTreeNode.getData().titleContent);
tvTitle.setTextColor(titleTreeNode.getData().textColor);
tvTitle.setBackgroundColor(titleTreeNode.getData().background);
}
@Override
protected void createFixView(View view, int position, TreeNode<Title> titleTreeNode) {
super.createFixView(view, position, titleTreeNode);
if(view==null){
return;
}
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Integer itemPostition = (Integer) v.getTag();
collapseGroup(itemPostition);
}
});
}
@Override
protected void changeFixViewData(View view, int position, TreeNode<Title> titleTreeNode) {
super.changeFixViewData(view, position, titleTreeNode);
if (view == null) {
return;
}
tvTitle = (TextView) view.findViewById(R.id.tv_title);
tvTitle.setText(titleTreeNode.getData().titleContent);
tvTitle.setTextColor(titleTreeNode.getData().textColor);
tvTitle.setBackgroundColor(titleTreeNode.getData().background);
}
};
treeNodeExpandRecycleViewAdapter.setmExpandRecycleView(expandRecycleView);
treeNodeExpandRecycleViewAdapter.setMeasureDataHeightMarginTop(false);//固定高度不用开启测量
treeNodeExpandRecycleViewAdapter.setOpenStickyTop(true);//打开吸顶功能(默认开启)
treeNodeExpandRecycleViewAdapter.refreshExpandData();//更新可展开数据
recyclerView.setAdapter(treeNodeExpandRecycleViewAdapter);
treeNodeExpandRecycleViewAdapter.setOnItemTriggerListner(new ExpandRecycleViewAdapter.OnItemTriggerListner<TreeNode<Title>>() {
@Override
public void onItemCheckListner(View v, int position, TreeNode<Title> treeNode, Integer[] ids, boolean isChecked) {
String idsStr = "";
for (int i = 0; i < ids.length; i++) {
idsStr += ids[i];
}
Log.e(TAG, "onItemCheckListner: position=" + position + " ids=" + idsStr + " isChecked=" + isChecked);
}
@Override
public void onItemExpandListner(View v, int position, TreeNode<Title> treeNode, Integer[] ids, boolean isExpand) {
String idsStr = "";
for (int i = 0; i < ids.length; i++) {
idsStr += ids[i];
}
Log.e(TAG, "onItemExpandListner: position=" + position + " ids=" + idsStr + " isExpand=" + isExpand);
if (isExpand) {
treeNodeExpandRecycleViewAdapter.collapseGroup(position);
} else {
treeNodeExpandRecycleViewAdapter.expandGroup(position);
// treeNodeExpandRecycleViewAdapter.expandOnlyOneGroup(position);
}
}
});
}
效果图
如何集成
项目配置
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
添加依赖
implementation 'com.github.dxh104.ExpandRecycleView:expand_recycleview:1.0.1'