图片资源:Drawable
在学习 View 的时候,我们学习过 ImageView。它除了在 xml 的 src 属性中设置图片之外,还可以通过setImageDrawable( )
和setBackgroundDrawable( )
两个 API 设置图片资源。这两个 API 都包含一个关键词——Drawable,那么我们这一节就来看看,Drawable 到底是个什么东西。
1. Drawable 是什么
Drawable 从字面上理解就是一个可绘制的图形对象,最简单的例子就是常用的 Bitmap 对象,在 Android 中可以通过一个 BitmapDrawable 对象来呈现。
每一个 Drawable 内部都保存了一个“res/drawable”目录下的私有文件,比如我们会将不同分辨率的图片资源存储在“mdpi”、“hdpi”、“xhdpi”以及“xxhdpi”里面,这些目录是在创建工程的时候 Android Studio 自动帮我们生成好的,然后在运行的时候系统会根据当前的设备类型自动选择一种合适的 bitmap 图片资源为我们所用。
2. 为 View 设置 Drawable
在 xml 中使用是很最常见的用法,而且我们一直在用,回顾一下我们设置的图片资源,比如一个 TextView 的背景样式,通常会这么写:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/text"
android:text="@string/hello_world" />
其中 background 属性的值就是一个 Drawable 对象,同样我们可以在 Java 代码中给 View 设置一个 Drawable 参数:
ImageView imageView = (ImageView) findViewById(R.id.imageview);
imageView.setImageResource(R.drawable.image);
3. 加载 Bitmap 和 Drawable
Android 提供了一个Bitmap
类来处理 bitmap 图片相关的功能,接下来我们看看如何通过 Java 代码创建一个 Bitmap 并将 Bitmap 转换成 Drawable:
AssetManager manager = getAssets();
// 从 assets 中读取 bitmap
InputStream open = null;
try {
open = manager.open("imooc.png");
Bitmap bitmap = BitmapFactory.decodeStream(open);
// 给 imageView 设置 bitmap 对象
ImageView view = (ImageView) findViewById(R.id.imageView1);
view.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (open != null) {
try {
open.close();
} catch (IOException e) {
e.printStackTrace();
}
}
除此之外,还可以从“res/drawable”文件夹中获取 Drawable,并在代码中转换成 Bitmap 对象,如下:
Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap);
在获取的同时,我们还可以对图像进行任意比例的缩放:
Bitmap originalBitmap = getBitmap();
Bitmap resizedBitmap = Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, false);
反过来,我们也可以将 Bitmap 转换成一个 Drawable 对象:
Drawable drawable = new BitmapDrawable(getResources(),bitmap);
4. 通过 xml 声明 Drawable
我们可以在 xml 中声明很多种类型的 Drawable,用于对各种动画、背景、状态等等进行描述。
4.1 Shape Drawables
Shape Drawables 可以用来定义一个 View 的外形、颜色、渐变等等属性,它的最大的有点就是可以根据任意尺寸的 View
进行自适应,代码示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="10dp"
android:color="#FFFFFFFF" />
<gradient
android:endColor="#EF3434AB"
android:startColor="#FF98df9d"
android:angle="45" />
<corners
android:bottomRightRadius="10dp"
android:bottomLeftRadius="10dp"
android:topLeftRadius="5dp"
android:topRightRadius="5dp" />
</shape>
以上代码分别为背景设置了边框、渐变、角弧度,编写完直接作为背景资源设置即可:
<?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:background="@drawable/myshape"
android:orientation="vertical" >
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</EditText>
<RadioGroup
android:id="@+id/radioGroup1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<RadioButton
android:id="@+id/radio0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/celsius" >
</RadioButton>
<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fahrenheit" >
</RadioButton>
</RadioGroup>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/calc"
android:onClick="myClickHandler">
</Button>
</LinearLayout>
4.2 State Drawables
State Drawables 用来描述一个 View 在不同状态下的形态,我们可以给每一种状态设置一中背景样式,比如我们可以让我们的 Button 在点击、选择和默认态下呈现不同的样式:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/button_pressed"
android:state_pressed="true" />
<item android:drawable="@drawable/button_checked"
android:state_checked="true" />
<item android:drawable="@drawable/button_default" />
</selector>
4.3 Animation Drawables
Animation Drawables 是一种动画资源,我们可以将动画的时长、形态、起终点等信息描述在 xml 中,如下:
<animation-list android:id="@+id/selected" android:oneshot="false">
<item android:drawable="@drawable/phase1" android:duration="100" />
<item android:drawable="@drawable/phase2" android:duration="200" />
<item android:drawable="@drawable/phase3" android:duration="300" />
</animation-list>
同样在 Java 代码中直接作为背景设置:
ImageView image = (ImageView)findViewById(R.id.img);
img.setBackgroundResource(R.drawable.animation);
AnimationDrawable frameAnimation = (AnimationDrawable) image.getBackground();
frameAnimation.start();
4.4 自定义 Drawable
除了上面常用的系统提供的 Drawable 之外,我们还可以自定义自己想要的图片资源,在本节的示例中我们就来自定义一个资源样式。
5. Drawable 使用示例
我们通常使用的 ImageView 都是矩形的,但是为了让 UI 样式更
5.1 Drawable 实现
既然要自定义 Drawable 资源,那么首先需要创建一个类继承自 Drawable,然后在构造器中创建画笔“Paint”,然后在draw()
方法中绘制图案即可,代码示例如下:
package com.emercy.myapplication;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
public class RoundCornerDrawable extends Drawable {
private Paint mPaint;
private Bitmap mBitmap;
public RoundCornerDrawable(Bitmap bitmap) {
this.mBitmap = bitmap;
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(bitmapShader);
}
@Override
public void draw(Canvas canvas) {
canvas.drawRoundRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()), 100, 100, mPaint);
}
@Override
public void setAlpha(int i) {
mPaint.setAlpha(i);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
// 返回drawable实际宽高
@Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}
@Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}
}
我们在构造器中创建了一个画笔,并传入了 Bitmap 对象,此后系统在绘制的时候回调draw()
方法,完成圆角形状的绘制,这样就完成了一个圆角 bitmap 的裁剪工作。
5.2 MainActivity 主逻辑
在主逻辑中我们直接拿到 ImageView,然后获取我们要设置的 Bitmap 资源,直接通过setBackground()
方法设置给 ImageView,这样呈现出来的图片就是圆角形状。
package com.emercy.myapplication;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.ImageView;
import java.io.InputStream;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView image = (ImageView) findViewById(R.id.image);
InputStream resource = getResources().openRawResource(R.raw.avatar);
Bitmap bitmap = BitmapFactory.decodeStream(resource);
image.setBackground(new RoundCornerDrawable(bitmap));
}
}
编译后效果如下,展示一张圆角图片:
6. 小结
本节学习了一个很多人经常用到却很少留意的类,在给 View 设置背景、前景等图片及样式的时候都离不开 Drawable 的参与。首先需要学习系统提供的一些常用 Drawable 对象,这样其实在大多数场景都能应对,如果要处理一些特殊的样式可以采用自定义 Drawable 的方式。