0

我的方法如下:

我有三个TextViews(嵌套在 a 中LinearLayout),我希望它们具有倾斜效果,以便它们看起来像在墙上(请看下图)。由于用户可以更改背景(不同的墙),我使用DrawingCachefrom the LinearLayout,倾斜它,最后将其渲染为放置在背景图像上方的透明图像。

在此处输入图像描述

为了确保文本在每个设备上的正确位置,我想出了一个解决方案,即拥有一个背景图像 (720x1022) 并将文本渲染为透明图像并将其放置在具有相同大小的图形中的正确坐标处(720x1022)。在应用程序中,图片以相同的方式缩放和放置。也许这不是最好的主意,因为有时(假设 10 次中有 1 次)我会遇到 OutOfMemory 错误。

java.lang.OutOfMemoryError
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:689)
at android.graphics.Bitmap.createBitmap(Bitmap.java:666)
at android.graphics.Bitmap.createBitmap(Bitmap.java:633)
//or

java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:502)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:355)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:785)
at android.content.res.Resources.loadDrawable(Resources.java:1965)
at android.content.res.TypedArray.getDrawable(TypedArray.java:601)

并且堆突然增长。

  Grow heap (frag case) to 73.520MB for 11773456-byte allocation

它不会每次都崩溃(比如说 10 次中的 1 次),而且不是在同一个位置。这是我的实现:

首先,我们从嵌套在 LinearLayout 中的 TextView 获取用户输入,并将其缩放到特定的高度和宽度(#2)以执行倾斜矩阵(#3)。

//#1 get Text from TextViews
View test = findViewById(R.id.signFakeContainer);
test.setDrawingCacheEnabled(true);
Bitmap mOfferText = test.getDrawingCache();

//#2 scale Text to specific width & height to perform skew effect
Bitmap mScaledBitmap = Bitmap.createScaledBitmap(mOfferText, 560, 720, false);

int mTextViewWidth = mScaledBitmap.getWidth();
int mTextViewHeight = mScaledBitmap.getHeight();

//#3 create skewing matrix
Matrix mMatrix2 = new Matrix();
float[] mStartSrc = new float[] { 0, 0, mTextViewWidth, 0, mTextViewWidth, mTextViewHeight, 0, mTextViewHeight };
float[] mDestSrc = new float[] { 0, 4f, 520f, 0f, 552f, 704f, 22f, 720f };
mMatrix2.setPolyToPoly(mStartSrc, 0, mDestSrc, 0, 4);

//#4 perform skewing
Bitmap mStrechtedTextView = Bitmap.createBitmap(mScaledBitmap, 0, 0, mTextViewWidth, mTextViewHeight, mMatrix2, false);

//#5 place 560x720 image in 720x1022 image at specific coordinates, to ensure its right position 
Bitmap bmp = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.transparent_dummy), 720, 1022, false);
Canvas comboImage = new Canvas(bmp);
comboImage.drawBitmap(mStrechtedTextView, 98, 110, null);

//#6 set rendered textview to image
ImageView mOfferImageView = (ImageView) findViewById(R.id.choose_sign_offer_image);
mOfferImageView.setImageBitmap(bmp);

我发现这篇非常好的文章:将图像加载到位图对象时出现奇怪的内存不足问题,但它仍然不能解决我的问题。我想我还有很多createBitmaps。也许有更好的方法来做到这一点,所以我很高兴每一个建议!

4

2 回答 2

1

对于这种情况,我们的最终解决方案是AsyncTask使用Image. 我们传递一个引用,LinearLayout其中包括TextViews(这是我们正在渲染的),一个ImageView我们插入渲染图片的引用和一个ProgressBar在图像渲染后隐藏 的引用

public class RenderBitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
  private final WeakReference<ImageView> imageViewReference;
  private final LinearLayout signViewReference;
  private final ProgressBar progressBar;

  public RenderBitmapWorkerTask(ImageView imageView, LinearLayout signView, ProgressBar mProgressBar) {
      // Use a WeakReference to ensure the ImageView can be garbage collected
      imageViewReference = new WeakReference<ImageView>(imageView);
      signViewReference = signView;
      progressBar = mProgressBar;
  }

  // Decode image in background.
  @Override
  protected Bitmap doInBackground(Integer... params) {
    signViewReference.setDrawingCacheEnabled(true);

    Bitmap mOfferText = signViewReference.getDrawingCache();

    int mTextViewWidth = 560;
        int mTextViewHeight = 720;

        mOfferText = Bitmap.createScaledBitmap(mOfferText, mTextViewWidth, mTextViewHeight, false);

        Matrix mMatrix2 = new Matrix();
        float[] mStartSrc = new float[] { 0, 0, mTextViewWidth, 0, mTextViewWidth, mTextViewHeight, 0, mTextViewHeight };
        float[] mDestSrc = new float[] { 0, 4f, 520f, 0f, 552f, 704f, 22f, 720f };
        mMatrix2.setPolyToPoly(mStartSrc, 0, mDestSrc, 0, 4);
        mOfferText = Bitmap.createBitmap(mOfferText, 0, 0, mTextViewWidth, mTextViewHeight, mMatrix2, false);

      return mOfferText;
  }

  // Once complete, see if ImageView is still around and set bitmap.
  @Override
  protected void onPostExecute(Bitmap bitmap) {
      if (imageViewReference != null && bitmap != null) {
          final ImageView imageView = imageViewReference.get();
          if (imageView != null) {

                progressBar.setVisibility(View.GONE);

              imageView.setImageBitmap(bitmap);
          }
      }
  }
}
于 2013-07-03T21:15:10.257 回答
0

我在您发布的代码片段中看到至少 4Bitmap秒。请务必在此 android 开发人员培训链接此 stackoverflow 问题上阅读有关正确位图回收的信息。

回收

添加recycle()调用时,请确保不要显示回收的位图,否则会出现另一个错误。在分配新的位图信息之前执行回收(如果不是null)和onDestroy视图(或者onPause如果你足够小心重新创建位图onResume

一个简单的例子:

// you see how I moved this up to class scope instead?
private Bitmap mScaledBitmap = null;

void someMethod(){
    // some code here
    // ...
    if(mScaledBitmap != null)
        mScaledBitmap.recycle();
    mScaledBitmap = Bitmap.createScaledBitmap(mOfferText, 560, 720, false);
    // more code here
    // ...
}

public void onDestroy(){
    // always call super
    super.onDestroy();
    // free memory from bitmap here
    if( mScaledBitmap != null ) {
        mScaledBitmap.recycle();
        mScaledBitmap = null;
    }
}

样本大小

如果即使经过适当的回收,此代码仍然会出现内存不足异常,您可以尝试使用sampleSize选项。确保使用 2 的幂。

另一个简单的例子:

Options opts = new Options();
opts.inSampleSize = 4;
// insert recycle here with proper checks
mScaledBitmap = BitmapFactory.decodeFile(imagePath, opts);
于 2013-06-05T10:04:31.160 回答