《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 封装了通用的图片加载逻辑。
相关推荐
lucky_tom8 小时前
【android Framework 探究】pixel 5 内核编译
android
achene_ql8 小时前
缓存置换:用c++实现最近最少使用(LRU)算法
开发语言·c++·算法·缓存
NO Exception?8 小时前
完美解决 mobile-ffmpeg Not overwriting - exiting
android·ffmpeg·pcm
0wioiw09 小时前
安卓基础(点击项目)
android·windows
帅得不敢出门9 小时前
Android Framework学习二:Activity创建及View绘制流程
android·java·学习·framework·安卓·activity·window
怀君10 小时前
Flutter——数据库Drift开发详细教程(二)
android·数据库·flutter
achene_ql11 小时前
缓存置换:用c++实现最不经常使用(LFU)算法
c++·算法·缓存
尽兴-11 小时前
缓存分片哈希 vs 一致性哈希:优缺点、区别对比及适用场景(图示版)
算法·缓存·哈希算法
wellnw12 小时前
[android]MT6835 Android 关闭selinux方法
android
程序猿阿伟12 小时前
《缓存策略:移动应用网络请求的“效能密钥” 》
网络·缓存