《Android 应用开发基础教程》——第十一章:Android 中的图片加载与缓存(Glide 使用详解)

目录

[第十一章:Android 中的图片加载与缓存(Glide 使用详解)](#第十一章:Android 中的图片加载与缓存(Glide 使用详解))

[🔹 11.1 Glide 简介](#🔹 11.1 Glide 简介)

[🔸 11.2 添加 Glide 依赖](#🔸 11.2 添加 Glide 依赖)

[🔸 11.3 基本用法](#🔸 11.3 基本用法)

[✦ 加载网络图片到 ImageView:](#✦ 加载网络图片到 ImageView:)

[✦ 加载本地资源 / 文件 / URI:](#✦ 加载本地资源 / 文件 / URI:)

[🔸 11.4 占位图、错误图、缩略图](#🔸 11.4 占位图、错误图、缩略图)

[🔸 11.5 图片变换:圆角、圆形、裁剪](#🔸 11.5 图片变换:圆角、圆形、裁剪)

[✦ CenterCrop 和 FitCenter:](#✦ CenterCrop 和 FitCenter:)

[✦ GlideTransform:实现圆角或圆形(需引入扩展库):](#✦ GlideTransform:实现圆角或圆形(需引入扩展库):)

[🔸 11.6 缓存策略](#🔸 11.6 缓存策略)

[🔸 11.7 在 RecyclerView 中使用 Glide](#🔸 11.7 在 RecyclerView 中使用 Glide)

[🔸 11.8 清除缓存](#🔸 11.8 清除缓存)

[✅ 实战练习建议](#✅ 实战练习建议)

习题答案

项目结构

[1. MainActivity.java](#1. MainActivity.java)

[2. ImageTextAdapter.java](#2. ImageTextAdapter.java)

[3. GlideUtils.java](#3. GlideUtils.java)

[4. CircleBorderTransform.java](#4. CircleBorderTransform.java)

[5. activity_main.xml](#5. activity_main.xml)

[6. item_image_text.xml](#6. item_image_text.xml)

[7. ImageTextItem.java](#7. ImageTextItem.java)

总结


第十一章:Android 中的图片加载与缓存(Glide 使用详解)

在移动应用中,图片加载是最常见也最耗资源的任务之一。为了提升性能并避免内存泄露,我们通常使用图片加载库。Glide 是 Google 推荐的 Android 图片加载库,功能强大、使用简单,支持图片加载、转换、缓存等操作。


🔹 11.1 Glide 简介

Glide 由 BumpTech 开发,具有以下特点:

  • 支持从网络、本地、资源中加载图片

  • 自动内存缓存与磁盘缓存

  • 支持图片缩放、裁剪、圆角、圆形

  • 支持 GIF 加载

  • 支持 RecyclerView 中高效加载


🔸 11.2 添加 Glide 依赖

build.gradle(:app) 文件中添加:

java 复制代码
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'

🔸 11.3 基本用法

✦ 加载网络图片到 ImageView:

java 复制代码
ImageView imageView = findViewById(R.id.imageView);

Glide.with(this)
     .load("https://example.com/image.jpg")
     .into(imageView);

✦ 加载本地资源 / 文件 / URI:

java 复制代码
// 资源图片
Glide.with(this)
     .load(R.drawable.sample)
     .into(imageView);

// 本地文件
File file = new File(getExternalFilesDir(null), "pic.jpg");
Glide.with(this)
     .load(file)
     .into(imageView);

🔸 11.4 占位图、错误图、缩略图

java 复制代码
Glide.with(this)
     .load("https://example.com/image.jpg")
     .placeholder(R.drawable.loading)    // 加载中显示的图
     .error(R.drawable.error_image)      // 加载失败显示的图
     .thumbnail(0.1f)                    // 显示缩略图
     .into(imageView);

🔸 11.5 图片变换:圆角、圆形、裁剪

✦ CenterCrop 和 FitCenter:

java 复制代码
Glide.with(this)
     .load(url)
     .centerCrop()    // 居中裁剪
     .into(imageView);

✦ GlideTransform:实现圆角或圆形(需引入扩展库):

javascript 复制代码
implementation 'jp.wasabeef:glide-transformations:4.3.0'
java 复制代码
import jp.wasabeef.glide.transformations.RoundedCornersTransformation;

Glide.with(this)
     .load(url)
     .transform(new RoundedCornersTransformation(20, 0)) // 圆角半径 20
     .into(imageView);

🔸 11.6 缓存策略

java 复制代码
Glide.with(this)
     .load(url)
     .diskCacheStrategy(DiskCacheStrategy.ALL)  // 磁盘缓存策略
     .skipMemoryCache(false)                    // 是否跳过内存缓存
     .into(imageView);

常用策略包括:

策略 描述
DiskCacheStrategy.ALL 原图和压缩图都缓存
DiskCacheStrategy.NONE 不缓存任何内容
DiskCacheStrategy.RESOURCE 仅缓存压缩后的图片

🔸 11.7 在 RecyclerView 中使用 Glide

java 复制代码
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
    Glide.with(holder.itemView.getContext())
         .load(itemList.get(position).getImageUrl())
         .into(holder.imageView);
}

注意:不要在 Adapter 中使用 with(context.getApplicationContext()),会影响 View 生命周期管理。


🔸 11.8 清除缓存

java 复制代码
// 清除内存缓存(主线程)
Glide.get(context).clearMemory();

// 清除磁盘缓存(子线程)
new Thread(() -> {
    Glide.get(context).clearDiskCache();
}).start();

✅ 实战练习建议

  1. 实现一个图文混排的 RecyclerView 列表,动态加载网络图片

  2. 使用 Glide 加载 GIF 动图

  3. 用 GlideTransform 实现头像圆形 + 外边框效果

  4. 编写工具类 GlideUtils 封装通用加载逻辑


📢 下一章预告:

第十二章:Material Design 组件实战(Toolbar、BottomNavigation、Snackbar 等)


习题答案

项目结构

javascript 复制代码
MainActivity.java
ImageTextAdapter.java
GlideUtils.java
CircleBorderTransform.java
activity_main.xml
item_image_text.xml

1. MainActivity.java

java 复制代码
package com.example.demo;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private ImageTextAdapter adapter;
    private List<ImageTextItem> itemList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        // 模拟数据
        itemList = new ArrayList<>();
        itemList.add(new ImageTextItem("https://example.com/image1.jpg", "这是描述文字 1"));
        itemList.add(new ImageTextItem("https://example.com/image2.gif", "这是描述文字 2"));
        itemList.add(new ImageTextItem("https://example.com/avatar.png", "这是圆形头像"));

        adapter = new ImageTextAdapter(itemList, this);
        recyclerView.setAdapter(adapter);
    }
}

2. ImageTextAdapter.java

java 复制代码
package com.example.demo;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;

public class ImageTextAdapter extends RecyclerView.Adapter<ImageTextAdapter.ViewHolder> {

    private List<ImageTextItem> itemList;
    private Context context;

    public ImageTextAdapter(List<ImageTextItem> itemList, Context context) {
        this.itemList = itemList;
        this.context = context;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_image_text, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        ImageTextItem item = itemList.get(position);

        // 使用 Glide 加载图片
        if (position == 2) {
            // 使用圆形头像 + 边框效果
            GlideUtils.loadCircleWithBorder(context, item.getImageUrl(), holder.imageView, 5, R.color.teal_200);
        } else {
            // 普通图片或 GIF
            GlideUtils.loadImage(context, item.getImageUrl(), holder.imageView);
        }

        holder.textView.setText(item.getDescription());
    }

    @Override
    public int getItemCount() {
        return itemList.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        ImageView imageView;
        TextView textView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.imageView);
            textView = itemView.findViewById(R.id.textView);
        }
    }
}

3. GlideUtils.java

java 复制代码
package com.example.demo;

import android.content.Context;
import android.graphics.Color;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;

public class GlideUtils {

    // 加载普通图片或 GIF
    public static void loadImage(Context context, String url, ImageView imageView) {
        Glide.with(context)
                .load(url)
                .into(imageView);
    }

    // 加载圆形头像 + 边框效果
    public static void loadCircleWithBorder(Context context, String url, ImageView imageView, int borderWidth, int borderColor) {
        RequestOptions options = new RequestOptions()
                .transform(new CircleBorderTransform(borderWidth, borderColor)); // 自定义 Transform
        Glide.with(context)
                .load(url)
                .apply(options)
                .into(imageView);
    }
}

4. CircleBorderTransform.java

java 复制代码
package com.example.demo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import androidx.annotation.NonNull;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import java.security.MessageDigest;

public class CircleBorderTransform extends BitmapTransformation {

    private final int borderWidth;
    private final int borderColor;

    public CircleBorderTransform(int borderWidth, int borderColor) {
        this.borderWidth = borderWidth;
        this.borderColor = borderColor;
    }

    @Override
    protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
        int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());
        int radius = diameter / 2;

        Bitmap result = pool.get(diameter + borderWidth * 2, diameter + borderWidth * 2, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setAntiAlias(true);

        // 绘制圆形头像
        RectF rectF = new RectF(borderWidth, borderWidth, diameter + borderWidth, diameter + borderWidth);
        canvas.drawOval(rectF, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(toTransform, null, rectF, paint);

        // 绘制边框
        paint.setXfermode(null);
        paint.setColor(borderColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(borderWidth);
        canvas.drawOval(rectF, paint);

        return result;
    }

    @Override
    public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
        messageDigest.update(("CircleBorderTransform" + borderWidth + borderColor).getBytes());
    }
}

5. activity_main.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp" />
</LinearLayout>

6. item_image_text.xml

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="8dp">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:scaleType="centerCrop"
        android:contentDescription="Image" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Description"
        android:textSize="16sp"
        android:paddingStart="16dp"
        android:gravity="center_vertical" />
</LinearLayout>

7. ImageTextItem.java

java 复制代码
package com.example.demo;

public class ImageTextItem {
    private String imageUrl;
    private String description;

    public ImageTextItem(String imageUrl, String description) {
        this.imageUrl = imageUrl;
        this.description = description;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public String getDescription() {
        return description;
    }
}

总结

  1. 图文混排 :通过 RecyclerView 展示图片和文字。
  2. 动态加载网络图片 :使用 Glide 加载普通图片和 GIF 动图。
  3. 圆形头像 + 边框 :通过自定义 CircleBorderTransform 实现。
  4. 工具类封装GlideUtils 封装了通用的图片加载逻辑。
相关推荐
阿巴斯甜19 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker20 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952721 小时前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android