【问题标题】:how to overlay image with multiline text(text will be in center of the canvas )如何用多行文本覆盖图像(文本将位于画布的中心)
【发布时间】:2012-11-08 08:51:09
【问题描述】:

我正在开发摄影应用程序,用文字覆盖图像。

这是我的代码:

Bitmap mBitmap = BitmapFactory.decodeResource(getResources(),
        R.drawable.themes11);
// create a mutable bitmap with the same size as the background image's size
bmOverlay = Bitmap.createBitmap(mBitmap.getWidth(),
        mBitmap.getHeight(), Bitmap.Config.ARGB_4444);
// create a canvas on which to draw
Canvas canvas = new Canvas(bmOverlay);
TextPaint paint = new TextPaint();
paint.setColor(Color.RED);

paint.setTextSize(40);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
// if the background image is defined in main.xml, omit this line
canvas.drawBitmap(mBitmap, 0, 0, null);

// draw the text and the point
canvas.drawPoint(50, 100, paint);
// canvas.drawText(InstaTextActivity.CurrentWord, 300, 200, paint);
StaticLayout layout = new StaticLayout(InstaTextActivity.CurrentWord,
        paint, display.getHeight(),
        android.text.Layout.Alignment.ALIGN_NORMAL, (float) 1.0,
        (float) 0.0, true);

canvas.translate(width / 5, height / 5);
layout.draw(canvas);
imageview_img.setImageBitmap(bmOverlay);

在此代码中,我将文本覆盖在屏幕宽度/2 和高度/2 上,它将显示在图像顶部,但我希望文本居中对齐。此外,当我写一个大文本时,它会将表单中心对齐。

看看图片,看看我想要什么:

图片背景:

而我想要的结果:

【问题讨论】:

    标签: java android xml android-manifest


    【解决方案1】:

    使用以下方法测量文本的高度和宽度。 然后在画布上绘制文字时

    left = width/2 - textWidth/2
    top = height/2 - textHeight/2

    但是如果你需要多行文本来处理长文本,那就有点棘手了。

    /**
     * Method to get the height of the paint
     * 
     * @param brush The TextPaint used to paint the text
     * @param text The text which needs to be measured
     * @return height of the text
     */
    public static int measureTextHeight(Paint brush, String text) {
        Rect result = new Rect();
        // Measure the text rectangle to get the height
        brush.getTextBounds(text, 0, text.length(), result);
        return result.height();
    }
    /**
     * Method to get the width of the paint
     * 
     * @param brush The TextPaint used to paint the text
     * @param text The text which needs to be measured
     * @return width of the text
     */
    public static int measureTextWidth(Paint brush, String text) {
        Rect result = new Rect();
        // Measure the text rectangle to get the height
        brush.getTextBounds(text, 0, text.length(), result);
        return result.width();
    }
    

    【讨论】:

    • 请问如何获取textWidth,textHeight
    • 使用我提到的方法textWidth = measureTextWidth("text", textPaint)
    • Atrix,代码正在运行,但如果你在画布上写文字,它不会在中心对齐,请给我一个建议..
    • 我已经这样做了,它对我有用。我使用 -> canvas.drawText("Test", left, top, paint)
    • 现在我在中心对齐文本。但是如何在画布上对齐多行文本请帮助我...
    【解决方案2】:

    我用这个。为我工作。

    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;
    }
    

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

    【讨论】:

      【解决方案3】:

      这不是一个非常精致的方法(没有针对内存使用进行优化),但可以完成工作。您将需要修改代码以便在保持单词完整的字符串处拆分。希望这可以帮助。

      package org.edu.abhi;
      
      import java.util.ArrayList;
      import java.util.List;
      
      import android.content.Context;
      import android.graphics.Canvas;
      import android.graphics.Color;
      import android.graphics.Paint;
      import android.os.AsyncTask;
      import android.util.AttributeSet;
      import android.util.DisplayMetrics;
      import android.util.Log;
      import android.view.View;
      import android.view.WindowManager;
      
      /**
       * @author Abhishek_Nandi
       * 
       */
      public class DummyUserSpace extends View {
      
          /***************************************************************************
           * Calls superclass constructor. This will call {@link #init(Context)}
           * 
           * @param context
           *            The Context the view is running in, through which it can
           *            access the current theme, resources, etc.
           **************************************************************************/
          public DummyUserSpace(Context context) {
              super(context);
              init(context);
          }
      
          /***************************************************************************
           * Calls superclass constructor. This will call {@link #init(Context)}
           * 
           * @param context
           *            The Context the view is running in, through which it can
           *            access the current theme, resources, etc.
           * @param attrs
           *            The attributes of the XML tag that is inflating the view.
           * @param defStyle
           *            The default style to apply to this view. If 0, no style will
           *            be applied (beyond what is included in the theme). This may
           *            either be an attribute resource, whose value will be retrieved
           *            from the current theme, or an explicit style resource.
           **************************************************************************/
          public DummyUserSpace(Context context, AttributeSet attrs, int defStyle) {
              super(context, attrs, defStyle);
              init(context);
          }
      
          /***************************************************************************
           * Calls superclass constructor. This will call {@link #init(Context)}
           * 
           * @param context
           *            The Context the view is running in, through which it can
           *            access the current theme, resources, etc.
           * @param attrs
           *            The attributes of the XML tag that is inflating the view.
           **************************************************************************/
          public DummyUserSpace(Context context, AttributeSet attrs) {
              super(context, attrs);
              init(context);
          }
      
          /**
           * Width of the View
           */
          int viewWidth = 0;
          /**
           * Height of the View
           */
          int viewHeight = 0;
      
          /**
           * {@link Paint} which is used to draw {@link #text}
           */
          Paint textPaint;
          /**
           * The text which needs to be drawn
           */
          String text = "can be spared the burden of having to perform manual memory management. In some languages, memory for the creation of objects is implicitly allocated on the stack, or explicitly allocated and deallocated from the heap. In the latter case the responsibility of managing memory resides with the programmer. If the program does not deallocate an object, a memory leak occurs. If the program attempts to access or deallocate memory that has already been deallocated, the result is undefined and difficult to predict, and the program is likely to become unstable and/or crash. This can be partially remedied by the use of smart pointers, but these add overhead and complexity. Note that garbage collection does not prevent \"logical\" memory leaks, i.e. those where the memory is still referenced but never used.. o have as few implementation dependencies as possible. It is intended to let application developers \"write once, run anywhere\" (WORA), meaning that code that runs on one platform does not need to be recompiled to run on another. Java is as of 2012 one of the most popular programming languages in use, particularly for client-server web applications, with a reported 10 million users";
      
          /**
           * Common constructor routine
           * 
           * @param context
           *            The Context the view is running in. It is used to get the
           *            density of the device
           */
          private void init(Context context) {
              textPaint = new Paint();
              textPaint.setColor(Color.WHITE);
              textPaint.setTextSize(40);
              textPaint.setStrokeWidth(6.0f);
              WindowManager manager = (WindowManager) context
                      .getSystemService(Context.WINDOW_SERVICE);
              DisplayMetrics outMetrics = new DisplayMetrics();
              manager.getDefaultDisplay().getMetrics(outMetrics);
              padding *= outMetrics.density;
              lineSpacing *= outMetrics.density;
          }
      
          /**
           * Split the string and compute the {@link #startY}, {@link #textHeight}
           * which is required for drawing the view
           */
          private void prepareForDrawing() {
              viewWidth = getWidth();
              viewHeight = getHeight();
              if (viewHeight == 0) {
                  return;
              }
      
              textHeight = Utility.measureTextHeight(textPaint, text);
              if (0 == textHeight) {
                  return;
              }
              maxLines = (viewHeight - 2 * padding) / (textHeight + lineSpacing);
              prepareMultiLineText(textPaint, text);
              int noOfLines = splittedText.size();
              int section = noOfLines / 2;
              int center = viewHeight / 2;
              if (noOfLines % 2 == 0) {
                  center -= lineSpacing / 2;
              } else {
                  center -= textHeight / 2;
              }
              startY = center - (section - 1) * (textHeight + lineSpacing)
                      + lineSpacing;
          }
      
          /**
           * The starting position from where the view will start drawing
           */
          private int startY = 0;
          /**
           * Height of the text
           */
          private int textHeight = 0;
          /**
           * Maximum lines this view can hold
           */
          private int maxLines = 1;
          /**
           * Padding from the view border
           */
          private int padding = 20;
          /**
           * Spacing between each line
           */
          private int lineSpacing = 10;
      
          private final String loadingText = "Loading..";
      
          /*
           * (non-Javadoc)
           * 
           * @see android.view.View#onDraw(android.graphics.Canvas)
           */
          @Override
          protected void onDraw(Canvas canvas) {
              if (!computationComplete) {
                  new DrawComputations().execute();
                  canvas.drawText(
                          loadingText,
                          getWidth() / 2
                                  - Utility.measureTextWidth(textPaint, loadingText)
                                  / 2,
                          getHeight() / 2
                                  - Utility.measureTextHeight(textPaint, loadingText)
                                  / 2, textPaint);
              } else {
                  for(int count = 0; count<splittedText.size() ; count++){
                      String trimmed = splittedText.get(count);
                      //System.out.println(trimmed);
                      canvas.drawText(
                              trimmed,
                              viewWidth / 2
                                      - Utility.measureTextWidth(textPaint, trimmed)
                                      / 2, startY + (count - 1)
                                      * (textHeight + lineSpacing), textPaint);
                  }
              }
      
          }
      
          /**
           * Denotes the computation has completed and the view is ready to be drawn
           */
          boolean computationComplete = false;
      
          /**
           * This {@link AsyncTask} performs the computation in background and updates
           * the view when computation is finished and the view is ready to be drawn
           * 
           * @author Abhishek_nandi
           * @version 1.0
           */
          class DrawComputations extends AsyncTask<Void, Void, Void> {
      
              /*
               * (non-Javadoc)
               * 
               * @see android.os.AsyncTask#doInBackground(Params[])
               */
              @Override
              protected Void doInBackground(Void... params) {
                  prepareForDrawing();
                  computationComplete = true;
                  return null;
              }
      
              /*
               * (non-Javadoc)
               * 
               * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
               */
              @Override
              protected void onPostExecute(Void result) {
                  invalidate();
              }
          }
      
          /**
           * Collection which holds the multi-line text
           */
          private List<String> splittedText = new ArrayList<String>();
      
          /**
           * This method is responsible for stripping {@link #text} into individual
           * lines in order to form the multi line text. This method can be used as a
           * utility.
           * 
           * @param paint
           *            The {@link Paint} which is used to draw the text
           * @param str
           *            The string which needs to be stripped
           */
          private void prepareMultiLineText(Paint paint, String str) {
              if (str == null || str.trim().length() == 0)
                  return;
              str = str.trim();
              String result = str;
              int boxWidth = viewWidth;
              try {
                  float textWidth = paint.measureText(str) + 2 * padding;
                  result = str;
                  while (textWidth > boxWidth) {
                      if (result.length() == 0)
                          break;//keeping the entire word intact//if(result.lastIndexOf(" ")!=-1){result = result.substring(0, result.lastIndexOf(" "))}else
                      result = result.substring(0, result.length() - 1);
                      textWidth = paint.measureText(result) + 2 * padding;
                  }
                  result = result.trim();
                  boolean exceeded = false;
                  if (splittedText.size() == maxLines) {
                      exceeded = true;
                      result = result.substring(0, result.length()-2).concat("..");
                  }
                  splittedText.add(result);
                  if (!exceeded && result.length() != str.length()) {
                      prepareMultiLineText(textPaint, str.substring(result.length()));
                  }
              } catch (Exception e) {
                  Log.e("CustomView", "prepareMultiLineText", e);
              }
          }
      
      } // END of class
      // END of file
      

      【讨论】:

      • 您可以简单地在扩展RelativeLayout 的自定义ViewGroup 中绘制位图,而不是所有这些麻烦。他们将 TextView 添加到指定布局参数的自定义 ViewGroup 中。
      • 你能解释一下实用程序类的一点点以及如何在我的活动中使用 DummyUserSpace
      • 实用程序只是一个实用程序类,它具有我在上一个答案中提到的两种方法
      • DummyUserSpace 只是我拥有的一个示例视图。我只是根据您的需要对其进行了修改。它实际上以中心对齐方式绘制文本。只需在 onDraw 中使用 canvas.drawBitmap,您就会得到您想要的。添加到 xml 的视图将绘制位图并覆盖居中对齐的文本。
      【解决方案4】:

      您应该为此使用StaticLayout。它测量和绘制多行文本,处理换行、行距、对齐等。

      StaticLayout layout = new StaticLayout("your long text", textPaint,
          pixelsToFitWidthTo, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, true);
      int boxHeight = layout.getHeight(); // measure
      

      文本甚至可以是 HTML 格式,只需将文本包裹在 Html.fromHtml() 中:

      Html.fromHtml("your long <b>and bold</b> text");
      

      【讨论】:

        猜你喜欢
        • 2015-10-17
        • 2015-01-05
        • 2012-12-08
        • 2013-06-22
        • 2020-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多