很久沒更新簡書了势决,感覺能寫的東西不是太多,很多網(wǎng)上都能搜到的从隆,感覺也沒寫的必要诚撵。不過最近遇到了個多級列表,而且每級菜單還要吸頂?shù)男枨蠹耄疫@里實(shí)現(xiàn)的方式在網(wǎng)上應(yīng)該比較少或者很難在網(wǎng)上搜到寿烟,所以在此記錄一下。剛開始在網(wǎng)上我也搜了不少思路辛燥,大多都是滑動recycleView/ListView時筛武,在getItemOffsets、onDraw挎塌、onDrawOver或者addOnScrollListener方法中徘六,根據(jù)recycleview條目的位置,畫個固定頭部或者造個假固定頭部榴都,關(guān)鍵位置滑過來就跟著一起移動待锈。但是這些方法感覺并不太適合我這種場景。于是我就嘗試了各種方法嘴高,最終決定自己打造一個自定義View(可以讓你輕松實(shí)現(xiàn)手機(jī)聯(lián)系人的吸頂效果竿音,或者多級菜單(每層菜單都支持吸頂/也可不開吸頂功能)的功能)。思路就不多介紹了拴驮,比較復(fù)雜谍失,有興趣的可以自己看一下源碼,不過使用上還是比較輕松的莹汤。
部分源碼
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mContext = parent.getContext();
View view = LayoutInflater.from(mContext).inflate(getLayoutId(viewType), parent, false);//------設(shè)置條目布局------
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);//------設(shè)置監(jiān)聽器------
return baseViewHolder;
}
@Override
public void onBindViewHolder(BaseViewHolder holder, final int position) {
T t = mDatas.get(position);
initView(holder);//------初始化控件------
bindData(holder, position, t);//------綁定數(shù)據(jù)------
}
@Override
public int getItemCount() {
return mDatas.size();
}
@Override
public long getItemId(int position) {
return position;
}
/**
* 返回ItemView布局類型---子類實(shí)現(xiàn)可以在外部用個變量保存返回值快鱼,初始化不同布局控件和點(diǎn)擊事件可以直接根據(jù)這個返回值判斷
*
* @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);
/**
* 設(shè)置監(jiān)聽事件
*/
protected abstract void setListener(BaseViewHolder holder, List<T> mDatas);
/**
* 綁定數(shù)據(jù)
*/
protected abstract void bindData(BaseViewHolder holder, int position, T t);
protected void createFixView(View view, int position, T t) {
}
/**
* 如果需要實(shí)現(xiàn)吸頂功能,需要在子Adapter中實(shí)現(xiàn)這個方法綁定view
* 如果需要在內(nèi)部實(shí)現(xiàn)點(diǎn)擊事件抹竹,可以用(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);//不會強(qiáng)制關(guān)閉,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);//打開吸頂功能(默認(rèn)開啟)
treeNodeExpandRecycleViewAdapter.refreshExpandData();//更新可展開數(shù)據(jù)
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);
}
}
});
}
效果圖
圖片.gif
如何集成
項(xiàng)目配置
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
添加依賴
implementation 'com.github.dxh104.ExpandRecycleView:expand_recycleview:1.0.1'