Best practices when presenting Android loading screen

Asked

Viewed 5,978 times

7

The best known and widespread approach to show the user that a request to the network is happening and make it wait until the end of this, has been through the use of the component ProgressDialog. The component is created at the beginning of the request and closed at the end. This can be achieved, for example, by using a AsyncTask.

Using the approach described we would have in the method onPreExecute() of our AsyncTask something like:

protected void onPreExecute() {
     dialog = new ProgressDialog(context);
     dialog.setTitle("Realizando o carregamento dos dados");
     dialog.setMessage("Aguarde o fim da requisição...");
     dialog.show();
}

And at the end of the requisition, in onPosExecute() of AsyncTask:

protected void onPostExecute(String retorno) {
    Activity activity = (Activity) context;
    TextView dados = (TextView) activity.findViewById(R.id.dados);
    dados.setText("Dados: " + retorno);
    dialog.dismiss();//Fecha o dialog após o fim da requisição
}

The product of the use of ProgressDialog would be something like:

inserir a descrição da imagem aqui

Although quite used this approach seems to have become a bad practice with regard to usability and appears between the 10 worst items that affect user experience.

Therefore, I ask: what is the best way to show the user that a request is taking place? How to adapt the code below so as to make it adapt to the new usability requirements?

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button botao = (Button) findViewById(R.id.botao);
        botao.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Task(v.getContext()).execute();
            }
        });
    }

    private class Task extends AsyncTask<Void, Void, String> {

        private ProgressDialog dialog;
        private Context context;

        public Task(Context context) {
            this.context = context;
        }

        @Override
        protected void onPreExecute() {
            dialog = new ProgressDialog(context);
            dialog.setTitle("Realizando o carregamento dos dados");
            dialog.setMessage("Aguarde o fim da requisição...");
            dialog.show();
        }

        @Override
        protected String doInBackground(Void... params) {

            String urlServico = "http://echo.jsontest.com/key/value/one/two";

            try {
                URL url = new URL(urlServico);
                HttpURLConnection http = (HttpURLConnection) url.openConnection();
                InputStream stream = http.getInputStream();

                Reader reader = new InputStreamReader(stream, "UTF-8");
                char[] buffer = new char[1024];
                reader.read(buffer);
                return new String(buffer);

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return "";
        }

        @Override
        protected void onPostExecute(String retorno) {
            Activity activity = (Activity) context;
            TextView dados = (TextView) activity.findViewById(R.id.dados);
            dados.setText("Dados: " + retorno);
            dialog.dismiss();
        }
    }
}
  • 1

    It depends on where you trigger the action: if it is a button o Progress can appear on the button itself, if it is on Actionbar the same thing, when filling a list may appear in the list, etc.

  • Hello, @ramaral. How would I use this approach that you highlighted? Is there any property or method in these described components that allows the presentation of Progress?

  • Behold this and this

2 answers

8

Your question has already been very well answered by Mr @ramaral. And, incidentally, the main point of the colleague’s answer is this:

Whatever the chosen form it must ensure that it does not block the screen and, if possible, allow the user to exit the application and back later to access the result of the request.

But how you used the tags and , I thought we could explore it further. So I’m only responding because I think I can collaborate by adding some information and scenarios to emphasize the above placement of the colleague. By the way, I’m not going to offer code examples simply because your question is not only for Android, but for any platform, and because that’s not the focus of my answer.


Usability in the Indication of Progress

Exclusively from the point of view of usability - where you want the product to be "usable" (hence the name "usability"), that is, easy to use, understand, learn, memorize, etc - the use of progress screens is especially necessary to convey information that the system is not dead. In other words, something important is being carried out and therefore the user must wait. The word "important" is highlighted because this importance is relative to the user: the task is important for him, so that he is willing to wait for its closure. A very simple example is of a 3D modeling application in which the artist created an entire scenario and then asks the rendering the image or video with the animations. This task takes time, the user is aware of this in the process of using the application, and will wait for the necessary time within the obvious limits of something feasible. But the app needs to demonstrate that it is doing such a task.

An obvious indication that the process is doing something is a simple animation. With the use of the system the user learns that while that animation is running the system is not simply "locked", but doing the task that was requested. Nowadays you use this circular disk that you mention in the question, but even in the past - in which systems were developed only in textual interface - animations were used with the addition of points or the alternation of characters like these:

inserir a descrição da imagem aqui

In addition to this visual indication that the process is running, ideally should also provide two additional information to improve usability: an estimate of completion and the option to cancel the process. This information is important so that the user not only has information about that task that is important to him (important usability criterion called feedback), but also to feel confident and safe in controlling the interaction (other important usability criteria are safety and error recovery).

The conclusion estimate can be offered in the form of a percentage (how much x has been done already) or in the form of time (how many seconds/minutes/hours/days/weeks/etc are missing to finish), and the choice depends on the domain of the problem and the calculation data being available (number of items to process). But in general (and when possible) both are used. A classic example is a download tool by Torrents. Further information on the calculation of these estimates can be obtained in this other question.

The option to cancel is important mainly in user-initiated tasks because it allows it to recover from errors. In the example of the 3D artist rendering a scene, in case he realizes he has missed adjusting some object in the scene he can cancel the processing to make such adjustment at any time, and do not need to wait until the end of the processing for this action.

But even in tasks that are not initiated by the user, both the progress feedback and the cancellation option are important to ensure a more enjoyable use (unlike , here one begins to think more about the user experience, or ).

Experience in Indicating Progress

When I said that the key point of my colleague @ramaral’s response was that it should always be possible for the user to exit the application and come back later, it was mainly by the Experience aspect. Your computer system (whether used on Android, iOS, Web, Playstation 4, Smarttv, doesn’t matter) is a product. And nothing is more stressful for the user than a product that does not allow to be used as desired.

The user tends to be more patient with tasks he has requested ("I am willing to wait for this download I requested") than with tasks that his system "thinks" needs to be performed. Here’s a very real scenario:

Ned Stark really likes watching episodes of series on Netflix during his lunch break. As he works from home, the hours of lunch is that important moment of relaxation for him (that keeps your head in place, let’s say). One day, he prepared his lunch, his drink, and sat comfortably on the couch looking forward to that new episode of House of Cards. So he turned on his Smarttv and opened the Netflix app, which then displayed the message: "There’s a update that NEEDS to be performed now. Please save.". Without option to cancel, the app took 40 minutes to be updated via Internet.

Certainly the user of this very real scenario was very disappointed with the product. The reason is quite simple: the product seems to know more than it, completely took away his control over when would be the best time to do a task (update) that he even requested. It may be something important, but not for him.

Despite the comicity in this scenario, this issue is serious and many people are increasingly concerned about this in their products (unfortunately not as many as we would like, even more in Brazil - I don’t know in Portugal). Thus, the practical summary is as follows:

  • Long tasks that are started by the user tend to be better received. But it is still important to offer options for cancellation, postponement or execution in the background. The user needs to feel in control of the interaction, either in the consideration that they will do something else while the task is executed, or in the consideration of giving up their execution.

  • Long tasks that are not initiated by the user should be performed much more carefully, in order to reduce the impact on their activities as much as possible. If it is something that interrupts the service, it should be performed only with the consent of the user and should be given the option to cancel at any time. Depending on the platform, useful offer more than one execution option to the user (for example, for updating can be done via Internet or via local file in Pen-Drive, downloaded from another device).

  • Where possible, estimates should be offered on what has already been achieved and what remains to be achieved. This information is fundamental for the user to feel in control and decide whether to wait or not, do something else while or cancel, etc. The use of a simple animation (without progress information and/or completeness estimate) is more acceptable in tasks requested by the user and whose duration is short (refresh screen, for example).

  • It is very useful to evaluate the use of its application with its users, both in prototypes during the development phase and with the actual application during the use phase. You should be aware of any bad comment regarding an interaction of this type, because it can point out a bigger problem when experienced by more users.

  • 2

    Very good answer.

7


It depends on where/how you trigger the request: if it is a button o Progressbar can appear on the button itself, if it is on Actionbar the same thing, when filling a list may appear in the list, etc.

Whichever way you choose it should ensure that it does not block the screen and, if possible, allow the user to exit the application and return later to access the result of the request.


Example in which the Progressbar appears on the button.

Create a layout with a Buttom and a Progressbar

progress_buttom.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" 
    android:id="@+id/progressBarLayout">

    <Button
        android:id="@+id/progressButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Download" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_alignTop="@id/progressButton"
        android:layout_alignRight="@id/progressButton"
        android:visibility="invisible"/>

</RelativeLayout>  

Replace your Activity xml button with this layout as follows:

<include layout="@layout/progress_button"/>

Your code would look like this:

public class MainActivity extends Activity {

    private final ProgressBar progressBar;
    private TextView dados;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button botao = (Button) findViewById(R.id.progressButton);
        progressBar = (ProgressBar)findViewById(R.id.progressBar);
        dados = (TextView)findViewById(R.id.dados);

        botao.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Task().execute();
            }
        });
    }

    private class Task extends AsyncTask<Void, Void, String> {

        @Override
        protected void onPreExecute() {
            progressBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected String doInBackground(Void... params) {

            String urlServico = "http://echo.jsontest.com/key/value/one/two";

            try {
                URL url = new URL(urlServico);
                HttpURLConnection http = (HttpURLConnection) url.openConnection();
                InputStream stream = http.getInputStream();

                Reader reader = new InputStreamReader(stream, "UTF-8");
                char[] buffer = new char[1024];
                reader.read(buffer);
                return new String(buffer);

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return "";
        }

        @Override
        protected void onPostExecute(String retorno) {
            dados.setText("Dados: " + retorno);
            progressBar.setVisibility(View.INVISIBLE);
        }
    }
}

Examples of the other forms I have cited can be found here and here.

  • Hello, Ramaral. The solution worked perfectly. One last question: the proper or recommended practice when the user performs a request is then not to "block" the screen and allow the user to use the app normally, is that it? You can include this in your reply?

  • I edited the answer.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.