我对Java相当陌生,对Android开发真的很陌生。
我正在尝试制作一个摩尔斯电码应用程序,该应用程序将在设备屏幕上的摩尔斯电码中闪烁用户输入的消息。
我的问题是我无法找到一种方法来暂停代码以添加时间。
我知道直接放置一个wait()或sleep()是不可能的。在网上搜索后,我找到了一些代码并将其实现到我的代码中-这是当前的代码-这只是倒计时来测试等待:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flash);
// ALL THIS STUFF IS AUTO-GENERATED
final View controlsView = findViewById(R.id.fullscreen_content_controls);
final View contentView = findViewById(R.id.fullscreen_content);
// Set up an instance of SystemUiHider to control the system UI for
// this activity.
mSystemUiHider = SystemUiHider.getInstance(this, contentView, HIDER_FLAGS);
mSystemUiHider.setup();
mSystemUiHider
.setOnVisibilityChangeListener(new SystemUiHider.OnVisibilityChangeListener() {
// Cached values.
int mControlsHeight;
int mShortAnimTime;
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public void onVisibilityChange(boolean visible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
// If the ViewPropertyAnimator API is available
// (Honeycomb MR2 and later), use it to animate the
// in-layout UI controls at the bottom of the
// screen.
if (mControlsHeight == 0) {
mControlsHeight = controlsView.getHeight();
}
if (mShortAnimTime == 0) {
mShortAnimTime = getResources().getInteger(
android.R.integer.config_shortAnimTime);
}
controlsView.animate()
.translationY(visible ? 0 : mControlsHeight)
.setDuration(mShortAnimTime);
} else {
// If the ViewPropertyAnimator APIs aren't
// available, simply show or hide the in-layout UI
// controls.
controlsView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
if (visible && AUTO_HIDE) {
// Schedule a hide().
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
}
});
// Set up the user interaction to manually show or hide the system UI.
contentView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (TOGGLE_ON_CLICK) {
mSystemUiHider.toggle();
} else {
mSystemUiHider.show();
}
}
});
// Upon interacting with UI controls, delay any scheduled hide()
// operations to prevent the jarring behavior of controls going away
// while interacting with the UI.
// EVERYTHING UP TO HERE IS AUTO-GENERATED
final TextView currentLabel = (TextView) findViewById(R.id.labelCurrent);
final TextView totalLabel = (TextView) findViewById(R.id.labelTotal);
new Thread () {
public void run() {
currentLabel.setText("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
currentLabel.setText("2");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
currentLabel.setText("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
startActivity(new Intent(getApplicationContext(), MainActivity.class));
}
}.start();
}
测试此代码时,我发现在仿真的AVD中,它直接返回到MainActivity,并且当我生成APK并在设备上对其进行测试时,该应用程序崩溃了。
任何帮助表示赞赏。
编辑1-这是logcat:
08-09 15:42:29.054 2012-2012/com.example.nat.morseflasher D/gralloc_goldfish﹕ Emulator without GPU emulation detected.
08-09 15:42:38.564 2012-2012/com.example.nat.morseflasher D/dalvikvm﹕ GC_FOR_ALLOC freed 143K, 8% free 3195K/3444K, paused 142ms, total 183ms
08-09 15:42:55.654 2012-2012/com.example.nat.morseflasher I/Choreographer﹕ Skipped 31 frames! The application may be doing too much work on its main thread.
08-09 15:42:56.294 2012-2012/com.example.nat.morseflasher I/Choreographer﹕ Skipped 47 frames! The application may be doing too much work on its main thread.
08-09 15:43:07.725 2012-2012/com.example.nat.morseflasher I/Choreographer﹕ Skipped 1159 frames! The application may be doing too much work on its main thread.
编辑2:
好的,尽管我更改了一些内容以使其适合我的需求,但我已经将您提供的代码实现到了我的代码中。
当我尝试运行此命令时,日志显示“ Starting morse code flasher ...”(正在启动莫尔斯电码闪烁...)消息,但它从未到达印刷字母或符号部分。当我点击“就绪”按钮时,什么都没有改变,它只是返回到onPostExecute函数中定义的主要活动。
这是XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0099cc"
tools:context="com.example.nat.morseflasher.FlashActivity">
<!-- The primary full-screen view. This can be replaced with whatever view
is needed to present your content, e.g. VideoView, SurfaceView,
TextureView, etc. -->
<TextView android:id="@+id/fullscreen_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
android:textColor="#33b5e5"
android:textStyle="bold"
android:textSize="50sp"
android:gravity="center"
android:text="@string/dummy_content"
android:background="#000000" />
<!-- This FrameLayout insets its children based on system windows using
android:fitsSystemWindows. -->
<FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="#000000"
android:id="@+id/frameLayout">
<LinearLayout android:id="@+id/fullscreen_content_controls"
style="?metaButtonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:background="@color/black_overlay"
android:orientation="horizontal"
tools:ignore="UselessParent">
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="-"
android:id="@+id/labelCurrent"
android:layout_gravity="center"
android:layout_marginBottom="40dp"
android:textSize="100sp"
android:textColor="#ffffff" />
<TextView
android:layout_width="fill_parent"
android:layout_height="60dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Ready?"
android:id="@+id/labelTotal"
android:layout_gravity="center"
android:layout_marginTop="40dp"
android:textSize="25sp"
android:gravity="center_horizontal"
android:textIsSelectable="false"
android:textColor="#ffffff" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tapToGo"
android:id="@+id/buttonGo"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="35dp"
android:onClick="doMorse" />
</FrameLayout>
</FrameLayout>
这是java:
package com.example.nat.morseflasher;
import com.example.nat.morseflasher.util.SystemUiHider;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;
/**
* An example full-screen activity that shows and hides the system UI (i.e.
* status bar and navigation/system bar) with user interaction.
*
* @see SystemUiHider
*/
public class FlashActivity extends Activity {
/**
* Whether or not the system UI should be auto-hidden after
* {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
*/
private static final boolean AUTO_HIDE = true;
/**
* If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after
* user interaction before hiding the system UI.
*/
private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
/**
* If set, will toggle the system UI visibility upon interaction. Otherwise,
* will show the system UI visibility upon interaction.
*/
private static final boolean TOGGLE_ON_CLICK = true;
/**
* The flags to pass to {@link SystemUiHider#getInstance}.
*/
private static final int HIDER_FLAGS = SystemUiHider.FLAG_HIDE_NAVIGATION;
/**
* The instance of the {@link SystemUiHider} for this activity.
*/
private SystemUiHider mSystemUiHider;
private final static String TAG = "FlashActivity";
private final static long TIME_UNIT = 250L;
private final static long ONE_SECOND = 1000L;
private final static long DOT_DELAY = TIME_UNIT;
private final static long DASH_DELAY = TIME_UNIT * 2;
private final static long INTRA_LETTER_DELAY = TIME_UNIT;
private final static long INTER_LETTER_DELAY = TIME_UNIT * 2;
private final static long INTER_WORD_DELAY = TIME_UNIT * 6;
Button goButton;
FrameLayout layoutFrame;
TextView currentLabel, totalLabel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flash);
final View controlsView = findViewById(R.id.fullscreen_content_controls);
final View contentView = findViewById(R.id.fullscreen_content);
// Set up an instance of SystemUiHider to control the system UI for
// this activity.
mSystemUiHider = SystemUiHider.getInstance(this, contentView, HIDER_FLAGS);
mSystemUiHider.setup();
mSystemUiHider
.setOnVisibilityChangeListener(new SystemUiHider.OnVisibilityChangeListener() {
// Cached values.
int mControlsHeight;
int mShortAnimTime;
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public void onVisibilityChange(boolean visible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
// If the ViewPropertyAnimator API is available
// (Honeycomb MR2 and later), use it to animate the
// in-layout UI controls at the bottom of the
// screen.
if (mControlsHeight == 0) {
mControlsHeight = controlsView.getHeight();
}
if (mShortAnimTime == 0) {
mShortAnimTime = getResources().getInteger(
android.R.integer.config_shortAnimTime);
}
controlsView.animate()
.translationY(visible ? 0 : mControlsHeight)
.setDuration(mShortAnimTime);
} else {
// If the ViewPropertyAnimator APIs aren't
// available, simply show or hide the in-layout UI
// controls.
controlsView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
if (visible && AUTO_HIDE) {
// Schedule a hide().
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
}
});
// Set up the user interaction to manually show or hide the system UI.
contentView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (TOGGLE_ON_CLICK) {
mSystemUiHider.toggle();
} else {
mSystemUiHider.show();
}
}
});
goButton = (Button) findViewById(R.id.buttonGo);
layoutFrame = (FrameLayout) findViewById(R.id.frameLayout);
currentLabel = (TextView) findViewById(R.id.labelCurrent);
totalLabel = (TextView) findViewById(R.id.labelTotal);
// Upon interacting with UI controls, delay any scheduled hide()
// operations to prevent the jarring behavior of controls going away
// while interacting with the UI.
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
delayedHide(100);
}
/**
* Touch listener to use for in-layout UI controls to delay hiding the
* system UI. This is to prevent the jarring behavior of controls going away
* while interacting with activity UI.
*/
View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (AUTO_HIDE) {
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
return false;
}
};
Handler mHideHandler = new Handler();
Runnable mHideRunnable = new Runnable() {
@Override
public void run() {
mSystemUiHider.hide();
}
};
/**
* Schedules a call to hide() in [delay] milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide(int delayMillis) {
mHideHandler.removeCallbacks(mHideRunnable);
mHideHandler.postDelayed(mHideRunnable, delayMillis);
}
public void doMorse(View vw) {
goButton.setVisibility(View.GONE);
(new DoMorseFlashing()).execute();
}
private class DoMorseFlashing extends AsyncTask <String, Boolean, Void> {
SharedPreferences mPrefs = getSharedPreferences("MorseFlasher", Context.MODE_PRIVATE);
String msgStr, currentLetterMorse;
@Override
protected Void doInBackground(String... message) {
Log.v(TAG, "Starting Morse Code flasher...");
msgStr = mPrefs.getString("prevMsg", "");
String totalStr = "";
/*currentLabel.setText("3");
*doDelay(ONE_SECOND);
*currentLabel.setText("2");
*doDelay(ONE_SECOND);
*currentLabel.setText("1");
*doDelay(ONE_SECOND);
*/
for(int i=0; i<msgStr.length(); i++) {
if(msgStr.charAt(i)=='|') {
doDelay(INTER_WORD_DELAY);
}
else {
currentLetterMorse = convert(msgStr.charAt(i));
currentLabel.setText(msgStr.substring(i, i + 1));
totalStr += msgStr.substring(i, i + 1);
totalLabel.setText(totalStr);
Log.v(TAG, " flashing letter " + msgStr.charAt(i) + " with morse code " + currentLetterMorse + ":");
for (int j = 0; j < currentLetterMorse.length(); j++) {
Log.v(TAG, "flashing symbol " + currentLetterMorse.charAt(j) + ":");
flash(currentLetterMorse.charAt(j));
doDelay(INTRA_LETTER_DELAY);
}
doDelay(INTER_LETTER_DELAY);
}
}
return null;
}
protected void onPostExecute(Void result) {
// Set the button message back to "touch here" to indicate that were done flashing:
startActivity(new Intent(getApplicationContext(), MainActivity.class));
}
void flash(char letter) {
layoutFrame.setBackgroundColor(Color.WHITE);
currentLabel.setTextColor(Color.BLACK);
totalLabel.setTextColor(Color.BLACK);
switch (letter) {
case '.':
doDelay(DOT_DELAY);
case '-':
doDelay(DASH_DELAY);
}
layoutFrame.setBackgroundColor(Color.BLACK);
currentLabel.setTextColor(Color.WHITE);
totalLabel.setTextColor(Color.WHITE);
}
String convert(char letter){
switch (letter){
case 'a':
return ".-";
case 'b':
return "-…";
case 'c':
return "-.-.";
case 'd':
return "-..";
case 'e':
return ".";
case 'f':
return "..-.";
case 'g':
return "--.";
case 'h':
return "….";
case 'i':
return "..";
case 'j':
return ".---";
case 'k':
return "-.-";
case 'l':
return ".-..";
case 'm':
return "--";
case 'n':
return "-.";
case 'o':
return "---";
case 'p':
return ".--.";
case 'q':
return "--.-";
case 'r':
return ".-.";
case 's':
return "...";
case 't':
return "-";
case 'u':
return "..-";
case 'v':
return "...-";
case 'w':
return ".--";
case 'x':
return "-..-";
case 'y':
return "-.--";
case 'z':
return "--..";
case ' ':
return "|";
default:
return "|";
}
}
void doDelay(Long delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// Ignore interruptions
}
}
}
}
这是LogCat:
08-10 18:30:43.856 2090-2090/com.example.nat.morseflasher D/gralloc_goldfish﹕ Emulator without GPU emulation detected.
08-10 18:30:46.146 2090-2106/com.example.nat.morseflasher D/dalvikvm﹕ GC_FOR_ALLOC freed 146K, 8% free 3192K/3448K, paused 31ms, total 39ms
08-10 18:30:50.816 2090-2090/com.example.nat.morseflasher I/Choreographer﹕ Skipped 464 frames! The application may be doing too much work on its main thread.
08-10 18:31:04.116 2090-2090/com.example.nat.morseflasher I/Choreographer﹕ Skipped 69 frames! The application may be doing too much work on its main thread.
08-10 18:31:06.486 2090-2090/com.example.nat.morseflasher I/Choreographer﹕ Skipped 62 frames! The application may be doing too much work on its main thread.
08-10 18:31:11.176 2090-2090/com.example.nat.morseflasher I/Choreographer﹕ Skipped 37 frames! The application may be doing too much work on its main thread.
08-10 18:31:11.937 2090-2106/com.example.nat.morseflasher V/FlashActivity﹕ Starting Morse Code flasher...
08-10 18:31:13.437 2090-2090/com.example.nat.morseflasher I/Choreographer﹕ Skipped 109 frames! The application may be doing too much work on its main thread.
编辑3:
好的,所以前面的问题是由于它没有从SharedPreferences中检索我的字符串,所以为了确保其他所有事情都可以,我在代码中将消息字符串设置为“ test”。现在,问题在于,当要更改屏幕的颜色时,我收到一条错误消息,说只有创建视图层次结构的主线程才能触摸其视图。
编辑4:
我已通过使用runOnUiThread()来更改屏幕和文本的颜色,从而修复了上述错误,现在可以闪烁了!
最佳答案
LogCat消息告诉您,您的应用程序在主线程上花费了太多时间,并且使GUI的处理时间变得空缺。
如果您只想刷新屏幕,则可以使用简单的AsyncTask而不是SystemUiHider
的所有复杂性。
这是一些代码,显示了如何使用简单的线程睡眠来处理后台AsyncTask。由于这是后台任务,因此休眠不会干扰主GUI线程。
主要功能是定期调用publishProgess()
来打开或关闭闪光灯。请注意,您不能在doInBackground()
方法堆栈中进行GUI更改,但是可以在onProgessUpdate()和onPostExecute()中进行更改。我使用了相同的按钮来开始和显示闪烁,因此我可以演示使用onPostExecute()
方法重置按钮文本。
显然,您需要为其他字母添加摩尔斯电码字符串,并可能需要调整时序值以使情况看起来正确。
MainActivity.java
package com.example.com;
import android.app.Activity;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity {
// String for LogCat documentation
private final static String TAG = "MainActivity";
// These define the lengths for the dots, dashes, and times in between:
private final static long TIME_UNIT = 250L;
private final static long DOT_DELAY = TIME_UNIT * 2;
private final static long DASH_DELAY = TIME_UNIT * 3;
private final static long INTRA_LETTER_DELAY = TIME_UNIT;
private final static long INTER_LETTER_DELAY = TIME_UNIT * 3;
private final static long INTER_WORD_DELAY = TIME_UNIT * 7;
private Button flasher = null;
private EditText message = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flasher = (Button) findViewById(R.id.flasher);
message = (EditText) findViewById(R.id.message);
}
public void doMorse(View vw) {
String messageStr = message.getText().toString();
if (messageStr.length() > 0) {
// This creates and starts a background task to do the flashing:
(new DoMorseFlashing()).execute(messageStr);
// Set the button message to indicate that we're flashing a message:
flasher.setText(R.string.flashing_message);
}
}
private class DoMorseFlashing extends AsyncTask <String, Boolean, Void> {
@Override
protected Void doInBackground(String... message) {
Log.v(TAG, "Starting Morse Code flasher...");
for (char letter : message[0].toCharArray()) {
if (letter == ' ')
doDelay(INTER_WORD_DELAY);
else
showLetter(letter);
}
return null;
}
protected void onProgressUpdate(Boolean... flasherOn) {
if (flasherOn[0])
flasher.setBackgroundColor(Color.BLUE);
else
flasher.setBackgroundColor(Color.WHITE);
}
protected void onPostExecute(Void result) {
// Set the button message back to "touch here" to indicate that we're done flashing:
flasher.setText(R.string.touch_here);
}
void showLetter(char letter) {
Log.v(TAG, "Flashing code for " + letter);
switch (letter) {
case 'A': showMorse(".-"); break;
case 'B': showMorse("-..."); break;
case 'C': showMorse("-.-."); break;
// cases for the other letters...
default: /* skip character */
}
doDelay(INTER_LETTER_DELAY);
}
void showMorse(String morse) {
Log.v(TAG, "Flashing code " + morse);
for (char dotDash: morse.toCharArray()) {
Log.v(TAG, "Flashing " + dotDash);
publishProgress(true);
if (dotDash == '.')
doDelay(DOT_DELAY);
else
doDelay(DASH_DELAY);
doDelay(INTRA_LETTER_DELAY);
publishProgress(false);
}
}
void doDelay(Long delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// Ignore interruptions
}
}
}
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textCapCharacters"
android:hint="@string/enter_message" />
<Button
android:id="@+id/flasher"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top|center"
android:background="@android:color/background_light"
android:hint="@string/touch_here"
android:onClick="doMorse" />
</LinearLayout>
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Morse Flasher</string>
<string name="enter_message">Enter message</string>
<string name="touch_here">Touch here to flash message</string>
<string name="flashing_message">Flashing message</string>
</resources>