OnDraw() вызывается только один раз после цикла

У меня есть программа, которую я пишу в андроид-студии, которая должна воспроизводить музыкальную ноту и иметь круг, увеличенный в размере на основе массива координат, названий песен и времени для воспроизведения нот. Однако проблема, с которой я сталкиваюсь, заключается в том, что все ноты воспроизводятся правильно, но рисуется только последний круг, и только после того, как я выхожу из цикла, все ноты воспроизводятся. Я посмотрел этот вопрос раньше, и я получил несколько ответов о звонке setWillNotDraw(false);, но я назвал это в моем конструкторе, и у меня все еще остается та же проблема. Я пробовал звонить invalidate(); в разных местах, но, кажется, ничего не работает. Любая помощь будет принята с благодарностью!

Мой код:

Конструктор

public PlayView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    saver = PersistentSaver.getInstance(context);
    this.context=context;
    setWillNotDraw(false);
}

На ничьей

@Override
public void onDraw(Canvas canvas){
    super.onDraw(canvas);
    this.canvas=canvas;
    if(mCurrentRect !=null&& colorPaint!=null) {
        canvas.drawOval(mCurrentRect, colorPaint);
    }
}

Цикл для воспроизведения заметок - я проверил, все координаты круга и заметки содержатся в правильных массивах:

public void playSong(){
    String song = saver.getCurrSongName();
    ArrayList<Long> Times = saver.getTimesForNotes(song);
    ArrayList<Float> xCoordinates = saver.getPressXCoordinates(song);
    ArrayList<Float> yCoordinates = saver.getPressYCoordinates(song);
    ArrayList<String> notes = saver.getNotesForSong(song);
    for(int i=0;i<Times.size();i++) {
        //pause until correct time. TODO: ADJUST ERROR BOUND
        while (true) {
            currTime = System.currentTimeMillis() - startTime;
            if (currTime <= Times.get(i) + epsilon && currTime >= Times.get(i) - epsilon) break;
        }
        //play note with that x and y coordinate and draw circle
        playNote(xCoordinates.get(i), yCoordinates.get(i), notes.get(i));
    }
}

Код для воспроизведения ноты и звука:

    private void playNote(float pressx, float pressy, String note){
    colorPaint=new Paint();
    colorPaint.setStyle(Paint.Style.STROKE);
    colorPaint.setStrokeWidth(10);
    colorPaint.setColor(generateColor(pressx,pressy));
    float translateX = 50.0f;
    float translateY = 50.0f;
    Random rand = new Random(10000);
    int xr = rand.nextInt();
    int yr = rand.nextInt();
    int startColor = generateColor(pressx,pressy);
    int midColor = generateColor(pressx+rand.nextInt(),pressy+rand.nextInt());
    int endColor = generateColor(pressx+xr,pressy+yr);
    mCurrentRect = new AnimatableRectF(pressx-50,pressy-50,pressx+50,pressy+50);
    debugx=pressx;
    playAnimation(startColor,midColor,endColor, translateX, translateY);
    playSound(note);
}

Код, который должен воспроизводить анимацию, но только вызывает onDraw(); после выхода из цикла

    private void playAnimation(int startColor, int midColor, int endColor, float translateX, float translateY){
    ObjectAnimator animateLeft = ObjectAnimator.ofFloat(mCurrentRect, "left", mCurrentRect.left, mCurrentRect.left-translateX);
    ObjectAnimator animateRight = ObjectAnimator.ofFloat(mCurrentRect, "right", mCurrentRect.right, mCurrentRect.right+translateX);
    ObjectAnimator animateTop = ObjectAnimator.ofFloat(mCurrentRect, "top", mCurrentRect.top, mCurrentRect.top-translateY);
    ObjectAnimator animateBottom = ObjectAnimator.ofFloat(mCurrentRect, "bottom", mCurrentRect.bottom, mCurrentRect.bottom+translateY);
    ArgbEvaluator evaluator = new ArgbEvaluator();
    ObjectAnimator animateColor = ObjectAnimator.ofObject(this, "color", evaluator, startColor,midColor,endColor);
    animateBottom.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        //TODO: DEBUG ANIMATOR
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            postInvalidate();
        }
    });
    AnimatorSet rectAnimation = new AnimatorSet();
    rectAnimation.playTogether(animateLeft, animateRight, animateTop, animateBottom,animateColor);
    rectAnimation.setDuration(1000).start();
    invalidate();


}

Методы playNote, playSound и generateColor

    //Generates color based on the press x and y.
private int generateColor(float pressx, float pressy){
    int Red = (((int) (pressx%255)) << 16) & 0x00FF0000; //Shift red 16-bits and mask out other stuff
    int Green = (((int) (pressy%255)) << 8) & 0x0000FF00; //Shift Green 8-bits and mask out other stuff
    int Blue = ((int)((pressx+pressy)%255)) & 0x000000FF; //Mask out anything not blue.

    return 0xFF000000 | Red | Green | Blue; //0xFF000000 for 100% Alpha. Bitwise OR everything together.

}

private void playSound(String noun){
    Resources res = context.getResources();
    int resID = res.getIdentifier(noun, "raw", context.getPackageName());
    MediaPlayer mediaPlayer = MediaPlayer.create(context,resID);
    mediaPlayer.start();
    mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mediaPlayer) {
            mediaPlayer.release();
        }
    });
}

private void playNote(float pressx, float pressy, String note){
    colorPaint=new Paint();
    colorPaint.setStyle(Paint.Style.STROKE);
    colorPaint.setStrokeWidth(10);
    colorPaint.setColor(generateColor(pressx,pressy));
    float translateX = 50.0f;
    float translateY = 50.0f;
    Random rand = new Random(10000);
    int xr = rand.nextInt();
    int yr = rand.nextInt();
    int startColor = generateColor(pressx,pressy);
    int midColor = generateColor(pressx+rand.nextInt(),pressy+rand.nextInt());
    int endColor = generateColor(pressx+xr,pressy+yr);
    mCurrentRect = new AnimatableRectF(pressx-50,pressy-50,pressx+50,pressy+50);
    debugx=pressx;
    playAnimation(startColor,midColor,endColor, translateX, translateY);
    playSound(note);
}

Спасибо!

1 ответ

Решение

Вместо того, чтобы использовать цикл while для занятости, подождите некоторое время, необходимое для воспроизведения следующей ноты, лучше всего использовать, например, Timer или же handler.postDelayed(runnable, delayMilliseconds)или, что еще лучше, используйте Rx's Observable.timer(),

Это связано с тем, что цикл блокировки препятствует выполнению потока пользовательского интерфейса и, следовательно, предотвращает любые обновления экрана. Самый простой способ, если вы не хотите изучать многозадачность, это сделать:

Handler mainHandler = new Handler(context.getMainLooper());

mainHandler.postDelayed(new Runnable() {
  public void run() { playNote(...) }
}, Times.get(i))
Другие вопросы по тегам