Android App开发之位图加工Bitmap中转换位图的像素色彩、裁剪内部区域、利用矩阵变换位图的讲解及实战(附源码和演示)

简介: Android App开发之位图加工Bitmap中转换位图的像素色彩、裁剪内部区域、利用矩阵变换位图的讲解及实战(附源码和演示)

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

一、转换位图的像素色彩

给图片添加装饰物,只是在局部变换,如果想让图片一边保持轮廓一边改变色彩,就要深入图像的每个像素点,将这些像素点统统采取某种算法修改一番,在像素级别更改图像的话,要先把图片转换成位图对象再进一步加工位图对象,此时用到了位图工具Bitmap 主要方法如下

1:createBitmap 创建一个新位图

2:getPixels 获取位图对象所有点的像素数组

3:setPixels 设置位图对象所有点的像素数组

效果如下 可以将一张图片以多种色彩效果显示出来

代码如下

Java类

package com.example.picture;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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 com.example.picture.util.BitmapUtil;
public class BitmapPixelActivity extends AppCompatActivity {
    private ImageView iv_picture; // 声明一个图像视图对象
    private Bitmap mOriginBitmap; // 原始位图
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bitmap_pixel);
        iv_picture = findViewById(R.id.iv_picture);
        mOriginBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.butterfly);
        initColorSpinner(); // 初始化色彩模式下拉框
    }
    // 初始化色彩模式下拉框
    private void initColorSpinner() {
        ArrayAdapter<String> colorAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, colorNameArray);
        Spinner sp_color = findViewById(R.id.sp_color);
        sp_color.setPrompt("请选择色彩模式");
        sp_color.setAdapter(colorAdapter);
        sp_color.setOnItemSelectedListener(new ColorSelectedListener());
        sp_color.setSelection(0);
    }
    private String[] colorNameArray = {"原色", "黑白", "底片", "怀旧", "模糊"};
    class ColorSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            if (arg2 == 0) { // 原色
                iv_picture.setImageBitmap(mOriginBitmap); // 设置图像视图的位图对象
            } else if (arg2 == 1) { // 黑白
                Bitmap bitmap = BitmapUtil.convertBlack(mOriginBitmap); // 转换为黑白效果
                iv_picture.setImageBitmap(bitmap); // 设置图像视图的位图对象
            } else if (arg2 == 2) { // 底片
                Bitmap bitmap = BitmapUtil.convertNegative(mOriginBitmap); // 转换为底片效果
                iv_picture.setImageBitmap(bitmap); // 设置图像视图的位图对象
            } else if (arg2 == 3) { // 怀旧
                Bitmap bitmap = BitmapUtil.convertOld(mOriginBitmap); // 转换为怀旧效果
                iv_picture.setImageBitmap(bitmap); // 设置图像视图的位图对象
            } else if (arg2 == 4) { // 模糊
                Bitmap bitmap = BitmapUtil.convertBlur(mOriginBitmap); // 转换为模糊效果
                iv_picture.setImageBitmap(bitmap); // 设置图像视图的位图对象
            }
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
}

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">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:paddingLeft="5dp"
            android:gravity="center"
            android:text="请选择色彩模式"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_color"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <ImageView
        android:id="@+id/iv_picture"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:src="@drawable/butterfly" />
</LinearLayout>

二、裁剪位图内部区域

createBitmap方法不仅可以创建空白位图,甚至能从原位图上截取一部分下来,裁剪出来的新位图来自原始位图,为了清楚的标记它在原位图的位置,可在图像视图上方覆盖新的图层,然后新图层先画一遍半透明的阴影,再画裁剪的位图部分,观察新老图层就可以看出裁剪的部位 效果如下

可以在下拉框中红选择裁剪不同区域并且保留图片

代码如下

Java类

package com.example.picture;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.picture.util.BitmapUtil;
import com.example.picture.util.DateUtil;
import com.example.picture.widget.CropImageView;
public class BitmapCutActivity extends AppCompatActivity {
    private CropImageView civ_over; // 声明一个裁剪视图对象
    private ImageView iv_old; // 声明一个原始图片的图像视图对象
    private ImageView iv_new; // 声明一个最新图片的图像视图对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bitmap_cut);
        civ_over = findViewById(R.id.civ_over);
        iv_old = findViewById(R.id.iv_old);
        iv_new = findViewById(R.id.iv_new);
        findViewById(R.id.btn_save_image).setOnClickListener(v -> {
            civ_over.setVisibility(View.GONE);
            Bitmap bitmap = civ_over.getCropBitmap(); // 获取裁剪视图处理后的位图
            iv_new.setImageBitmap(bitmap); // 设置图像视图的位图对象
            // 生成图片文件的保存路径
            String path = String.format("%s/%s.jpg",
                    getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(),
                    DateUtil.getNowDateTime());
            BitmapUtil.saveImage(path, bitmap); // 把位图保存为图片文件
            BitmapUtil.notifyPhotoAlbum(this, path); // 通知相册来了张新图片
            Toast.makeText(this, "成功保存图片文件:" + path, Toast.LENGTH_SHORT).show();
            initZoneSpinner(); // 初始化裁剪区域下拉框
        });
        iv_old.setDrawingCacheEnabled(true); // 开启位图视图的绘图缓存
        iv_old.setImageResource(R.drawable.butterfly); // 设置图像视图的资源编号
        new Handler(Looper.myLooper()).postDelayed(() -> initZoneSpinner(), 200);
    }
    // 初始化裁剪区域下拉框
    private void initZoneSpinner() {
        ArrayAdapter<String> zoneAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, zoneNameArray);
        Spinner sp_zone = findViewById(R.id.sp_zone);
        sp_zone.setPrompt("请选择裁剪区域");
        sp_zone.setAdapter(zoneAdapter);
        sp_zone.setOnItemSelectedListener(new ZoneSelectedListener());
        sp_zone.setSelection(0);
    }
    private String[] zoneNameArray = {"不裁剪", "中间", "左上角", "右上角", "左下角", "右下角"};
    class ZoneSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            civ_over.setVisibility(arg2==0?View.GONE:View.VISIBLE);
            Bitmap bitmap = iv_old.getDrawingCache(); // 从绘图缓存获取位图对象
            int width = bitmap.getWidth(), height = bitmap.getHeight();
            civ_over.setOrigBitmap(bitmap); // 设置裁剪视图的原始位图
            // 以下依据裁剪区域分别设置裁剪视图的位图边界
            if (arg2 == 1) { // 中间
                civ_over.setBitmapRect(new Rect(width/4, height/4, width/2, height/2));
            } else if (arg2 == 2) { // 左上角
                civ_over.setBitmapRect(new Rect(0, 0, width/2, height/2));
            } else if (arg2 == 3) { // 右上角
                civ_over.setBitmapRect(new Rect(width/2, 0, width/2, height/2));
            } else if (arg2 == 4) { // 左下角
                civ_over.setBitmapRect(new Rect(0, height/2, width/2, height/2));
            } else if (arg2 == 5) { // 右下角
                civ_over.setBitmapRect(new Rect(width/2, height/2, width/2, height/2));
            }
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
}

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">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:paddingLeft="5dp"
            android:gravity="center"
            android:text="请选择裁剪区域"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_zone"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="200dp" >
        <ImageView
            android:id="@+id/iv_old"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <com.example.picture.widget.CropImageView
            android:id="@+id/civ_over"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/transparent"
            android:scaleType="fitXY"
            android:visibility="gone" />
    </FrameLayout>
    <Button
        android:id="@+id/btn_save_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="保存图片"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <ImageView
        android:id="@+id/iv_new"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:scaleType="centerInside" />
</LinearLayout>

三、利用矩阵变换位图

可以利用矩阵工具Matrix对图片完成缩放 旋转 平移等变换操作 常用方法如下

postScale 指定横纵坐标两个方向的缩放比率

postRotate  指定旋转角度

postTranslate 指定横纵坐标两个方向的偏移大小

postSkew 指定横纵坐标两个方向的倾斜比例

效果如下 可在下拉框中选择旋转角度以及缩放比例、是否左右反转图像等等 同样可以保存图片

 

代码如下

Java类

package com.example.picture;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.picture.util.BitmapUtil;
import com.example.picture.util.DateUtil;
import com.example.picture.widget.BitmapView;
public class BitmapChangeActivity extends AppCompatActivity {
    private BitmapView bv_image; // 声明一个位图视图对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bitmap_change);
        CheckBox ck_flip = findViewById(R.id.ck_flip);
        bv_image = findViewById(R.id.bv_image);
        ck_flip.setOnCheckedChangeListener((buttonView, isChecked) -> {
            bv_image.flip(); // 左右翻转图像
        });
        findViewById(R.id.btn_save_image).setOnClickListener(v -> {
            Bitmap bitmap = bv_image.getDrawingCache(); // 从绘图缓存获取位图对象
            // 生成图片文件的保存路径
            String path = String.format("%s/%s.jpg",
                    getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(),
                    DateUtil.getNowDateTime());
            BitmapUtil.saveImage(path, bitmap); // 把位图保存为图片文件
            BitmapUtil.notifyPhotoAlbum(this, path); // 通知相册来了张新图片
            Toast.makeText(this, "成功保存图片文件:" + path, Toast.LENGTH_LONG).show();
        });
        initScaleSpinner(); // 初始化缩放比率下拉框
        initRotateSpinner(); // 初始化旋转角度下拉框
    }
    @Override
    protected void onStart() {
        super.onStart();
        bv_image.setDrawingCacheEnabled(true); // 开启位图视图的绘图缓存
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.butterfly);
        bv_image.setImageBitmap(bitmap); // 设置位图视图的位图对象
    }
    @Override
    protected void onStop() {
        super.onStop();
        bv_image.setDrawingCacheEnabled(false); // 关闭位图视图的绘图缓存
    }
    // 初始化缩放比率下拉框
    private void initScaleSpinner() {
        ArrayAdapter<String> scaleAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, scaleArray);
        Spinner sp_scale = findViewById(R.id.sp_scale);
        sp_scale.setPrompt("请选择缩放比率");
        sp_scale.setAdapter(scaleAdapter);
        sp_scale.setOnItemSelectedListener(new ScaleSelectedListener());
        sp_scale.setSelection(3);
    }
    private String[] scaleArray = {"0.25", "0.5", "0.75", "1.0", "1.5", "2.0", "4.0"};
    class ScaleSelectedListener implements OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            // 设置缩放比例
            bv_image.setScaleRatio(Float.parseFloat(scaleArray[arg2]), true);
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    // 初始化旋转角度下拉框
    private void initRotateSpinner() {
        ArrayAdapter<String> rotateAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, rotateArray);
        Spinner sp_rotate = findViewById(R.id.sp_rotate);
        sp_rotate.setPrompt("请选择旋转角度");
        sp_rotate.setAdapter(rotateAdapter);
        sp_rotate.setOnItemSelectedListener(new RotateSelectedListener());
        sp_rotate.setSelection(0);
    }
    private String[] rotateArray = {"0", "45", "90", "135", "180", "225", "270", "315"};
    class RotateSelectedListener implements OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            // 设置旋转角度
            bv_image.setRotateDegree(Integer.parseInt(rotateArray[arg2]), true);
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
}

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="wrap_content"
        android:paddingLeft="5dp"
        android:orientation="horizontal" >
        <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_scale"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:spinnerMode="dialog" />
        <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_rotate"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <CheckBox
        android:id="@+id/ck_flip"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:text="是否左右翻转图像"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <com.example.picture.widget.BitmapView
        android:id="@+id/bv_image"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="@color/white" />
    <Button
        android:id="@+id/btn_save_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="保存图片"
        android:textColor="@color/black"
        android:textSize="17sp" />
</LinearLayout>

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

相关文章
|
2月前
|
安全 数据库 Android开发
在Android开发中实现两个Intent跳转及数据交换的方法
总结上述内容,在Android开发中,Intent不仅是活动跳转的桥梁,也是两个活动之间进行数据交换的媒介。运用Intent传递数据时需注意数据类型、传输大小限制以及安全性问题的处理,以确保应用的健壯性和安全性。
145 11
|
6月前
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
1213 77
|
3月前
|
安全 Java Android开发
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
170 0
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
|
4月前
|
Android开发 开发者
Android企业级实战-界面篇-3
本文是《Android企业级实战-界面篇》系列的第三篇,主要介绍分割线和条形跳转框的实现方法,二者常用于设置和个人中心界面。文章通过具体代码示例展示了如何实现这两种UI组件,并提供了效果图。实现前需准备`dimens.xml`、`ids.xml`、`colors.xml`等文件,部分资源可参考系列第一、二篇文章。代码中详细说明了布局文件的配置,如分割线的样式定义和条形跳转框的组件组合,帮助开发者快速上手并应用于实际项目中。
|
4月前
|
XML Android开发 数据格式
Android企业级实战-界面篇-2
本文为《Android企业级实战-界面篇》系列第二篇,主要介绍三个UI模块的实现:用户资料模块、关注与粉丝统计模块以及喜欢和收藏功能模块。通过详细的XML代码展示布局设计,包括dimens、ids、colors配置文件的使用,帮助开发者快速构建美观且功能齐全的界面。文章结合实际效果图,便于理解和应用。建议配合第一篇文章内容学习,以获取完整工具类支持。
|
4月前
|
算法 Java Android开发
Android企业级实战-界面篇-1
本文详细介绍了Android企业级开发中界面实现的过程,涵盖效果展示、实现前准备及代码实现。作者通过自身经历分享了Android开发经验,并提供了`dimens.xml`、`ids.xml`、`colors.xml`和`strings.xml`等配置文件内容,帮助开发者快速构建规范化的UI布局。文章以一个具体的用户消息界面为例,展示了如何使用线性布局(LinearLayout)和相对布局(RelativeLayout)实现功能模块排列,并附带注意事项及使用方法,适合初学者和进阶开发者参考学习。
|
7月前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
210 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
7月前
|
Android开发 开发者 Kotlin
Android实战经验之Kotlin中快速实现MVI架构
MVI架构通过单向数据流和不可变状态,提供了一种清晰、可预测的状态管理方式。在Kotlin中实现MVI架构,不仅提高了代码的可维护性和可测试性,还能更好地应对复杂的UI交互和状态管理。通过本文的介绍,希望开发者能够掌握MVI架构的核心思想,并在实际项目中灵活应用。
308 8
|
7月前
|
安全 Android开发 iOS开发
escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
escrcpy 是一款基于 Scrcpy 的开源项目,使用 Electron 构建,提供图形化界面来显示和控制 Android 设备。它支持 USB 和 Wi-Fi 连接,帧率可达 30-120fps,延迟低至 35-70ms,启动迅速且画质清晰。escrcpy 拥有丰富的功能,包括自动化任务、多设备管理、反向网络共享、批量操作等,无需注册账号或广告干扰。适用于游戏直播、办公协作和教育演示等多种场景,是一款轻量级、高性能的 Android 控制工具。
502 1

热门文章

最新文章