2015-10-01 8 views
0

У меня проблема с рисунком на SurfaceView, когда растровые изображения на SurfaceView перемещаются, они мерцают (или разрываются). У меня не было этой проблемы в предыдущих итерациях моего кода. Но теперь, когда я, наконец, получил растровые изображения для правильного масштабирования, используя отдельный Canvas для каждого растрового изображения, эта проблема возникла. Эти важные части моего пользовательского SurfaceView класса:Android: SurfaceView Мерцание

public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback { 

    public DrawingSurface(Context context, AttributeSet attributeSet, int defStyle) { 
     super(context, attributeSet, defStyle); 
     initialize(context); 
    } 

    private void initialize(Context context) { 
     mContext = context; 
     getHolder().addCallback(this); 
     setFocusable(true); 
    } 

    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
     mHost = (EditorActivity) mContext; 
     Log.d("DrawingSurface", "SURFACE CREATED"); 
     mDrawingThread = new DrawingThread(this, REFRESH_RATE); 
     mDrawingThread.setRunning(true); 
     mDrawingThread.start(); 
     onConfigurationChanged(getResources().getConfiguration()); 
    } 

    public Bitmap createBitmap() { 
     Bitmap bitmap = Bitmap.createBitmap(mBitmap); 
     Canvas canvas = new Canvas(bitmap); 
     Sticker[] stickers = mStickers.toArray(new Sticker[mStickers.size()]); 
     for (Sticker sticker : stickers) { 
      canvas.drawBitmap(sticker.getBitmap(), new Matrix(), mPaint); 
     } return bitmap; 
    } 

    public void drawSurface(Canvas canvas) { 
     Bitmap bitmap = createBitmap(); 
     mMatrix.setScale(1.0f/mScaleFactor, 1.0f/mScaleFactor); 
     canvas.drawBitmap(bitmap, mMatrix, mPaint); 
    } 

    private void setScaleFactor() { 
     mScaleFactor = ((float) mBitmap.getWidth())/getWidth(); 
    } 

    public void addSticker(int drawableId) { 
     BitmapFactory.Options options = new BitmapFactory.Options(); 
     options.inScaled = false; 
     Sticker sticker = new Sticker(
       BitmapFactory.decodeResource(getResources(), drawableId, options), 
       new PointF(mBitmap.getWidth(), mBitmap.getHeight()), 
       mScaleFactor); 
     mActiveSticker = sticker; 
     mStickers.add(sticker); 
    } 
} 

Это мой обычай Thread:

public class DrawingThread extends Thread { 

    private volatile boolean mRunning = false; 

    private long mRefreshRate; 
    private DrawingSurface mSurface; 

    public DrawingThread (DrawingSurface surface, long time) { 
     super(); 
     mSurface = surface; 
     mRefreshRate = time; 
    } 

    public void setRunning (boolean run) { 
     mRunning = run; 
     Log.d("DrawingThread", "Running: " + mRunning); 
    } 

    @Override 
    public void run() { 
     while (mRunning) { 
      try { 
       sleep(mRefreshRate); 
       onSurfaceUpdate(); 
      } catch (InterruptedException exception) { 
       exception.printStackTrace(); 
      } 
     } 
    } 

    public void onSurfaceChanged(Configuration config, Point fit, float ratio) { 
     float width, height; 
     if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) { 
      width = fit.y * ratio; 
      height = fit.y; 
     } else if (config.orientation == Configuration.ORIENTATION_PORTRAIT) { 
      width = fit.x; 
      height = fit.x/ratio; 
     } else { 
      width = fit.x; 
      height = fit.x/ratio; 
     } mSurface.getHolder().setFixedSize((int) width, (int) height); 
    } 

    private void onSurfaceUpdate() { 
     Canvas canvas = null; 
     try { 
      canvas = mSurface.getHolder().lockCanvas(); 
      synchronized (mSurface.getHolder()) { 
       if (canvas != null) { 
        mSurface.drawSurface(canvas); 
       } 
      } 
     } finally { 
      if (canvas != null) { 
       mSurface.getHolder().unlockCanvasAndPost(canvas); 
      } 
     } 
    } 
} 

А класс наклейка используется для хранения каждого отдельного Canvas и Bitmap:

public class Sticker { 
    private static final float START_SCALE = 0.5f; 
    private static final float MIN_SCALE = 0.3f; 
    private static final float MAX_SCALE = 7f; 

    private float mScale = 1f; 
    private float mScaleFactor; 

    private Canvas mCanvas = new Canvas(); 
    private Bitmap mSticker; 
    private Bitmap mSurface; 

    private PointF mCenter = new PointF(); 

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    private Matrix mMatrix = new Matrix(); 

    public Sticker(Bitmap sticker, PointF size, float scaleFactor) { 
     mSticker = sticker; 

     mSurface = Bitmap.createBitmap((int) size.x, (int) size.y, Bitmap.Config.ARGB_8888); 
     mCanvas = new Canvas(mSurface); 
     mCanvas.drawColor(0, PorterDuff.Mode.CLEAR); 

     setScaleFactor(scaleFactor); 
     postSticker(); 
    } 

    private void postSticker() { 
     mMatrix.postScale(START_SCALE, START_SCALE); 
     setCenter(); 
     setTranslate(mCenter.x, mCenter.y); 
    } 

    public boolean collider(PointF point) { 
     int x = (int) (point.x * mScaleFactor); 
     int y = (int) (point.y * mScaleFactor); 
     int color = mSurface.getPixel(x, y); 

     if(color != Color.TRANSPARENT) { 
      return true; 
     } else { 
      return false; 
     } 
    } 

    public Bitmap getBitmap() { 
     return mSurface; 
    } 

    public void flipSticker() { 
     Matrix matrix = new Matrix(); 
     matrix.preScale(-1, 1); 
     mSticker = Bitmap.createBitmap(mSticker, 0, 0, 
       mSticker.getWidth(), mSticker.getHeight(), matrix, false); 
    } 

    public void setScaleFactor(float scaleFactor) { 
     mScaleFactor = scaleFactor; 
    } 

    public void setTranslate(float deltaX, float deltaY) { 
     mMatrix.postTranslate(deltaX * mScaleFactor, deltaY * mScaleFactor); 
     draw(); 
    } 

    public void setScale(float deltaScale, PointF midpoint) { 
     mScale *= deltaScale; 
     if(MIN_SCALE < mScale && mScale < MAX_SCALE) { 
      mMatrix.postScale(deltaScale, deltaScale, 
        midpoint.x * mScaleFactor, midpoint.y * mScaleFactor); 
     } draw(); 
    } 

    public void setRotate(float deltaRotate, PointF midpoint) { 
     mMatrix.postRotate(deltaRotate, midpoint.x * mScaleFactor, midpoint.y * mScaleFactor); 
     draw(); 
    } 

    private void setCenter() { 
     float width = (mSurface.getWidth()/2) - ((mSticker.getWidth()/2) * START_SCALE); 
     float height = (mSurface.getHeight()/2) - ((mSticker.getHeight()/2) * START_SCALE); 
     mCenter.set(width, height); 
    } 

    private void draw() { 
     mCanvas.drawColor(0, PorterDuff.Mode.CLEAR); 
     mCanvas.drawBitmap(mSticker, mMatrix, mPaint); 
    } 
} 

Я нашел различные обсуждения по той же теме в StackOverflow, а также на других сайтах. Все они заключают, что мерцание связано с тем, как SurfaceView обрабатывает буферизацию, используя две поверхности для обмена между ними. Решение состоит из использования отдельного растрового изображения для первого рисования всех изображений, а затем рисования этого растрового изображения в SurfaceView. Как вы можете видеть, я сделал это в своем методе createBitmap, но мерцание сохраняется.

+1

В вашем 'drawSurface()', если вы начинаете с вызова 'canvas.drawARGB (255, 255, 0, 0)', вы видите красный? Когда вы вызываете 'onSurfaceChanged()'? Почему вы синхронизируете SurfaceHolder? Как выглядит мерцание - он прыгает назад, чередуется ли между рендерингом и черным? Запись с помощью «screenrecord» и прохождение по кадру за кадром иногда может освещаться. – fadden

+0

@fadden Нет, не вижу красных. 'onSurfaceChanged()' вызывается во время 'onConfigurationChanged()' в 'DrawingSurface', чтобы изменить размер' SurfaceView'. Честно говоря, я не знаю, почему я синхронизируюсь на «SurfaceHolder», должно быть, видел это в некоторых обучающих программах, которые я использовал. Кажется, что все работает без него. Что касается мерцания, фон всегда визуализируется отлично, но растровые изображения, которые я рисую сверху, не кажутся готовыми к рисованию между каждым фреймом. Большую часть времени они разрезают пополам, причем верхняя половина показывает, иногда ее нижнюю половину, а иногда и разрезается на линии. – Bryan

ответ

0

Я думаю, что мерцание было связано с тем, что рисунок Canvas не растачивается до того, как он будет использован в createBitmap(). Я изменил метод getBitmap(), чтобы вернуть mSticker вместо гораздо большего размера mSurface (который используется для обнаружения того, где пользователь прикасается), так что рисованный растровый рисунок был не таким большим. И я добавил метод getMatrix(), чтобы вернуть mMatrix из каждого Sticker и использовать его для метода canvas.drawBitmap() в createBitmap(). Это остановило мерцание, хотя файлы png, которые я использую для своих наклеек, довольно большие (большинство из них - 1024 x 1024).

+1

Если вы рисуете растровое изображение в одном потоке и рендеринг из этого растрового изображения в другом потоке, вам нужно будет синхронизировать потоки, чтобы удалить условия гонки. – fadden