OnDraw() 仅在循环后调用一次
Posted
技术标签:
【中文标题】OnDraw() 仅在循环后调用一次【英文标题】:OnDraw() only called once after loop 【发布时间】:2017-08-21 20:40:20 【问题描述】:我有一个我正在 android studio 中编写的程序,它应该播放一个音符,并根据坐标、歌曲音符名称和播放音符的时间的数组列表扩大一个圆圈的大小。但是,我遇到的问题是 所有音符都正确播放,但 只有最后一个圆圈被绘制,并且 在我退出播放所有音符的循环之后备注。我之前查过这个问题,我得到了一些关于调用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);
谢谢!
【问题讨论】:
看起来您可能会在使用方法 playNote() 播放每个声音后重置全局变量 mCurrentRect。 mCurrentRect = new AnimatableRectF(pressx-50,pressy- 50,pressx+50,pressy+50);如果它只是开始绘制,并且播放另一个声音,它将开始绘制下一个对象(再次调用 onDraw())。这可以解释为什么你只看到最后一个。 但是当我将矩形存储在数组列表中并从中绘制时,它仍然不会绘制到最后,当它一次绘制所有矩形时。看起来 onDraw 本身甚至从未在循环中被调用,我不知道为什么 - 特别是因为我在 playAnimation 方法中调用了 invalidate()。 【参考方案1】:在播放下一个音符之前,最好不要使用 while 循环来忙于等待所需的时间,最好使用例如Timer
或 handler.postDelayed(runnable, delayMilliseconds)
,甚至更好,使用 Rx 的 Observable.timer()
。
这是因为阻塞循环会阻止 UI 线程运行,因此会阻止任何屏幕更新。如果您不想学习多任务处理,最简单的方法是:
Handler mainHandler = new Handler(context.getMainLooper());
mainHandler.postDelayed(new Runnable()
public void run() playNote(...)
, Times.get(i))
【讨论】:
以上是关于OnDraw() 仅在循环后调用一次的主要内容,如果未能解决你的问题,请参考以下文章
iOS 13 仅在用户“允许一次”后再次调用 requestAlwaysAuthorization
iOS 10 - didRegisterForRemoteNotificationsWithDevicetoken 仅在第一次调用