Android 图像绘制之 Drawable的各种用法

Drawable的实现类

官网上查看Drawable 的实现类,如ShapeDrawable,BitmapDrawable,ClipDrawable,RoundBitmapDrawable,StateListDrawableRoateDrawable。我们今天就是大致介绍一下比较常用的类。Drawable 是一种媒介,它可以把内容绘制到 Canvas 上。

GradientDrawable

一个创建图形的 Drawable,图形包括直线(line),
椭圆(oval),圆形(oval,当椭圆的宽高相等的时候就是圆形),矩形(rectangle),圆环(ring)。GradientDrawable
它在 XML 文件中对应的根节点为<Shape>

**下面是shape里一些相关属性:**
<shape>  Android:shape=["rectangle" | "oval" | "line" | "ring"]
其中 rectagle 矩形,oval 椭圆,line 水平直线,ring 环形

**<shape> 标签中 子节点的常用属性:**
<gradient> 渐变:
Android:startColor  
起始颜色
 
Android:endColor  
结束颜色             
 
Android:angle  
渐变角度,0从左到右,90表示从下到上,数值为45的整数倍,默认为0;
 
Android:type  
渐变的样式 liner线性渐变 radial环形渐变 sweep

<solid >  内部填充
Android:color  

<stroke >描边
Android:width 
描边的宽度
 
Android:color 
描边的颜色
 
Android:dashWidth
表示'-'横线的宽度
 
Android:dashGap 
表示'-'横线之间的距离

<corners >圆角
Android:radius  
圆角的半径 值越大角越圆
 
Android:topRightRadius  
右上圆角半径
 
Android:bottomLeftRadius 
右下圆角角半径
 
Android:topLeftRadius 
左上圆角半径
 
Android:bottomRightRadius 
左下圆角半径

<padding >边界填充
android:bottom="1.0dip" 
底部填充
 
android:left="1.0dip" 
左边填充
 
android:right="1.0dip" 
右边填充
 
android:top="0.0dip" 
上面填充

<selector >
根据不同的选定状态来定义不同的现实效果
> 分为四大属性:
> android:state_selected 是否选中
> android:state_focused 是否获得焦点
> android:state_pressed 是否按压
> android:state_enabled 是否设置是否响应事件,指所有事件 另: android:state_window_focused
> 默认时的背景图片 引用位置:res/drawable/文件的名称.xml

直线

<?xml version="1.0" encoding="utf-8"?>
<shape    
    xmlns:android="http://schemas.android.com/apk/res/android"    
    android:shape="line">    
    <stroke android:width="5dp" android:color="#ffff0000"/>
</shape>

虚线

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="line"
    >
    <stroke android:width="5dp" android:color="@color/colorAccent"
        android:dashGap="@dimen/cardview_default_radius"
        android:dashWidth="@dimen/cardview_default_radius"
        />
    <!--dashGap 虚线间隔-->
    <!--dashWidth 虚线宽度-->
</shape>

在这里插入图片描述

椭圆或者圆形

<?xml version="1.0" encoding="utf-8"?>
<shape    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <stroke android:width="5dp" android:color="#ff0000"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<!--宽高相等就是圆形了-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <stroke android:width="1dp" android:color="#ffff0000"/><!--描边-->
    <size android:width="100dp" android:height="100dp"/>
    <solid android:color="@color/colorAccent"/><!--填充颜色-->
</shape>

在这里插入图片描述
如果不用 Stroke( 描边,对应 Paint.STROKE ),而用Solid (填充,对应 Paint.FILL),会出现实心圆形。

<?xml version="1.0" encoding="utf-8"?>
<!--宽高相等就是圆形了-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <stroke android:width="1dp" android:color="#ffff0000"/><!--描边-->
    <size android:width="100dp" android:height="100dp"/>
    <solid android:color="@color/colorAccent"/><!--填充颜色-->

    <!--渐变色-->
    <gradient
        android:startColor="@color/colorAccent"
        android:endColor="@color/colorPrimary"/>
</shape>

矩形

<?xml version="1.0" encoding="utf-8"?>
<shape 
    xmlns:android="http://schemas.android.com/apk/res/android"    
    android:shape="rectangle">    
<corners 
    android:radius="8dp"/>    
<stroke 
    android:width="2dp" 
    android:color="@color/colorAccent"/>    
<size        
    android:width="100dp"        
    android:height="100dp"/>
</shape>

圆环

<?xml version="1.0" encoding="utf-8"?>
<shape 
    xmlns:android="http://schemas.android.com/apk/res/android"       
    android:innerRadius="100dp"       
    android:shape="ring"       
    android:thickness="10dp"       
    android:useLevel="false">    
<solid 
    android:color="@color/colorAccent"/>
</shape>
  1. 这里要说下这个 useLevel, 只有在 LevelListDrawable 中才设置 true,这里要设置
    false,否则显示不出来。
  2. 其实 GradientDrawable 或者说是 ShapeDrawable 的绘制原理就是利用 Canvas.drawCircle() ,Canvas.drawRect() 等等方法(Canvas 我将在后面的文章介绍)。

LayerListDrawable

图层列表,就像我们平时把一张照片放在另外一张照片上,最后的照片在最上面。 LayerListDrawable 就是这样设计的。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="oval">
            <solid android:color="@color/colorAccent"/>
        </shape>
    </item>
    <item android:width="64dp" android:height="64dp">
        <bitmap
            android:gravity="center"
            android:src="@android:drawable/ic_input_delete"/>
    </item>
</layer-list>

StateListDrawable

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!--按钮被点击-->
    <item android:state_pressed="true">
        <shape>
            <corners android:radius="10dp"/>
            <solid android:color="@color/colorAccent"/>
        </shape>
    </item>

    <item android:state_pressed="false">
        <shape>
            <corners android:radius="10dp"/>
            <stroke
                android:width="1dp"
                android:color="#685252"/>
        </shape>
    </item>
</selector>

LevelListDrawable

经典案例:WiFi图标

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:maxLevel="0" android:drawable="@drawable/ic_wifi_signal_1" />
    <item android:maxLevel="2" android:drawable="@drawable/ic_wifi_signal_2" />
    <item android:maxLevel="5" android:drawable="@drawable/ic_wifi_signal_3" />
    <item android:maxLevel="9" android:drawable="@drawable/ic_wifi_signal_4" />
</level-list>

maxLevel = 0 就是默认图片,看下默认效果
在这里插入图片描述

    <ImageView
        android:layout_centerInParent="true"
        android:id="@+id/wifi_image"
        android:onClick="onClickWifi"
        android:background="@drawable/level_list_drawable"
        android:layout_width="200dp"
        android:layout_height="200dp"/>
    private static int i = 1;
    public void onClickWifi(View view) {
        ImageView imageView = (ImageView) findViewById(R.id.wifi_image);
        imageView.getBackground().setLevel(i++ % 10);
    }

ColorDrawable

ColorDrawable colorDrawable = new ColorDrawable(Color.RED);
imageView.setBackground(colorDrawable);

RoundedBitmapDrawable

其实这个名字应该叫做 圆角图片,它是设置图片四个角的圆形半径。当图片是矩形的时候,就有四个圆角,再特殊一点,如果图片是正方形,正好圆角的半径为正方形边长的一半,那么它就是一个圆。

 RoundedBitmapDrawable roundedBitmapDrawable2 =
                RoundedBitmapDrawableFactory.create(getResources(),
                        BitmapFactory.decodeResource(getResources(),R.mipmap.image1));
        //roundedBitmapDrawable2.setCornerRadius(150);  //设置圆角为150度
        roundedBitmapDrawable2.setCircular(true);//设置为圆形
        roundedBitmapDrawable2.setBounds(10,10,10,10);
        im01.setImageDrawable(roundedBitmapDrawable2);

有时候我们如果用的图片过大,就需要截取需要的哪一部分成正方形(矩形),然后生成圆形图片。 图片就会变形

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample_5);
        // 创建 600*600 正方形 Bitmap,截取头像部分
        Bitmap newBitmap = Bitmap.createBitmap(bitmap,
                bitmap.getWidth() / 2 - 600 / 2, //x
                100,// y
                600,//width
                600//height
        );
        // 回收没有用的 bitmap
        bitmap.recycle();
        RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(getResources(), newBitmap);
        roundedBitmapDrawable.setCornerRadius(newBitmap.getWidth() / 2);
        imageView.setImageDrawable(roundedBitmapDrawable);

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.image3);
        int height = bitmap.getHeight();//图片的高 1200
        int width = bitmap.getWidth();//图片的宽 1920
        mTextView.setText("height:"+height+"width:"+width);
        //创建800*800的正方形Bitmap
        /*
        @param source 需要裁剪的bitmap
        @param x 裁剪x点起始坐标(横向)
        @param y 裁剪y点起始坐标(纵向)
        @param width 裁剪后,新生成的bitmap的宽度
        @param height 裁剪后,新生成的bitmap的高度
        * */
        Bitmap newBitmap = Bitmap.createBitmap(
          bitmap,bitmap.getWidth() / 2-600/2,
                50,
                800,
                800
        );
        //回收掉没用的bitmap
        bitmap.recycle();
        RoundedBitmapDrawable roundedBitmapDrawable =
                RoundedBitmapDrawableFactory.create(getResources(),newBitmap);
        roundedBitmapDrawable.setCornerRadius(newBitmap.getWidth()/2);//自己计算圆角度
        //roundedBitmapDrawable.setCircular(true);//设置为圆形
        im01.setImageDrawable(roundedBitmapDrawable);

不同大小的图片,需要裁减的位置也不一样,才能完成完美的图片效果,需要搞一个算法(。。。)

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.image2);
        int height = bitmap.getHeight();//图片的高 1000
        int width = bitmap.getWidth();//图片的宽 993
        mTextView.setText("height:"+height+"width:"+width);
        //创建800*800的正方形Bitmap
        /*
        @param source 需要裁剪的bitmap
        @param x 裁剪x点起始坐标(横向)
        @param y 裁剪y点起始坐标(纵向)
        @param width 裁剪后,新生成的bitmap的宽度
        @param height 裁剪后,新生成的bitmap的高度
        * */
        Bitmap newBitmap = Bitmap.createBitmap(
                //x + width must be <= bitmap.width()
          bitmap,bitmap.getWidth() / 2,
                0,
                350,
                350
        );
        //回收掉没用的bitmap
        bitmap.recycle();
Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 

Bitmap.Config ARGB_8888:每个像素占四位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位

Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。

一般情况下我们都是使用的ARGB_8888,由此可知它是最占内存的,因为一个像素占32位,8位=1字节,所以一个像素占4字节的内存。假设有一张480x800的图片,如果格式为ARGB_8888,那么将会占用1500KB的内存

  /*
    * 使用RoundedBitmapDrawable,图片过大,系统会利用矩阵对我们的图片进行缩放处理,
    * 系统将图片缩放成了正方形,所以图片里的东西看起来有点变形,我们利用代码加工图片,
    * RoundedBitmapDrawable需要正方形,我们就给他正方形
    * */
    private Bitmap createRectBitmap(Bitmap bitmap){
        int oldHeight = bitmap.getHeight();//原图高度
        int oldWidth = bitmap.getWidth();//原图宽度
        int borderWidth = 20 ;    //边框宽度

        //转换为正方形的宽高  取最小
        int widthOrHeight = Math.min(oldHeight,oldWidth);
        //最后的宽高 需要加上边框的宽度
        int newBitmapWH = widthOrHeight + borderWidth;
        Bitmap newBitmap = Bitmap.createBitmap(newBitmapWH,newBitmapWH,Bitmap.Config.ARGB_8888);
        //画布
        Canvas canvas = new Canvas(newBitmap);
        //画布定义起始点坐标 (x,y)x:-700: y:20
        int x = borderWidth + widthOrHeight - oldWidth;
        int y = borderWidth + widthOrHeight - oldHeight;
        Log.e("x:"+x,"y:"+y);

        //裁剪后图像,注意X,Y要除以2 来进行一个中心裁剪
        canvas.drawBitmap(bitmap, x/2, y/2, null);
        //画布抗锯齿
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0,
                Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
        Paint borderPaint = new Paint();
        borderPaint.setStyle(Paint.Style.STROKE);//画笔mode
        borderPaint.setStrokeWidth(borderWidth);//画笔宽度
        borderPaint.setAntiAlias(true);//抗锯齿
        borderPaint.setFilterBitmap(true);//抗锯齿
        borderPaint.setColor(Color.WHITE);
        //添加边框
     /* @param cx要绘制的的圆的圆心的x坐标  @param cy要绘制的圆的圆心的y坐标
        @param radius要绘制的线圈半径 @param paint用于绘制圆圈的油漆*/
        canvas.drawCircle(canvas.getWidth()/2, canvas.getWidth()/2, newBitmapWH/2, borderPaint);

        bitmap.recycle();//释放没用的bitmap
        return newBitmap;

    }

//使用上述方法
 Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.image3);
        int height = bitmap.getHeight();//图片的高
        int width = bitmap.getWidth();//图片的宽
        mTextView.setText("height:"+height+"width:"+width);
        Bitmap bitmap1 = createRectBitmap(bitmap);

        RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(getResources(),bitmap1);
        roundedBitmapDrawable.setGravity(Gravity.CENTER);//居中
        roundedBitmapDrawable.setCircular(true);//设置为圆形
        im01.setImageDrawable(roundedBitmapDrawable);

其他

 /**
     * 按宽/高缩放图片到指定大小并进行裁剪得到中间部分图片
     *
     * @param bitmap 源bitmap
     * @param w 缩放后指定的宽度
     * @param h 缩放后指定的高度
     * @return 缩放后的中间部分图片
     */
    public static Bitmap zoomBitmap(Bitmap bitmap, int w, int h) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scaleWidht, scaleHeight, x, y;
        Bitmap newbmp;
        Matrix matrix = new Matrix();
        if (width > height) {
            scaleWidht = ((float) h / height);
            scaleHeight = ((float) h / height);
            x = (width - w * height / h) / 2;//获取bitmap源文件中x做表需要偏移的像数大小
            y = 0;
        } else if (width < height) {
            scaleWidht = ((float) w / width);
            scaleHeight = ((float) w / width);
            x = 0;
            y = (height - h * width / w) / 2;//获取bitmap源文件中y做表需要偏移的像数大小
        } else {
            scaleWidht = ((float) w / width);
            scaleHeight = ((float) w / width);
            x = 0;
            y = 0;
        }
        matrix.postScale(scaleWidht, scaleHeight);
        try {
            newbmp = Bitmap.createBitmap(bitmap, (int) x, (int) y, (int) (width - x), (int) (height - y), matrix,
                    true);//createBitmap()方法中定义的参数x+width要小于或等于bitmap.getWidth(),y+height要小于或等于bitmap.getHeight()
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return newbmp;
    }

参考链接::不惜留恋_

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值