How do I write text over a picture in Android and save it?

You can put an EditText and write into it, and after writing, you first convert it to Bitmap like:

Bitmap bmp = Bitmap.createBitmap(mEditText.getDrawingCache());

Now you can add created image bmp to your original image like this:

Call: Bitmap combined = combineImages(bgBitmap,bmp);

public Bitmap combineImages(Bitmap background, Bitmap foreground) { 

        int width = 0, height = 0;
        Bitmap cs;

        width = getWindowManager().getDefaultDisplay().getWidth();
        height = getWindowManager().getDefaultDisplay().getHeight();

        cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas comboImage = new Canvas(cs);
        background = Bitmap.createScaledBitmap(background, width, height, true);
        comboImage.drawBitmap(background, 0, 0, null);
        comboImage.drawBitmap(foreground, matrix, null);

        return cs;
    }

You have to implement a canvas that allows the user to draw on it and then set the background of that canvas to that particular image. This is just a guess but its somewhere there abouts.


Lets help you.. First of all you must have to use canvas for drawing. Make a custom view which extends ImageView. Here is the helping code for onDraw function..:

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mIcon.setBounds(0, 0, mIcon.getMinimumWidth(),
                mIcon.getMinimumHeight());

        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor, canvas.getWidth() * 0.5f,
                canvas.getWidth() * 0.5f);
        mIcon.draw(canvas);

        rect = canvas.getClipBounds();

        for (Path path : listPath) {
            canvas.drawPath(path, paint);

        }

    }

Now for onTouch:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(ev);

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {

            if (drawing) {

                startPoint = new Point((int) ev.getX(), (int) ev.getY());
                path = new Path();
                float x = ev.getX() / mScaleFactor + rect.left;
                float y = ev.getY() / mScaleFactor + rect.top;
                path.moveTo(x, y);
                path.lineTo(x, y);

                mLastTouchX_1 = x;
                mLastTouchY_1 = y;
            }

            else {
                final float x = ev.getX();
                final float y = ev.getY();

                mLastTouchX = x;
                mLastTouchY = y;
                mActivePointerId = ev.getPointerId(0);
            }
            break;
        }

        case MotionEvent.ACTION_MOVE: {

            if (drawing) {

                float x = ev.getX() / mScaleFactor + rect.left;
                float y = ev.getY() / mScaleFactor + rect.top;

                path.moveTo(mLastTouchX_1, mLastTouchY_1);
                path.lineTo(x, y);

                mLastTouchX_1 = x;
                mLastTouchY_1 = y;
            }

            else {
                final int pointerIndex = ev
                        .findPointerIndex(mActivePointerId);
                final float x = ev.getX(pointerIndex);
                final float y = ev.getY(pointerIndex);

                // Only move if the ScaleGestureDetector isn't processing a
                // gesture.
                if (!mScaleDetector.isInProgress()) {
                    final float dx = x - mLastTouchX;
                    final float dy = y - mLastTouchY;

                    mPosX += dx;
                    mPosY += dy;

                    invalidate();
                }

                mLastTouchX = x;
                mLastTouchY = y;
            }
            break;
        }

        case MotionEvent.ACTION_UP: {
            path_helper_1 = new Path();
            path_helper_2 = new Path();

            endPoint = new Point((int) ev.getX(), (int) ev.getY());

            int left, top, right, bottom;



            left = endPoint.x - 10;
            top = endPoint.y - 10;

            right = endPoint.x + ((endPoint.y / 2));
            bottom = endPoint.x + ((endPoint.y / 2));



            float dx, dy;
            dx = endPoint.x - startPoint.x;
            dy = endPoint.y - startPoint.y;

            dx = dx * -1;
            dy = dy * -1;

            // dx = dy = 100;

            double cos = 0.866 * .1;
            double sin = 0.500 * .1;

            PointF end1 = new PointF((float) (endPoint.x + (dx * cos + dy
                    * -sin)), (float) (endPoint.y + (dx * sin + dy * cos)));
            PointF end2 = new PointF((float) (endPoint.x + (dx * cos + dy
                    * sin)), (float) (endPoint.y + (dx * -sin + dy * cos)));

            // path.moveTo(endPoint.x, endPoint.y);
            //
            // path.lineTo(endPoint.x, endPoint.y);
            //
            // path.lineTo(end1.x, end1.y);
            //
            // path.moveTo(endPoint.x, endPoint.y);
            //
            // path.lineTo(end2.x, end2.y);
            //
            //
            // listPath.add(path);

            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

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

        case MotionEvent.ACTION_POINTER_UP: {
            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = ev.getPointerId(pointerIndex);
            if (pointerId == mActivePointerId) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mLastTouchX = ev.getX(newPointerIndex);
                mLastTouchY = ev.getY(newPointerIndex);
                mActivePointerId = ev.getPointerId(newPointerIndex);
            }
            break;
        }
        }
        invalidate();

        return true;
    }

Also make a private class if you want to implement zooming functionality like this:

    private class ScaleListener extends
            ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {

            if (zoom) {
                mScaleFactor *= detector.getScaleFactor();

                // Don't let the object get too small or too large.
                mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
            }
            invalidate();
            return true;
        }
    }
}

Take the idea from above code and make changes according to your requirements. NOTE Above code implements Drawing And Zooming functionality. If you want to add text over images then like Path drawing you can also draw text on canvas using canvas.drawText. Make an arraylist if you want multiple text overs image.


Add text on bitmap :

public Bitmap drawTextToBitmap(Context gContext, 
  int gResId, 
  String gText) {
  Resources resources = gContext.getResources();
  float scale = resources.getDisplayMetrics().density;
  Bitmap bitmap = 
      BitmapFactory.decodeResource(resources, gResId);

  android.graphics.Bitmap.Config bitmapConfig =
      bitmap.getConfig();
  // set default bitmap config if none
  if(bitmapConfig == null) {
    bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
  }
  // resource bitmaps are imutable, 
  // so we need to convert it to mutable one
  bitmap = bitmap.copy(bitmapConfig, true);

  Canvas canvas = new Canvas(bitmap);
  // new antialised Paint
  Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
  // text color - #3D3D3D
  paint.setColor(Color.rgb(61, 61, 61));
  // text size in pixels
  paint.setTextSize((int) (14 * scale));
  // text shadow
  paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);

  // draw text to the Canvas center
  Rect bounds = new Rect();
  paint.getTextBounds(gText, 0, gText.length(), bounds);
  int x = (bitmap.getWidth() - bounds.width())/2;
  int y = (bitmap.getHeight() + bounds.height())/2;

  canvas.drawText(gText, x, y, paint);

  return bitmap;
}

Source : http://www.skoumal.net/en/android-how-draw-text-bitmap/

Add multi line text on bitmap :

public Bitmap drawMultilineTextToBitmap(Context gContext,
                                   int gResId,
                                   String gText) {

  // prepare canvas
  Resources resources = gContext.getResources();
  float scale = resources.getDisplayMetrics().density;
  Bitmap bitmap = BitmapFactory.decodeResource(resources, gResId);

  android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
  // set default bitmap config if none
  if(bitmapConfig == null) {
    bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
  }
  // resource bitmaps are imutable,
  // so we need to convert it to mutable one
  bitmap = bitmap.copy(bitmapConfig, true);

  Canvas canvas = new Canvas(bitmap);

  // new antialiased Paint
  TextPaint paint=new TextPaint(Paint.ANTI_ALIAS_FLAG);
  // text color - #3D3D3D
  paint.setColor(Color.rgb(61, 61, 61));
  // text size in pixels
  paint.setTextSize((int) (14 * scale));
  // text shadow
  paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);

  // set text width to canvas width minus 16dp padding
  int textWidth = canvas.getWidth() - (int) (16 * scale);

  // init StaticLayout for text
  StaticLayout textLayout = new StaticLayout(
    gText, paint, textWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);

  // get height of multiline text
  int textHeight = textLayout.getHeight();

  // get position of text's top left corner
  float x = (bitmap.getWidth() - textWidth)/2;
  float y = (bitmap.getHeight() - textHeight)/2;

  // draw text to the Canvas center
  canvas.save();
  canvas.translate(x, y);
  textLayout.draw(canvas);
  canvas.restore();

  return bitmap;
}

Source : http://www.skoumal.net/en/android-drawing-multiline-text-on-bitmap/