首页 > 解决方案 > 如何强制对 UI 元素进行阻塞重绘?

问题描述

我有一个简单的登录页面,用户在其中输入密码,该密码用于解密一些数据并创建主要活动。生成密钥、调用数据库和创建主要活动的过程大约需要 5 秒,所以我想在用户单击登录按钮后立即在登录屏幕上显示一个进度轮。

不幸的是,由于 android 以非阻塞方式处理 UI 刷新,因此在登录功能运行之前不会出现进度条。我似乎无法找到一种方法来强制为 Android 中的视图进行阻塞式 UI 刷新。invalidate()并且postInvalidate()两者都不起作用,因为它们只是通知 Android 应该在将来的某个时候发生重绘。

这是一些示例代码来解释我要完成的工作:

        try {
            progressBar.setVisibility(View.VISIBLE);
            passwordEditText.setEnabled(false);

            Key key = doLogin(passwordEditText.getText()); // Long operation

            Intent intent = new Intent(getActivity(), MainActivity.class);
            intent.putExtra("Key", key);
            startActivity(intent);

            getActivity().finish();

        } catch (Exception e) {
            passwordEditText.setError(getString(R.string.login_error));
            Log.e("Login", e.getMessage());
        } finally {
            progressBar.setVisibility(View.INVISIBLE);
            passwordEditText.setEnabled(true);
        }

如果无法覆盖默认行为并强制立即阻塞重绘,那么在doLogin()方法运行时如何最好地实现进度轮?

标签: androidandroid-viewandroid-progressbar

解决方案


修复了问题。这是我的解决方案。它可能有点过头了,但我试图让它易于扩展,以便它可以应用于其他类似的场景。

用于登录的 AsyncTask:


    static class LoginTask extends AsyncTask<String, Void, LoginTask.LoginTaskResultBundle> {

        private TaskActions mActions;

        LoginTask(@NonNull TaskActions actions) {
            this.mActions = actions;
        }

        @Override
        protected void onPreExecute() {
            mActions.onPreAction();
        }

        @Override
        protected LoginTaskResultBundle doInBackground(String... args) {
            try {
                Key key = doLogin();

                return new LoginTaskResultBundle(LoginTaskResultBundle.SUCCEEDED, key);
            } catch (Exception e) {

                return new LoginTaskResultBundle(LoginTaskResultBundle.FAILED, e);
            }
        }

        @Override
        protected void onPostExecute(LoginTaskResultBundle result) {
            if (result.getStatus() == LoginTaskResultBundle.SUCCEEDED)
                mActions.onPostSuccess(result);
            else
                mActions.onPostFailure(result);
        }

        // Result Bundle

        class LoginTaskResultBundle {
            static final int FAILED = 0;
            static final int SUCCEEDED = 1;

            private int mStatus;
            private Exception mException;
            private Key mKey;

            LoginTaskResultBundle(int status, Key key) {
                mStatus = status;
                mKey = key;
                mException = null;
            }

            LoginTaskResultBundle(int status, Exception exception) {
                mStatus = status;
                mException = exception;
            }

            int getStatus() {
                return mStatus;
            }

            Exception getException() {
                return mException;
            }

            Key getKey() {
                return mKey;
            }
        }

        // Interface

        interface TaskActions {
            void onPreAction();
            void onPostSuccess(LoginTaskResultBundle bundle);
            void onPostFailure(LoginTaskResultBundle bundle);
        }
    }

对 LoginAsyncTask 的示例调用:

         new LoginTask(
                  new LoginTask.TaskActions() {
                    @Override
                    public void onPreAction() {
                        showProgressBar();
                    }

                    @Override
                    public void onPostSuccess(LoginTask.LoginTaskResultBundle bundle) {
                        launchMainActivity();
                    }

                    @Override
                    public void onPostFailure(LoginTask.LoginTaskResultBundle bundle) {
                        hideProgressBar();

                        passwordEditText.setError(bundle.getException().getMessage());
                        Log.e("Login", bundle.getException().getMessage());
                    }
                } )
                .execute(passwordEditText.getText().toString());

推荐阅读