98

I am using a custom ProgressBar. Now while a task is going on, I am showing the progress bar, but user can still interact with the views and controls. How do I disable the user interaction on whole view just like a ProgressDialog does , when it is visible.

Do I need to use a transparent view on top of main view and show the progress bar on that view and hide that view once a task is completed.

Or just get the id of my parentView and set it disabled ? But then I won't be able to dim the background, just like what happens when a dialog appears on the view/Activity/Fragment. Right?

I just want to know the way to disallow the user from any interaction while the progressbar is visible.

Thanks

intellignt_idiot
  • 1,962
  • 2
  • 16
  • 23
  • Possible duplicate of [Custom Drawable for ProgressBar/ProgressDialog](http://stackoverflow.com/questions/2819778/custom-drawable-for-progressbar-progressdialog) – Graph Theory Apr 28 '16 at 16:51
  • 4
    @txteclipse I do not want to customize progress bar, I have already done that. So, my question is not duplicate. I just want to disable user interaction while the progress bar is visible, same way when a ProgressDialog is visible user cannot interact to other UI controls without dismissing the progress dialog. Now how does my question is duplicate of that question. My question is not even remotely related to customization. – intellignt_idiot Apr 28 '16 at 17:58
  • 1
    If you want all the functionality of a dialog, you should use a dialog. Modality is non-trivial to implement: the accepted answer doesn't block keyboard interaction, for instance. The second answer in the linked question demonstrates how to use custom ProgressBar resources with a ProgressDialog. If that doesn't fulfill your needs, you can use a [custom layout](http://developer.android.com/guide/topics/ui/dialogs.html#CustomLayout) with your ProgressBar. – Graph Theory May 02 '16 at 16:28

7 Answers7

188

Your question: How to disable the user interaction while ProgressBar is visible in android?

To disable the user interaction you just need to add the following code

getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

To get user interaction back you just need to add the following code

getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

Here is an example: Note:I am giving you just an example to show how to disable or retain user interaction

Add a progress bar in your xml.Something like this

<ProgressBar
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/progressBar"
    android:visibility="gone"/>

In MainActivity when a button pressed you show the progressbar and disable the user interaction.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mImageView = (ImageView) findViewById(R.id.imageView);
    mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
    mImageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mProgressBar.setVisibility(View.VISIBLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
        }
    });
}

And when user backPressed you remove the progressbar again retain the user interaction.Something like this

  @Override
public void onBackPressed() {
    super.onBackPressed();
    mProgressBar.setVisibility(View.GONE);
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}

If you want to add a feature of disable and greyed out display, you need to add in your xml layout file a linear layout that fills the parent. Set its background to #B0000000 and its visibilty to GONE. Then programmatically set its visibility to VISIBLE.

Hope this help!

Miha_x64
  • 5,973
  • 1
  • 41
  • 63
Niyamat Ullah
  • 2,384
  • 1
  • 16
  • 26
  • 1
    There is something interesting about this implementation. I'm developing a wearable app for a polar watch and this solution has been giving me some troubles. I noticed that whenever the user touches the screen fast if the action after such touch is blocking the UI using your solution (To prevent multiple events) for some reason android opens any app or option that is behind my app. To try to explain it a little better its like my app minimizes for a fraction of a second real quick and allows me to touch anything that is on the screen at that moment. Has anyone experienced anything similar? – Roger Jun 18 '18 at 01:29
15

I have fixed this issue by adding root layout to the ProgressBar.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:clickable="true"
    android:gravity="center"
    android:visibility="gone"
    android:id="@+id/progress">
    <ProgressBar
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:indeterminate="true"
        android:indeterminateTintMode="src_atop"
        android:indeterminateTint="@color/primary"/>
</LinearLayout>

Made the root layout clickable

android:clickable="true"

NOTE: In my main view, I had RelativeLayout as root and have added above-mentioned code inside the root layout at the last position (last child).

Hope this helps!!

Nirav Dangi
  • 3,607
  • 4
  • 49
  • 60
  • I would suggest a `FrameLayout` instead of a `LinearLayout`, other than that, this answer is perfect and it just works. Thanks Nivar – Lucas P. Sep 18 '19 at 13:05
  • Does this work when there are buttons in the background view? The elevation of those seems higher (api 21+ at least) and they will be clickable, unless you set a higher elevation in this root layout. – User May 21 '20 at 21:57
12

just set:

android:clickable="true" 

in your xml

<ProgressBar...

Only this makes magic!

6

To extend (pun intended) on the accepted Answer :

When you use kotlin you can use extension functions. That way you have a quick and nice looking method for blocking and unblocking UI.

fun AppCompatActivity.blockInput() {
    window.setFlags(
        WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
        WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
}

fun AppCompatActivity.unblockInput() {
    window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
}

fun AppCompatActivity.blockInputForTask(task: () -> Unit) {
    blockInput()
    task.invoke()
    unblockInput()
}

You can use the blocking and unblocking functions in your activity. Also, you can add more functionality like showing a Toast or something.

When using it in a custom view or any other view, you can simply cast the context to activity and use the functions.

Use blockInputForTask to surround simple linear tasks and blockInputand unblockInput when they are needed in different scopes.

You can use blockInputForTask like this:

blockInputForTask { 
    // Your lines of code
    // Can be multiple lines
}
Benjamin Basmaci
  • 2,247
  • 2
  • 25
  • 46
  • 1
    If you wanted to call any `suspend` function in the `task` you can change the `blockInputForTask()` signature: `suspend fun Activity.blockInputForTask(task: suspend () -> Unit)` – James Apr 25 '23 at 16:34
3

Use document default method progressbar.setCancelable(false)

Ramesh sambu
  • 3,577
  • 2
  • 24
  • 39
3

Make a dialog with transparent background. The issue with getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); is that when app will go in background and come back user will be able to interact with UI components, a lot more handling. So for blocking UI make a transparent dialog and if you want to set time for hide/show. Do this in a runnable thread. So the solution will be

public class TransparentDialogHelper {

    private Dialog overlayDialog;

    @Inject
    public TransparentDialogHelper() {

    }

    public void showDialog(Context context) {
        if (AcmaUtility.isContextFinishing(context)) {
            return;
        }
        if (overlayDialog == null) {
            overlayDialog = new Dialog(context, android.R.style.Theme_Panel);
            overlayDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
        }
        overlayDialog.show();
    }

    public void hideDialog() {
        if (overlayDialog == null || AcmaUtility.isContextFinishing(overlayDialog.getContext())) {
            return;
        }
        overlayDialog.cancel();
    }
}

-------- Timer

Handler handler = new Handler();
handler.postDelayed( () -> {
    view.hideProgress();
}, 2000);
Azeem
  • 11,148
  • 4
  • 27
  • 40
Qumber Abbas
  • 560
  • 1
  • 4
  • 11
  • excellent solution, maintains explicit control over the process. Oh yeah, and it woks, as well. UPVOTED :) – tony gil Mar 10 '20 at 10:18
1

Make your parent layout as Relative Layout & add this :

    <RelativeLayout ... >

    <other layout elements over which prog bar will appear>

<RelativeLayout android:id="@+id/rl_progress_bar"
                android:layout_width="match_parent" android:clickable="true"
                android:layout_height="match_parent" >
<ProgressBar android:id="@+id/pb"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_centerInParent="true"
             android:indeterminateOnly="true"
             style="@android:style/Widget.DeviceDefault.ProgressBar"
             android:theme="@style/AppTheme.MyProgressBar"
    />
</RelativeLayout>

If you have floating buttons in your UI, they still grab all the focus & remain clickable when the progress bar is visible. for this use : (when your prog bar is visible & re-enable them when you make your prog bar invisible/gone)

fb.setEnabled(false);
AndroidGuy
  • 1,270
  • 4
  • 15
  • 32