diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d429a0a..4b9c634 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -36,6 +36,7 @@
-
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/github/nukc/sample/MainActivity.java b/app/src/main/java/com/github/nukc/sample/MainActivity.java
index ebb7f5b..d47a53a 100644
--- a/app/src/main/java/com/github/nukc/sample/MainActivity.java
+++ b/app/src/main/java/com/github/nukc/sample/MainActivity.java
@@ -124,5 +124,9 @@ public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SetViewActivity.class));
}
});
+
+ findViewById(R.id.btn_swipe_recycler).setOnClickListener(v -> {
+ startActivity(new Intent(MainActivity.this, SwipeRefreshRecyclerActivity.class));
+ });
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/nukc/sample/SwipeRefreshRecyclerActivity.kt b/app/src/main/java/com/github/nukc/sample/SwipeRefreshRecyclerActivity.kt
new file mode 100644
index 0000000..07f6437
--- /dev/null
+++ b/app/src/main/java/com/github/nukc/sample/SwipeRefreshRecyclerActivity.kt
@@ -0,0 +1,134 @@
+package com.github.nukc.sample
+
+import android.graphics.Color
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.util.TypedValue
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.RecyclerView
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import com.github.nukc.stateview.StateView
+import kotlin.math.roundToInt
+
+/**
+ * a example for SwipeRefreshLayout + RecyclerView
+ */
+class SwipeRefreshRecyclerActivity: AppCompatActivity(), SwipeRefreshLayout.OnRefreshListener {
+
+ private class ViewHolderImpl(v: View): RecyclerView.ViewHolder(v)
+
+ private class Adapter: RecyclerView.Adapter() {
+
+
+ private val mDataSet = ArrayList()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderImpl {
+ val tv = TextView(parent.context)
+ tv.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50F, parent.context.resources.displayMetrics).roundToInt()
+ )
+ tv.gravity = Gravity.CENTER
+ return ViewHolderImpl(tv)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolderImpl, position: Int) {
+ val tv = holder.itemView as TextView
+ tv.text = mDataSet[position]
+ }
+
+ override fun getItemCount(): Int = mDataSet.size
+
+ fun appendDataSet(dataSet: List) {
+ val from = mDataSet.size
+
+ mDataSet.addAll(dataSet)
+ notifyItemRangeInserted(from, dataSet.size)
+ }
+ }
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_swipe_refresh_recycler)
+
+
+ mSwipeRecyclerView = findViewById(R.id.swipe_refresh).apply {
+ setOnRefreshListener(this@SwipeRefreshRecyclerActivity)
+ }
+
+
+ val recyclerView = findViewById(R.id.recycler_view).apply {
+ addItemDecoration(DividerItemDecoration(this@SwipeRefreshRecyclerActivity, DividerItemDecoration.HORIZONTAL))
+ adapter = mAdapter
+
+ }
+
+ // start refreshing
+ mSwipeRecyclerView.isRefreshing = true
+ onRefresh()
+
+ // important:
+ // we inject into RecyclerView after onResume()
+ // instead of onCreate(), then the bug occurred !!!
+
+ mSwipeRecyclerView.post {
+ mStateView = StateView.inject(recyclerView)
+ mStateView.onRetryClickListener = object : StateView.OnRetryClickListener {
+ override fun onRetryClick() {
+ mSwipeRecyclerView.isRefreshing = true
+ onRefresh()
+ }
+ }
+ }
+ }
+
+ private val mHandler = Handler(Looper.getMainLooper())
+
+ private val mAdapter = Adapter()
+
+ private lateinit var mStateView: StateView
+ private lateinit var mSwipeRecyclerView: SwipeRefreshLayout
+
+
+ override fun onDestroy() {
+ super.onDestroy()
+ mHandler.removeCallbacksAndMessages(null)
+ }
+
+ private var mStateId = 0
+
+ override fun onRefresh() {
+ // success or failure ?
+
+ if ((++mStateId and 1) == 1) {
+ // success
+ mHandler.postDelayed(1000) {
+
+ var idx = 0
+ val newDataSet = Array(10) { idx++.toString() }
+
+ mAdapter.appendDataSet(newDataSet.asList())
+ mStateView.showContent()
+ mSwipeRecyclerView.isRefreshing = false
+ }
+ }
+ else {
+ // ops :(
+ mHandler.postDelayed(1000) {
+ mStateView.showRetry()
+ mSwipeRecyclerView.isRefreshing = false
+ }
+ }
+ }
+}
+
+fun Handler.postDelayed(time: Long, action: Runnable) {
+ postDelayed(action, time)
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 0e4f1fa..b0358a5 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -109,5 +109,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="setEmptyView" />
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_swipe_refresh_recycler.xml b/app/src/main/res/layout/activity_swipe_refresh_recycler.xml
new file mode 100644
index 0000000..388a36b
--- /dev/null
+++ b/app/src/main/res/layout/activity_swipe_refresh_recycler.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kotlin/build.gradle b/kotlin/build.gradle
index ef88561..6ca2575 100644
--- a/kotlin/build.gradle
+++ b/kotlin/build.gradle
@@ -32,6 +32,7 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
compileOnly 'androidx.constraintlayout:constraintlayout:2.0.4'
+ compileOnly 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
diff --git a/kotlin/src/main/java/com/github/nukc/stateview/Injector.kt b/kotlin/src/main/java/com/github/nukc/stateview/Injector.kt
index 5015bf9..e53e997 100644
--- a/kotlin/src/main/java/com/github/nukc/stateview/Injector.kt
+++ b/kotlin/src/main/java/com/github/nukc/stateview/Injector.kt
@@ -12,6 +12,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.NestedScrollingChild
import androidx.core.view.NestedScrollingParent
import androidx.core.view.ScrollingView
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import java.lang.Exception
/**
* @author Nukc.
@@ -24,6 +26,13 @@ internal object Injector {
false
}
+ val swipeRefreshLayoutAvailable = try {
+ Class.forName("androidx.swiperefreshlayout.widget.SwipeRefreshLayout") != null
+ }
+ catch (ignore: Throwable) {
+ false
+ }
+
/**
* Create a new FrameLayout (wrapper), let parent's remove children views, and add to the wrapper,
* stateVew add to wrapper, wrapper add to parent
@@ -139,4 +148,22 @@ internal object Injector {
stateView.stateListAnimator = target.stateListAnimator
}
}
+
+ /**
+ * fix bug: if someone injects SwipeRefreshLayout's child after SwipeRefreshLayout is resumed,
+ * this child will be hidden.
+ */
+ fun injectIntoSwipeRefreshLayout(layout: SwipeRefreshLayout) {
+ try {
+ val mTargetField = SwipeRefreshLayout::class.java.getDeclaredField("mTarget");
+ mTargetField.isAccessible = true
+ mTargetField.set(layout, null)
+
+ // we replace the mTarget field with 'null', then the SwipeRefreshLayout
+ // will look for it's real child again.
+ }
+ catch (e: Throwable) {
+ e.printStackTrace()
+ }
+ }
}
\ No newline at end of file
diff --git a/kotlin/src/main/java/com/github/nukc/stateview/StateView.kt b/kotlin/src/main/java/com/github/nukc/stateview/StateView.kt
index 5bcab6d..384eb71 100644
--- a/kotlin/src/main/java/com/github/nukc/stateview/StateView.kt
+++ b/kotlin/src/main/java/com/github/nukc/stateview/StateView.kt
@@ -19,6 +19,7 @@ import androidx.core.view.NestedScrollingChild
import androidx.core.view.NestedScrollingParent
import androidx.core.view.ScrollingView
import androidx.core.view.ViewCompat
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
/**
* StateView is an invisible, zero-sized View that can be used
@@ -340,6 +341,12 @@ class StateView @JvmOverloads constructor(
ViewGroup.LayoutParams.MATCH_PARENT
)
Injector.setStateListAnimator(stateView, view)
+
+ // special for SwipeRefreshLayout
+ if (Injector.swipeRefreshLayoutAvailable && parent is SwipeRefreshLayout) {
+ Injector.injectIntoSwipeRefreshLayout(parent)
+ }
+
return stateView
}
throw ClassCastException("view.getParent() must be ViewGroup")