OnDraw() 只在循环后调用一次

OnDraw() only called once after loop

我有一个正在 android 工作室编写的程序,该程序应该播放一个音符,并根据坐标、歌曲音符名称和播放时间的数组列表扩大圆圈的大小笔记。但是,我遇到的问题是 所有音符都可以正确播放,但是 只有最后一个圆圈被绘制,它是 之后我退出播放所有音符的循环 。我之前查过这个问题,我得到了一些关于调用 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);
}

谢谢!

与其使用 while 循环来忙于等待播放下一个音符之前所需的时间,不如使用例如Timerhandler.postDelayed(runnable, delayMilliseconds),甚至更好,使用 Rx 的 Observable.timer()

这是因为阻塞循环阻止了 UI 线程 运行,因此阻止了任何屏幕更新。如果您不想学习多任务处理,最简单的方法是:

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

mainHandler.postDelayed(new Runnable() {
  public void run() { playNote(...) }
}, Times.get(i))