1

Here is my loading screen design code:

<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    android:padding="50dp">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_marginBottom="8dp"
        android:text="Loading..."
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.517"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar"
        app:layout_constraintVertical_bias="0.003" />
</androidx.constraintlayout.widget.ConstraintLayout>

java class:

package blood.hemoglobin.detection;


import android.app.Activity;
import android.app.AlertDialog;
import android.view.LayoutInflater;

class LoadingDialog {
    private Activity activity;
    private AlertDialog dialog;

    LoadingDialog(Activity myActivity){
        activity=myActivity;
    }
    void startLoadingDialog(){
        AlertDialog.Builder builder=new AlertDialog.Builder(activity);
        LayoutInflater inflater=activity.getLayoutInflater();
        builder.setView(inflater.inflate(R.layout.custom_dialog,null));
        builder.setCancelable(true);
        dialog=builder.create();
        dialog.show();
    }
    void dismissDialog(){
        dialog.dismiss();
    }
}

I have created an object in MainActivity class:

LoadingDialog loadingDialog;

inside onCreate method I initiated the object

loadingDialog=new LoadingDialog(MainActivity.this);

Now I have a method named start which will work on button click. On that button click my app will do some process on an image, during that time I want my app to show the loading screen. After my app finished the processing on the image it will show the processed image on ImageView as a result my loading screen will disappear. Here is my start method inside MainActivity Class:

 public void  start(View view){
        textView.setText("loading");
        loadingDialog.startLoadingDialog();
        Log.i("checking","loading");
        Bitmap b= BitmapFactory.decodeResource(getResources(), R.drawable.test);

        b = b.copy( Bitmap.Config.ARGB_8888 , true);
        Bitmap scaled = Bitmap.createScaledBitmap(b, 500, 800, true);
        int w = b.getWidth();
        int h = b.getHeight();
        int colour;
        int[][] red = new int[500][800];
        int[][] green = new int[500][800];
        int[][] blue=new int[500][800];
        int[][] alpha=new int[500][800];

        for(int i=0;i<500;i++)
            for(int j=0;j<800;j++)
            {
                colour=scaled.getPixel(i,j);
                red[i][j]=Color.red(colour);
                green[i][j]=Color.green(colour);
                blue[i][j]=Color.blue(colour);
                alpha[i][j]= Color.alpha(colour);
            }
        int[][][] data = pyf.callAttr("test", red,green,blue).toJava(int[][][].class);
        for(int x=0; x<500;x++) {
            for(int y=0; y<800; y++) {

                int color = Color.argb(255,data[0][y][x], data[1][y][x], data[2][y][x]);

                scaled.setPixel(x, y, color);
            }
        }
        int[] intArray = new int[]{ w,h };
        Bitmap scaledup = Bitmap.createScaledBitmap(scaled, 625, 1000, true);
        imageView.setImageBitmap(scaledup);
        textView.setText(Arrays.toString(intArray));
        loadingDialog.dismissDialog();
    }

If I comment out this 2 line:

textView.setText(Arrays.toString(intArray));
loadingDialog.dismissDialog();

My loading screen appears after finishing the execution of the start function. But I want loading screen to appear when my start function is being executed and disappear when the execution of start function is finished, what am I doing wrong?

reyad
  • 1,392
  • 2
  • 7
  • 12

2 Answers2

1

You are running all the code in the same thread, so the ui doesn't have the chance to update and show your dialog. That's bad practice because everything related to graphics will seem to be frozen until your processing is finished. Try doing the heavy work in a separate thread, and when you're done call the dismiss dialog in the UI thread.

You have many options, such as using threads directly, rxJava or kotlin Coroutines which is the easiest and lately is the official way to do that kind of thing.

Last official video : https://youtu.be/6manrgTPzyA

Fabio
  • 2,654
  • 16
  • 31
1

Short answer


write your start() function as following:
(note: this code is not tested, but it should get you on the right track)

public void start(View view) {
    // i'm assuming parameter view is the textView
    TextView textView = (TextView) view;

    // if textView is declared somewhere globally,
    // comment the previous line
    textView.setText("loading");
    loadingDialog.startLoadingDialog();
    Log.i("checking","loading");

    // now, execute your code for processed
    //      as an event
    //      add the code in the event loop queue
    //      so, this event will always be called after
    //      the event execution of setText
    textView.post(new Runnable() {
        @Override
        public void run() {
            try {
                Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.test);
                b = b.copy( Bitmap.Config.ARGB_8888 , true);
                Bitmap scaled = Bitmap.createScaledBitmap(b, 500, 800, true);
                int w = b.getWidth();
                int h = b.getHeight();
                int colour;
                int[][] red = new int[500][800];
                int[][] green = new int[500][800];
                int[][] blue=new int[500][800];
                int[][] alpha=new int[500][800];

                for(int i=0;i<500;i++) {
                    for(int j=0;j<800;j++) {
                       colour=scaled.getPixel(i,j);
                       red[i][j]=Color.red(colour);
                       green[i][j]=Color.green(colour);
                       blue[i][j]=Color.blue(colour);
                       alpha[i][j]= Color.alpha(colour);
                    }
                }
                int[][][] data = pyf.callAttr("test", red,green,blue).toJava(int[][][].class);
                for(int x=0; x<500;x++) {
                    for(int y=0; y<800; y++) {
                        int color = Color.argb(255,data[0][y][x], data[1][y][x], data[2][y][x]);
                        scaled.setPixel(x, y, color);
                    }
                }
                int[] intArray = new int[]{ w,h };
                Bitmap scaledup = Bitmap.createScaledBitmap(scaled, 625, 1000, true);
                imageView.setImageBitmap(scaledup);
                textView.setText(Arrays.toString(intArray));
                loadingDialog.dismissDialog();
            } catch(InterruptedException e) {
                // if you need to do something, if it gets interrupted
            } finally {
                // do something when execution finishes
            }
        }
    });
}

The long answer


You should know first how android library(or, framework, whatever you call it) works. I hope you are familiar with event-loop mechanism. If not, then read this.

When a android program is in execution, a event loop queue(or, you may say message queue) is created for events like io(events), handlers, or events like onCreate etc. So, when calling setText(), it registers a DRAW event in the event loop queue. And the view won't be done until when DRAW is called from event loop. And that won't happen until the previous events of event loop executed.

Now, if you are clear with the concept I've talked about previously, we can solve what is wrong with your code:

  1. Why your setText() won't have an effect until the start function executed fully: cause, setText() takes effect through a DRAW event, which is passed into event queue when setText() is called. And, events from the event loop would only start executing after all the code of your function finished executing.
  2. Why loading screen is not appearing at all: if both of your setText() function is called sequentially, then both DRAW(for each setText) will be registered in the event loop just one after another, but there would be no time gap between them(cause, you've already done image processing sequentially). So, loading screen won't appear. To appear loading screen, you also have to do the image processing as an event. So, the second draw event will be called after the event of image processing code and loading screen would appear in that time-gap.

[P.S.]: Even after that, your loading screen does not appear or appear for a very short while, then you may choose to implement them as separate threads and only run the image processing code(let's assume it as second thread) after the first thread's(which would call setText("loading")) execution is finished. You may also call Thread.sleep(for some seconds) in the first thread to show the loading screen for a while. You may check this to know, how to run a thread when one threads execution is finished. For the very basics of multi-threading, you may check Herbert Schild's Java: The Complete Reference book's multi-threading chapter.

reyad
  • 1,392
  • 2
  • 7
  • 12