Introduction
In modern-day mobile development, it is common to encounter situations where data retrieval takes longer than expected, resulting in a blank screen or a partial view. When this happens, we can help the user by providing a custom error view that displays an error message and a retry button.
This tutorial will walk you through the process of creating a custom error view for network loading in Android. This view can be used to indicate that network communication has failed and allow the user to retry the network operation. We will use a ContentLoadingProgressBar to display a spinner while waiting for the network operation to complete.
We will start by creating a new Kotlin class named ErrorProgressView
. This class will extend LinearLayoutCompat
class ErrorProgressView : LinearLayoutCompat, View.OnClickListener {
private lateinit var mProgressBar: ContentLoadingProgressBar
private lateinit var mRetryButton: Button
private lateinit var mErrorTitleTxt: TextView
private lateinit var mErrorMessageTxt: TextView
private lateinit var mErrorImage: AppCompatImageView
private lateinit var onRetryAction: OnRetryAction
companion object {
const val TYPE_NETWORK: Int = 2
const val TYPE_LOADING_ERROR: Int = 1
}
interface OnRetryAction {
fun onRetry()
}
constructor(context: Context?) : super(context!!) {
orientation = VERTICAL
init(context)
}
constructor(context: Context?, attrs: AttributeSet?) : super(
context!!, attrs
) {
orientation = VERTICAL
init(context)
}
constructor (context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context!!,
attrs,
defStyleAttr
) {
orientation = VERTICAL
init(context)
}
/** Initialize all widgets via id
* @param mErrorMessageTxt: String
* @return
*/
private fun init(context: Context) {
LayoutInflater.from(context).inflate(R.layout.error_progress_view, this)
mProgressBar = findViewById(R.id.error_progress)
mRetryButton = findViewById(R.id.error_retry_btn)
mErrorTitleTxt = findViewById(R.id.error_title)
mErrorMessageTxt = findViewById(R.id.error_message)
mErrorImage = findViewById(R.id.error_image)
mRetryButton.setOnClickListener(this)
}
override fun onClick(view: View?) {
onRetryAction.onRetry()
}
/** show progress icon
* @param boolean
* @return
*/
fun mShowProgress(show: Boolean) {
when (show) {
true -> {
mProgressBar.visibility = VISIBLE
mShowImage(false)
mShowButton(false)
mErrorTitleTxt.visibility = GONE
mErrorMessageTxt.visibility = GONE
}
else -> {
mProgressBar.visibility = GONE
}
}
}
/** show and gone retry button
* @param show: Boolean
* @return
*/
private fun mShowButton(show: Boolean) {
if (show) mRetryButton.visibility = VISIBLE else mRetryButton.visibility = GONE
}
/** show and gone Error image
* @param show: Boolean
* @return
*/
private fun mShowImage(show: Boolean) {
if (!show) mErrorImage.visibility = GONE else mErrorImage.visibility = VISIBLE
}
/** Based on the error type => show the error (eg :button, message, what is error)
* @param type:int
* @return
*/
fun setErrorType(type: Int) {
when (type) {
TYPE_NETWORK -> {
mShowError(
R.drawable.error_view_network,
R.string.error_view_no_network_available,
R.string.error_view_network_text
)
}
TYPE_LOADING_ERROR -> {
mShowError(
R.drawable.ic_baseline_error_24,
R.string.error_view_general_error,
R.string.error_view_network_text
)
}
}
}
/** show the error
* @param
* @return
*/
private fun mShowError(
error_view_empty: Int,
error_view_empty_list: Int,
error_view_network_text: Int
) {
mShowProgress(false)
mShowButton(true)
mShowImage(true)
mErrorTitleTxt.visibility = VISIBLE
mErrorImage.setImageResource(error_view_empty)
setErrorMessageText(context.getString(error_view_empty_list))
setButtonText(context.getString(error_view_network_text))
}
/** set error message in textview
* @param mErrorMessageTxt: String
* @return
*/
private fun setErrorMessageText(mErrorMessageTxt: String?) {
showView(this.mErrorMessageTxt)
this.mErrorMessageTxt.text = mErrorMessageTxt
}
/** set error message in Button
* @param mRetryButton: String
* @return
*/
private fun setButtonText(mRetryButton: String?) {
showView(this.mRetryButton)
this.mRetryButton.text = mRetryButton
}
/** show error messageText
* @param mErrorMessageTxt: String
* @return
*/
private private fun showView(mErrorMessageTxt: View) {
mErrorMessageTxt.visibility = VISIBLE
}
fun setOnRetryAction(onRetryAction: OnRetryAction?) {
this.onRetryAction = onRetryAction!!
}
}
The view has three main methods: mShowProgress(), mShowButton(), and mShowImage(). These methods allow you to show and hide the progress bar, retry button, and error image, respectively.
The setErrorType() method is used to display different types of errors, such as network errors and loading errors. Based on the error type, the mShowError() method is called, which shows the relevant image, error message, and button text.
The setErrorMessageText() and setButtonText() methods are used to set the error message and button text, respectively. These methods take in a string parameter and set the relevant text view’s text.
Finally, the setOnRetryAction() method is used to set the OnRetryAction listener, which is called when the user clicks the retry button. The interface has one method, onRetry(), which is implemented by the calling class to handle the retry action.
Step 4: Creating the error_progress_view Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:layout_gravity="center"
android:orientation="vertical">
<androidx.core.widget.ContentLoadingProgressBar
tools:visibility="visible"
android:id="@+id/error_progress"
style="@style/Base.Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<LinearLayout
android:layout_width="@dimen/error_view_max_width_200dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/error_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:visibility="gone"
tools:visibility="visible"
app:srcCompat="@drawable/error_view_network" />
<TextView
android:gravity="center"
tools:visibility="visible"
android:visibility="gone"
android:id="@+id/error_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/activity_horizontal_margin_16dp"
android:text="@string/error_view_main_txt"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:gravity="center"
tools:visibility="visible"
android:visibility="gone"
android:id="@+id/error_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="@dimen/min_padding_margin_8dp"
android:layout_marginTop="@dimen/min_padding_margin_8dp"
android:text="@string/error_view_network_text"
android:textAppearance="?android:attr/textAppearanceSmall" />
<Button
tools:visibility="visible"
android:visibility="gone"
android:id="@+id/error_retry_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/min_padding_margin_8dp"
android:text="@string/retry" />
</LinearLayout>
</LinearLayout>
String xml
<resources>
<string name="retry">retry</string>
<string name="error_view_no_network_available">Network connection could be established</string>
<string name="error_view_general_error">Some thing went wrong.</string>
<string name="error_view_main_txt">Oops…</string>
<string name="error_view_network_text">Retry</string>
<string name="back_to_online">Back to Online</string>
</resources>
Dimens xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="activity_horizontal_margin_16dp">16dp</dimen>
<dimen name="min_padding_margin_8dp">8dp</dimen>
<dimen name="error_view_max_width_200dp">200dp</dimen>
<dimen name="image_height">200dp</dimen>
</resources>
Set Custom ErrorProgressView on Android Main Activity Layout File
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SplashScreenActivity">
<com.example.bibin.utils.ErrorProgressView
android:id="@+id/error_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java Class with ErrorProgressView
class MainActivity : AppCompatActivity(),ErrorProgressView.OnRetryAction {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityCakeListBinding.inflate(layoutInflater)
setContentView(mBinding.root)
mInit()
}
/** mInit initilize
* Error view retry button (custom created view )
* @param
* @return
*/
private fun mInit() {
mBinding.errorView.setOnRetryAction(this)
}
/** Call cake list from api via viewmodel class
* @param
* @return
*/
fun mGetList() {
mBinding.errorView.mShowProgress(true)
/// do the operation network calling if it fails ,
///show approiate message from errorView
// mBinding.errorView.mShowProgress(false)
}
/** Retry to load the cake data
* @param
* @return
*/
override fun onRetry() {
Log.d("TAG", "onRetry: ")
mGetList()
}
}
ErrorProgressView is a custom view in Android that displays an error message and a progress bar when an error occurs. It also includes a retry button that allows the user to attempt the operation again. To handle the click events on this retry button, you can implement the OnRetryAction
interface in your activity or fragment.
Show Progress in Custom ErrorProgressView using
mBinding.errorView.mShowProgress(true)
Hide Progress in Custom ErrorProgressView using
mBinding.errorView.mShowProgress(false)
Set Error Types in Custom ErrorProgressView using mBinding.setErrorType()
mBinding.setErrorType(TYPE_NETWORK)
mBinding.setErrorType(TYPE_LOADING_ERROR)