Android itemDecoration接口实现吸顶悬浮标题
作者:Super-B 发布时间:2023-03-14 00:58:31
方案
1.设置一个悬浮的视图挂在recycleView顶部,随着item的移动位置,悬浮标题自动跟随移动或者是保持原地不动。
2.使用recyclerView的ItemDecoration,给指定的item设置不同的itemDecoration,并且跟随item的移动而移动或者保持不变。
本文采用第二种方式实现,效果图:
了解ItemDecoration
这是个接口,一共有六个方法:
public static abstract class ItemDecoration {
/**
* Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
* Any content drawn by this method will be drawn before the item views are drawn,
* and will thus appear underneath the views.
*
* @param c Canvas to draw into
* @param parent RecyclerView this ItemDecoration is drawing into
* @param state The current state of RecyclerView
*/
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}
/**
* Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
* Any content drawn by this method will be drawn after the item views are drawn
* and will thus appear over the views.
*
* @param c Canvas to draw into
* @param parent RecyclerView this ItemDecoration is drawing into
* @param state The current state of RecyclerView.
*/
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
/**
* Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
* the number of pixels that the item view should be inset by, similar to padding or margin.
* The default implementation sets the bounds of outRect to 0 and returns.
*
* <p>
* If this ItemDecoration does not affect the positioning of item views, it should set
* all four fields of <code>outRect</code> (left, top, right, bottom) to zero
* before returning.
*
* <p>
* If you need to access Adapter for additional data, you can call
* {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
* View.
*
* @param outRect Rect to receive the output.
* @param view The child view to decorate
* @param parent RecyclerView this ItemDecoration is decorating
* @param state The current state of RecyclerView.
*/
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
/**
* @deprecated
* Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
*/
@Deprecated
public void onDrawOver(Canvas c, RecyclerView parent) {
}
/**
* @deprecated
* Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
*/
@Deprecated
public void onDraw(Canvas c, RecyclerView parent) {
}
/**
* @deprecated
* Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
*/
@Deprecated
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
outRect.set(0, 0, 0, 0);
}
}
其中有三个方法是@deprecated的,那么我们只需要看以下三个方法:
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
第一个方法的意思是绘制分割线本身;
第二个方法是在item项目绘制完成之后进行的绘制操作(这个会覆盖在item上面);
第三个方法是设置分割线的左间距,上间距,右间距,下间距,保存在outRect中。
如图所示:
其中最底层黄色部分大小是getItemOffsets方法返回的itemDecoration的矩阵设置边距宽度,onDraw方法根据设置的间距宽度来进行绘制黄色区域,其中棕红色部分是onDrawOver方法覆盖绘制在item上层的部分。
利用ItemDecoration来绘制悬浮标题栏
我们给每个需要title的item设置rect.top = titleHeight(标题栏高度);其他的间距可以先不考虑,不重要;
重写onDraw方法,绘制我们的itemDecoration标题栏;
我们需要重写onDrawOver方法,在滑动的时候去判断,
1)如果顶部标题区域是在该标题的items范围之内的滑动的话,那么我们需要覆盖绘制一个处于recyclerView.getpaddingTop位置的title,这样的话这个范围内滑动就有一个悬停的标题栏;
2)如果顶部的标题栏区域恰好下面紧跟着下一个标题栏,那么继续向上滑动的时候,需要下面的标题栏把上面的标题栏顶出界面之外。那么绘制的顶部标题栏的起始位置就是所处的item.bottom - titleHeight的位置。
使用以上三个步骤就可以做出一个流畅并且定制化很高的悬浮标题栏功能了。
代码
class MyDecoration(context: Context): RecyclerView.ItemDecoration() {
var mPaint:Paint? = null
var mPaint2:Paint? = null
var mTextPaint:Paint? = null
var mTitleHeight:Int? = null
var mTitleHeight2:Int? = null
var mTitleTextSize:Float? = null
init {
mTitleHeight =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
30f,
context.getResources().getDisplayMetrics()
).toInt()
mTitleHeight2 =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
3f,
context.getResources().getDisplayMetrics()
).toInt()
mTitleTextSize =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
16f,
context.getResources().getDisplayMetrics()
)
mTextPaint = Paint()
mTextPaint?.let {
it.setTextSize(mTitleTextSize!!)
it.setAntiAlias(true)
it.setColor(Color.WHITE)
}
mPaint = Paint()
mPaint?.let {
it.setAntiAlias(true)
it.setColor(Color.RED)
}
mPaint2 = Paint()
mPaint2?.let {
it.setAntiAlias(true)
it.setColor(Color.BLUE)
}
}
/**
* recyclerView绘制onDraw -> item.onDraw -> onDrawOver
*/
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
for (index in 0 until parent.childCount) {
val childView = parent.getChildAt(index)
childView?.let {
val rect = Rect()
val position = parent.getChildAdapterPosition(it)
if (isTitleItem(position)) {
rect.top = childView.top - mTitleHeight!!
rect.bottom = childView.top
} else {
rect.top = childView.top - mTitleHeight2!!
rect.bottom = childView.top
}
rect.left = parent.paddingLeft
rect.right = parent.width - parent.paddingRight
if (isTitleItem(position)) {
mPaint?.let { it1 -> c.drawRect(rect, it1) }
mTextPaint?.let { it3 ->
c.drawText(
getTitleStr(position),
0f,
rect.top.toFloat() + (mTitleHeight?.div(2.00f)?:0f),
it3)}
} else {
mPaint2?.let { it1 -> c.drawRect(rect, it1) }
}
}
}
}
/**
* recyclerView绘制onDraw -> item.onDraw -> onDrawOver
* 绘制覆盖在item上面的分割线效果
*/
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val childView = parent.getChildAt(0)
var nextView:View? = null;
if (1 < parent.childCount) {
nextView = parent.getChildAt(1)
}
childView?.let {
val rect = Rect()
val position = parent.getChildAdapterPosition(it)
mTitleHeight?.let { height ->
if (nextView != null
&& it.bottom - height < parent.paddingTop
&& isTitleItem(parent.getChildAdapterPosition(nextView))
&& !isSameTitle(parent.getChildAdapterPosition(nextView),position)) {
rect.top = it.bottom - height
rect.bottom = it.bottom
} else {
rect.top = parent.paddingTop
rect.bottom = rect.top + height
}
}
rect.left = parent.paddingLeft
rect.right = parent.width - parent.paddingRight
mPaint?.let { it1 -> c.drawRect(rect, it1) }
mTextPaint?.let { it3 ->
c.drawText(
getTitleStr(position),
0f,
rect.top + (mTitleHeight?.div(2.00f)?:0f),
it3)}
}
}
/**
* 用于设置item周围的偏移量的,类似于设置padding喝margin效果,
* 空出的效果用于绘制分割线
*/
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position:Int = parent.getChildAdapterPosition(view)
if (position % 4 == 0) {
outRect.top = mTitleHeight!!
} else{
outRect.top = mTitleHeight2!!
}
}
fun isTitleItem(position: Int):Boolean {
return position % 4 == 0
}
fun getTitleStr(position: Int):String {
return "标题:${position / 4}"
}
fun isSameTitle(position1: Int,position2: Int):Boolean {
return (position1 / 4) == (position2 / 4)
}
}
来源:https://blog.csdn.net/u012345683/article/details/122816021


猜你喜欢
- easyExcel简介:Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的
- Java虚拟机(JVM)是可运行Java代码的假想计算机。只要根据JVM规格描述将解释器移植到特定的计算机上,就能保证经过编译的任何Java
- /// <summary>/// 获取字符串最长的数字/// </summary>/// <param nam
- idea工具使用 Java Exception Breakpoint 添加异常断点,在IDE里,新建一个断点,类型是Java Excepti
- 1)如何获得MediaPlayer实例:可以使用直接new的方式:MediaPlayer mp = new MediaPlayer();也可
- 以前使用spring的使用要注入property要配置PropertyPlaceholder的bean对象。在springboot除&nbs
- 本文实例主要进行java Timer(定时调用、固定时间执行)测试,具体实现代码如下。测试1当任务执行时间小于重复执行的间隔时间代码:pub
- 我们知道,进入百度图片后,输入一个关键字后,首先看到的是很多缩略图,当我们点击某张缩略图时,我们就可以进入到大图显示页面,在大图显示页面,中
- 前言目前Flutter三大主流状态管理框架分别是provider、flutter_bloc、getx,三大状态管理框架各有优劣,本篇文章将介
- 本文实例为大家分享了Android自定义View倒计时圆的具体代码,供大家参考,具体内容如下创建attr<?xml version=&
- java 枚举的功能挺多,但是坑更多,使用的时候要注意。如下面这个枚举。@Getter@AllArgsConstructorpublic e
- 这个CardStackViewpager的灵感来自Github上面的 FlippableStackView开源项目,而我想实现的效果方向上恰
- 前言日常的Android开发中,我们会用到IntentFilter的匹配规则。IntentFilter的主要规则分为action、categ
- 单点登录(SSO):SSO是指在多个应用系统中个,用户只需要登陆一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应
- 随着时间的推移现在的软件要求显示的内容越来越多,所以要在小的屏幕上能够更好的显示更多的内容,首先我们会想到底部菜单栏,但是有时候想网易新闻要
- CountDownLatch在日常开发中经常会遇到需要在主线程中开启多个线程去并行执行任务,并且主线程需要等待所有子线程执行完毕后再进行汇总
- 最近,项目上涉及到了图像压缩,发现原有的图像压缩功能,虽然保证了图像的大小300K以内,但是压缩后的图像看的不在清晰,并且,限定了图片的He
- 实例如下:import java.util.concurrent.CountDownLatch;import java.util.concu
- 实践过程😜富文本芝麻粒儿提醒:标签是成对出现的就不要省略,有的不是成对的在修改了后就恢复过来,如下方alpha示例,否则多了很容易出现意外的
- 背景:重做系统后重新配置Android studio 安装虚拟机后无法启动log中显示为启动AVD的进程被杀控制台显示为:在虚拟机列表里没有