android学习笔记----关于图形的基本处理讲解

版权声明:转载请注明 https://blog.csdn.net/qq_34115899/article/details/82347880

 

目录

表示图形的几种方式:

创建原图的副本:

对图形的处理(旋转、缩放、平移、镜面、倒影):

实现简易画板:

撕衣服小案例:

关于dp、dip、dpi、px、sp:

Bitmap及缓存相关阅读:


简易画板代码:https://github.com/liuchenyang0515/SimpleDrawingBoard

撕衣服代码:https://github.com/liuchenyang0515/ScrapeBeauty

表示图形的几种方式:

假设给定一张800*400像素的图片,即32万像素的图片,保存为bmp格式,分别按照单色,16色,256色,24位来保存

用单色保存:32W*1/8=40000byte,因为有一些额外信息,比如保存时间等,所以图片比40000byte要多一点

用16色保存:32W*1/2=160000byte,因为有一些额外信息,比如保存时间等,所以图片比160000byte要多一点

用256色保存:32W*1=32Wbyte,同理,比32Wbyte要多一点

用24位保存,24个2进制数是2的24次方为16777216,每个像素可以表示16777216种颜色,这种表示方法为RGB三种像素,每个字节表示3个像素,32W像素*3=96Wbyte,同理,比96Wbyte多一点

bmp文件比jpg的大,jpg把bmp格式图片进行压缩,相邻位图差不多的就合并了,而png也是将bmp格式的图片压缩,压缩算法和jpg不一样,并且更高级。

android采用的图片是png,采用的色彩模式是ARGB,其中A( Alpha )是透明度,RGB是红绿蓝。android的图片一个像素占4个byte。

 

缩放显示大图片原理:

一般是用在图片比屏幕大的情况,合理的加载出图片

相关阅读:

一张图片占用多少内存:https://www.cnblogs.com/popfisher/p/6959106.html

让图片占用尽可能少的内存:http://www.cnblogs.com/popfisher/p/6770018.html

android屏幕和尺寸单位:http://xiaoyaozjl.iteye.com/blog/2178415

缩放步骤:

1.获取图片分辨率,比如2400*3200(水平为宽,竖直为高)

2.获取手机分辨率,比如320*480

3.计算缩放比(图片的宽除以手机分辨率的宽,图片的高除以手机分辨率的高)

宽  7(整型除法)

高  6

4.按照大的比例去缩放

MainActivity.java

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private ImageView imageView;
    private int width;
    private int height;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.iv);
        // 获取手机的分辨率windowManager
        //WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        // 过时的方法,没有包含虚拟按键高度,1080*1794
        /*int height = windowManager.getDefaultDisplay().getHeight(); // 过时方法获取高
        int width = windowManager.getDefaultDisplay().getWidth(); // 过时方法获取宽*/
        // 这种方法并没有包含底部虚拟按钮的高度,所以测试的高度会小于实际高度,1080*1794
        /*Point point = new Point();
        windowManager.getDefaultDisplay().getSize(point);
        Log.d(TAG, "width:" + point.x + "----height:" + point.y);*/
        Point point = new Point();
        // API 17之后使用,获取的像素宽高包含虚拟键所占空间,在API 17之前通过反射获取
        // 获取显示的实际大小,而不减去任何窗口装饰或应用任何兼容性缩放因子。
        getWindowManager().getDefaultDisplay().getRealSize(point);
        width = point.x;
        height = point.y;
        Log.d(TAG, "width:" + width + "----height:" + height); // 准确1080*1920
    }

    // 点击按钮加载一张大图片
    public void click(View view) {
        // 避免申请读权限,暂且放在这里的目录,而不用/mnt/sdcard/...
        // 如果heapSize太小就会OOM,本模拟器是256M
        // 本图片是500*364*4/1024/1024=0.69M,不会溢出
        // Bitmap bitmap = BitmapFactory.decodeFile("/data/data/com.example.loadlargeimages/cache/mingren.jpg");
        // 创建一个位图工厂配置参数
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 解码器不去真正解析位图,但是还能够获取图片的宽高信息
        // api如下:
        /*如果设置为true,解码器将返回null(无位图),但仍将设置out ...字段,允许调用者查询位图而无需为其像素分配内存。*/
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile("/data/data/com.example.loadlargeimages/cache/datu.jpg",
                options);
        //Log.d(TAG, "bitmap:" + bitmap);// 是null,所以不需要保存返回值
        int imgWidth = options.outWidth;
        int imgHeight = options.outHeight;
        Log.d(TAG, "图片的宽高:width:" + imgWidth + "----height:" + imgHeight);
        

        // 计算缩放比
        int scale = 1; // 默认为1
        int scaleX = imgWidth / width;
        int scaleY = imgHeight / height;
        if (scaleX > scaleY && scaleX > 1) { // 用缩放比大的
            scale = scaleX;
        } else if (scaleY > scaleX && scaleY > 1) {
            scale = scaleY;
        }
        Log.d(TAG, "缩放比==:" + scale);
        //  按照缩放比显示
        options.inSampleSize = scale;
        // 按缩放比解析位图
        options.inJustDecodeBounds = false; // 真正的解析位图
        Bitmap bitmap = BitmapFactory.decodeFile("/data/data/com.example.loadlargeimages/cache/datu.jpg",
                options);
        // 把bitmap显示到imageview
        imageView.setImageBitmap(bitmap);
    }
}

既然已经设置了预加载,而不是直接加载图片。一定记住解码图片时的第二个参数BitmapFactory.Options,控制下采样和图像是否应该被完全解码的选项,或者只是返回大小。

一张图片3200*2000尺寸的图片放在1080*1920尺寸的手机上

运行结果:

缩放比向下取整,如果向上取整可能会出现图片本来能占满屏幕,却没有占满屏幕的情况,边缘留白。

这里宽度比为2,高度比为1,所以是缩放比是2.

当旋转之后,重新点击按钮显示

这里宽度比是1,高度比是1,所以缩放比是1 

 

public static class BitmapFactory.Options extends Object 

 public boolean inJustDecodeBounds

        如果设置为true,解码器将返回null(无位图),但仍将设置outWidth、outHeight字段,允许调用者查询位图而无需为其像素分配内存。

   public int inSampleSize

        如果设置为> 1的值,请求解码器对原始图像进行二次采样,返回较小的图像以节省内存。样本大小是任一维度中与解码位图中的单个像素相对应的像素数。例如,inSampleSize == 4返回的图像是原始宽度/高度的1/4,像素数量的1/16。任何值<= 1都被视为1.注意:解码器使用基于2的幂的最终值,任何其他值将向下舍入到最接近的2的幂。


创建原图的副本:

MainActivity.java

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.TypedValue;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private ImageView iv_src;
    private ImageView iv_copy;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_src = (ImageView) findViewById(R.id.iv_src);
        iv_copy = (ImageView) findViewById(R.id.iv_copy);
        // 把图片转成bitmap到imageview
        // res目录下都是资源
        Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mingren);
        // 操作图片,修改某一点坐标颜色
        // srcBitmap.setPixel(20, 30, Color.RED); // 报错java.lang.IllegalStateException
        iv_src.setImageBitmap(srcBitmap);
        // 创建原图的副本
        // 首先创建一个模版,相当于创建了一个和原图一样大小的空白纸
        Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
        // 想作画需要一个画笔
        Paint paint = new Paint();
        // 创建一个画布,把白纸铺到画布上
        Canvas canvas = new Canvas(copyBitmap);
        // 开始作画,参考原图画矩阵
        canvas.drawBitmap(srcBitmap, new Matrix(), paint);

        // 操作画出来的图片
        for (int i = 0; i < 200; ++i) {
            copyBitmap.setPixel(20 + i, 30, Color.RED); // 一次修改一个像素看不出来,用循环
        }

        // 把copybitmap显示到iv_copy
        iv_copy.setImageBitmap(copyBitmap);
    }
}

批注:

drawBitmap重载方法较多,具体见官方api

public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint)

使用指定的矩阵绘制位图。

参数
bitmap Bitmap:要绘制的位图

这个值绝不能是null.

matrix Matrix:用于在绘制位图时转换位图的矩阵。

这个值绝不能是null.

paint Paint:可能为空。用于绘制位图的油漆

activity_main.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">

    <ImageView
        android:id="@+id/iv_src"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <ImageView
        android:id="@+id/iv_copy"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp" />
</LinearLayout>

运行结果

可以看到副本的图左上方多了一条线

对图形的处理(旋转、缩放、平移、镜面、倒影):

MainActivity.java

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private ImageView iv_src;
    private ImageView iv_copy;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_src = (ImageView) findViewById(R.id.iv_src);
        iv_copy = (ImageView) findViewById(R.id.iv_copy);
        // 把图片转成bitmap到imageview
        // res目录下都是资源
        Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mingren);
        // 操作图片,修改某一点坐标颜色
        // srcBitmap.setPixel(20, 30, Color.RED); // 报错java.lang.IllegalStateException
        iv_src.setImageBitmap(srcBitmap);
        // 创建原图的副本
        // 首先创建一个模版,相当于创建了一个和原图一样大小的空白纸
        Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
        // 想作画需要一个画笔
        Paint paint = new Paint();
        // 创建一个画布,把白纸铺到画布上
        Canvas canvas = new Canvas(copyBitmap);

        // 开始作画,参考原图画矩阵
        Matrix matrix = new Matrix();

        // 对图片进行旋转
        // matrix.setRotate(30, srcBitmap.getWidth() >> 1, srcBitmap.getHeight() >> 1);

        // 对图片进行缩放
        // matrix.setScale(0.5f, 0.5f);
        // matrix.setScale(0.5f, 0.5f,100f, 100f); // 此处缩小,如果是放大,记得将画布设置变大

        // 对图片进行平移
        // matrix.setTranslate(200, 0);

        // 对图片进行镜面,就是使用缩放和平移的组合
        // 不能setScale和setTranslate组合,应该setScale和postTrasetnslate组合
        /*matrix.setScale(-1.0f, 1.0f);
        // post是在上一次修改的基础上再次修改,set是每次操作都重新初始化再进行,多个setxxx方法会以最后为准
        matrix.postTranslate(srcBitmap.getWidth(), 0);*/

        // 对图片进行倒影效果
        matrix.setScale(1.0f, -1.0f);
        matrix.postTranslate(0, srcBitmap.getHeight());


        canvas.drawBitmap(srcBitmap, matrix, paint);

        // 操作画出来的图片
        for (int i = 0; i < 200; ++i) {
            // 这一条斜线不会因为图片旋转而改变
            copyBitmap.setPixel(20 + i, 30 + i, Color.RED); // 一次修改一个像素看不出来,用循环
        }

        // 把copybitmap显示到iv_copy
        iv_copy.setImageBitmap(copyBitmap);
    }
}

批注:

        因为里面修改像素点的有划线语句,这条线不会因为图片的旋转而改变。

setScale(float sx,float sy):设置Matrix进行缩放,sx,sy控制X,Y方向上的缩放比例;

setScale(float sx,float sy,float px,float py):设置Matrix以px,py为轴心进行缩放(此处有坑),默认以画布左上角的点(0,0)缩放,sx,sy控制X,Y方向上的缩放比例;

post是在上一次修改的基础上再次修改,set是每次操作都重新初始化再进行,多个setxxx方法会以最后为准,比如先缩放setScale(0.5f,0.5f);接着平移setTranslation(200, 0);那么就只会看到平移效果,缩放效果被覆盖。

activity_main.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">

    <ImageView
        android:id="@+id/iv_src"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <ImageView
        android:id="@+id/iv_copy"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp" />
</LinearLayout>

旋转效果图:

缩放效果图:

 

平移效果图:

镜面效果图:

倒影效果图:

实现简易画板:

MainActivity.java

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private ImageView imageView;
    private Bitmap copyBitmap;
    private Paint paint;
    private Canvas canvas;
    private long lastMillis;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 用来显示我们画的内容
        imageView = (ImageView) findViewById(R.id.iv);
        // 获取原图
        Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.drawablebg);
        // 获取原图的副本,相当于空白纸
        copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
        // 创建画笔
        paint = new Paint();
        // 创建画布
        canvas = new Canvas(copyBitmap);
        // 开始作画
        canvas.drawBitmap(srcBitmap, new Matrix(), paint); // 此时copyBitmap就有内容了
        //canvas.drawLine(20f, 30f, 50f, 80f, paint);
        imageView.setImageBitmap(copyBitmap);
        imageView.setOnTouchListener(new View.OnTouchListener() {
            float startX = 0f;
            float startY = 0f;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 获取当前时间的类型
                int action = event.getAction();
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        Log.d(TAG, "触摸: ");
                        // 获取开始位置(划线)
                        startX = event.getX();
                        startY = event.getY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        Log.d(TAG, "移动: ");

                        // 获取结束位置
                        float stopX = event.getX();
                        float stopY = event.getY();
                        // 不停的划线
                        canvas.drawLine(startX, startY, stopX, stopY, paint);
                        // 再次显示到iv
                        imageView.setImageBitmap(copyBitmap);
                        // 获取开始位置(划线)
                        startX = event.getX();
                        startY = event.getY();
                        break;
                    case MotionEvent.ACTION_UP:
                        Log.d(TAG, "抬起: ");
                        break;
                }
                return true; // 如果为false,只能执行第一个处理的事件,认为还未处理完
            }
        });
    }

    public void click(View view) {
        switch (view.getId()) {
            case R.id.btn1:
                paint.setColor(Color.RED);
                break;
            case R.id.btn2:
                paint.setStrokeWidth(15);
                break;
            case R.id.btn3:
                applyPermissions();
                save();
                break;
        }
    }

    private void save() {
        // 保存图片是耗时操作
        new Thread() {
            @Override
            public void run() {
                /*
                 * 第一个参数format:保存图片的格式
                 * 第二个参数quality:保存图片的质量
                 * 第三个参数是输出流
                 * 其中命名用了SystemClock.uptimeMillis()是当前手机已开机的时间
                 * */
                // 简单防抖实现
                long millis = SystemClock.uptimeMillis();
                if (millis - lastMillis < 200) { // 200ms,防止快速点击,可以根须需要设置
                    return;
                }
                String fileName = millis + "test.png";
                File file = new File(Environment.getExternalStorageDirectory().getPath(), fileName);
                lastMillis = millis;
                try {
                    FileOutputStream fos = new FileOutputStream(file);
                    copyBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                // action为Intent.ACTION_MEDIA_SCANNER_SCAN_FILE扫描指定文件
                Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                Uri uri = Uri.fromFile(file);
                intent.setData(uri);
                sendBroadcast(intent);
            }
        }.start();
    }

    private void applyPermissions() {
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
        } else {
            save();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    save();
                } else {
                    Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

activity_main.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">

    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="click"
            android:text="红色" />

        <Button
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="click"
            android:text="加粗" />

        <Button
            android:id="@+id/btn3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="click"
            android:text="保存" />
    </LinearLayout>
</LinearLayout>

运行结果(华为荣耀V9真机8.0.0系统测试):

批注:

画线的功能一定经历了3个动作,第一次点上去会识别为触摸,然后在不断移动,最后放手识别为抬起

Bitmap类里的一个方法

public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)

      将位图的压缩版本写入指定的输出流。如果返回true,则可以通过将相应的输入流传递给BitmapFactory.decodeStream()来重建位图。注意:并非所有Formats都直接支持所有位图配置,因此BitmapFactory返回的位图可能位于不同的bitdepth中,和/或可能丢失了每像素alpha(例如,JPEG仅支持不透明像素)。

此方法可能需要几秒钟才能完成,因此只能从工作线程(辅助线程)调用它。

参数
format Bitmap.CompressFormat: 压缩图像的格式
quality int: 提示压缩器,0-100。 0表示压缩小尺寸,100表示​​压缩以获得最高质量。某些格式,如无损的PNG,将忽略质量设置
stream OutputStream: 输出流写入压缩数据。
Returns
boolean 如果成功压缩到指定的流,则为true。

图像的压缩格式有JPEG、PNG、WEBP

撕衣服小案例:

真机测试(华为荣耀V9,8.0.0系统)运行效果图:

Mainctivity.java

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private ImageView iv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv = (ImageView) findViewById(R.id.iv2);
        //改变图片大小
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inSampleSize = 1;
        // 获取要操作的原图
        Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a);
        // 创建一个副本,相当于和一个原图一样的白纸
        final Bitmap alterBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
        // 创建画笔
        Paint paint = new Paint();
        // 创建画布 把白纸铺到画布
        Canvas canvas = new Canvas(alterBitmap);
        // 开始作画
        canvas.drawBitmap(srcBitmap, new Matrix(), paint);

        iv.setImageBitmap(alterBitmap);
        // 给iv设置一个触摸事件
        iv.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 具体判断一下触摸事件
                switch (event.getAction()) {
                    case MotionEvent.ACTION_MOVE:  // 移动的事件
                        for (int i = -20; i < 20; ++i) {
                            for (int j = -20; j < 20; ++j) {
                                if (Math.sqrt(i * i + j * j) < 20) {
                                    int x = (int) event.getX();
                                    int y = (int) event.getY();
                                    if (x + i < 0 || x + i >= alterBitmap.getWidth()) // 0到width-1的闭区间
                                        continue;
                                    if (y + j < 0 || y + j >= alterBitmap.getHeight())
                                        continue;
                                    Log.d(TAG, "getX: " + x + " getY:" + y);
                                    alterBitmap.setPixel(x + i, y + j, Color.TRANSPARENT); // 设置为透明
                                }
                            }
                        }
                        iv.setImageBitmap(alterBitmap);
                        break;
                    default:
                        break;
                }
                return true;
            }
        });
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/iv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/b" />

    <ImageView
        android:id="@+id/iv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/a" />

</RelativeLayout>

关于dp、dip、dpi、px、sp:

更详细的请参考:

android适配(一) 之dp、dip、dpi、px、sp简介及相关换算:https://blog.csdn.net/qq_23042121/article/details/53118853

Android屏幕适配(一)dp、px、dpi、sp的理解:https://blog.csdn.net/yuanmingxiang/article/details/51384420

Bitmap及缓存相关阅读:

Android 之 Bitmap:https://www.jianshu.com/p/98c88f9ceafa

其中计算采样率的时候可以有如下写法

    // pixelW是期待的宽,pixelH是期待的高,前面两个参数也可以直接options传进来再获取也行
    public static int getSimpleSize(int originalW, int originalH, int pixelW, int pixelH) {
        int simpleSize = 1;
        if (originalW > originalH && originalW > pixelW) {
            simpleSize = originalW / pixelW;
        } else if (originalW < originalH && originalH > pixelH) {
            simpleSize = originalH / pixelH;
        }
        if (simpleSize <= 0) {
            simpleSize = 1;
        }
        return simpleSize;
    }

Android图片缓存之Bitmap详解:http://www.cnblogs.com/whoislcj/p/5547758.html

inScaled用法学习笔记:

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private ImageView imageView;
    private int width;
    private int height;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.iv);
        // 获取手机的分辨率windowManager
        //WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        // 过时的方法,没有包含虚拟按键高度,1080*1794
        /*int height = windowManager.getDefaultDisplay().getHeight(); // 过时方法获取高
        int width = windowManager.getDefaultDisplay().getWidth(); // 过时方法获取宽*/
        // 这种方法并没有包含底部虚拟按钮的高度,所以测试的高度会小于实际高度,1080*1794
        /*Point point = new Point();
        windowManager.getDefaultDisplay().getSize(point);
        Log.d(TAG, "width:" + point.x + "----height:" + point.y);*/
        Point point = new Point();
        // API 17之后使用,获取的像素宽高包含虚拟键所占空间,在API 17之前通过反射获取
        // 获取显示的实际大小,而不减去任何窗口装饰或应用任何兼容性缩放因子。
        getWindowManager().getDefaultDisplay().getRealSize(point);
        width = point.x;
        height = point.y;
        Log.d(TAG, "width:" + width + "----height:" + height); // 准确1080*1920
    }

    // 点击按钮加载一张大图片
    public void click(View view) {
        // 避免申请读权限,暂且放在这里的目录,而不用/mnt/sdcard/...
        // 如果heapSize太小就会OOM,本模拟器是256M
        // 本图片是500*364*4/1024/1024=0.69M,不会溢出
        // Bitmap bitmap = BitmapFactory.decodeFile("/data/data/com.example.loadlargeimages/cache/mingren.jpg");
        // 创建一个位图工厂配置参数
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 解码器不去真正解析位图,但是还能够获取图片的宽高信息
        // api如下:
        /*如果设置为true,解码器将返回null(无位图),但仍将设置out ...字段,允许调用者查询位图而无需为其像素分配内存。*/
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), R.drawable.datu, options);
        //Log.d(TAG, "bitmap:" + bitmap);// 是null,所以不需要保存返回值
        int imgWidth = options.outWidth;
        int imgHeight = options.outHeight;
        Log.d(TAG, "图片的宽高:width:" + imgWidth + "----height:" + imgHeight);

        //  按照缩放比显示
        options.inSampleSize = getSimpleSize(options, 200, 200);
        options.inJustDecodeBounds = false; // 真正的解析位图
        options.inScaled = false; // 保证drawable-任何dpi都是缩放后初始的尺寸,不会因为机型屏幕再次缩放bitmap对象的尺寸
        // 比如inSampleSize=10,那么3200*2000分辨率的图就是bitmap.getWidth()=320. bitmap.getHeight()=200
        // 如果drawable文件夹设置得不对,那么获取的值就不是这个,而是对应比例的缩放
        // 但是还是会因为机型屏幕改变其大小去显示(因为dpi每英寸像素密度不同)
        // 这就是可能出现获得bitmap.getwidth()和getHeight()的尺寸和显示的尺寸不同,因为图片放在drawable不同的目录
        // 按缩放比解析位图
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.datu, options);
        Log.d(TAG, "图片的宽高:width:" + bitmap.getWidth() + "----height:" + bitmap.getHeight());
        // 测试结果
        // 如果width:3200----height:2000,simpleSize:16,屏幕dpi为420,缩放后应该width=200,height=175
        // 在对应的drawable-420dpi文件夹没有对应的图,而在drawable-640dpi有图,那么
        // bitmap.getWidth()和getHeight()获取的值有两种情况,如果inScaled=false,那么就是width=200,height=175
        // 如果没有设置inScaled,默认为true,那么width=131, height=82
        // 但是那么设置了inScaled=false,获取的尺寸是我们想要的,但放在屏幕上却不是这个200*175的尺寸
        // 因为图在drawable-640dpi文件夹下,而屏幕420dpi,会让这个图适应屏幕而进行自动缩放显示。
        // inScale是真正改变bitmap的尺寸,而选择图放在哪个文件夹只是改变屏幕上的显示
        
        // 把bitmap显示到imageview
        imageView.setImageBitmap(bitmap);
    }

    static int getSimpleSize(BitmapFactory.Options options, int pixelW, int pixelH) {
        int simpleSize = 1;
        int originalW = options.outWidth;
        int originalH = options.outHeight;
        Log.d(TAG, "options.outWidth:" + originalW + "   options.outHeight:" + originalH);
        if (originalW > originalH && originalW > pixelW) {
            simpleSize = originalW / pixelW;
        } else if (originalW < originalH && originalH > pixelH) {
            simpleSize = originalH / pixelH;
        }
        if (simpleSize <= 0) {
            simpleSize = 1;
        }
        Log.d(TAG, "simpleSize:" + simpleSize);
        return simpleSize;
    }
}

Bitmap通过getWidth和getHeight获取尺寸不符请见:https://blog.csdn.net/renwudao24/article/details/47680789

===========================Talk is cheap, show me the code========================

猜你喜欢

转载自blog.csdn.net/qq_34115899/article/details/82347880