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

これを行うには、スレッドを使用できます。これが素晴らしいチュートリアルです:

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 に答える
0

ループとイベントハンドラーの両方が同じスレッドを使用している可能性があるため、スレッドはループ内でスタックし、イベントハンドラーへの制御の放棄を拒否します。

新しいスレッドでループを開始して、UIスレッドがイベント処理を実行できるようにすることをお勧めします。これを行うと、イベントハンドラーはUIスレッドで処理され、ループは別のスレッドで引き続き機能します。

于 2012-10-29T14:22:53.720 に答える