参考
Drawable子类之—— BitmapDrawable (可控制对齐平铺的图像)
明明图片拉进去对应的文件之后我们就直接设置为背景,那么谷歌还要弄一个BitmapDrawable干嘛,简单说就是你直接设背景能控制背景怎么对齐吗,能控制背景如何平铺吗,不能。
一、Drawable简介
Drawable有很多种,它们都表示一种可以在Canvas上进行绘制的图像概念,但是它们又不全是图片,通过颜色也可以构造出各式各样的图像效果。Drawable使用简单,比自定义view的成本要低。另外,非图片类型的Drawable占用空间较小,这对减小apk的大小也有帮助。
在实际开发中,Drawable常被用作View的背景或者作为ImageView中的图像显示。Drawable一般都是通过XML来定义的,也可以通过代码构造。
二、Canvas
名为画布,我们可以看作是一种处理过程,使用各种方法来管理Bitmap、GL或者Path路径,同时它可以配合Matrix矩阵类给图像做旋转、缩放等操作,同时Canvas类还提供了裁剪、选取等操作。Canvas主要用于2D绘图,那么它也提供了很多相应的drawXxx()方法,方便我们在Canvas对象上画画,drawXxx()具有多种类型,可以画出:点、线、矩形、圆形、椭圆、文字、位图等的图形,这里就不再一一介绍了,只介绍几个Canvas中常用的方法:
void drawBitmap(Bitmap bitmap,float left,float top,Paint paint):
android系统不允许直接修改原图,类似Photoshop中的锁定,必须通过原图创建一个同样大小的Bitmap,并将原图绘制到该Bitmap中,以一个副本的形式来修改图像。代码如下,bm为原图,bmp为创建的副本。
<pre>
Bitmap bmp = Bitmap.createBitmap(bm.getWidth(),bm.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
canvas.drawBitmap(bm,0,0,paint);
</pre>
void drawLine(float startX,float startY,float stopX,float stopY,Paint paint):根据给定的起始点和结束点之间绘制连线
void drawPath(Path path,Paint paint):根据给定的path,绘制连线
void drawPoint(float x,float y,Paint paint):根据给定的坐标,绘制点
void drawText(String text,int start,int end,Paint paint):根据给定的坐标,绘制文字
int getHeight():得到Canvas的高度
int getWidth():得到Canvas的宽度
详细可参考《Android群英传》P117
三、Paint
我们可以把它看做一个画图工具,比如画笔、画刷。他管理了每个画图工具的字体、颜色、样式,主要用于设置绘图风格,包括画笔颜色、画笔粗细、填充风格等。如果涉及一些Android游戏开发、显示特效可以通过这些底层图形类来高效实现自己的应用。 Paint中提供了大量设置绘图风格的方法,这里仅列出一些常用的:
setARGB(int a,int r,int g,int b):设置ARGB颜色。
setColor(int color):设置颜色。
setAlpha(int a):设置透明度。
setPathEffect(PathEffect effect):设置绘制路径时的路径效果。
setShader(Shader shader):设置Paint的填充效果。
setAntiAlias(boolean aa):设置是否抗锯齿。
setStrokeWidth(float width):设置Paint的笔触宽度。
setStyle(Paint.Style style):设置Paint的填充风格。
setTextSize(float textSize):设置绘制文本时的文字大小。
setXfermode(Xfermode xfermode):设置绘制的渲染模式
四、Canvas,Drawable,Paint关系
Canvas就是一张画布,上面可以让你画你想画的东西,你可以想像成他就是小画家工具。那画完以后怎么办?很简单,把它装到容器里面,容器有哪些?就是我们前面讲的ImageView、GridView、ListView…等等,这是系统帮我们做好的容器,不过这次是存成属于自己的View,讲白话一点就是把我们画的东西包成一个容器,容器里面装的是我们的画布。
每个Drawable都会有一个draw的方法,它就是会帮你把这些图形贴到画布上面,然后再装到自定的容器View里面,最后就变成一种容器,看你是要装进系统的容器或者直接呈现出来都可以。
Paint就是画笔,你在小画家上面画画的时候,都会选择画笔来作画,像什么颜色啊,粗细啊之类的属性,像上面的例子当中,我们取得Drawable的画笔,然后将画笔的颜色改成蓝色,这样画出来的颜色就会变成蓝色的矩形了。那是画在哪边?当然是画布上面,通常Paint都会跟在Drawable的相关类别或者自定View类别一起使用。
五、Canvas的使用方式
以下参考Andriod中绘(画)图----Canvas的使用详解
Canvas的两种使用情形,从Canvas对象的获得角度分析:
1、 自定义View和自定义SurfaceView中获得Canvas对象
由于自定义View和SurfaceView在显示界面中已经获得了显示区域,canvas对象只不过是在其显示(绘画)区域进行界面布局的设计,当操作完毕后,系统会显示canvas的操作结果。自定义View的绘图方法为:
<pre>
//存在canvas对象,即存在默认的显示区域
@Override
public void draw(Canvas canvas) {
//canvas绘图
}
</pre>2、在其他情形下,我们需要通过代码创建一个Canvas对象,并且在绘画成功后,将该画图区域转换为Drawable图片或者通过setBitmap(bitmap)显现出来。一般步骤为:
<pre>
//创建一个的Bitmap对象
Bitmap bitmap = Bitmap.createBitmap(200, 100, Config.ARGB_8888);
//创建一个canvas对象,并且开始绘图
Canvas canvas = new Canvas (bitmap);
ImageView imgView = new ImageView(this);//或者其他可以设置背景图片的View控件
//为ImageView设置图像
//将Bitmap对象转换为Drawable图像资
Drawable drawable = new BitmapDrawable(bitmap);
imgView .setBackgroundDrawable(drawable);
//或者简单点:imgView.setImageBitmap(bitmap);
</pre>
六、【Android】Drawable、Bitmap、Canvas、Paint之间区别
(1)Bitmap 转化为 byte
<pre>
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
byte[] array= out.toByteArray();
</pre>
(2)byte转化为bitmap
<pre>
private Bitmap Bytes2Bimap(byte[] b){
if(b.length!=0){
return BitmapFactory.decodeByteArray(b, 0, b.length);
}
else {
return null;
}
}
</pre>
(3)bitmap 转换 drawable
<pre>
Bitmap bitmap = new Bitmap(...);
Drawable drawable = new BitmapDrawable(bitmap);
//Drawable drawable = new FastBitmapDrawable(bitmap);
</pre>
(4)Drawable to Bitmap
<pre>
public static Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = Bitmap.createBitmap(
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
drawable.getOpacity() != PixelFormat.OPAQUE ?Bitmap.Config.ARGB_8888: Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
//canvas.setBitmap(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.draw(canvas);
return bitmap;
}
</pre>
七、自定义Drawable
通常我们没有必要自定义Drawable,这是因为自定义的Drawable无法在XML中使用,使用范围有限。如果要自定义Drawable,draw、setAlpha、setColorFilter、getOpacity这几个方法必须要实现。
以下是个圆形的drawable,半径会随着view的变化而变化。
<pre>
public class CustomDrawable extends Drawable{
private Paint mPaint;
public CustomDrawable(int color){
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(color);
}
@override
public void draw(Canvas canvas){
final Rect r = getBounds();
float cx = r.exactCenterX();
float cy = r.exxactCenterY();
canvas.drawCircle(cx,cy,Math.min(cx,cy),mPaint);
}
@override
public void setAlpha(int alpha){
mPaint.setAlpha(alpha);
invalidateSelf();
}
@override
public void setColorFilter(ColorFilter cf){
mPaint.setColorFilter(cf);
invalidateSelf();
}
@override
public int getOpacity(){
return PixelFormat.TRANSLUCENT;
}
}
</pre>
八、Android中的13种Drawable小结
1.BitmapDrawable
参考Drawable子类之—— BitmapDrawable (可控制对齐平铺的图像)
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/image1"
android:tileMode="repeat"
/>
2.NinePatchDrawable
<?xml version="1.0" encoding="utf-8"?>
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android
android:dither="false"
android:src="@drawable/a" />
注意:@drawable/a中的a图片就是drawable中a.9.png图片
3.ShapeDrawable
参考Drawable子类之—— ShapeDrawable (图形定义)
通过颜色来构造图片,可以是纯色,也可以是渐变色。
比如给按钮背景图(纯色背景、带边框、圆角)可以用shape而不是Png图片:
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<stroke android:width="0.5dp" android:color="@color/white"/>
<gradient android:startColor="#ffffff" android:endColor="#ffffff" android:angle="0.0" />
<corners android:topLeftRadius="4dp" android:topRightRadius="0dp" android:bottomLeftRadius="4dp" android:bottomRightRadius="0dp" />
</shape>
</item>
</selector>
4.LayerDrawable
参考Drawable子类之——LayoutDrawable (图层叠加)
层次化的drawable集合,有叠加效果。使用item标签来表示一个Drawable,可以有多个item.
有些需求中需要一种图片,但是明显这个图片是其他几个图片简单叠加而已,那么可以使用layer-list来达到目的.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<!-- 最底层的图片,以x,y轴坐标为中心进行旋转-->
<rotate android:pivotX="0" android:pivotY="0"
android:fromDegrees="-10" android:toDegrees="-10">
<bitmap android:src="@drawable/chatting_bg_default_thumb"/>
</rotate>
</item>
<!-- 第二层的图片,以x,y轴坐标为中心进行旋转-->
<item>
<rotate android:pivotX="0" android:pivotY="0"
android:fromDegrees="15" android:toDegrees="15">
<bitmap android:src="@drawable/chatting_bg_purecolor_thumb"/>
</rotate>
</item>
<!-- 最上层的图片,以x,y轴坐标为中心进行旋转-->
<item>
<rotate android:pivotX="0" android:pivotY="0"
android:fromDegrees="35" android:toDegrees="55">
<bitmap android:src="@drawable/mark"/>
</rotate>
</item>
</layer-list>
5.StateListDrawable
也是一个drawable集合,每个Drawable对应view一个状态,系统会根据当前View的状态从selector中选择对应的item
<selector...>
<item...>
<item...>
<item...>
</selector>
6.LevelListDrawable
drawable集合,里面每个drawable都对应一个等级。通过setLevel方法设置不同等级可以切换具体的drawable。
7.TransitionDrawable
参考Drawable子类之——TransitionDrawable (渐变)
transition_simple.xml
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/pic1" />
<item android:drawable="@mipmap/pic2" />
</transition>
import android.app.Activity;
import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.widget.ImageView;
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.mIv);
//得到一个Drawable,属于 TransitionDrawable 类型的
TransitionDrawable transition = (TransitionDrawable)getResources().
getDrawable(R.drawable.transition_simple);
image.setImageDrawable(transition);
transition.startTransition(2000); // 设定渐变的变化市场
}
}
8.InsetDrawable
参考Drawable子类之——InsetDrawable (嵌入)
将其它Drawable嵌入自己当中,并可以在四周留出一定的间距。
InsetDrawable对应的标签是<inset>他可以将其他的Drawable内嵌到自己的里面。个人觉得其实没什么用,他能做到的,LayerDrawable也能做,或者有的时候直接设置一下padding就可以了。
insetdrawable_simple.xml
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetBottom="60dp"
android:insetLeft="30dp"
android:insetRight="30dp"
android:insetTop="60dp" >
<!--待会要插入的一个蓝色的矩形-->
<shape android:shape="rectangle" >
<solid android:color="#0000ff" />
</shape>
</inset>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".MainActivity">
<!--Inset这东西其实就没什么用,大概也就这样子吧,
背景一个,图片一个,利用Inset让背景比较大-->
<ImageView
android:id="@+id/mIv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="@mipmap/op"
android:src="@drawable/insetdrawable_simple"
/>
</RelativeLayout>
9.ScaleDrawable
根据自己的等级将指定的drawable缩放到一定比例。
<scale...
10.ClipDrawable
参考Drawable子类之——ClipDrawable (裁剪图像)
根据当前等级裁剪一个Drawable,裁剪方向受clipOrientation和gravity两个属性共同控制。
clipdrawable_simple.xml
<?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=".MainActivity">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@mipmap/star"
android:layout_marginBottom="20dp"/>
<ImageView
android:id="@+id/mIvClip"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@drawable/clipdrawable_simple"
/>
</LinearLayout>
import android.app.Activity;
import android.graphics.drawable.ClipDrawable;
import android.os.Bundle;
import android.widget.ImageView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = (ImageView) findViewById(R.id.mIvClip);
//ClipDrawable clipDrawable = (ClipDrawable) imageView.getDrawable(); 这样写会报空指针异常
ClipDrawable clipDrawable = (ClipDrawable) imageView.getBackground();
clipDrawable.setLevel(5000);
}
}
11.RotateDrawable
这里两个图片是两个按钮箭头,但是仅仅方向不同而已,其实可以只用其中一个图片即可,而另一个用RotateDrawable来让其“调转”180度
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_arrow_left"
android:fromDegrees="180"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="180" />