我逐个字母地将文本写入文本视图。只有在显示整个文本后我才想开始运行进度条。我想我可以通过通知/等待来实现它,但我不知道如何实现。有什么帮助吗?
我创建了
Typewriter
类,它扩展了 Textview
:
private Handler mHandler = new Handler();
public Runnable characterAdder = new Runnable() {
@Override
public void run() {
setText(mText.subSequence(0, mIndex++));
if (mIndex <= mText.length()) {
mHandler.postDelayed(characterAdder, mDelay);
}
}
};
public void animateText(CharSequence text) {
mText = text;
mIndex = 0;
setText("");
mHandler.removeCallbacks(characterAdder);
mHandler.postDelayed(characterAdder, mDelay);
}
然后我在主要活动中使用它:
Typewriter writer = (Typewriter)findViewById(R.id.typewriter);
writer.animateText("Lorem ipsum dolor sit amet");
//wait until animation finishes to perform further actions
由于我的两个操作都在 UI 线程上运行,所以我没有找到任何同步解决方案。相反,我推迟了第二个操作,直到打印所有字母所需的时间:
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
animation.start();
}
}, timeNeeded);
我确实喜欢在 Kotlin 中使用协程。
val handler = Handler()
var isWaitingHandler = true
GlobalScope.async {
handler.post {
// handler does something.
isWaitingHandler = false
}
while(isWaitingHandler) {
delay(50)
}
Unit
}.await()
这不完全是我写的,但我认为可行。
我在这里也遇到了一些问题,我需要执行 UI 更新,而后台线程正在等待 ui 部分完成。
因此,我编写了一个名为 UIUtils 的类,它通过两个静态方法提供了对处理程序调用的轻松访问:
invoke()
和 invokeAndAwait()
invokeAndAwait()
基本上只是将创建的Handler
注册到awaitingCompletion
列表中,然后立即进入等待循环,直到再次从列表中删除Handler
。
如果您意外地从 UI 线程调用
invokeAndAwait()
,则传递的可运行对象将作为对 run()
的阻塞调用来执行。
以非阻塞方式调用某些东西:
//Some code here
UiUtils.invoke(() -> view.setText("abc"));
//More code running right away here.
以阻塞/等待的方式调用某些东西:
//Some code here
UiUtils.invokeAndAwait(() -> view.setText("abc"));
//Code here is running, when invocation is completed.
到目前为止,这对于我的需求来说非常完美。这并不理想,因为它只每 20 毫秒检查一次处理程序是否完成,但对于非常非常非常罕见的用例,我们需要等待执行 UI-Action,我可以接受。
public class UiUtils {
private static Object runningBlockingHandlerListLock = new Object();
private static Collection<Handler> runningBlockingHandlers = new ArrayList<>();
public static boolean isUIThread(){
return Thread.currentThread() == Looper.getMainLooper().getThread();
}
public static void invoke(Runnable r){
new Handler(Looper.getMainLooper()).post(r);
}
public static void invokeAndAwait(Runnable r){
if (isUIThread()){
//On the UI Thread, we cannot juse InvokeAndWait, but we just can run it right away.
Log.w(UiUtils.class.getSimpleName(), "invokeAndAwait() called on UI-Thread. Using blocking execution of runnable instead.");
r.run();
return;
}
Handler h = new Handler(Looper.getMainLooper());
synchronized (runningBlockingHandlerListLock) {
runningBlockingHandlers.add(h);
}
h.post(() -> {
try {
r.run();
}catch (Exception e){
Log.e(UiUtils.class.getSimpleName(), "Exception during Mainthread invokation", e);
}finally {
Log.d(UiUtils.class.getSimpleName(), "Handler completed: " + h.hashCode());
synchronized (runningBlockingHandlerListLock) {
runningBlockingHandlers.remove(h);
}
}
});
boolean isActive = true;
while (isActive){
try {
Log.v(UiUtils.class.getSimpleName(), "Awaiting completion of Handler: " + h.hashCode());
Thread.sleep(20);
synchronized (runningBlockingHandlerListLock) {
isActive = runningBlockingHandlers.contains(h);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}