第12章 图片处理
本系列学习笔记第12章
前言
打算把android基本知识点写一个系列,旨在把android基础书,例如《Android 第一行代码 第2版》、《爱上android》、《疯狂android讲义》等书的一些知识点记录一下,会持续更新内容,为了方便自己复习,也希望可以帮助到大家!
1、Bitmap和Drawable
1.1 基本介绍
android中常用Bitmap和Drawable显示图片。
Bitmap,称为位图,位图文件格式后缀为bmp,编码器也有很多,例如RGB565、RGB888等,是一种以像素为基本单位作为显示对象,执行率高,但是存储效率低,可以简单理解为存储对象比较好。
Drawable,作为android下通用的图形对象,它可以装载常用格式的图像,比如GIF、PNG、JPG,当然也支持BMP,还提供一些高级的可视化对象,比如渐变、图形等。
image.png
1.2 从资源目录获取BItmap
public Bitmap getBitmapFromResourse(int resId){
Resources resources = getResources();
return BitmapFactory.decodeResource(resources,resId);
}
1.3 从资源目录获取Drawable
public Drawable getDrawableFromResourse(int resId){
Resources resources = getResources();
Drawable drawable = resources.getDrawable(resId);
return drawable;
}
1.4 Bitmap --> Drawable
public Drawable bitmapToDrawable(Bitmap bitmap){
Resources resources = getResources();
BitmapDrawable drawable = new BitmapDrawable(resources, bitmap);
return drawable;
}
1.5 Drawable--> Bitmap
public Bitmap drawableToBitmap(Drawable drawable){
Resources resources = getResources();
//创建bitmap
Bitmap bitmap = Bitmap.createBitmap(
//drawable本身的宽高
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
//根据drawable是否是透明来采取不同的设置
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565
);
//创建画布
Canvas canvas = new Canvas(bitmap);
//设置
drawable.setBounds(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());
//把drawable绘制到画布上
drawable.draw(canvas);
return bitmap;
}
1.6 Bitmap的详细讲解
Bitmap压缩方式有:
1)ARGB_8888
2)ARGB_4444(已经放弃)
3)RGB——565
4)ALPHA_8
ARGB的含义:
1)A:Alpha的缩写,代表透明度
2)R:Red的缩写,代表红色
3)G:Green的缩写,代表绿色
4)B:Blue的缩写,代表蓝色
各种压缩方式的区别:
1)Bitmap.Config.ARGB_4444:每个颜色用4位描述,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位,一个字节等于8位,所以每个像素点占了2个byte内存
2)Bitmap.Config.ARGB_8888:每个颜色用4位描述,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位,一个字节等于8位,所以每个像素点占了4个byte内存
3)Bitmap.Config.RGB_565:每个颜色用4位描述,即R=5,G=6,B=5,那么一个像素点占5+6+5=16位,一个字节等于8位,所以每个像素点占了2个byte内存
4)Bitmap.Config.ALPHA_8:只有透明度,没有颜色,透明度占8位,一个字节等于8位,所以每个像素点占了1个byte内存
android默认的颜色模式是ARGB_8888,这个颜色模式色彩最细腻,显示质量最高,但同样,占用的内存也最大。
2、大图的加载
当加载大图片时,加载图片需要的内存空间不是按图片的大小来算的,而是按像素点的多少来算的,图片加载到内存中需要把每一个像素都加载到内存中,对内存的要求非常高。
image.png
2.1 实现图片缩放加载
image.pngimage.png
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.photohandledemo.MainActivity">
<ImageView
android:id="@+id/iv_photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
public class MainActivity extends AppCompatActivity {
private ImageView ivPhoto;
private String name = "meizi.jpg";
private Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
loadImage();
}
private void loadImage() {
RxPermissions.getInstance(this).request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean aBoolean) {
if (aBoolean){
scaleLoadImage();
}
}
});
}
private void scaleLoadImage(){
File file = new File(Environment.getExternalStorageDirectory(),name);
if(!file.exists()){
return;
}
BitmapFactory.Options options = new BitmapFactory.Options();
//设置为true,代表不加载图片,只是获取图片的宽高
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(),options);
int outWidth = options.outWidth;
int outHeight = options.outHeight;
//得到屏幕的宽高
Display defaultDisplay = getWindowManager().getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
defaultDisplay.getMetrics(displayMetrics);
int screenWidth = defaultDisplay.getWidth();
int screenHeight = defaultDisplay.getHeight();
//计算缩放比例
int widthScale = screenWidth / outWidth;
int heightScale = screenHeight / outHeight;
int scale = widthScale > heightScale ? widthScale :heightScale;
//设置为false就代表可以加载图片
options.inJustDecodeBounds = false;
options.inSampleSize = scale;
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
ivPhoto.setImageBitmap(bitmap);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
}
}
private void initView() {
ivPhoto = (ImageView) findViewById(R.id.iv_photo);
}
}
image.png
3、图片加水印
图片加水印的原理就是给图片画些东西,主要用到了android提供的俩个类:Canvas 和 Paint。
Canvas 画画板,用于绘制各种图形(点、线、圆、矩形等)
image.png
Paint画笔,和Canvas搭配使用,用于指定绘制的颜色,线条的粗细、过渡、渐变等效果。
image.png image.png
public class DrawMarkActivity extends AppCompatActivity {
private ImageView ivPhoto;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_draw_mark);
initView();
drawMark();
}
private void drawMark() {
//获取原图
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.meizi);
//获取水印图
Bitmap logoBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
//创建新Bitmap,在这上面合成
Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(newBitmap);
canvas.drawBitmap(bitmap,0,0,null);
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
canvas.drawBitmap(logoBitmap,0,0,paint);
ivPhoto.setImageBitmap(newBitmap);
}
private void initView() {
ivPhoto = (ImageView) findViewById(R.id.iv_photo);
}
}
image.png
private void drawMark() {
//获取原图
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.meizi);
//获取水印图
Bitmap logoBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
//创建新Bitmap,在这上面合成
Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(newBitmap);
canvas.drawBitmap(bitmap,0,0,null);
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
// canvas.drawBitmap(logoBitmap,0,0,paint);
paint.setColor(Color.RED);
paint.setTextSize(50);
canvas.drawText("追梦小乐",0,100,paint);
ivPhoto.setImageBitmap(newBitmap);
}
image.png
4、图片特效:Matrix
图片的特效包括,图形的缩放、倒影、镜面、旋转、位移等。图片的特效是将原图的图形矩阵乘以一个特效矩阵,形成一个新的图形矩阵来实现的。
image.png
image.png
4.1 缩放
image.png private void scale(){
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth() * 2, bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Matrix matrix = new Matrix();
//通过矩阵的方式
float[] value = new float[]{2,0,0,0,1,0,0,0,1};
matrix.setValues(value);
//不通过矩阵的方式
// matrix.setScale(2,1,0.5f,0.5f);
canvas.drawBitmap(bitmap,matrix,null);
ivPhoto.setImageBitmap(newBitmap);
}
image.png
4.2 倒影
image.png private void reflect(){
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth() * 2, bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Matrix matrix = new Matrix();
float[] value = new float[]{1,0,0,0,-1,0,0,0,1};
matrix.setValues(value);
//超出屏幕了,反方向设置距离让其显示
matrix.postTranslate(0,bitmap.getHeight());
canvas.drawBitmap(bitmap,matrix,null);
ivPhoto.setImageBitmap(newBitmap);
}
image.png
4.3 镜面
image.png private void mirror(){
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.meizi);
Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth() * 2, bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Matrix matrix = new Matrix();
float[] value = new float[]{-1,0,0,0,1,0,0,0,1};
matrix.setValues(value);
//超出屏幕了,反方向设置距离让其显示
matrix.postTranslate(bitmap.getWidth(),0);
canvas.drawBitmap(bitmap,matrix,null);
ivPhoto.setImageBitmap(newBitmap);
}
image.png
4.4 旋转
image.png private void rotate(){
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.meizi);
Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth() * 2, bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Matrix matrix = new Matrix();
matrix.setRotate(30,bitmap.getWidth(),bitmap.getHeight());
canvas.drawBitmap(bitmap,matrix,null);
ivPhoto.setImageBitmap(newBitmap);
}
image.png
4.5 位移
image.png private void translate(){
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.meizi);
Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth() * 2, bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Matrix matrix = new Matrix();
matrix.setTranslate(300,-100);
canvas.drawBitmap(bitmap,matrix,null);
ivPhoto.setImageBitmap(newBitmap);
}
image.png
5、图片颜色处理 ----- 打造自己的美图秀秀
5.1 颜色过滤器ColorMatrixColorFilter
image.pngimage.png
5.2 实现图片美化功能
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ImageView
android:src="@mipmap/meizi"
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="R:"
/>
<SeekBar
android:id="@+id/sb_red"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="255" />
</LinearLayout>
<LinearLayout
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="G:"
/>
<SeekBar
android:id="@+id/sb_green"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="255" />
</LinearLayout>
<LinearLayout
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="B:"
/>
<SeekBar
android:id="@+id/sb_blue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="255" />
</LinearLayout>
<LinearLayout
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RGB:"
/>
<SeekBar
android:id="@+id/sb_rgb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="255" />
</LinearLayout>
</LinearLayout>
public class MTXXActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener{
private static final String TAG = MTXXActivity.class.getSimpleName();
private ImageView iv;
private SeekBar sbRed;
private SeekBar sbGreen;
private SeekBar sbBlue;
private SeekBar sbRgb;
private float[] arrays = new float[]{
1,0,0,0,0,
0,1,0,0,0,
0,0,1,0,0,
0,0,0,1,0
};
//声明颜色过滤器
private ColorFilter colorFilter = new ColorMatrixColorFilter(arrays);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mtxx);
initView();
initListener();
}
private void initListener() {
sbRed.setOnSeekBarChangeListener(this);
sbGreen.setOnSeekBarChangeListener(this);
sbBlue.setOnSeekBarChangeListener(this);
sbRgb.setOnSeekBarChangeListener(this);
}
private void initView() {
iv = (ImageView) findViewById(R.id.iv);
sbRed = (SeekBar) findViewById(R.id.sb_red);
sbGreen = (SeekBar) findViewById(R.id.sb_green);
sbBlue = (SeekBar) findViewById(R.id.sb_blue);
sbRgb = (SeekBar) findViewById(R.id.sb_rgb);
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
int id = seekBar.getId();
switch (id){
case R.id.sb_red:
arrays[4] = progress;
break;
case R.id.sb_green:
arrays[9] = progress;
break;
case R.id.sb_blue:
arrays[14] = progress;
break;
case R.id.sb_rgb:
arrays[4] = arrays[4] = arrays[4] = progress;
break;
default:
break;
}
colorFilter = new ColorMatrixColorFilter(arrays);
iv.setColorFilter(colorFilter);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
image.png
6、案例 ----- 随手涂鸦
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.baidu.chapter7.section6.HandWritingActivity">
<ImageView
android:id="@+id/iv"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:onClick="save"
android:text="保存"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:onClick="clear"
android:text="清除"/>
</LinearLayout>
</LinearLayout>
public class HandWritingActivity extends AppCompatActivity implements View.OnTouchListener {
private ImageView iv;
private Bitmap bitmap;
private int startX;
private int startY;
private Canvas canvas;
private Paint paint;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hand_writing);
initView();
initListener();
}
private void initListener() {
iv.setOnTouchListener(this);
}
private void initView() {
iv = (ImageView) findViewById(R.id.iv);
}
public void save(View view){
if (bitmap == null){
Toast.makeText(this, "没有图片可以保存", Toast.LENGTH_SHORT).show();
return;
}
File file = new File(getCacheDir(),"pic"+System.currentTimeMillis()+".jpg");
FileOutputStream stream = null;
try {
stream = new FileOutputStream(file);
boolean compress = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
if (compress){
Toast.makeText(this, "保存成功"+file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(this, "保存失败"+e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}finally {
if(stream != null){
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void clear(View view){
if (bitmap != null){
bitmap = null;
}
iv.setImageBitmap(null);
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
if (bitmap == null){
bitmap = Bitmap.createBitmap(iv.getWidth(),iv.getHeight(),Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
}
startX = (int) motionEvent.getX();
startY = (int) motionEvent.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) motionEvent.getX();
int moveY = (int) motionEvent.getY();
canvas.drawLine(startX,startY,moveX,moveY,paint);
iv.setImageBitmap(bitmap);
startX = moveX;
startY = moveY;
break;
default:
break;
}
return true;
}
}
image.png
7、加载网络图片
7.1 网络图片的缓存策略
爱上Android截图爱上Android截图
爱上Android截图
爱上Android截图
7.2 图片加载库Picasso的使用
implementation 'com.squareup.picasso:picasso:2.71828'
优点:
1)使用复杂的图片压缩转换算法来尽可能减少内存消耗
2)自带内存和硬盘缓存二级缓存
基本用法:
1)常用用法
Picasso.get().load(url).into(iv);
2)对图片进行处理
Picasso.get()
.load(url)
//调整图片大小,节省内存
.resize(50,50)
//裁剪模式
.centerCrop()
.into(iv);
3)加载占位符及错误图片
Picasso.get()
.load(url)
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.into(iv);
4)加载res、assert、本地图片资源
Picasso.get().load(R.drawable.landing_screen).into(imageView1);
Picasso.get().load("file:///android_asset/DvpvklR.png").into(imageView2);
Picasso.get().load(new File(...)).into(imageView3);