it编程 > App开发 > Android

Android实现压缩本地图片功能

7人参与 2025-04-24 Android

1. 项目概述

在各类社交、商城、相册等应用中,图片常常需要经过压缩处理以降低文件体积、缩减上传流量和减少内存占用。图片压缩不仅能够提高传输效率,还能在保证一定质量的前提下改善用户体验。
本项目旨在通过 android 平台实现本地图片压缩功能,主要目标包括:

2. 背景与相关技术解析

2.1 图片压缩的意义与应用场景

2.2 android 图片处理基础与 bitmap 概述

2.3 bitmapfactory.options 与 insamplesize 的作用

2.4 bitmap.compress() 方法及图片编码

3. 项目需求与实现难点

3.1 项目需求说明

本项目主要需求如下:

  1. 选择本地图片

    • 利用系统相册或文件选择器,让用户选择需要上传的图片,并在界面上显示预览。

  2. 图片压缩

    • 通过 bitmapfactory.options 控制 insamplesize 进行采样压缩;

    • 通过 bitmap.compress() 对 bitmap 进行质量压缩,支持 jpeg、png 等格式,用户可以选择压缩质量参数。

  3. 结果展示与保存

    • 将压缩后的图片显示在界面 imageview 上,并支持保存压缩图片到本地存储。

  4. 后台异步操作

    • 网络传输与图片处理操作必须在后台线程中完成,避免阻塞 ui 主线程。

  5. 异常与权限管理

    • 处理内存溢出、 io 异常等问题;在 androidmanifest.xml 中声明必要的权限,如 read_external_storage 和 write_external_storage。

  6. 代码结构与扩展性

    • 所有 java 与 xml 代码均整合在一起,通过详细注释区分不同模块,保证代码清晰、便于维护和后续扩展(例如批量压缩、在线上传结合等)。

3.2 实现难点与挑战

主要难点与挑战包括:

4. 设计思路与整体架构

4.1 总体设计思路

本项目采用基于 bitmap 操作和 canvas 绘制的方式实现图片压缩,主要设计思路如下:

4.2 模块划分与逻辑结构

项目主要模块分为以下几部分:

  1. 图片选择与加载模块

    • 通过系统相册调用,让用户选择图片,并使用 bitmapfactory.options 加载原始 bitmap。

  2. 图片压缩工具模块(imagecompressionutils.java)

    • 提供静态方法 compressimagebysampling() 和 compressimagebyquality(),分别实现采样压缩与质量压缩功能。

  3. 保存与分享模块

    • 提供保存 bitmap 到文件的方法,并支持调用系统分享功能。

  4. ui 与活动模块

    • mainactivity 展示图片预览、压缩参数设置、压缩执行和结果展示,并调用图片压缩工具模块。

  5. 布局与资源管理模块

    • 整合所有 xml 布局文件(主 activity 布局、按钮与预览区域)、颜色、字符串和样式资源,通过详细注释区分各部分。

5. 完整代码实现

下面提供完整代码示例,其中所有 java 与 xml 代码均整合在一起,并通过详细注释分隔不同模块。本示例中采用 imagecompressionutils 工具类实现图片压缩功能,mainactivity 负责图片选择、显示和调用压缩方法。

5.1 java 代码实现

// ===========================================
// 文件: imagecompressionutils.java
// 描述: 图片压缩工具类,提供基于采样率与质量压缩两种方法
// ===========================================
package com.example.uploaddemo;
 
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import java.io.bytearrayoutputstream;
import java.io.file;
import java.io.fileoutputstream;
 
public class imagecompressionutils {
 
    /**
     * 利用 bitmapfactory.options 设置 insamplesize 实现采样压缩
     * @param filepath 图片文件的本地路径
     * @param reqwidth 期望宽度
     * @param reqheight 期望高度
     * @return 采样压缩后的 bitmap
     */
    public static bitmap compressimagebysampling(string filepath, int reqwidth, int reqheight) {
        // 第一次读取设置 injustdecodebounds 为 true 仅获取图片尺寸
        final bitmapfactory.options options = new bitmapfactory.options();
        options.injustdecodebounds = true;
        bitmapfactory.decodefile(filepath, options);
        // 计算 insamplesize 值
        options.insamplesize = calculateinsamplesize(options, reqwidth, reqheight);
        // 第二次读取设置 injustdecodebounds 为 false 获取实际 bitmap
        options.injustdecodebounds = false;
        return bitmapfactory.decodefile(filepath, options);
    }
 
    /**
     * 根据原始图片尺寸和目标尺寸计算合理的 insamplesize
     */
    public static int calculateinsamplesize(bitmapfactory.options options, int reqwidth, int reqheight) {
        // 原始图片宽高
        final int height = options.outheight;
        final int width = options.outwidth;
        int insamplesize = 1;
        if (height > reqheight || width > reqwidth) {
            // 计算宽高比例
            final int halfheight = height / 2;
            final int halfwidth = width / 2;
            // 保证压缩后宽高仍大于目标宽高
            while ((halfheight / insamplesize) >= reqheight && (halfwidth / insamplesize) >= reqwidth) {
                insamplesize *= 2;
            }
        }
        return insamplesize;
    }
 
    /**
     * 利用 bitmap.compress() 方法实现质量压缩
     * @param bitmap 原始 bitmap
     * @param format 压缩格式(jpeg、png、webp)
     * @param quality 压缩质量(0-100,100为最高质量)
     * @return 压缩后的 bitmap
     */
    public static bitmap compressimagebyquality(bitmap bitmap, bitmap.compressformat format, int quality) {
        if (bitmap == null) return null;
        bytearrayoutputstream baos = new bytearrayoutputstream();
        bitmap.compress(format, quality, baos);
        byte[] data = baos.tobytearray();
        return bitmapfactory.decodebytearray(data, 0, data.length);
    }
 
    /**
     * 将 bitmap 保存为文件
     * @param bitmap 待保存的 bitmap
     * @param destfile 保存目标文件
     * @param format 保存格式
     * @param quality 保存质量
     * @return true 保存成功,false 保存失败
     */
    public static boolean savebitmaptofile(bitmap bitmap, file destfile, bitmap.compressformat format, int quality) {
        if (bitmap == null || destfile == null) {
            return false;
        }
        fileoutputstream fos = null;
        try {
            fos = new fileoutputstream(destfile);
            bitmap.compress(format, quality, fos);
            fos.flush();
            return true;
        } catch (exception e) {
            e.printstacktrace();
            return false;
        } finally {
            if (fos != null) {
                try { fos.close(); } catch (exception ex) { }
            }
        }
    }
}
 
// ===========================================
// 文件: mainactivity.java
// 描述: 示例 activity,展示如何选择本地图片、进行压缩预览和保存压缩结果
// ===========================================
package com.example.uploaddemo;
 
import android.manifest;
import android.content.intent;
import android.content.pm.packagemanager;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.net.uri;
import android.os.asynctask;
import android.os.bundle;
import android.provider.mediastore;
import androidx.annotation.nullable;
import androidx.appcompat.app.appcompatactivity;
import androidx.core.app.activitycompat;
import androidx.core.content.contextcompat;
import android.view.view;
import android.widget.button;
import android.widget.imageview;
import android.widget.toast;
import java.io.file;
import java.io.inputstream;
 
public class mainactivity extends appcompatactivity {
 
    private static final int request_code_select_image = 100;
    private imageview ivoriginal;
    private imageview ivcompressed;
    private button btncompress;
    private uri selectedimageuri;
    private bitmap originalbitmap;
 
    @override
    protected void oncreate(bundle savedinstancestate) {
        super.oncreate(savedinstancestate);
        // 设置布局文件 activity_main.xml
        setcontentview(r.layout.activity_main);
 
        ivoriginal = findviewbyid(r.id.iv_original);
        ivcompressed = findviewbyid(r.id.iv_compressed);
        btncompress = findviewbyid(r.id.btn_compress);
 
        // 点击原始图片区域选择图片
        ivoriginal.setonclicklistener(new view.onclicklistener() {
            @override
            public void onclick(view v) {                
                intent intent = new intent(intent.action_pick, mediastore.images.media.external_content_uri);
                startactivityforresult(intent, request_code_select_image);
            }
        });
 
        // 点击压缩按钮执行图片压缩,采用质量压缩方式示例
        btncompress.setonclicklistener(new view.onclicklistener() {
            @override
            public void onclick(view v) {                
                if(originalbitmap != null) {
                    // 异步任务执行压缩操作,防止阻塞主线程
                    new compressimagetask().execute();
                } else {
                    toast.maketext(mainactivity.this, "请先选择一张图片", toast.length_short).show();
                }
            }
        });
 
        // 检查存储读取权限
        if (contextcompat.checkselfpermission(this, manifest.permission.read_external_storage)
                != packagemanager.permission_granted) {
            activitycompat.requestpermissions(this,
                    new string[] { manifest.permission.read_external_storage }, 1);
        }
    }
 
    @override
    protected void onactivityresult(int requestcode, int resultcode, @nullable intent data) {
        super.onactivityresult(requestcode, resultcode, data);
        if(requestcode == request_code_select_image && resultcode == result_ok && data != null) {
            selectedimageuri = data.getdata();
            try {
                // 加载预览 bitmap
                inputstream inputstream = getcontentresolver().openinputstream(selectedimageuri);
                originalbitmap = bitmapfactory.decodestream(inputstream);
                ivoriginal.setimagebitmap(originalbitmap);
            } catch (exception e) {
                e.printstacktrace();
            }
        }
    }
 
    /**
     * compressimagetask 异步任务,后台执行图片压缩操作
     */
    private class compressimagetask extends asynctask<void, void, bitmap> {
 
        @override
        protected void onpreexecute() {
            toast.maketext(mainactivity.this, "开始压缩图片", toast.length_short).show();
        }
 
        @override
        protected bitmap doinbackground(void... voids) {
            // 示例:先采样压缩,再进行质量压缩
            string imagepath = pathutil.getrealpathfromuri(mainactivity.this, selectedimageuri);
            // 通过采样降低分辨率(例如目标尺寸 800x800)
            bitmap sampledbitmap = imagecompressionutils.compressimagebysampling(imagepath, 800, 800);
            // 然后进行质量压缩,设定 jpeg 格式和 70 的压缩质量
            return imagecompressionutils.compressimagebyquality(sampledbitmap, bitmap.compressformat.jpeg, 70);
        }
 
        @override
        protected void onpostexecute(bitmap compressedbitmap) {
            if(compressedbitmap != null) {
                ivcompressed.setimagebitmap(compressedbitmap);
                // 保存压缩后的图片到本地(保存目录与文件名自定义)
                file dir = getexternalfilesdir("compressedimages");
                if(dir != null && !dir.exists()) {
                    dir.mkdirs();
                }
                file outfile = new file(dir, system.currenttimemillis() + ".jpg");
                boolean success = imagecompressionutils.savebitmaptofile(compressedbitmap, outfile, bitmap.compressformat.jpeg, 70);
                if(success) {
                    toast.maketext(mainactivity.this, "压缩图片保存成功:" + outfile.getabsolutepath(), toast.length_long).show();
                } else {
                    toast.maketext(mainactivity.this, "压缩图片保存失败", toast.length_short).show();
                }
            } else {
                toast.maketext(mainactivity.this, "图片压缩失败", toast.length_short).show();
            }
        }
    }
}
 
// ===========================================
// 文件: pathutil.java
// 描述: 工具类,从图片 uri 中获取实际文件路径,适配不同 android 版本
// ===========================================
package com.example.uploaddemo;
 
import android.content.context;
import android.database.cursor;
import android.net.uri;
import android.provider.mediastore;
 
public class pathutil {
    public static string getrealpathfromuri(context context, uri contenturi) {
        string[] proj = { mediastore.images.media.data };
        cursor cursor = context.getcontentresolver().query(contenturi, proj, null, null, null);
        if(cursor != null){
            int column_index = cursor.getcolumnindexorthrow(mediastore.images.media.data);
            cursor.movetofirst();
            string path = cursor.getstring(column_index);
            cursor.close();
            return path;
        }
        return null;
    }
}

5.2 xml 资源文件实现

<!-- ===========================================
     文件: activity_main.xml
     描述: mainactivity 的布局文件,包含原始图片预览、压缩后图片预览和上传按钮
     =========================================== -->
<?xml version="1.0" encoding="utf-8"?>
<scrollview  
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:padding="16dp">
 
    <linearlayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="center_horizontal">
 
        <textview
            android:id="@+id/tv_select"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="点击下方图片选择原始图片"
            android:textsize="18sp"
            android:layout_marginbottom="8dp"/>
 
        <imageview
            android:id="@+id/iv_original"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:background="#eeeeee"
            android:scaletype="centercrop"
            android:layout_marginbottom="16dp"
            android:contentdescription="原始图片预览"/>
 
        <button
            android:id="@+id/btn_compress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="压缩图片"
            android:layout_marginbottom="16dp"/>
 
        <textview
            android:id="@+id/tv_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="压缩后图片预览"
            android:textsize="18sp"
            android:layout_marginbottom="8dp"/>
 
        <imageview
            android:id="@+id/iv_compressed"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:background="#eeeeee"
            android:scaletype="centercrop"
            android:contentdescription="压缩后图片预览"/>
    </linearlayout>
</scrollview>
 
<!-- ===========================================
     文件: colors.xml
     描述: 定义项目中使用的颜色资源
     =========================================== -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="white">#ffffff</color>
    <color name="primary">#3f51b5</color>
</resources>
 
<!-- ===========================================
     文件: styles.xml
     描述: 定义应用主题与样式资源,采用 appcompat 主题
     =========================================== -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="apptheme" parent="theme.appcompat.light.noactionbar">
        <item name="android:windowbackground">@color/white</item>
        <item name="android:textcolorprimary">@color/primary</item>
    </style>
</resources>

6. 代码解读与详细讲解

6.1 图片采样与内存控制

6.2 bitmap.compress() 方法及参数说明

6.3 异步任务与 ui 更新

7. 性能优化与调试技巧

7.1 内存管理与 bitmap 重用

7.2 异步任务与线程优化

7.3 日志调试与异常捕获

8. 项目总结与未来展望

8.1 项目总结

本项目详细介绍了如何在 android 应用中实现对本地图片的压缩功能,包括采样压缩和质量压缩两种方式。主要成果包括:

8.2 未来扩展与优化方向

未来可以进一步在以下方面扩展与优化本项目:

  1. 批量图片压缩

    • 扩展为支持多图片批量处理,并实现压缩进度显示和队列管理;

  2. 更高效的异步处理

    • 利用 rxjava、kotlin coroutine 或 workmanager 进行后台任务调度,提高代码响应性;

  3. 动态调整压缩参数

    • 提供界面允许用户自定义采样率、压缩格式和质量参数,并实时预览压缩效果;

  4. 与图片上传结合

    • 整合图片压缩与上传功能,缩小上传文件体积,提高网络传输效率;

  5. 质量检测与优化

    • 结合图像处理算法,自动检测压缩后图片质量,调整压缩策略,兼顾视觉效果与文件大小;

  6. 异常和错误处理

    • 构建完善的错误捕获和重试机制,在图片处理或文件保存失败时给予详细反馈和自动重试。

以上就是android实现压缩本地图片功能的详细内容,更多关于android压缩本地图片的资料请关注代码网其它相关文章!

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

Android ImageButton 使用方法示例详解

04-24

Android中三种onClick的实现方式与对比

04-24

Android实现跳转第三方百度地图导航

04-24

Android实现视频图片轮播功能

04-24

Android OKHttp拦截器和缓存案例详解

04-24

Android实现Android APP自动更新功能

04-24

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论