使用 Glide 加载位图到 ImageView

Using Glide to load bitmap into ImageView

如何使用 Glide 库将位图加载到我的 ImageView 中? 我想创建一个带有文本的自定义 图像 并使用 Glide 将其加载到图像视图中。

这是我用文本创建自定义位图的方法

public Bitmap imageWithText(String text) {
    TextView tv = new TextView(context);
    tv.setText(text);
    tv.setTextColor(Color.WHITE);
    tv.setBackgroundColor(Color.BLACK);
    tv.setTypeface(null, Typeface.BOLD);
    tv.setGravity(Gravity.CENTER);
    tv.setTextSize(20);
    tv.setPadding(0, 25, 0, 0);
    Bitmap testB = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(testB);
    tv.layout(0, 0, 100, 100);
    tv.draw(c);
    return testB;
}

但是当我尝试使用 glide 加载此位图时出现错误

Glide.with(getContext()).load(imageWithText("Random text")).into(holder.imgPhoto);

根据文档,我认为 load() 方法不能将位图作为参数。图像源(即参数)可以是 URL、可绘制资源和文件。

但是,如果要将位图加载到 ImageView 中,则不需要使用 Glide 库。只需使用以下语句

holder.imgPhoto.setImageBitmap(imageWithText("Random text"));

, for a reason: acquiring a Bitmap usually takes time and sometimes blocking I/O. So it's not good practice to call imageWithText on the UI thread. Update: That being said I proposed this feature 不久前;虽然破解更容易,但您可以在下面找到我强烈推荐的 "Glide way"。

Glide 被设计成灵活的,这个问题很好地证明了这一特性。下面的实现看似冗长,但每一个片段都有其存在的理由。考虑到性能提升,将您的生成器融入 Glide 世界的代码量并不多。我试图将其格式化为简短的格式,折叠不相关的部分并使用静态导入来缩短(参见导入的结尾)。

该代码还包括以编程方式生成的 UI,因此您只需将以下所有代码复制粘贴到 GlideGeneratedImageListFragment.java 和 运行 中即可;唯一的外部依赖是支持 lib 的 RecyclerView.

class GeneratingAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    // See https://docs.google.com/drawings/d/1KyOJkNd5Dlm8_awZpftzW7KtqgNR6GURvuF6RfB210g/edit?usp=sharing
    //                                  ModelType/A,    DataType/T,     ResourceType/Z, TranscodeType/R
    private final GenericRequestBuilder<GenerateParams, GenerateParams, Bitmap,         GlideDrawable> generator;

    public GeneratingAdapter(final Context context) {
        generator = Glide // this part should be cleaner in Glide 4.0, but that's not released yet
        .with(context)
        .using(new GenerateParamsPassthroughModelLoader(), GenerateParams.class)          // custom class
        .from(GenerateParams.class)
        .as(Bitmap.class)
        .transcode(new BitmapToGlideDrawableTranscoder(context), GlideDrawable.class)     // builtin
        .decoder(new GenerateParamsBitmapResourceDecoder(context))                        // custom class
        .encoder(new BitmapEncoder(Bitmap.CompressFormat.PNG, 0/*ignored for lossless*/)) // builtin
        .cacheDecoder(new FileToStreamDecoder<Bitmap>(new StreamBitmapDecoder(context)))  // builtin
        //.placeholder(new ColorDrawable(Color.YELLOW)) // you can pre-set placeholder and error
        .error(new ColorDrawable(Color.RED))            // so it's easier when binding
        //.diskCacheStrategy(DiskCacheStrategy.NONE)    // only for debugging to always regenerate
        //.skipMemoryCache(true)                        // only for debugging to always regenerate
        ;
    }
    @Override public int getItemCount() { return 1000; }

    private final float[] colorCache = new float[] {0, 1.0f, 0.5f};
    private final float[] bgCache = new float[] {0, 0.5f, 1.0f};
    @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        colorCache[0] = bgCache[0] = (position * 15) % 360; // just to have a fancy example :)
        GenerateParams params = new GenerateParams(
                // omit position to see Glide caching in action (every 24th item / 12th row is the same)
                "android text"/* + " #" + position*/,
                Color.HSVToColor(colorCache),
                Color.HSVToColor(bgCache)
        );
        generator/*.clone() in case you see weird behavior*/.load(params).into((ImageView)holder.itemView);
    }

    @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        int height = parent.getContext().getResources().getDisplayMetrics().heightPixels / 3;
        ImageView view = new ImageView(parent.getContext());
        view.setLayoutParams(new RecyclerView.LayoutParams(MATCH_PARENT, height));
        view.setScaleType(ImageView.ScaleType.FIT_CENTER);
        return new RecyclerView.ViewHolder(view) {}; // anon class for brevity
    }
}

public class GlideGeneratedImageListFragment extends Fragment {
    @Override public @Nullable View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        RecyclerView view = new RecyclerView(container.getContext());
        view.setLayoutParams(new MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        view.setLayoutManager(new GridLayoutManager(container.getContext(), 2 /*columns*/));
        view.setAdapter(new GeneratingAdapter(view.getContext()));
        return view;
    }
}

/** Extracted params from imageWithText, but left some hardcoded values like 20sp/bold/center in {@link Generators}. */
class GenerateParams {
    final String text;
    final int color;
    final int background;

    public GenerateParams(String text, int color, int bg) {
        this.text = text;
        this.color = color;
        this.background = bg;
    }

    public String getId() {
        // TODO make sure it's unique for every possible instance of GenerateParams
        // because it will affect how the resulting bitmap is cached
        // the below is correct correct for the current fields, if those change this has to change
        return String.format(Locale.ROOT, "%s-%08x-%08x", text, color, background);
    }
}

/** Boilerplate because of the degeneration in ModelType == DataType, but important for caching.
 *  @see GeneratingAdapter#generator */
class GenerateParamsPassthroughModelLoader implements ModelLoader<GenerateParams, GenerateParams> {
    @Override public DataFetcher<GenerateParams> getResourceFetcher(final GenerateParams model, int width, int height) {
        return new DataFetcher<GenerateParams>() {
            @Override public GenerateParams loadData(Priority priority) throws Exception { return model; }
            @Override public void cleanup() { }
            @Override public String getId() { return model.getId(); }
            @Override public void cancel() { }
        };
    }
}

/** Handles pooling to reduce/prevent GC lagging from too many {@link Bitmap#createBitmap}s */
class GenerateParamsBitmapResourceDecoder implements ResourceDecoder<GenerateParams, Bitmap> {
    private final Context context;
    public GenerateParamsBitmapResourceDecoder(Context context) { this.context = context; }
    @Override public Resource<Bitmap> decode(GenerateParams source, int width, int height) throws IOException {
        BitmapPool pool = Glide.get(context).getBitmapPool();
        Bitmap bitmap = pool.getDirty(width, height, Bitmap.Config.ARGB_8888);
        if (bitmap == null) {
            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        }
        Generators.imageWithTextNoLayout(context, bitmap, source);
        return BitmapResource.obtain(bitmap, pool);
    }
    @Override public String getId() {
        // be careful if you change the Generator implementation you have to change this
        // otherwise the users may see a cached image; or clear cache on app update
        return "com.example.MyImageGenerator";
    }
}

class Generators {
    /** OP's original implementation fixed for real centering */
    public static Bitmap imageWithText(Context context, Bitmap bitmap, GenerateParams params) {
        TextView view = new TextView(context);
        view.setText(params.text);
        view.setTextColor(params.color);
        view.setBackgroundColor(params.background);
        view.setTypeface(null, Typeface.BOLD);
        view.setGravity(Gravity.CENTER);
        view.setTextSize(20 /*sp*/);
        Canvas canvas = new Canvas(bitmap);
        view.measure(makeMeasureSpec(canvas.getWidth(), EXACTLY), makeMeasureSpec(canvas.getHeight(), EXACTLY));
        view.layout(0, 0, canvas.getWidth(), canvas.getHeight());
        view.draw(canvas);
        return bitmap;
    }

    /** Generate centered text without creating a View, more lightweight.
     * Consider  for multiline support. */
    public static Bitmap imageWithTextNoLayout(Context context, Bitmap bitmap, GenerateParams params) {
        Paint paint = new Paint();
        paint.setColor(params.color);
        paint.setTextAlign(Paint.Align.CENTER); // text's anchor for the x given in drawText
        paint.setTextSize(applyDimension(COMPLEX_UNIT_SP, 20, context.getResources().getDisplayMetrics()));
        paint.setTypeface(Typeface.DEFAULT_BOLD);

        Canvas canvas = new Canvas(bitmap);
        canvas.drawColor(params.background);
        canvas.drawText(params.text, canvas.getWidth() / 2, canvas.getHeight() / 2, paint);
        return bitmap;
    }
}

// Here are the imports in case you need it;
// didn't want to put it in the beginning to keep the relevant code first.

import java.io.IOException;
import java.util.Locale;

import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.*;
import android.view.*;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.*;

import static android.util.TypedValue.*;
import static android.view.View.MeasureSpec.*;
import static android.view.ViewGroup.LayoutParams.*;

import com.bumptech.glide.*;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.resource.bitmap.*;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.load.resource.file.FileToStreamDecoder;
import com.bumptech.glide.load.resource.transcode.BitmapToGlideDrawableTranscoder;

这是它的样子(真正的滚动更流畅,GIF 的 FPS 真的很低):

注意它是如何加载前几项然后逐渐加载其余项的。内存缓存和池预热需要一点时间,但如果您想要更流畅的显示,可以使用预加载器。预热后,它可以很好地滚动。操作栏上的删除按钮调用 Glide.clearDiskCache()Glide.clearMemory(),因此它再次开始重新生成项目。

我不知道性能,但你可以试试这个:

首先将位图转换为 byte[] 数组:

private byte[] bitmapToByte(Bitmap bitmap){
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
    byte[] byteArray = stream.toByteArray();
    return byteArray;
}

然后这样使用glide:

Glide.with(holder.imagePhoto.getContext()).load(bitmapToByte(yourBitmap)).asBitmap().override(300, 300).fitCenter().into(holder.imagePhoto);

你的情况:

Glide.with(holder.imagePhoto.getContext()).load(bitmapToByte(yourBitmap)).asBitmap().into(holder.imagePhoto); //>>not tested

你可以试试, 下面的代码将一个 ImageView 和一个 TextView 添加到 FrameLayout :

FrameLayout frameLayout = new FrameLayout(mContext);
            TextView textView = new TextView(mContext);
            textView.setText("+" + (listImage.size() - countImage));
            textView.setBackground(
                    mContext.getResources().getDrawable(R.drawable.bg_border_tv));
            textView.setTextColor(Color.parseColor("#ffffffff"));
            textView.setGravity(Gravity.CENTER);
            textView.setTextSize(18);
            Glide.with(mContext)
                    .load(listImage.get(i))
                    .apply(new RequestOptions().override(size, size)
                            .transform(new RoundedCorners(20)))
                    .into(imageView);
            frameLayout.addView(imageView);
            frameLayout.addView(textView);
            holder.mItemListLnListImage.addView(frameLayout);

结果如下图所示: