3

我的节拍器应用程序有问题。应用程序需要在按下开始按钮时循环,并继续循环直到按下停止按钮。我试过循环调用方法,但它甚至不听按钮,因为它卡在循环中。我目前的代码是。

public class MainActivity extends Activity {
    // constant
    final int MINUTE = 60000;

    // variables
    SeekBar seekbar1, seekbar2;
    EditText txtBPM1, txtBPM2;
    Spinner spnTop, spnBottom;
    int value1, value2;
    boolean playing = false;
    int tick;
    int beat;
    SoundPool sp;

    // loop variable
    int i = 0;
    int tickCount = 0;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        sp = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
        tick = sp.load(this, R.raw.tick, 1);
        beat = sp.load(this, R.raw.beat, 1);

        final Spinner spnTop = (Spinner) findViewById(R.id.spnTop);
        // Create an ArrayAdapter using the string array and a default spinner
        // layout
        ArrayAdapter<CharSequence> adapter1 = ArrayAdapter.createFromResource(
                this, R.array.times1, android.R.layout.simple_spinner_item);
        // Specify the layout to use when the list of choices appears
        adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        // Apply the adapter to the spinner
        spnTop.setAdapter(adapter1);
        spnTop.setSelection(2);

        final Spinner spnBottom = (Spinner) findViewById(R.id.spnBottom);
        // Create an ArrayAdapter using the string array and a default spinner
        // layout
        ArrayAdapter<CharSequence> adapter2 = ArrayAdapter.createFromResource(
                this, R.array.times2, android.R.layout.simple_spinner_item);
        // Specify the layout to use when the list of choices appears
        adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        // Apply the adapter to the spinner
        spnBottom.setAdapter(adapter2);

          ////////////////////////////////////////////////
         //Seekbar/EditText//////////////////////////////
        ////////////////////////////////////////////////
        seekbar1 = (SeekBar) findViewById(R.id.skbBPM1);
        txtBPM1 = (EditText) findViewById(R.id.txtBPM1);

        txtBPM1.setText("" + 120, TextView.BufferType.EDITABLE);

        seekbar1.setVerticalScrollbarPosition(value1);

        seekbar1.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            public void onProgressChanged(SeekBar seekbar, int progress,
                    boolean fromUser) {
                value1 = progress + 30;
                txtBPM1.setText("" + value1, TextView.BufferType.EDITABLE);
            }

            @Override
            public void onStartTrackingTouch(SeekBar arg0) {
                // TODO Auto-generated method stub
                // stops metronome if playing
                i = 1;
            }

            @Override
            public void onStopTrackingTouch(SeekBar arg0) {
                // set the textbox to a new value
                txtBPM1.setText("" + value1, TextView.BufferType.EDITABLE);
            }
        });

        Button btnStart1 = (Button) findViewById(R.id.btnStart1);
        btnStart1.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                value1 = Integer.parseInt(txtBPM1.getText().toString());
                if (value1 > 250 || value1 < 30) {
                    Toast.makeText(
                            getApplicationContext(),
                            "BPM value must be at least 30 and no more than 250.",
                            Toast.LENGTH_LONG).show();
                } else {
                    // set frequency and timers

                    int tickTime = MINUTE / value1;
                    int n = Integer.parseInt(spnBottom.getSelectedItem()
                            .toString());
                    int beat = Integer.parseInt(spnTop.getSelectedItem()
                            .toString());

                    tickTime = (tickTime * 4) / n;

                    // loop to play sound every specified incriment
                    for (i = 0; i > 0; i += 0)
                    {
                        tickCount++;
                        if (tickCount == beat) {
                            try
                            {
                                Thread.sleep(tickTime);
                            }
                            catch (InterruptedException e)
                            {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            sp.play(beat, 1, 1, 0, 0, 1);
                            tickCount = 0;
                            }
                        else
                        {
                            try
                            {
                                Thread.sleep(tickTime);
                            }
                            catch (InterruptedException e)
                            {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }//end catch
                            sp.play(tick, 1, 1, 0, 0, 1);
                            tickCount++;

                        }//end else
                    }//end loop
                }//end else
            }
        });

        Button btnStop1 = (Button) findViewById(R.id.btnStop1);
        btnStop1.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (playing == true) {
                    i = 1;
                } else {
                    Toast.makeText(
                            getApplicationContext(),
                            "Metrenome is not playing. To begin playing press the Start button.",
                            Toast.LENGTH_LONG).show();
                }
            }
        });

        //////////////////////////////////////////////
        //Second spinner boxes/buttons////////////////
        //////////////////////////////////////////////
        /*
        seekbar2 = (SeekBar) findViewById(R.id.skbBPM2);
        txtBPM2 = (EditText) findViewById(R.id.txtBPM2);

        txtBPM2.setText("" + 120, TextView.BufferType.EDITABLE);

        value2 = Integer.parseInt(txtBPM2.getText().toString());

        seekbar2.setVerticalScrollbarPosition(value2);

        seekbar2.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            public void onProgressChanged(SeekBar seekbar, int progress,
                    boolean fromUser) {
                value2 = progress + 30;
                txtBPM2.setText("" + value2, TextView.BufferType.EDITABLE);
            }

            @Override
            public void onStartTrackingTouch(SeekBar arg0) {
                // TODO Auto-generated method stub
                // Nothing happens
            }

            @Override
            public void onStopTrackingTouch(SeekBar arg0) {
                // set the textbox to a new value
                txtBPM2.setText("" + value2, TextView.BufferType.EDITABLE);
            }
        });
            */
    }// end onCreate
}// end class
4

3 回答 3

1

我很确定你做错了什么是在 UI 线程中启动一个循环。当您在 UI 线程中执行全职操作时 - 您会阻止所有按钮并且它们不再可点击。您应该做的是启动 AsyncTask 或 FutureTask (异步实现可能更容易)并在“停止按钮”上中断并取消它。在您的循环中,您必须检查 Thread.getCurrentThread().isInterrupted() 以便它可以尽快停止。

我会给你一些伪代码:

    onStartClickListener{
        onClick(){
            //make some checks (if (value1 > 250 || value1 < 30) etc..)
            myTask = new MyTask();
            myTask.execute();
        }
    }

    onStopClickListener{
        onClick(){
            myTask.cancel(true);
        }
    }

    class MyTask extends AsyncTask<Void,Void,Void>{
        doInBackground(){
            while(true){
                // WE ARE IN DIFFERENT (non UI) thread in this method

                // looping and playing sound or whatever else you want
                // remember you cant do UI actions here
                // when you want to update UI from here you need to do a mHandler = new Handler()
                // in onCreate and do mHandler.post(new Runnable()) here
                if (Thread.currentThread().isInterrupted()) break;
            }
        }

        onPostExecute(){
            // use it when you want to update UI when background task was finished
        }

        onCancelled(){
            // use it when you want to update UI when task was canceled. This is your case I think.
        }
    }

希望有帮助:)

于 2012-10-29T14:25:03.347 回答
0

似乎循环和事件处理程序可能正在使用同一个线程,因此线程卡在循环中并拒绝将控制权交给事件处理程序。

我的建议是在新线程中启动循环,以便留给 UI 线程来执行事件处理。如果这样做,事件处理程序将在 UI 线程上处理,并且循环将继续在单独的线程上运行。

于 2012-10-29T14:22:53.720 回答
0

您可以使用线程来执行此操作。这是一个很好的教程:

http://www.vogella.com/articles/JavaConcurrency/article.html

// create a thread to execute the metronome tick
ThreadPoolExecutor threadPoolExecutor = Executors.newSingleThreadExecutor();
Runnable makeTheMetronomeTick = new Runnable();

// add the thread to the thread pool:
Future makeTheMetronomeTickFuture = threadPoolExecutor.submit(makeTheMetronomeTick );

...

// when you want to cancel it
makeTheMetronomeTickFuture.cancel(true);
于 2012-10-29T14:34:39.307 回答