HTML.ImageGetter TextView
Итак, я использую ImageGetter для отображения изображений из записей в блоге JSON. Я получаю правильный источник в журнале, но URL меняется, когда он достигает заданных значений. Есть какие-нибудь идеи?
Код:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blog_view);
Intent intent = getIntent();
Uri blogUri = intent.getData();
mPost = blogUri.toString();
mUrl = getIntent().getStringExtra("mUrl");
TextView textView = (TextView) findViewById(R.id.scrollView1);
textView.setText(Html.fromHtml(mPost, imgGetter, null));
}
private ImageGetter imgGetter = new ImageGetter(){
@Override
public Drawable getDrawable(String source){
Drawable drawable = Drawable.createFromPath(source);
try {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
}catch (NullPointerException e){
logException(e);
}
return drawable;
}
};
"Исходный код" перед попыткой - это
http://www.domain.com/images_blog/feature.png
но в catch ошибка:
Не удается декодировать поток:
java.io.FileNotFoundException: /http:/www.domain.com/images_blog/feature.png : open failed: ENOENT (No such file or directory)
Переведено автоматически
Ответ 1
самое простое решение - это:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import org.pskink.soom.R;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LevelListDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.text.Spanned;
import android.util.Log;
import android.widget.TextView;
public class TestImageGetter extends Activity implements ImageGetter {
private final static String TAG = "TestImageGetter";
private TextView mTv;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_image_getter);
String source = "this is a test of <b>ImageGetter</b> it contains " +
"two images: <br/>" +
"<img src=\"http://developer.android.com/assets/images/dac_logo.png\"><br/>and<br/>" +
"<img src=\"http://developer.android.com/assets/images/icon_search.png\">";
Spanned spanned = Html.fromHtml(source, this, null);
mTv = (TextView) findViewById(R.id.text);
mTv.setText(spanned);
}
@Override
public Drawable getDrawable(String source) {
LevelListDrawable d = new LevelListDrawable();
Drawable empty = getResources().getDrawable(R.drawable.ic_launcher);
d.addLevel(0, 0, empty);
d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight());
new LoadImage().execute(source, d);
return d;
}
class LoadImage extends AsyncTask<Object, Void, Bitmap> {
private LevelListDrawable mDrawable;
@Override
protected Bitmap doInBackground(Object... params) {
String source = (String) params[0];
mDrawable = (LevelListDrawable) params[1];
Log.d(TAG, "doInBackground " + source);
try {
InputStream is = new URL(source).openStream();
return BitmapFactory.decodeStream(is);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
Log.d(TAG, "onPostExecute drawable " + mDrawable);
Log.d(TAG, "onPostExecute bitmap " + bitmap);
if (bitmap != null) {
BitmapDrawable d = new BitmapDrawable(bitmap);
mDrawable.addLevel(1, 1, d);
mDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
mDrawable.setLevel(1);
// i don't know yet a better way to refresh TextView
// mTv.invalidate() doesn't work as expected
CharSequence t = mTv.getText();
mTv.setText(t);
}
}
}
}
существует один не совсем элегантный способ изменить макет TextView после загрузки изображения:
// i don't know yet a better way to refresh TextView
// mTv.invalidate() doesn't work as expected
CharSequence t = mTv.getText();
mTv.setText(t);
если кто-нибудь знает лучшее решение, пожалуйста, дайте мне знать
Ответ 2
Альтернативное решение, использующее Glide
и Coroutines
с предположением, что повторная попытка не требуется:
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.text.Html
import android.widget.TextView
import androidx.lifecycle.LifecycleCoroutineScope
import com.bumptech.glide.RequestManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.roundToInt
class HtmlImageGetter(
private val scope: LifecycleCoroutineScope,
private val res: Resources,
private val glide: RequestManager,
private val htmlTextView: TextView
) : Html.ImageGetter {
override fun getDrawable(url: String): Drawable {
val holder = BitmapDrawablePlaceHolder(res, null)
scope.launch(Dispatchers.IO) {
runCatching {
val bitmap = glide
.asBitmap()
.load(url)
.submit()
.get()
val drawable = BitmapDrawable(res, bitmap)
val scale = 1.25 // This makes the image scale in size.
val width = (drawable.intrinsicWidth * scale).roundToInt()
val height = (drawable.intrinsicHeight * scale).roundToInt()
drawable.setBounds(0, 0, width, height)
holder.setDrawable(drawable)
holder.setBounds(0, 0, width, height)
withContext(Dispatchers.Main) { htmlTextView.text = htmlTextView.text }
}
}
return holder
}
internal class BitmapDrawablePlaceHolder(res: Resources, bitmap: Bitmap?) : BitmapDrawable(res, bitmap) {
private var drawable: Drawable? = null
override fun draw(canvas: Canvas) {
drawable?.run { draw(canvas) }
}
fun setDrawable(drawable: Drawable) {
this.drawable = drawable
}
}
}
В Fragment
или Activity
использовании с HtmlCompat
val imageGetter = HtmlImageGetter(lifecycleScope, resources, glide, htmlTextView)
val styledText = HtmlCompat.fromHtml(htmlString, flags, imageGetter, null)
htmlTextView.text = styledText
Ответ 3
Если у вас есть mTV (я имею в виду TextView), вы можете рассчитать на основе его размеров (mTv.getWidth() и mTv.getHeight()) и размера созданного растрового изображения (bitmap.getWidth() и bitmap.getHeight()) и установить эти значения в качестве новых размеров TextView (mTv).
if (bitmap != null) {
BitmapDrawable d = new BitmapDrawable(bitmap);
mDrawable.addLevel(1, 1, d);
int width = mTv.getWidth() < bitmap.getWidth() ? mTv.getWidth() : bitmap.getWidth();
int height = bitmap.getHeight() * width / bitmap.getWidth();
mDrawable.setBounds(0, 0, width, height);
mDrawable.setLevel(1);
// i don't know yet a better way to refresh TextView
// mTv.invalidate() doesn't work as expected
// but we can calculate new TextView dimensions
mTv.setHeight(height);
CharSequence t = mTv.getText();
mTv.setText(t);
}
Ответ 4
Этот ответ может кому-то помочь. Я использовал Jsoup для извлечения <Img/>
тега из строки, затем я показываю изображение в ImageView
и <p>
в Textview
. Результаты соответствовали тому, что мне было нужно. Также я использовал универсальный загрузчик изображений Libaray для загрузки изображений в ImageView
Затем я программно добавил view в макет, в моем случае layout был linearlayout, поэтому я создал вспомогательный класс и передал content, html-строку и linear layout в качестве параметра.
добавьте jsoup в свой проект.
compile 'org.jsoup:jsoup:1.9.2'
Вот несколько фрагментов.
public class PostContentHandler {
Context context;
String content;
LinearLayout linearLayout;
public PostContentHandler(Context context, String content, LinearLayout linearLayout) {
this.context = context;
this.content = content;
this.linearLayout = linearLayout;
}
public void setContentToView() {
//custom font
Typeface bitterBoldFont = Typeface.createFromAsset(context.getAssets(), "fonts/Bitter-Regular.otf");
List<String> p = new ArrayList<>();
List<String> src = new ArrayList<>();
List<String> li = new ArrayList<>();
Document doc = Jsoup.parse(content);
Elements elements = doc.getAllElements();
for (Element element : elements) {
Tag tag = element.tag();
if (tag.getName().matches("h[1-6]{1}")) {
String heading = element.select(tag.getName().toString()).text();
TextView textView = new TextView(context);
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) textView.getLayoutParams();
int top = (int) context.getResources().getDimension(R.dimen.heading_margin_top);
int start = (int) context.getResources().getDimension(R.dimen.content_margin);
int end = (int) context.getResources().getDimension(R.dimen.content_margin);
params.setMargins(start, top, end, 0);
textView.setTextSize(20);
textView.setTypeface(bitterBoldFont);
textView.setText(heading);
textView.setTextColor(context.getResources().getColor(R.color.black));
linearLayout.addView(textView);
}
if (tag.getName().equalsIgnoreCase("p")) {
element.select("img").remove();
String body = element.html();
TextView textView = new TextView(context);
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) textView.getLayoutParams();
int top = (int) context.getResources().getDimension(R.dimen.heading_margin_top);
int start = (int) context.getResources().getDimension(R.dimen.content_margin);
int end = (int) context.getResources().getDimension(R.dimen.content_margin);
params.setMargins(start, top, end, 0);
textView.setTypeface(bitterBoldFont);
textView.setLinksClickable(true);
textView.setAutoLinkMask(Linkify.WEB_URLS);
textView.setText(Html.fromHtml(body));
textView.setTextColor(context.getResources().getColor(R.color.content_color));
linearLayout.addView(textView);
p.add(body);
}
if (tag.getName().equalsIgnoreCase("ol")) {
String ol = element.select(tag.getName().toString()).outerHtml();
TextView textView = new TextView(context);
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) textView.getLayoutParams();
params.setMarginStart((int) context.getResources().getDimension(R.dimen.content_margin));
int top = (int) context.getResources().getDimension(R.dimen.heading_margin_top);
int start = (int) context.getResources().getDimension(R.dimen.content_margin);
int end = (int) context.getResources().getDimension(R.dimen.content_margin);
params.setMargins(start, top, end, 0);
textView.setTypeface(bitterBoldFont);
textView.setLinksClickable(true);
textView.setAutoLinkMask(Linkify.WEB_URLS);
textView.setTextColor(context.getResources().getColor(R.color.content_color));
textView.setText(Html.fromHtml(ol, null, new MyTagHandler()));
linearLayout.addView(textView);
}
if (tag.getName().equalsIgnoreCase("ul")) {
String ul = element.select(tag.getName().toString()).outerHtml();
TextView textView = new TextView(context);
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) textView.getLayoutParams();
int top = (int) context.getResources().getDimension(R.dimen.heading_margin_top);
int start = (int) context.getResources().getDimension(R.dimen.content_margin);
int end = (int) context.getResources().getDimension(R.dimen.content_margin);
params.setMargins(start, top, end, 0);
textView.setTypeface(bitterBoldFont);
textView.setLinksClickable(true);
textView.setAutoLinkMask(Linkify.WEB_URLS);
textView.setTextColor(context.getResources().getColor(R.color.content_color));
textView.setText(Html.fromHtml(ul, null, new MyTagHandler()));
linearLayout.addView(textView);
}
if (tag.getName().equalsIgnoreCase("img")) {
String url = element.select("img").attr("src");
final ImageView imageView = new ImageView(context);
imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
final ProgressBar progressBar = new ProgressBar(context);
linearLayout.addView(progressBar);
progressBar.setVisibility(View.GONE);
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(url, imageView, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
progressBar.setVisibility(View.INVISIBLE);
int height = loadedImage.getHeight();
imageView.getLayoutParams().height = getScreenWidth();
imageView.setAdjustViewBounds(true);
imageView.requestLayout();
}
@Override
public void onLoadingStarted(String imageUri, View view) {
super.onLoadingStarted(imageUri, view);
progressBar.setVisibility(View.VISIBLE);
}
});
linearLayout.addView(imageView);
src.add(url);
}
}
}
public static int getScreenWidth() {
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
}
Я надеюсь, что мой ответ кому-нибудь поможет.