如何强制阻塞重绘 UI 元素?
How can I force a blocking redraw of a UI element?
我有一个简单的登录页面,用户可以在其中输入密码,该密码用于解密一些数据并创建主 activity。生成密钥、调用数据库和创建主程序的过程 activity 大约需要 5 秒,所以我想在用户单击登录按钮后立即在登录屏幕上显示一个进度轮。
不幸的是,由于 android 以非阻塞方式处理 UI 刷新,在登录功能运行之前进度条不会出现。我似乎无法找到一种方法来强制阻塞 UI 刷新 Android 中的视图。 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()
方法运行时如何最好地实现进度轮?
I can't seem to find a way to force a blocking UI refresh for a view in Android
正确。这不是 Android 视图系统的工作方式。
then how best can I best implement a progress wheel while the doLogin() method runs?
在后台线程上执行 doLogin()
。当后台工作完成时,更新主应用程序线程上的 UI。考虑到您的 UI 可能不再存在(例如,用户按下 BACK)或可能被替换(例如,用户旋转设备或以其他方式触发配置更改),而后台工作正在进行。
在现代 Android 应用程序开发中,执行此操作的三种主要方法是使用 ViewModel
and LiveData
以及:
RxJava
协程(用于 Kotlin 中的应用程序开发)
你自己的后台线程
已解决问题。这是我的解决方案。可能有点过头了,但我尽量让它易于扩展,以便它可以应用于其他类似的场景。
用于登录的 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());
我有一个简单的登录页面,用户可以在其中输入密码,该密码用于解密一些数据并创建主 activity。生成密钥、调用数据库和创建主程序的过程 activity 大约需要 5 秒,所以我想在用户单击登录按钮后立即在登录屏幕上显示一个进度轮。
不幸的是,由于 android 以非阻塞方式处理 UI 刷新,在登录功能运行之前进度条不会出现。我似乎无法找到一种方法来强制阻塞 UI 刷新 Android 中的视图。 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()
方法运行时如何最好地实现进度轮?
I can't seem to find a way to force a blocking UI refresh for a view in Android
正确。这不是 Android 视图系统的工作方式。
then how best can I best implement a progress wheel while the doLogin() method runs?
在后台线程上执行 doLogin()
。当后台工作完成时,更新主应用程序线程上的 UI。考虑到您的 UI 可能不再存在(例如,用户按下 BACK)或可能被替换(例如,用户旋转设备或以其他方式触发配置更改),而后台工作正在进行。
在现代 Android 应用程序开发中,执行此操作的三种主要方法是使用 ViewModel
and LiveData
以及:
RxJava
协程(用于 Kotlin 中的应用程序开发)
你自己的后台线程
已解决问题。这是我的解决方案。可能有点过头了,但我尽量让它易于扩展,以便它可以应用于其他类似的场景。
用于登录的 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());