Creating a Custom Error View for Network Loading in Android

Creating a Custom Error View for Network Loading in Android

·

5 min read

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)