《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 封装了通用的图片加载逻辑。
相关推荐
Zhu7581 小时前
在k8s集群部署MySQL单实例,支持多个主流发行版
android·mysql·kubernetes
AFinalStone5 小时前
Android 7系统异常问题排查(四)Framework层(上)—System Server Watchdog
android·系统异常
宝杰X76 小时前
Android Room3 多平台数据库
android·数据库
huibin1478523699 小时前
热缓解学习记录
android·学习
小田的博客12 小时前
SAP MM 供应商银行主数据更新报错!message R1228!
android·java·服务器
一笑的小酒馆13 小时前
Android智能猫砂盆视频加载慢卡顿问题分析
android
只会cv的小前端15 小时前
七巧低代码服务端脚本使用方法
android·低代码·rxjava
15 小时前
Android 自定义 View 实战:从零打造工业级全向摇杆控件(OmniJoystickView)
android·kotlin·自定义view·摇杆控件
雨白15 小时前
深入理解 Kotlin 协程 (十一):以逸待劳,探秘 select 多路复用与并发安全策略
android·kotlin
hunterandroid17 小时前
[Android 从零到一] Android 深度链接与 App Links:从 URI Scheme 到可验证的应用跳转
android