Search code examples
androidcanvasandroid-camerasurfaceviewtouch-event

How to drag an image overlaid on top of a Camera Preview


I have an image overlaid on top of a Camera Preview. The camera preview starts inside a FrameLayout on click of a button. The image is also overlaid on the camera preview on click of another button.

Here goes the code below --

MainActivity:

public class MainActivity extends AppCompatActivity {

public Camera mCamera;
public CameraPreview mCameraPreView;
Bitmap bitmap;
FrameLayout frameLayout;
DrawOnTop drawOnTop;
FrameLayout.LayoutParams layoutParams;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    frameLayout = (FrameLayout)findViewById(R.id.camera_preview);
    drawOnTop = null;
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

public void onCamViewButtonClicked (View view)
{
    mCamera = getCameraInstance();
    mCameraPreView = new CameraPreview(this,mCamera);
    frameLayout.addView(mCameraPreView);
}

public Camera getCameraInstance()
{
    Camera camera = null;
    try
    {
        camera = Camera.open();
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
    return camera;
}

public void onOverlayImageButtonClicked(View view)
{
    if(mCameraPreView == null)
    {
        Toast.makeText(getApplicationContext(),"Preview is not available now!",Toast.LENGTH_LONG).show();
        return;
    }
    else {
        if(drawOnTop != null)
        {
            frameLayout.removeView(drawOnTop);
            drawOnTop = null;
        }
        else
        {
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.internetothings);
            drawOnTop = new DrawOnTop(this, bitmap);
            layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
                    FrameLayout.LayoutParams.WRAP_CONTENT);
            layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
            frameLayout.addView(drawOnTop, layoutParams);
            Toast.makeText(getApplicationContext(),"Click again to remove!",Toast.LENGTH_LONG).show();
        }
    }
}
}

CameraPreview.java:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

private SurfaceHolder surfaceHolder;
private android.hardware.Camera camera;

public CameraPreview (Context context, android.hardware.Camera cam)
{
    super(context);
    camera = cam;

    surfaceHolder = getHolder();
    surfaceHolder.addCallback(this);
}

public void surfaceCreated(SurfaceHolder holder)
{
    try {
        camera.setPreviewDisplay(holder);
        camera.startPreview();
    }
    catch (IOException ioe)
    {
        ioe.printStackTrace();
    }
}

public void surfaceDestroyed(SurfaceHolder holder)
{
    camera.stopPreview();
    camera.release();
    camera = null;
}

public void surfaceChanged(SurfaceHolder holder,int format,int w,int h)
{
    if(surfaceHolder.getSurface() == null)
    {
        return;
    }

    camera.stopPreview();

    try {
        camera.setPreviewDisplay(holder);
        camera.startPreview();
    }
    catch (IOException ioe)
    {
        ioe.printStackTrace();
    }
}
}

DrawOnTop.java:

public class DrawOnTop extends View {

Bitmap bitmap;
private int mActivePointerId = 9999;
public static float mLastTouchX,mLastTouchY,mPosX,mPosY;
ImageView view = null;

public DrawOnTop (Context context, Bitmap bmp)
{
    super(context);
    bitmap = bmp;
}

protected void onDraw(Canvas canvas)
{
    super.onDraw(canvas);
    canvas.drawBitmap(bitmap, canvas.getWidth() / 2, canvas.getHeight() / 2, null);
}
}

I have used the below code for dragging, but this is getting fired even if I touch the camera preview, which I don't want.

public boolean onTouchEvent(MotionEvent event)
{

    int action = event.getActionMasked();

    switch (action)
    {
        case MotionEvent.ACTION_DOWN:
        {
            int pointerIndex = event.getActionIndex();
            final float x = MotionEventCompat.getX(event, pointerIndex);
            final float y = MotionEventCompat.getY(event, pointerIndex);
            mLastTouchX = x;
            mLastTouchY = y;
            mActivePointerId = event.getPointerId(pointerIndex);
            break;
        }

        case MotionEvent.ACTION_MOVE:
        {
            final int pointerIndex =
                    MotionEventCompat.findPointerIndex(event, mActivePointerId);

            final float x = MotionEventCompat.getX(event, pointerIndex);
            final float y = MotionEventCompat.getY(event, pointerIndex);

            final float dx = x - mLastTouchX;
            final float dy = y - mLastTouchY;

            mPosX += dx;
            mPosY += dy;

            invalidate();

            mLastTouchX = x;
            mLastTouchY = y;

            break;
        }

        case MotionEvent.ACTION_UP:
        {
            mActivePointerId = 9999;
            break;
        }

        case MotionEvent.ACTION_CANCEL:
        {
            mActivePointerId = 9999;
            break;
        }

        case MotionEvent.ACTION_POINTER_UP:
        {

            final int pointerIndex = MotionEventCompat.getActionIndex(event);
            final int pointerId = MotionEventCompat.getPointerId(event, pointerIndex);

            if (pointerId == mActivePointerId) {
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mLastTouchX = MotionEventCompat.getX(event, newPointerIndex);
                mLastTouchY = MotionEventCompat.getY(event, newPointerIndex);
                mActivePointerId = MotionEventCompat.getPointerId(event, newPointerIndex);
            }
            break;
        }
    }
    return true;
}

My question is: How to drag this particular image on touching it? Touching the camera preview should do nothing, only by touching the image I should be able to drag it.

Thanks in advance!


Solution

  • After several trials and banging my head, finally I got this issue fixed and working! I am able to drag the image over the Camera Preview. :)

    Now I am not going to use DrawOnTop.java. Instead of that, I have created an ImageView under the FrameLayout and populating that same ImageView with the required image. Then I have added OnTouchListener on the ImageView.

    Here goes the code below --

    public class MainActivity extends AppCompatActivity implements View.OnTouchListener {
    
    public Camera mCamera;
    public CameraPreview mCameraPreView;
    FrameLayout frameLayout;
    ImageView imageView;
    float mLastTouchX,mLastTouchY,mPosX,mPosY;
    Boolean clicked;
    Bitmap bitmap;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        frameLayout = (FrameLayout)findViewById(R.id.camera_preview);
        imageView = new ImageView(this);
        ActionBar.LayoutParams layoutParams = new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT,ActionBar.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
        imageView.setLayoutParams(layoutParams);
        clicked = false;
    }
    
    public void onCamViewButtonClicked (View view)
    {
        mCamera = getCameraInstance();
        mCameraPreView = new CameraPreview(this,mCamera);
        frameLayout.addView(mCameraPreView);
    }
    
    public Camera getCameraInstance()
    {
        Camera camera = null;
        try
        {
            camera = Camera.open();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return camera;
    }
    
    public void onOverlayImageButtonClicked(View view)
    {
        if(mCameraPreView == null)
        {
            Toast.makeText(getApplicationContext(),"Preview is not available now!",Toast.LENGTH_LONG).show();
            return;
        }
        else
        {
            if(clicked)
            {
                imageView.setImageDrawable(null);
                clicked = false;
            }
            else
            {
                bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.internetothings);
                imageView.setImageBitmap(bitmap);
                frameLayout.addView(imageView);
                Toast.makeText(getApplicationContext(),"Click again to remove!",Toast.LENGTH_LONG).show();
                imageView.setOnTouchListener(this);
                clicked = true;
            }
        }
    }
    
    public boolean onTouch(View view, MotionEvent event)
    {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN: {
                final float x = event.getX();
                final float y = event.getY();
    
                // Remember where we started
                mLastTouchX = x;
                mLastTouchY = y;
    
                break;
            }
    
            case MotionEvent.ACTION_MOVE: {
                final float x = event.getX();
                final float y = event.getY();
    
                // Calculate the distance moved
                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;
    
                // Move the object
                mPosX += dx;
                mPosY += dy;
    
                // Remember this touch position for the next move event
                mLastTouchX = x;
                mLastTouchY = y;
    
                imageView.setTranslationX(mPosX);
                imageView.setTranslationY(mPosY);
    
                break;
            }
    
            default:
                break;
        }
        return true;
    }
    }