先上效果图
很常见的地址选择器 之所以自己写主要是闲的
首先分析一下
- 当选择器滑动到顶部和底部时
1.顶部时有一个空白item 而底部有2个空白item 所以itemcount需要+3 重写adapter
override fun getItemCount(): Int {
return if(data.isEmpty()) 0 else data.size+3
}
override fun getItem(position: Int): AddressModel {
if(data.isEmpty()){
return AddressModel()
}
if(position>0&&position<=data.size){
return data[position-1]
}
return data[0]
}
//设置数据
override fun setData(data: AddressModel) {
root.visibility=if(position==0||position>=adapter.itemCount-2) View.INVISIBLE else View.VISIBLE
address.text=data.name
}
2.可以看到选中的item和未选中的item明显有区别 这一块选择了改变item的alpha来实现这个效果 重写recyclerview的drawChild方法
override fun drawChild(canvas: Canvas?, child: View, drawingTime: Long): Boolean {
//获取第一个item的position
val first = layoutManager!!.findFirstVisibleItemPosition()
//设置选中的item position 自由更改
val center = 1+ first
val position = getChildAdapterPosition(child)
if (position == center) {
child.alpha = 1.0f
} else {
//没选中的改为0.4F
child.alpha = 0.4f
}
return super.drawChild(canvas, child, drawingTime)
}
3.选中item 上下方的线 我这里选择重写recyclerview直接画
override fun draw(canvas: Canvas) {
super.draw(canvas)
if (mItemH != 0) {
val size = 1
canvas.drawLine(
0f, (mItemH * size).toFloat(), itemWith.toFloat(), (mItemH
* size).toFloat(), mHoloPaint!!
)
canvas.drawLine(
0f, (mItemH * (size + 1)).toFloat(), itemWith.toFloat(), (mItemH
* (size + 1)).toFloat(), mHoloPaint!!
)
}
}
代码很简单 然后我们还要提供一个获取选中的位置的方法 还有recyclerview高度需要重新设置 recyclerview完整代码如下
class SelectRv: RecyclerView {
private var layoutManager: LinearLayoutManager? = null
private var mItemH = 0
private var mHoloPaint: Paint? = null
private val mItemSize = 4
private var itemWith = 0
constructor(context: Context) : this(context,null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs,0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
){
init()
}
private fun init(){
mHoloPaint = Paint()
mHoloPaint?.strokeWidth = 1f
mHoloPaint?.color = Color.parseColor("#E1E1E1")
viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (childCount > 0 && mItemH == 0) {
itemWith = width
viewTreeObserver.removeOnGlobalLayoutListener(this)
mItemH = getChildAt(0).height
if (mItemH != 0) {
val params = layoutParams
params.height = mItemH * mItemSize
requestLayout()
}
}
}
})
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
layoutManager = getLayoutManager() as? LinearLayoutManager
}
override fun drawChild(canvas: Canvas?, child: View, drawingTime: Long): Boolean {
//获取第一个item的position
val first = layoutManager!!.findFirstVisibleItemPosition()
//设置选中的item position 自由更改
val center = 1+ first
val position = getChildAdapterPosition(child)
if (position == center) {
child.alpha = 1.0f
} else {
//没选中的改为0.4F
child.alpha = 0.4f
}
return super.drawChild(canvas, child, drawingTime)
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
if (mItemH != 0) {
val size = 1
canvas.drawLine(
0f, (mItemH * size).toFloat(), itemWith.toFloat(), (mItemH
* size).toFloat(), mHoloPaint!!
)
canvas.drawLine(
0f, (mItemH * (size + 1)).toFloat(), itemWith.toFloat(), (mItemH
* (size + 1)).toFloat(), mHoloPaint!!
)
}
}
fun getSelect(): Int {
//因为adapter会添加一个头 所以不用+1
return layoutManager?.findFirstVisibleItemPosition()?:-1
}
}
接下来我们要解决另外一个问题 每次滑动结束需要对齐头部item
如果是奇数的item展示 比如一次展示3个或者5个item 不需要重写LinearSnapHelper
偶数的话则需要重写LinearSnapHelper
class TopLinearSnapHelper: LinearSnapHelper() {
private var mVerticalHelper: OrientationHelper? = null
private var mHorizontalHelper: OrientationHelper? = null
override fun calculateDistanceToFinalSnap(
layoutManager: RecyclerView.LayoutManager,
targetView: View
): IntArray? {
val out = IntArray(2)
if (layoutManager.canScrollHorizontally()) {
out[0] = distanceToTop(
targetView,
getHorizontalHelper(layoutManager)
)
} else {
out[0] = 0
}
if (layoutManager.canScrollVertically()) {
out[1] = distanceToTop(
targetView,
getVerticalHelper(layoutManager)
)
} else {
out[1] = 0
}
return out
}
private fun distanceToTop( targetView:View, helper: OrientationHelper):Int{//获取top位置距离顶部差距
return helper.getDecoratedStart(targetView)
}
private fun getVerticalHelper(layoutManager: RecyclerView.LayoutManager): OrientationHelper {
if (mVerticalHelper == null) {
mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager)
}
return mVerticalHelper!!
}
private fun getHorizontalHelper(layoutManager: RecyclerView.LayoutManager): OrientationHelper {
if (mHorizontalHelper == null) {
mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager)
}
return mHorizontalHelper!!
}
override fun findSnapView(layoutManager: RecyclerView.LayoutManager): View? {
val helper=if(layoutManager.canScrollHorizontally()) getHorizontalHelper(layoutManager) else getVerticalHelper(layoutManager)
return find(layoutManager,helper)
}
private fun find(layoutManager: RecyclerView.LayoutManager,helper:OrientationHelper):View?{
val childCount: Int = layoutManager.childCount
if (childCount == 0) {
return null
}
val first=layoutManager.getChildAt(0)
var closestChild: View? = first
val itemHalf=helper.getDecoratedMeasurement(first)/2 //获取item一半的位置
val itemTop=helper.getDecoratedStart(first)
if(abs(itemTop)>=itemHalf){ //如果超过一半 滚动到下一个 否则滚动到上一个
if(childCount>1){
closestChild=layoutManager.getChildAt(1)
}
}
return closestChild
}
}
顺便附上snap的使用 设置完adapter后attach就好了
bind.addressRv1.layoutManager=LinearLayoutManager(context)
bind.addressRv2.layoutManager=LinearLayoutManager(context)
bind.addressRv3.layoutManager=LinearLayoutManager(context)
bind.addressRv1.adapter=adp1
bind.addressRv2.adapter=adp2
bind.addressRv3.adapter=adp3
val mSnap1 = TopLinearSnapHelper()
val mSnap2 = TopLinearSnapHelper()
val mSnap3 = TopLinearSnapHelper()
mSnap1.attachToRecyclerView(bind.addressRv1)
mSnap2.attachToRecyclerView(bind.addressRv2)
mSnap3.attachToRecyclerView(bind.addressRv3)
到这里基本上已经完成一大半了,剩下的就是监听rv的滑动。
类似这样
bind.addressRv1.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if(newState==RecyclerView.SCROLL_STATE_IDLE){//滚动结束需要更新数据
setData(false)
}
}
})
还有就是dialog加上动画 ,传递选择的数据就好了。 优点嘛也有,不用引入第三方库, 可以任意的自定义。
然后直接调用即可
AddressDialog().setGravity(DialogGravity.BOTTOM).show(fragment!!.childFragmentManager,"addressDialog")
感谢观看