android 横向单选,Android RecyclerView 定制单选多选模式

ListView的小插曲

其实ListView或者说它们这一类都是实现了单选多选模式的,如果你使用CheckedTextview等实现了Checkable的子类的话,根本不用写什么代码,单选多选模式就搞定了。

8026bcbd290f

AdapterView.png

* Register a callback to be invoked when an item in this AdapterView has

* been selected.

*

* @param listener The callback that will run

*/

public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {

mOnItemSelectedListener = listener;

}

但是,但是,这个回调在 ListView 或者 GridView 里面似乎并没有什么卵用呢!这是为撒呢?!

在 AdapterView里面有个handleDataChanged()的方法,这个方法,就是正常执行回调OnItemSelectedListener滴,但是,这个方法被AbsListView给重写了,而且,没有再去回调对应的OnItemSelectedListener了,所以,在ListView和GridView中设置了这个回调也是没有用的。详细的自己去扒一扒源码看看吧,这里不细说了。。

Why is my onItemSelectedListener not called in a ListView?

虽然没有了相关回调,但是我们可以在适当的地方通过调用方法:SparseBooleanArray positions = mListView.getCheckedItemPositions() 来获取我们选中的位置的。

RecyclerView实现单选和多选模式

上面说了 ListView 里面的单选多选模式,接下来就是 RecyclerView 的实现了,首先考虑需要处理哪些问题:

选择标识的添加和相关选择状态保存,item的点击事件(普通模式点击响应,选择模式下刷新为选中或非选中状态),最后就是要弄好回调方法。

SelectRefreshRecycleAdapter的类用来专门处理单选多选模式,当然,它肯定是继承自RefreshRecycleAdapter滴。在这个类中,根据是选择模式或者是普通模式来进行了不同的点击处理。处理了第一个问题。

选择标识的添加 选择状态保存

对于选择标识的添加,首先这个一般来说应该和各种业务没有关系,所以不应该定义在json数据中,当然啦,如果选择状态你真的已经定义了相关字段并且时刻根据后台数据刷新的话,这里也是可以解决滴。

首先我这里定义了一个接口Iselect,然后,这里接着也定义好了一个实现类了。

public interface ISelect {

boolean isSelected();

void setSelected(boolean selected);

}

public class SelectBean implements ISelect, Parcelable {

private boolean isSelected;

public boolean isSelected() {

return isSelected;

}

public void setSelected(boolean selected) {

isSelected = selected;

}

...

}

在使用的时候,如果你的Bean中已经有了相关选中状态的字段了,那么实现Iselect接口,如果没有定义相关字段,那么直接继承SelectBean,其他的就不用去管了。这是关于添加选择标识的解决,然后就是选中状态的保存,这个很简单了,内部维护了一个集合,选中了添加进去,取消选中就移除了。

Item的点击及同步刷新

因为这里有单选模式和多选模式两种情况嘛,接下来一次分析下。

如果是单选模式的话,我们在选中item2之后,不仅要刷新item2的状态,而且还要将之前的选中item的状态更新,意思就是可能要更新两个item。所以这里要定义一个prePos的字段来保存之前的那个item。

如果是多选模式的话,那么就比较简单了,糊糊选就好了(不要纠结为什么用糊糊)。

得益于RecyclerView的后天优势,我们每次只需要调用notifyItemChanged(pos)的方法来刷新指定的item就行了。

@Override

public void performClick(final View itemView, final int position) {

final T testBean = list.get(position);

if (isSelectMode) {

Log.e("TAG", "onViewHolderBind: " + position + "点击了!!");

//点击后取反当前位置

boolean selected = !testBean.isSelected();

testBean.setSelected(selected);

dispatchSelected(itemView, position, testBean, selected);

if (currentMode == SingleMode && position != prePos && testBean.isSelected()) {

//单选模式的prePos处理

list.get(prePos).setSelected(false);

dispatchSelected(itemView, prePos, testBean, false);

notifyItemChanged(prePos);

}

notifyItemRangeChanged(position, 1);

prePos = position;

} else {

//不是选择模式就正常回调咯

if (listener != null) {

listener.onItemClick(itemView, position);

}

}

}

最后就是Viewholder里面的具体实现了:

private static class MyViewHolder extends RecyclerView.ViewHolder {

private final CheckedTextView mTv;

public MyViewHolder(View itemView) {

super(itemView);

mTv = (CheckedTextView) itemView.findViewById(R.id.text);

}

public void bindDateView(TestBean s) {

mTv.setText(s.isSelected() ? "选中:" + s.getName() : s.getName());

mTv.setChecked(s.isSelected());

}

}

相关回调

根据上面 ListView 的那个问题,如果在选择过程中,没有相关回调我们是会抓狂的。参照 ListView 的相关接口,有了以下的定义:

interface OnItemSelectedListener {

void onItemSelected(View view, int position, boolean isSelected);

void onNothingSelected();

}

额,最后加了一个isSelected的参数,感觉就说你选中了撒没有选中的不说是不是有点儿不厚道?!万一你自己要在这里去维护自己的一个数据呢?那不是坑你啦,其实我不想给你说你完全可以通过getSelectedBeans()的方法来直接获取已经选中的集合。

private void dispatchSelected(View itemView, int position, T testBean, boolean isSelected) {

if (isSelected) {

selectedBeans.add(testBean);

} else {

selectedBeans.remove(testBean);

if (selectedListener != null && selectedBeans.isEmpty()) {

selectedListener.onNothingSelected();

}

}

if (selectedListener != null) {

selectedListener.onItemSelected(itemView, position, isSelected);

}

}

额外赠送

bug修复

之前写的版本中,加载更多还没有适配GridLayoutManager,这里也做了相关的适配,并且提供了回调,方便你指定对应的某个 position 的spanSize:

public void setLayoutManager(final RecyclerView.LayoutManager manager) {

this.manager = manager;

if (manager instanceof GridLayoutManager) {

((GridLayoutManager) manager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {

@Override

public int getSpanSize(int position) {

switch (adapter.getItemViewType(position)) {

case TYPE_BOTTOM:

return ((GridLayoutManager) manager).getSpanCount();

default:

return (spanSizeCallBack != null ? spanSizeCallBack.getSpanSize(position) : 0) == 0 ? 1 : spanSizeCallBack.getSpanSize(position);

}

}

});

}

mRecyclerView.setLayoutManager(manager);

}

你只需要这么调用:

mRecycleView.setLayoutManager(manager);

mRecycleView.setSpanSizeCallBack(new SwipeRefreshRecycleView.SpanSizeCallBack() {

@Override

public int getSpanSize(int position) {

return 1;

}

});

强大的ItemDecoration

因为说到了GridLayoutManager了嘛,那么必须说下这个ItemDecoration,例如我们需要让多个item的留白是等距离的,那么就要使用这个东东了。

8026bcbd290f

前后左右留白.png

8026bcbd290f

前后留白.png

另外它还可以来用作头布局,还可以做出sticky的效果。上面的核心代码大概就是酱紫的:

/**

*

* @param space item之间的空间

* @param count 列数

* @param showEdge 是否显示左右边缘

*/

public SpacesItemDecoration(int space, int count, boolean showEdge) {

this.spacing = space;

this.spanCount = count;

this.showEdge = showEdge;

pre = spacing * 1.0f / spanCount;

}

@Override

public void getItemOffsets(Rect outRect, View view,

RecyclerView parent, RecyclerView.State state) {

int position = parent.getChildLayoutPosition(view);

int column = position % 3;

if (showEdge) {

outRect.left = (int) (spacing - column * pre);//left

outRect.right = (int) ((column + 1) * pre);//right

} else {

outRect.left = (int) (column * pre);

outRect.right = (int) (spacing - (column + 1) * pre);

}

if (position < spanCount) { // top

outRect.top = spacing;

}

outRect.bottom = spacing; // bottom

}

到这里,RecyclerView 的单选或者多选模式就搞定了!如果对于这个 Adapter 有相关疑问的可以先去看看上面的一篇哟。

附上最新的相关效果:

8026bcbd290f

PullRefresh3.gif

---- Edit By Joe At 2016 11 26 ----

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值