利用NestedScrollingParent2实现Recycl
2020-08-03 本文已影响0人
卖炭少年炭治郎
/**
* @author : zyl
* //感觉这种方式 虽然能实现下啦刷新 上拉加载 但NestedScrollingParent2 这套接口感觉主要是用于嵌套滚动
*/
val BOTTOM_MAX_HEIGHT_DEFAULT = 100.dp
val HEAD_MAX_HEIGHT_DEFAULT = 100.dp
const val SCROLL_DEFAULT_DURATION = 400
const val RESISTANCE_FACTOR_DEFAULT = 0.6
class RefreshLayout(context: Context?, attrs: AttributeSet?) : ViewGroup(context, attrs),
NestedScrollingParent2 {
private val TAG = RefreshLayout::class.java.simpleName
private var refreshState = RefreshState.NONE
private set(value) {
if (field != value) {
field = value
refreshStateChangeListener?.refreshStateChange(value)
}
}
private var loadMoreState = LoadState.NONE
private set(value) {
if (field != value && canLoadMore.get()) {
field = value
refreshStateChangeListener?.loadMoreStateChange(value)
}
}
/**
* 是否能够触发加载更多的回调
*/
private val canLoadMore = AtomicBoolean(true)
private var headHeight = 0
private var footHeight = 0
/**
* 阻力系数
*/
private var resistance = RESISTANCE_FACTOR_DEFAULT
private var bottomMaxHeight = BOTTOM_MAX_HEIGHT_DEFAULT
private var headMaxHeight = HEAD_MAX_HEIGHT_DEFAULT
private var targetView: View? = null
private val overScroller = OverScroller(context)
private val parentHelper = NestedScrollingParentHelper(this).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
isNestedScrollingEnabled = true
}
}
var refreshListener: OnRefreshListener? = null
/**
* 刷新状态变化通知 方便外部头部view 做状态变化
*/
var refreshStateChangeListener: RefreshStateChangeListener? = null
/**
* 添加刷新头
*/
fun addRefreshHeadView(view: View) {
removeAllViews()
addView(view)
addView(targetView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
requestLayout()
}
/**
* 添加刷新头和尾
*/
fun addRefreshHeadAndFooter(headView: View, footerView: View) {
removeAllViews()
addView(headView)
addView(targetView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
addView(footerView)
requestLayout()
}
/**
* 布局加载完毕了 此时找到加需要刷新头的那个targetView
*/
override fun onFinishInflate() {
super.onFinishInflate()
if (childCount > 1) throw Exception("xml only support one child")
targetView = getChildAt(0)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var selfWidth = 0
var selfHeight = 0
for (index in 0 until childCount) {
val child = getChildAt(index)
//忽略margin值的策略
measureChild(child, widthMeasureSpec, heightMeasureSpec)
selfWidth = max(child.measuredWidth, selfWidth)
selfHeight += child.measuredHeight
}
setMeasuredDimension(selfWidth, selfHeight)
//防止没有刷新头的情况
if (childCount > 1) headHeight = getChildAt(0).measuredHeight
//得到刷新尾部的高度
if (childCount > 2) footHeight = getChildAt(2).measuredHeight
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
//隐藏头部布局
var top = -headHeight
//摆放子控件 竖直排列
for (index in 0 until childCount) {
val child = getChildAt(index)
//居中摆放
child.layout(
measuredWidth / 2 - child.measuredWidth / 2,
top,
measuredWidth / 2 + child.measuredWidth / 2,
top + child.measuredHeight
)
top += child.measuredHeight
}
}
/**
* 当是刷新状态时,不允许用户操作
*/
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (refreshState == RefreshState.REFRESHING
&& ev.actionMasked == MotionEvent.ACTION_DOWN
&& headHeight > 0
) return true
if (loadMoreState == LoadState.LOADING
&& ev.actionMasked == MotionEvent.ACTION_DOWN
&& footHeight > 0
) return true
return super.onInterceptTouchEvent(ev)
}
/**
* 重置列表刷新加载状态
*/
fun onRefreshDone() {
if (scrollY > 0) {
loadMoreState = LoadState.DONE
} else {
refreshState = RefreshState.REFRESHING_COMPLETE
}
overScroller.startScroll(0, scrollY, 0, -scrollY, SCROLL_DEFAULT_DURATION)
postInvalidateOnAnimation()
}
/**
* 设置是否触发加载更多 if true 触发
*/
fun setTriggerLoadMore(trigger: Boolean) {
canLoadMore.set(trigger)
}
override fun computeScroll() {
if (overScroller.computeScrollOffset()) {
scrollTo(overScroller.currX, overScroller.currY)
postInvalidateOnAnimation()
}
}
// NestedScrollingParent2
override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) {
parentHelper.onNestedScrollAccepted(child, target, axes, type)
}
override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
//目前只支持竖直方向
return nestedScrollAxes and ViewCompat.SCROLL_AXIS_VERTICAL != 0
}
//目前只支持竖直方向
override fun getNestedScrollAxes(): Int {
return ViewCompat.SCROLL_AXIS_VERTICAL
}
override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray?, type: Int) {
//此方法 我的理解 是滑动之前 需要问下父空间是否需要处理 目前没有作为子空间的场景 所以不需要处理
Log.v(TAG, "onNestedPreScroll target = $target,dy = $dy,scrollY = ${scrollY},type = $type")
if (scrollY != 0) {
consumed?.let {
it[1] = dy
onNestedScroll(target, dx, 0, 0, dy, TYPE_TOUCH)
}
}
}
//控制fling
override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {
return scrollY != 0
}
override fun onNestedScroll(
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int
) {
if (dyUnconsumed != 0) {
//真正处理滑动事件
var fixDy = fixYOffset(dyUnconsumed)
// scrollBy(0, fixDy)
scrollBy(0, fixDy - (fixDy*resistance).toInt())
if (scrollY < 0) {
refreshState = if (headHeight > 0 && -scrollY >= headHeight) {
RefreshState.RELEASE_TO_REFRESH
} else {
RefreshState.PULL
}
} else {
loadMoreState = if (footHeight in 1..scrollY) {
LoadState.RELEASE_TO_LOADING
} else {
LoadState.PULL
}
}
}
//处理完剩余的事件 看看父亲控件 是否需要 ,此情况暂不考虑
}
override fun onStopNestedScroll(target: View, type: Int) {
Log.v(TAG, "onStopNestedScroll target = $target,scrollY = $scrollY")
if (refreshState == RefreshState.RELEASE_TO_REFRESH) {
Log.v(TAG, "onStopNestedScroll refreshing")
//触发刷新回调
refreshState = RefreshState.REFRESHING
//滚动到指定高度
overScroller.startScroll(0, scrollY, 0, -scrollY - headHeight, SCROLL_DEFAULT_DURATION)
postInvalidateOnAnimation()
refreshListener?.reFresh()
} else if (loadMoreState == LoadState.RELEASE_TO_LOADING) {
Log.v(TAG, "onStopNestedScroll loadMore")
loadMoreState = LoadState.LOADING
//底部滑动到指定位置
overScroller.startScroll(
0,
scrollY,
0,
-(scrollY - footHeight),
SCROLL_DEFAULT_DURATION
)
postInvalidateOnAnimation()
refreshListener?.loadMore()
} else {
//不触发刷新 或是加载
if (scrollY > 0) {
loadMoreState = LoadState.NONE
} else {
refreshState = RefreshState.NONE
}
overScroller.startScroll(0, scrollY, 0, -scrollY, SCROLL_DEFAULT_DURATION)
postInvalidateOnAnimation()
}
parentHelper.onStopNestedScroll(target, type)
}
/**
* 修正滑动位置 防止滑动越界
*/
private fun fixYOffset(dyUnconsumed: Int): Int {
var fixDy = dyUnconsumed
if (scrollY < 0) {
fixDy = if (dyUnconsumed < 0) {
if (headMaxHeight >= -(dyUnconsumed + scrollY)) {
dyUnconsumed
} else {
0
}
} else {
if (dyUnconsumed + scrollY <= 0) {
dyUnconsumed
} else {
-scrollY
}
}
} else if (scrollY > 0) {
fixDy = if (dyUnconsumed > 0) {
if (bottomMaxHeight >= scrollY + dyUnconsumed) {
dyUnconsumed
} else {
0
}
} else {
if (scrollY + dyUnconsumed >= 0) {
dyUnconsumed
} else {
-scrollY
}
}
}
return fixDy
}
}