Android Studio App开发之利用图像解码器ImageDecoder播放GIF动图、Webp、HEIF图片(附源码 简单实用)

简介: Android Studio App开发之利用图像解码器ImageDecoder播放GIF动图、Webp、HEIF图片(附源码 简单实用)

需要源码和图片集请点赞关注收藏后评论区留言~~~

一、图像解码器ImageDecoder

早期的Android只支持3种图像格式,分别是JPEG,PNG,GIF 虽然这三类图片都能在ImageView上显示,但对于GIF格式图来说,图像视图仅仅能显示动图的初始画面,无法直接播放动画效果,并且随着图片大小 越来越大,使得手机存储空间越发吃紧,这也要求更高效的压缩算法

目前智能手机行业仅剩安卓和IOS两大阵营,两大阵营纷纷推出新的图像压缩算法,安卓推出了WebP格式,IOS推出了HEIF格式,它们都具备以下的优异特性

1:支持透明背景 JPEG不支持

2:支持动画效果 JPEG和PNG不支持动画效果

3:支持有损压缩 PNG和GIF不支持有损压缩 因为它们图片体积偏大

利用图像解码器加载并显示图片的步骤分为以下三步

1:调用ImageDecoder的createSource方法 从指定地方获得数据源

2:调用ImageDecoder的decodeDrawable方法 从数据源解码得到Drawable类型的图形信息

3:调用图像视图的setImageDrawable方法 设置图像视图的图形对象

ImageDecoder相比于传统方式主要有以下两个优点

1:调用带两个参数的decodeDrawable方法,此时输入第二个监听器参数,在监听器种可以获得图像的媒体类型,以及该图像是否为动图

2:判断解码得到的图形对象是否为Animatable类型,如果是的话,就调用start方法播放动画

效果如下 动图的播放动画已上传至我的个人主页 可前往观看~~~

代码如下

Java类

package com.example.chapter13;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.TargetApi;
import android.graphics.ImageDecoder;
import android.graphics.ImageDecoder.OnHeaderDecodedListener;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
@TargetApi(Build.VERSION_CODES.P)
public class ImageSpecialActivity extends AppCompatActivity {
    private TextView tv_info; // 声明一个文本视图对象
    private ImageView iv_pic; // 声明一个图像视图对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_special);
        tv_info = findViewById(R.id.tv_info);
        iv_pic = findViewById(R.id.iv_pic);
        initTypeSpinner(); // 初始化图像类型下拉框
    }
    // 初始化图像类型下拉框
    private void initTypeSpinner() {
        ArrayAdapter<String> typeAdapter = new ArrayAdapter<String>(this,
                R.layout.item_select, typeArray);
        Spinner sp_type = findViewById(R.id.sp_type);
        sp_type.setPrompt("请选择图像类型");
        sp_type.setAdapter(typeAdapter);
        sp_type.setOnItemSelectedListener(new ImageSpecialActivity.ImageTypeListener());
        sp_type.setSelection(0);
    }
    private String[] typeArray = {"直接显示GIF", "直接显示WebP", "显示GIF动图", "显示WebP动图", "显示HEIF图片"};
    class ImageTypeListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            if (arg2 == 0) {
                tv_info.setText("");
                iv_pic.setImageResource(R.drawable.happy);
            } else if (arg2 == 1) {
                tv_info.setText("");
                iv_pic.setImageResource(R.drawable.world_cup_2014);
            } else if (arg2 == 2) {
                showImage(R.drawable.happy); // 显示gif和webp图片
            } else if (arg2 == 3) {
                showImage(R.drawable.world_cup_2014); // 显示gif和webp图片
            } else if (arg2 == 4) {
                showHeic(R.raw.lotus); // 显示Heif图片(扩展名为heif或者heic)
            }
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    // 显示Heif图片(扩展名为heif或者heic)
    private void showHeic(int imageId) {
        try (InputStream is = getResources().openRawResource(imageId)) { // 从资源文件中获取输入流对象
            byte[] bytes = new byte[is.available()]; // 创建临时存放的字节数组
            is.read(bytes); // 从输入流中读取字节数组
            // 利用Android 9.0新增的ImageDecoder读取图片
            ImageDecoder.Source source = ImageDecoder.createSource(ByteBuffer.wrap(bytes));
            showImageSource(source); // 显示指定来源的图像
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 显示gif和webp图片
    private void showImage(int imageId) {
        try {
            // 利用Android 9.0新增的ImageDecoder读取图片
            ImageDecoder.Source source = ImageDecoder.createSource(getResources(), imageId);
            showImageSource(source); // 显示指定来源的图像
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 显示指定来源的图像
    private void showImageSource(ImageDecoder.Source source) throws IOException {
        // 从数据源解码得到图形信息
        Drawable drawable = ImageDecoder.decodeDrawable(source, new OnHeaderDecodedListener() {
            @Override
            public void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, ImageDecoder.Source source) {
                // 获取图像信息的媒体类型与是否动图
                String desc = String.format("该图片类型为%s,它%s动图",
                        info.getMimeType(), info.isAnimated()?"是":"不是");
                tv_info.setText(desc);
            }
        });
        iv_pic.setImageDrawable(drawable); // 设置图像视图的图形对象
        if (drawable instanceof Animatable) { // 如果是动画图形,则开始播放动画
            ((Animatable) iv_pic.getDrawable()).start();
        }
    }
}

解码器类

package com.example.chapter13;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.TargetApi;
import android.content.Intent;
import android.graphics.ImageDecoder;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
@TargetApi(Build.VERSION_CODES.P)
public class ImageDecoderActivity extends AppCompatActivity implements View.OnClickListener {
    private final static String TAG = "ImageDecoderActivity";
    private ImageView iv_photo; // 声明一个图像视图对象
    private int CHOOSE_CODE = 3; // 选择照片的请求码
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_decoder);
        iv_photo = findViewById(R.id.iv_photo);
        findViewById(R.id.btn_choose).setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_choose) {
            // 创建一个内容获取动作的意图(准备跳到系统相册)
            Intent albumIntent = new Intent(Intent.ACTION_GET_CONTENT);
            albumIntent.setType("image/*"); // 设置内容类型为图像
            startActivityForResult(albumIntent, CHOOSE_CODE); // 打开系统相册
        }
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (resultCode == RESULT_OK && requestCode == CHOOSE_CODE) {
            if (intent.getData() != null) { // 从相册选择一张照片
                Uri imageUri = intent.getData();
                showDecodedImage(imageUri); // 显示解码后的图像
            }
        }
    }
    // 显示解码后的图像
    private void showDecodedImage(Uri imageUri) {
        try {
            // 利用Android 9.0新增的ImageDecoder读取图片
            ImageDecoder.Source source = ImageDecoder.createSource(getContentResolver(), imageUri);
            // 从数据源解码得到图形信息
            Drawable drawable = ImageDecoder.decodeDrawable(source);
            iv_photo.setImageDrawable(drawable); // 设置图像视图的图形对象
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal"
        android:layout_marginLeft="5dp" >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="图像类型:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_type"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <TextView
        android:id="@+id/tv_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:textColor="#000000"
        android:textSize="17sp"/>
    <ImageView
        android:id="@+id/iv_pic"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:scaleType="fitCenter" />
</LinearLayout>

创作不易 觉得有帮助请 点赞关注收藏~~~

相关文章
|
8天前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
21天前
|
Android开发 开发者 Kotlin
探索安卓开发中的新特性
【9月更文挑战第14天】本文将引导你深入理解安卓开发领域的一些最新特性,并为你提供实用的代码示例。无论你是初学者还是经验丰富的开发者,这篇文章都会给你带来新的启示和灵感。让我们一起探索吧!
|
6天前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
|
5天前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
22 7
|
8天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台解决方案
【9月更文挑战第27天】在移动应用开发的广阔天地中,安卓和iOS两大操作系统如同双子星座般耀眼。开发者们在这两大平台上追逐着创新的梦想,却也面临着选择的难题。如何在保持高效的同时,实现跨平台的开发?本文将带你探索跨平台开发的魅力所在,揭示其背后的技术原理,并通过实际案例展示其应用场景。无论你是安卓的忠实拥趸,还是iOS的狂热粉丝,这篇文章都将为你打开一扇通往跨平台开发新世界的大门。
|
5天前
|
缓存 Java Linux
探索安卓开发:从新手到专家的旅程
【9月更文挑战第30天】在这篇文章中,我们将一起踏上一段激动人心的旅程,探索安卓开发的广阔世界。无论你是刚入门的新手,还是希望提升技能的开发者,本文都将为你提供宝贵的知识和指导。我们将深入探讨安卓开发的基础知识、关键概念、实用工具和最佳实践,帮助你在安卓开发领域取得更大的成功。让我们一起开启这段精彩的旅程吧!
|
6天前
|
监控 安全 Java
Kotlin 在公司上网监控中的安卓开发应用
在数字化办公环境中,公司对员工上网行为的监控日益重要。Kotlin 作为一种基于 JVM 的编程语言,具备简洁、安全、高效的特性,已成为安卓开发的首选语言之一。通过网络请求拦截,Kotlin 可实现网址监控、访问时间记录等功能,满足公司上网监控需求。其简洁性有助于快速构建强大的监控应用,并便于后续维护与扩展。因此,Kotlin 在安卓上网监控应用开发中展现出广阔前景。
9 1
|
9天前
|
XML 移动开发 前端开发
使用duxapp开发 React Native App 事半功倍
对于Taro的壳子,或者原生React Native,都会存在 `android` `ios`这两个文件夹,而在duxapp中,这些文件夹的内容是自动生成的,那么对于需要在这些文件夹中修改的配置内容,例如包名、版本号、新架构开关等,都通过配置文件的方式配置了,而不需要需修改具体的文件
|
16天前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
40 10
|
9天前
|
存储 开发工具 Android开发
使用.NET MAUI开发第一个安卓APP
【9月更文挑战第24天】使用.NET MAUI开发首个安卓APP需完成以下步骤:首先,安装Visual Studio 2022并勾选“.NET Multi-platform App UI development”工作负载;接着,安装Android SDK。然后,创建新项目时选择“.NET Multi-platform App (MAUI)”模板,并仅针对Android平台进行配置。了解项目结构,包括`.csproj`配置文件、`Properties`配置文件夹、平台特定代码及共享代码等。
下一篇
无影云桌面