分组悬停列表-2:复杂的xml
Tags
#Android
第一发:分组悬停列表-1:简单的Text
这是分组悬停列表效果的第二发,在第一发里面,我们已经利用RecyclerView.ItemDecoration实现了分组悬停列表的简单Text。第二发里面,主要是实现分组悬停列表的复杂xml
一、效果图


之前我们使用onDraw(),onDrawOver(),都是用canvas的方法活生生的绘制出一个View,这对于很多人(包括我)来说都并不容易,xy坐标的确认,尺寸都较难把握,基本上调UI效果时间都很长。
尤其是canvas.drawText()方法的y坐标,特别是baseLine的位置,不了解的童鞋肯定要踩很多坑。
如果当我们想要绘制的分类title、悬停头部复杂一点时,我都不敢想象要调试多久了,这个时候我们还敢用ItemDecoration吗?
有没有一种方法,就像我们平时使用的那样,在Layout布局xml里画好View,然后inflate出来就可以了呢?
因为ItemDecoration并不是一个View,没法直接addView,那么怎么才能添加一个xml画好的View呢?
最后,看到了别人的解决方案:直接调用这个view的measure、layout、draw方法,将它绘制出来即可。
二、实现代码
- ItemDecoration会依次调用getItemOffsets、onDraw、onDrawOver
- 在第一次调用getItemOffsets时,加载xml,执行measure、layout,并创建viewHolder,避免重复的inflate和findViewById
- 在实际绘制时,加了一个dispatchDraw,根据ShowType,显示Text或XML
- 在显示XML时,通过接口回调,调用了tagDisplayer.showData(viewholder,item)
- 在决定view的显示位置时,普通的layout、offsetTopAndBottom等方式貌似无效,最后我只好通过canvas的位移来决定view的显示位置
/**
* 分组悬停视图
* <p>
* 1、显示简单Text
* 2、显示复杂的xml
* <p>
* 当设置了xml时,简单的Text会失效
* 作者:余天然 on 2016/12/21 下午6:51
*/
public class PinnedDivider<T extends Pinnable> extends RecyclerView.ItemDecoration {
private Paint paint;//画笔
private Rect rect = new Rect();//用于存放测量文字Rect
private Drawable divider;//分割线颜色
private Builder<T> builder;
private BaseViewHolder viewHolder;
private ShowType type;//1-简单Text,2-复杂xml
private PinnedDivider(Builder builder) {
this.builder = builder;
if (builder.displayer != null) {
type = ShowType.XML;
} else {
type = ShowType.TEXT;
}
this.paint = new Paint();
this.paint.setTextSize(builder.tagTextSize);
this.paint.setAntiAlias(true);
this.divider = new ColorDrawable(builder.dividerColor);
}
/**
* 设置分组悬停视图的显示区域
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (type == ShowType.XML && viewHolder == null) {
viewHolder = createViewHolder(parent);
}
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
int position = params.getViewLayoutPosition() - builder.headerCount;
//防止越界
if (position > builder.data.size() - 1 || position < 0) {
return;
}
//第1项肯定要有tag
if (position == 0) {
outRect.set(0, builder.tagHeight, 0, 0);
}
//其余项,不为空且跟前一个tag不一样了,说明是新的分类,也要tag
else if (!builder.data.get(position).getPinnedTag().equals(builder.data.get(position - 1).getPinnedTag())) {
outRect.set(0, builder.tagHeight, 0, 0);
}
//和下一项一样的,都需要分割线
for (int i = 0; i < builder.data.size() - 1; i++) {
String tag1 = builder.data.get(i).getPinnedTag();
String tag2 = builder.data.get(i + 1).getPinnedTag();
if (tag1.equals(tag2)) {
三、使用方式
- 调用pinnedDivider.tagDisplayer(),将上面的displayer传入即可。
public class MainActivity extends AppCompatActivity {
private SingleAdapter<Bean> adapter;
private RecyclerView rv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initView() {
rv = (RecyclerView) findViewById(R.id.rv);
rv.setLayoutManager(new LinearLayoutManager(this));
adapter = new SingleAdapter<Bean>(this, R.layout.item_tv) {
@Override
protected void bindData(BaseViewHolder holder, Bean item, int position) {
TextView tv = holder.getView(R.id.tv);
tv.setText(item.getCity());
}
};
rv.setAdapter(adapter);
}
private void initData() {
List<Bean> list = new ArrayList<>();
list.add(new Bean("A", "安达"));
list.add(new Bean("A", "安化"));
list.add(new Bean("A", "安康"));
list.add(new Bean("A", "安陆"));
list.add(new Bean("B", "包头"));
list.add(new Bean("B", "保山"));
list.add(new Bean("B", "宝兴"));
list.add(new Bean("B", "北京"));
list.add(new Bean("B", "本溪"));
list.add(new Bean("B", "宾阳"));
list.add(new Bean("C", "茶陵"));
list.add(new Bean("C", "朝阳"));
list.add(new Bean("C", "昌黎"));
list.add(new Bean("C", "常德"));
list.add(new Bean("C", "常州"));
list.add(new Bean("C", "郴州"));
list.add(new Bean("C", "成都"));
list.add(new Bean("C", "承德"));
list.add(new Bean("C", "赤壁"));
list.add(new Bean("C", "崇阳"));
list.add(new Bean("C", "滁州"));
list.add(new Bean("C", "长春"));
list.add(new Bean("C", "长春"));
list.add(new Bean("C", "长春"));
list.add(new Bean("C", "长春"));
list.add(new Bean("C", "长春"));
list.add(new Bean("C", "长春"));
PinnedDivider.TagDisplayer<Bean> displayer =
最后奉上源码:Github