先展示效果
1、用法
Step 1. Add the JitPack repository to your build file
Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.PMMKing:KSwipeRefreshLayout:0.9.9'
}
項(xiàng)目中加入本庫的依賴枫匾,配合另一個(gè)recycleview通用adapter食用更佳腋逆。
layout文件代碼如下
<?xml version="1.0" encoding="utf-8"?>
<com.yuan.library.KSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/ks_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:refreshMode="both"
app:refreshView="com.yuan.kswiperefreshlayout.CustomView">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.yuan.library.KSwipeRefreshLayout>
Activity中代碼
val view = LayoutInflater.from(this).inflate(R.layout.image, null, false)
ks_layout.setLoadView(view)
ks_layout.setOnRefreshListener(object : OnRefreshListener {
override fun onRefresh() {
Handler().postDelayed({ ks_layout.setRefresh(false) }, 1500)
}
override fun onLoad() {
Handler().postDelayed({ ks_layout.setRefresh(false) }, 1500)
}
})
Adapter代碼
val adapter = MultiAdapter<String>(this).addTypeView(object : ITypeView<String> {
override fun isForViewType(p0: String?, p1: Int): Boolean {
return true
}
override fun createViewHolder(p0: Context, p1: ViewGroup): BaseViewHolder<*> {
return ViewHolder(p0, LayoutInflater.from(p0).inflate(R.layout.image, p1, false))
}
})
rv_list.layoutManager = LinearLayoutManager(this)
rv_list.adapter = adapter
val list = mutableListOf<String>()
for (i in 1..2) {
list.add("")
}
adapter.data = list
adapter.setOnItemClickListener { view, any, i ->
startActivity(Intent(this, TestActivity::class.java))
}
更多功能
layout文件中自定義屬性只有三個(gè)
1惩歉、app:refreshMode
刷新模式有四種,top只有下拉刷新撑蚌,bottom只有上拉加載,both下拉和上拉同時(shí)存在粉楚,never下拉和上拉都不存在亮垫。
2、app:refreshView
接受一個(gè)string類型的值饮潦,值的內(nèi)容是下拉刷新顯示view的全路徑
app:refreshView="com.yuan.kswiperefreshlayout.CustomView"
內(nèi)部使用反射調(diào)用只有Context參數(shù)的構(gòu)造函數(shù)來實(shí)例化一個(gè)view設(shè)置為下拉刷新的view。
3回俐、app:loadView
同設(shè)置下拉刷新view
代碼中設(shè)置刷新模式
ks_layout.setRefreshMode(RefreshMode.BOTH)
支持回調(diào)下拉上拉距離的方法,自定義View實(shí)現(xiàn)RefreshCall接口仅颇,重寫四個(gè)方法
class CustomView : LinearLayout, RefreshCall {
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) {
initView()
}
fun initView() {
LayoutInflater.from(context).inflate(R.layout.refresh_header, this, true)
setBackgroundColor(Color.GRAY)
}
override fun startRefresh() {
tv.text = "開始下拉刷新"
}
override fun refreshing() {
tv.text = "下拉刷新中..."
thread {
while (true) {
Thread.sleep(6)
iv.post {
iv.rotation++
}
}
}
}
override fun endRefresh() {
tv.text = "下拉刷新成功"
}
override fun refreshDiff(diffY: Int) {
iv.rotation = diffY.toFloat()
}
}
拉動(dòng)的時(shí)候會(huì)回調(diào) refreshDiff(diffY: Int) 方法
2忘瓦、代碼分析
這個(gè)庫使用Kotlin寫的,其實(shí)Kotlin已經(jīng)用了很長一段時(shí)間了耕皮,具體說Kotlin有什么優(yōu)勢還是很泛泛的,但是用上就很難切換回去汽摹。語法糖用著確實(shí)很爽翱嘞恰!
像這種下拉刷新上拉加載的Layout無非就是在事件分發(fā)處理的時(shí)候做文章拉庶,所以重點(diǎn)也是這一塊的代碼秃励。
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
val action = ev.action
when (action) {
MotionEvent.ACTION_DOWN -> {
rawY = ev.rawY
}
MotionEvent.ACTION_MOVE -> {
val diffY = ev.rawY - rawY
val can = (diffY > 10 && canRefresh()) || (diffY < -10 && canLoad())
if (can) return true
}
}
return super.onInterceptTouchEvent(ev)
}
layout繼承的ViewGroup,首先應(yīng)該判斷事件是否應(yīng)該攔截自己來處理夺鲜,在DOWN的時(shí)候記錄一下按下的y坐標(biāo),MOVE的時(shí)候計(jì)算一下Y軸滑動(dòng)距離慷蠕,判斷一下滑動(dòng)距離是否大于某個(gè)值食呻,這個(gè)值主要是為了處理在點(diǎn)擊的時(shí)候手抖動(dòng)會(huì)造成誤判導(dǎo)致事件被攔截。然后判斷一下是否可以刷新
private fun canRefresh(): Boolean {
val canScrollVertically = ViewCompat.canScrollVertically(mTargetView, -1)
return !canScrollVertically && canRefresh && mRefreshView != null
}
說明一下 ViewCompat.canScrollVertically(mTargetView, -1)這個(gè)方法是SDK14才加入的每辟,但是現(xiàn)在最低版本已經(jīng)是14了干旧,所以不用兼容以前的版本了。
返回true的話說明事件可以攔截莱革,交給自己的onTouchEvent方法處理。
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
rawY = event.rawY
return true
}
MotionEvent.ACTION_MOVE -> {
val diffY = event.rawY - rawY
val diff = diff(diffY)
when (refreshState) {
RefreshState.DEFAULT -> {
layoutChildren(diff(diffY))
refreshState = if (diffY >= 0) RefreshState.DOWNPULL else RefreshState.UPPULL
when (refreshState) {
RefreshState.DOWNPULL -> {
mRefreshCall?.startRefresh()
mRefreshCall?.refreshDiff(diff)
}
RefreshState.UPPULL -> {
mLoadCall?.startRefresh()
mLoadCall?.refreshDiff(diff)
}
}
}
RefreshState.DOWNPULL -> {
if (diffY >= 0 && canRefresh()) {
layoutChildren(diff)
mRefreshCall?.refreshDiff(diff)
} else {
layoutChildren()
rawY = event.rawY
}
}
RefreshState.UPPULL -> {
if (diffY <= 0 && canLoad()) {
layoutChildren(diff)
mLoadCall?.refreshDiff(diff)
} else {
layoutChildren()
rawY = event.rawY
}
}
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
val diffY = Math.abs(event.rawY - rawY)
val diff = diff(diffY)
when (refreshState) {
RefreshState.DOWNPULL -> {
val height = mRefreshView?.measuredHeight ?: 200
if (diff > height) {
downRefresh(height)
} else {
refreshState = RefreshState.DEFAULT
layoutChildren()
}
}
RefreshState.UPPULL -> {
val height = mLoadView?.measuredHeight ?: 200
if (diff > height) {
upLoad(height)
} else {
refreshState = RefreshState.DEFAULT
layoutChildren()
}
}
}
}
}
return super.onTouchEvent(event)
}
交給自身onTouchEvent處理,接收到Down事件一定要返回true镶蹋,表示接下來的事件都要交給我來處理赏半,返回false會(huì)把事件交給父ViewGroup處理。MOVE事件的時(shí)候判斷一下狀態(tài)断箫,把計(jì)算出來的位移的值傳遞進(jìn)去,抬起的時(shí)候判斷一下是否觸發(fā)刷新婶熬,調(diào)用相應(yīng)方法埃撵。
事件分發(fā)機(jī)制說明,參考自這個(gè)博文
onInterceptTouchEvent方法對(duì)觸屏事件的攔截處理需要和onTouchEvent方法配合使用暂刘。
1、down事件首先傳遞到onInterceptTouchEvent方法中
2募寨、onInterceptTouchEvent返回false表示將down事件交由子View來處理森缠;若某一層子View的onTouchEvent返回了true绪商,后續(xù)的move辅鲸、up等事件都將先傳遞到ViewGroup的onInterceptTouchEvent的方法,并繼續(xù)層層傳遞下去例书,交由子View處理刻炒;若子View的onTouchEvent都返回了false,則down事件將交由該ViewGroup的onTouchEvent來處理坟奥;如果ViewGroup的onTouchEvent返回false拇厢,down傳遞給父ViewGroup晒喷,后續(xù)事件不再傳遞給該ViewGroup;如果ViewGroup的onTouchEvent返回true衣盾,后續(xù)事件不再經(jīng)過該ViewGroup的onInterceptTouchEvent方法爷抓,直接傳遞給onTouchEvent方法處理
3、onInterceptTouchEvent返回ture蓝撇,down事件將轉(zhuǎn)交該ViewGroup的onTouchEvent來處理;若onTouchEvent返回true据悔,后續(xù)事件將不再經(jīng)過該ViewGroup的onInterceptTouchEvent方法耘沼,直接交由該ViewGroup的onTouchEvent方法處理;若onTouchEvent方法返回false群嗤,后續(xù)事件都將交由父ViewGroup處理,不再經(jīng)過該ViewGroup的onInterceptTouchEvent方法和onTouchEvent方