如何避免包含无限 ImageSpan 的 TextView 内存不足?
我正在开发一个手写笔记应用程序。
该应用程序的工作方式如下:用户在触摸屏上书写(绘制)文本,应用程序将手写内容转换为位图(getBitmapFromHandwriting()),然后使用该位图生成 SpannableString 并在 TextView 中将其显示给用户(在我的例子中,我使用 EditText,但为了简单起见,我们假设它是 TextView)。
以下是扩展 TextView 中的代码片段:
class MyTextView extends TextView{
...
BitmapDrawable bmd = new BitmapDrawable(getBitmapFromHandwriting());
int width = bmd.getIntrinsicWidth();
int height = bmd.getIntrinsicHeight();
bmd.setBounds(
(int)(height/4.0f),
(int)(height/4.0f),
(int)(width + height/4.0f),
(int)(height*5.0f/4.0f));
final ImageSpan img = new ImageSpan(bmd);
getText().setSpan(img, position, position+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
…
}
在 getBitmapFromHandwriting() 中,应用程序为用户绘制的每个文本创建一个新的位图。
在笔记变大之前一切正常(意味着本机内存中有很多位图),当 createBitmap() 时会抛出 OutOfMemoryError。
经过大量谷歌搜索后,我了解到位图使用“本机内存”,这是非常有限的。
我还了解到位图的 gc 不会与 JVM 的 gc 同时发生。因此,当位图不再需要时,人们最好执行 bitmap.recycle() 。
根据以上知识,我认为我需要回收不可见文本的位图,并仅在必要时(即可见)加载该位图
在回收位图之前,我必须确保它将来不会被使用,否则,将得到“RuntimeException:Canvas:尝试使用回收的位图...”
即,我必须清除附加到 SpannableString 的相应跨度。 (通过 getText().removeSpan() 或 getText().clearSpans(),但那是另一个故事了。)
为此,我需要区分哪些字符串可见,哪些字符串不可见。在像 GridView 这样的 ViewGroup 中,我可以调用“getFirstVisiblePosition()”,但我不知道如何为 TextView 执行此操作。
我知道可以对位图进行下采样以支持更多位图,但我想要的是 TextView 支持无限的图像跨度。
如果是 GridView 或 ListView,则延迟加载实现将完成这项工作。但如何在单个 TextView 中做到这一点?
将内容划分为多个页面理论上应该适用于某些情况,但在我的应用程序中,用户还可以“导出”注释。导出只是复制当前视图(如何在不创建新位图的情况下进行导出是另一个问题,本线程不会讨论。)。如果注释分为多个页面,用户必须为每个页面“导出”,因此这不是一个选项。
我的问题是:
1. 是否可以判断TextView中可见的字符串?
2.是否有更好的方法(模式)来实现这种“包含大量ImageSpans的TextView”而不会出现OutOfMemoryError?
任何想法表示赞赏。提前致谢!
I am developing a Handwriting note-taking app.
The app works in this way: the user write(draw) a text on the touchscreen, the app convert the handwriting to a Bitmap(getBitmapFromHandwriting()), then use this bitmap to produce a SpannableString and show it to the user in a TextView(In my case, I use an EditText, but to make things simple, let's say it's TextView).
Here is the code snippet in the extended TextView:
class MyTextView extends TextView{
...
BitmapDrawable bmd = new BitmapDrawable(getBitmapFromHandwriting());
int width = bmd.getIntrinsicWidth();
int height = bmd.getIntrinsicHeight();
bmd.setBounds(
(int)(height/4.0f),
(int)(height/4.0f),
(int)(width + height/4.0f),
(int)(height*5.0f/4.0f));
final ImageSpan img = new ImageSpan(bmd);
getText().setSpan(img, position, position+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
…
}
In getBitmapFromHandwriting(), the app create a new bitmap for each text drawn by the user.
Everything works fine before the note grows big(means there are lots of bitmaps in the native memory), an OutOfMemoryError is thrown when createBitmap().
After lots of googling, I learned bitmaps use "native memory", which is very limited.
I also learned gc for bitmap doesn't happen at the same time with the gc of the JVM take place. So people would better do bitmap.recycle() when the bitmap is surely not necessary any more.
From the above knowledge, I think I need to recycle the bitmaps for invisible texts , and load that bitmap only when necessary(i.e., visible)
Before recycling a bitmap, I must make sure it won't used in the future, otherwise, will get a "RuntimeException: Canvas: trying to use a recycled bitmap…"
i.e., I have to clear the corresponding spans attached to the SpannableString. (By getText().removeSpan(), or getText().clearSpans(), but that's another story.)
For doing that, I need to tell which strings are visible and which are invisible. In ViewGroups like GridView, I can call "getFirstVisiblePosition()", but I don't know how to do that for a TextView.
I know it's possible to down sample the bitmap to support more bitmaps, but what I want is a TextView supports UNLIMITED ImageSpans.
If it's GridView or ListView, a lazy loading implementation will do the work. But how to do it in a single TextView?
Dividing the content to multiple pages should theoretically work for some situations, but in my app, user can also "export" the note. The exporting simply do a copy of the current view(How to do the export without creating new bitmap is another problem, won't discuss in this thread.). If the note is divided to multiple pages, user have to "export" for each page, so it's not an option.
My questions are:
1. Is it possible to determine the visible strings in TextView?
2. Is there any better way(patterns) to implement such "TextView containing lots of ImageSpans" without getting OutOfMemoryError?
Any idea is appreciated. Thanks in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论