我想创建一个会说话的头像,在Android中使用Android和drawable frame anmation。唇形同步图像存储在drawable文件夹中。这是按下发言按钮时执行的功能。
该功能的要点是,根据出现的字母,每个相应的唇形同步动作被添加到动画中。其余的动作基于所选择的语言,决定音高和语速。线程用于运行动画和语音并行。
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//thread to add the animation
Thread avatarSp = new Thread(new Runnable() {
@Override
public void run() {
String toSpeak = ed1.getText().toString();
String[] words = toSpeak.split(" ");
for (String word : words) {
word = word.toLowerCase();
char[] letters = word.toCharArray();
for (int i = 0; i < letters.length; i++) {
if (letters[i] == 'a') {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.a_i), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.a_i), 750);
} else if (letters[i] == 'e') {
if (letters[i + 1] == 'i' || letters[i + 1] == 'a' || letters[i + 1] == 'e') {
i++;
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.e), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.e), 750);
} else {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.l), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.l), 750);
}
} else if (letters[i] == 'i') {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.c_d_g_k_n_r_s_th_y_z), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.c_d_g_k_n_r_s_th_y_z), 750);
} else if (letters[i] == 'o') {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.o), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.o), 750);
} else if (letters[i] == 'u') {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.u), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.u), 750);
} else if (letters[i] == 'w' || letters[i] == 'q') {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.w_q), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.w_q), 750);
} else if (letters[i] == 'f' || letters[i] == 'v') {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.f_v), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.f_v), 750);
} else if (letters[i] == 'l') {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.l), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.l), 750);
} else if (letters[i] == 'm' || letters[i] == 'b' || letters[i] == 'p') {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.m_b_p), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.m_b_p), 750);
} else {
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.c_d_g_k_n_r_s_th_y_z), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.c_d_g_k_n_r_s_th_y_z), 750);
}
}
if (sr1 == 1)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.rest), 500);
else if (sr1 == 2)
avatarSpeak.addFrame(getResources().getDrawable(R.drawable.rest), 750);
}
avatar.post(new Starter());
}
});
//start the thread
avatarSp.start();
tts1.setPitch(p1);
tts1.setSpeechRate(sr1);
String toSpeak = ed1.getText().toString();
lang = sp1.getSelectedItem().toString();
if (lang.equals("US")) {
System.out.print("Condition satisfied");
tts1.setLanguage(Locale.US);
} else if (lang.equals("UK"))
tts1.setLanguage(Locale.UK);
else if (lang.equals("Germany"))
tts1.setLanguage(Locale.GERMANY);
else if (lang.equals("Italy"))
tts1.setLanguage(Locale.ITALY);
else if (lang.equals("Japan"))
tts1.setLanguage(Locale.JAPAN);
else
tts1.setLanguage(Locale.CHINA);
Toast.makeText(getApplicationContext(), toSpeak, Toast.LENGTH_SHORT).show();
//speak
tts1.speak(toSpeak, TextToSpeech.QUEUE_FLUSH, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
tts1.speak(toSpeak, TextToSpeech.QUEUE_FLUSH, null, null);
} else {
tts1.speak(toSpeak, TextToSpeech.QUEUE_FLUSH, null);
}
}
});
}
整个编码测试的API级别是Android 5.0(21)和Android 6.0(AVD 23)。但当我按下发言按钮时,API会在AVD和手机上崩溃。你能告诉我:
1. Where the error could possibly be?
2. Is there a better way to do this and how?
如果它是新手,请耐心等待我,但我真的想知道更好的方法来实现功能,在我自己的代码中。
更新1
添加了日志信息。
10-29 09:02:26.245 1931-1931/com.bluesbegone.avatarspeak I/art﹕ Not late-enabling -Xcheck:jni (already on)
10-29 09:02:26.246 1931-1931/com.bluesbegone.avatarspeak I/art﹕ Late-enabling JIT
10-29 09:02:26.411 1931-1931/com.bluesbegone.avatarspeak I/art﹕ JIT created with code_cache_capacity=2MB compile_threshold=1000
10-29 09:02:27.282 1931-1931/com.bluesbegone.avatarspeak W/System﹕ ClassLoader referenced unknown path: /data/app/com.bluesbegone.avatarspeak-2/lib/x86
10-29 09:02:28.992 1931-1931/com.bluesbegone.avatarspeak I/TextToSpeech﹕ Sucessfully bound to com.svox.pico
10-29 09:02:29.026 1931-1956/com.bluesbegone.avatarspeak D/OpenGLRenderer﹕ Use EGL_SWAP_BEHAVIOR_PRESERVED: true
10-29 09:02:29.056 1931-1931/com.bluesbegone.avatarspeak D/﹕ HostConnection::get() New Host Connection established 0xa3fff460, tid 1931
10-29 09:02:29.300 1931-1938/com.bluesbegone.avatarspeak W/art﹕ Suspending all threads took: 37.049ms
10-29 09:02:29.459 1931-1956/com.bluesbegone.avatarspeak D/﹕ HostConnection::get() New Host Connection established 0xa3fff830, tid 1956
10-29 09:02:29.509 1931-1956/com.bluesbegone.avatarspeak I/OpenGLRenderer﹕ Initialized EGL, version 1.4
10-29 09:02:29.671 1931-1956/com.bluesbegone.avatarspeak W/EGL_emulation﹕ eglSurfaceAttrib not implemented
10-29 09:02:29.672 1931-1956/com.bluesbegone.avatarspeak W/OpenGLRenderer﹕ Failed to set EGL_SWAP_BEHAVIOR on surface 0xad77ac60, error=EGL_SUCCESS
10-29 09:02:29.912 1931-1931/com.bluesbegone.avatarspeak I/Choreographer﹕ Skipped 41 frames! The application may be doing too much work on its main thread.
10-29 09:02:31.082 1931-1931/com.bluesbegone.avatarspeak I/Choreographer﹕ Skipped 69 frames! The application may be doing too much work on its main thread.
10-29 09:02:31.297 1931-1931/com.bluesbegone.avatarspeak I/TextToSpeech﹕ Connected to ComponentInfo{com.svox.pico/com.svox.pico.PicoService}
10-29 09:02:31.822 1931-1968/com.bluesbegone.avatarspeak I/TextToSpeech﹕ Set up connection to ComponentInfo{com.svox.pico/com.svox.pico.PicoService}
10-29 09:02:32.280 1931-1938/com.bluesbegone.avatarspeak W/art﹕ Suspending all threads took: 288.632ms
10-29 09:02:35.320 1931-1938/com.bluesbegone.avatarspeak W/art﹕ Suspending all threads took: 12.137ms
10-29 09:02:43.335 1931-2173/com.bluesbegone.avatarspeak E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-83
Process: com.bluesbegone.avatarspeak, PID: 1931
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081)
at android.view.View.invalidateInternal(View.java:12713)
at android.view.View.invalidate(View.java:12649)
at android.view.View.invalidateDrawable(View.java:16788)
at android.widget.ImageView.invalidateDrawable(ImageView.java:248)
at android.graphics.drawable.DrawableContainer.invalidateDrawable(DrawableContainer.java:377)
at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385)
at android.graphics.drawable.Drawable.setVisible(Drawable.java:764)
at android.graphics.drawable.DrawableContainer.initializeDrawableForDisplay(DrawableContainer.java:510)
at android.graphics.drawable.DrawableContainer.selectDrawable(DrawableContainer.java:459)
at android.graphics.drawable.AnimationDrawable.setFrame(AnimationDrawable.java:274)
at android.graphics.drawable.AnimationDrawable.addFrame(AnimationDrawable.java:251)
at com.bluesbegone.avatarspeak.MainActivity$4$1.run(MainActivity.java:190)
at java.lang.Thread.run(Thread.java:818)
10-29 09:02:44.093 1931-1942/com.bluesbegone.avatarspeak I/art﹕ Background sticky concurrent mark sweep GC freed 10741(781KB) AllocSpace objects, 0(0B) LOS objects, 39% free, 2MB/3MB, paused 1.490ms total 313.129ms
10-29 09:02:44.122 1931-1942/com.bluesbegone.avatarspeak W/art﹕ Suspending all threads took: 26.447ms
10-29 09:02:44.278 1931-1938/com.bluesbegone.avatarspeak W/art﹕ Suspending all threads took: 146.147ms
10-29 09:02:45.073 1931-1931/com.bluesbegone.avatarspeak I/Choreographer﹕ Skipped 77 frames! The application may be doing too much work on its main thread.
10-29 09:02:45.160 1931-1956/com.bluesbegone.avatarspeak W/EGL_emulation﹕ eglSurfaceAttrib not implemented
10-29 09:02:45.161 1931-1956/com.bluesbegone.avatarspeak W/OpenGLRenderer﹕ Failed to set EGL_SWAP_BEHAVIOR on surface 0xa29586e0, error=EGL_SUCCESS
10-29 09:02:45.231 1931-1956/com.bluesbegone.avatarspeak E/Surface﹕ getSlotFromBufferLocked: unknown buffer: 0xab81f1e0
10-29 09:02:47.005 1931-1956/com.bluesbegone.avatarspeak E/Surface﹕ getSlotFromBufferLocked: unknown buffer: 0xab81f1e0
10-29 09:02:48.334 1931-2173/com.bluesbegone.avatarspeak I/Process﹕ Sending signal. PID: 1931 SIG: 9
答案 0 :(得分:1)
您尝试从后台线程设置动画视图,但只有主线程可以触摸视图。摆脱线程,只需将代码放在主要的onClick方法中。