It is necessary to make the picture move after the finger ( Bitmap f ), and the “erase” is performed, but the algorithm of the motion of the picture loads the device very strongly, and as a result FPS=8 . Without movement of the picture, “erasing” occurs at a rather large frame rate. How can I speed up / optimize this code so that the picture moves without lags? Thank you so much in advance!

 public class WScratchView extends SurfaceView implements SurfaceHolder.Callback { private Context mContext; public WScratchViewThread mThread; List<Path> mPathList = new ArrayList<Path>(); private int mOverlayColor; private Paint mOverlayPaint; private int mRevealSize; private boolean mIsScratchable = true; private boolean mIsAntiAlias = false; private Path path; private float startX = 0; private float startY = 0; private boolean mScratchStart = false; private MediaPlayer phrase; Boolean timerRunned = false; int fWidth,fHeight, fX,fY, fIndent; boolean ignore_drawable = false; Bitmap bitmap, f; public static long scores = 0; public Paint paint, paintf; public Rect destin, destinf; public int fLeft, fTop, fRight, fBottom; Handler h; Runnable updateScores; public WScratchView(Context ctx, AttributeSet attrs) { super(ctx, attrs); init(ctx, attrs); } public WScratchView(Context context) { super(context); init(context, null); } private void init(Context context, AttributeSet attrs) { mContext = context; mOverlayColor = DEFAULT_COLOR; mRevealSize = DEFAULT_REVEAL_SIZE; setZOrderOnTop(true); SurfaceHolder holder = getHolder(); holder.addCallback(this); holder.setFormat(PixelFormat.TRANSPARENT); mOverlayPaint = new Paint(); mOverlayPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR)); mOverlayPaint.setStyle(Paint.Style.STROKE); mOverlayPaint.setStrokeCap(Paint.Cap.ROUND); mOverlayPaint.setStrokeJoin(Paint.Join.ROUND); bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.layer); f = BitmapFactory.decodeResource(getResources(), R.drawable.f); Resources res = getResources(); fHeight = res.getInteger(R.integer.fHeight); fWidth = res.getInteger(R.integer.fWidth); fIndent = res.getInteger(R.integer.fIndent); h = new Handler(); updateScores = new Runnable() { @Override public void run() { Main.tvScores.setText(Long.toString(scores)); } }; paint = new Paint(); paintf = new Paint(); paint.setFilterBitmap(true); paintf.setFilterBitmap(false); Log.d("LOG_", "init complete"); } @SuppressLint("DrawAllocation") @Override public void onDraw(Canvas canvas) { if (ignore_drawable)return; destin = new Rect(0, 0, getWidth(), getHeight()); canvas.drawBitmap(bitmap, null, destin, paint); //Log.d("LOG_","draw start"); for (Path path: mPathList) { mOverlayPaint.setAntiAlias(mIsAntiAlias); mOverlayPaint.setStrokeWidth(mRevealSize); canvas.drawPath(path, mOverlayPaint); } destinf = new Rect(fLeft, fTop, fRight, fBottom); canvas.drawBitmap(f, null, destinf, paintf); } @Override public boolean onTouchEvent(MotionEvent me) { ignore_drawable = true; synchronized (mThread.getSurfaceHolder()) { ignore_drawable = false; if (!mIsScratchable) { return true; } if (!timerRunned) { timerRunned = true; Main.cdt.start(); } Log.d("LOG_", "bye"); switch (me.getAction()) { case MotionEvent.ACTION_DOWN: path = new Path(); path.moveTo(me.getX(), me.getY()); startX = me.getX(); startY = me.getY(); mPathList.add(path); break; case MotionEvent.ACTION_MOVE: playrandom(); fX = (int)me.getRawX(); fY = (int)me.getRawY(); fLeft = fX - fIndent; fTop = fY - fIndent - fIndent; if (fLeft < 0)fLeft = 0; if (fTop < 0) fTop = 0; fRight = fX - fIndent + fWidth; fBottom = fY - (fIndent*2) + fHeight; if (mScratchStart) { path.lineTo(me.getX(), me.getY()); } else { if (isScratch(startX, me.getX(), startY, me.getY())) { mScratchStart = true; path.lineTo(me.getX(), me.getY()); } } scores += Main.level; h.post(updateScores); invalidate(); break; case MotionEvent.ACTION_UP: mScratchStart = false; try {phrase.stop();} catch (Exception ex) {} break; } invalidate(); return true; } } public void playrandom() { //Тут идёт очень большой метод, не связанный с вопросом. Я его пропущу. } private boolean isScratch(float oldX, float x, float oldY, float y) { float distance = (float) Math.sqrt(Math.pow(oldX - x, 2) + Math.pow(oldY - y, 2)); if (distance > mRevealSize * 2) { return true; } else { return false; } } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {} @Override public void surfaceCreated(SurfaceHolder arg0) { mThread = new WScratchViewThread(getHolder(), this); mThread.setRunning(true); mThread.setPriority(1); mThread.start(); } @Override public void surfaceDestroyed(SurfaceHolder arg0) { boolean retry = true; mThread.setRunning(false); while (retry) { try { mThread.join(); retry = false; } catch (InterruptedException e) {} } } class WScratchViewThread extends Thread { private SurfaceHolder mSurfaceHolder; private WScratchView mView; private boolean mRun = false; public WScratchViewThread(SurfaceHolder surfaceHolder, WScratchView view) { mSurfaceHolder = surfaceHolder; mView = view; } public void setRunning(boolean run) { mRun = run; } public SurfaceHolder getSurfaceHolder() { return mSurfaceHolder; } @SuppressLint("WrongCall") @Override public void run() { Canvas c; while (mRun) { c = null; try { c = mSurfaceHolder.lockCanvas(null); if (c != null) synchronized (mSurfaceHolder) { { mView.onDraw(c); } } } finally { if (c != null) { mSurfaceHolder.unlockCanvasAndPost(c); } } try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) {} } } } public void resetView() { synchronized (mThread.getSurfaceHolder()) { mPathList.clear(); } } public boolean isScratchable() { return mIsScratchable; } public void setScratchable(boolean flag) { mIsScratchable = flag; } public void setOverlayColor(int ResId) { mOverlayColor = ResId; } public void setRevealSize(int size) { mRevealSize = size; } public void setAntiAlias(boolean flag) { mIsAntiAlias = flag; } } 
  • What is the size of the moved image? Somehow you can check it takes so much time to draw? - Michael M
  • The size of the moved image is very small: 150x300 (15Kb) "Erasable" object is drawn immediately, if you remove the code: destinf = new Rect (fLeft, fTop, fRight, fBottom); canvas.drawBitmap (f, null, destinf, paintf); - Helisia

1 answer 1

Only by code it is difficult to say something specific. It is worth driving your application through a profiler (as I recall, it is included in the Android SDK) and check which method eats up the most time. I will not say exactly, but it seems to me that this is the onTouchEvent method. First of all, it is better not to use the synchronized block, such synchronization eats up too many resources. Instead, it is better to use locks (also read about the lockCanvas() and unlockCanvasAndPost() methods. Secondly, this method will work out many times per second for each touch, which most likely loads the processor. In order to avoid this, it is worthwhile to limit the number of responses of this method per second. To do this, the test for the time of the previous method triggering is suitable: if it worked less than, say, 50 milliseconds ago, then we do not call the handler.