我对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>

10-07 16:46