Drawable的进阶用法

1、前言

在前一篇博客当中提到关于Drawable的一些常用的用法,和使用系统自身的提供Drawable子类,来实现一定的具有功能性的Drawable,那么在今天的博客当中,我们就去自己定义Drawable的子类去实现我们自己功能的Drawable,

2、正文

首先我们去自己考虑一下,自定义Drawable是什么,先不着急,我们思考一下Android系统为我们提供的Drawable的子类实现,我们明确是对我们原有的图片进行一定的封装,然后进行一定的处理,让它和原来的最根本的res资源Drawable有某一种小功能,比如我们的ClipDrawable,ScaleDrawable,RotateDrawable,第一个是对原有的res资源Drawable有裁剪的功能,第二ScaleDrawable,是对原有的的res下的根本Drawable有一定的缩放功能,而第三个RotateDrawable就是对原来最原始的Drawable有一定的旋转功能。我们自定义Drawable首先我们要明确我们自定义的Drawable去实现什么功能,在这里我们去实现一个百叶窗的动画效果,百叶窗效果:为了和原图作比较,我们把原图和百叶窗的效果一起展示出来,

百叶窗效果

首先来看我们的自定义Drawable,我们起名为ShuttersDrawable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;

/**
* 自定义百叶窗的Drawable对象
* @author liusuansuan
*/
//TODO(suansuan):由于创建Bitmap的时候,我们的宽度不能是0,
//TODO(suansuan): 所以在做动画的时候我们不能通过ValueAnimator从0开始
public class ShuttersDrawable extends Drawable{

/** 在这里定义一下我们所要 */
private Bitmap mBitmap; //原图的Bitmap的对象
private Bitmap[] mBitmaps; //对原图进行分割以后百叶窗,叶片的Bitmap对象数组
private int mWidth; //原图的宽度。
private int mHeight; //原图的高度
private int mCellWidth; //单个叶片的宽度
private int mCount; //叶片总数
private Paint mPaint; //画笔
private float scale = 1f; //比例,当前绘制的比例,主要通过该属性去控制整个百叶窗动画


/** 初始化操作 */
public ShuttersDrawable(Bitmap bitmap, int count){
this.mBitmap = bitmap;
this.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.mCount = count;
initBitmap();
}

/** 初始化Bitmap */
private void initBitmap() {
this.mHeight = mBitmap.getHeight();
this.mWidth = mBitmap.getWidth();
mCellWidth = mWidth / mCount;
mBitmaps = new Bitmap[mCount];
}

/** 绘制方法 */
@Override
public void draw(Canvas canvas) {
canvas.save();
float width = (mCellWidth * scale) + 1;
if(width >= mCellWidth){
width = mCellWidth;
}
for(int i = 0 ; i < mBitmaps.length; i++){
mBitmaps[i] = Bitmap.createBitmap(mBitmap, i * mCellWidth, 0,(int)width, mHeight);
canvas.drawBitmap(mBitmaps[i], i * mCellWidth, 0, mPaint);
}
canvas.restore();
}

/** 设置比例 */
public void setScale(float mScale){
this.scale = mScale;
invalidateSelf();
}

/** 主要是给以后使用到该Drawable,可以得到该Drawable的宽度 */
@Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}

/** 主要是给以后使用到该Drawable,可以得到该Drawable的高度 */
@Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}


@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}

@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}

@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
}

自定义Drawable我们首先需要继承Drawable,有几个方法我们是必须所要实现的那就是

  • 1、draw(Canvas canvas):就是使用参数Canvas画布对象,给该Drawable画上内容的方法。
  • getOpacity():
  • setAlpha(int alpha):设置透明度
  • setColorFilter(ColorFilter cf):设置颜色过滤。

其余我写的都比较简单,大家应该都能看懂是什么意思,我也就不献丑了。说一下具体的思路。就是我们把Bitmap分成一个个的叶片对象,然后对叶片对象进行绘制,然后通过sclae比例去控制绘制叶片的宽度,

activity_main.xml layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">

<!-- 用来放至原来的Drawable对象 -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="0dip"
android:layout_weight="1"
android:background="#F00"
android:src="@drawable/pressed"/>

<!-- 用来放至现在的ShuttersDrawable对象的载体 -->
<ImageView
android:id="@+id/iv_imageView"
android:layout_width="wrap_content"
android:background="#0F0"
android:layout_height="0dip"
android:layout_weight="1"/>

  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="click me start Animator"
    android:onClick="click"/>


</LinearLayout>

使用Button来控制开启动画,
再来我们就看看在Activity中的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
*
* @author suansuan
*
*/
public class MainActivity extends Activity {

private ShuttersDrawable mShuttersDrawable;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initImageView();
}

/** 初始化ImageView */
private void initImageView() {
ImageView iv = (ImageView) findViewById(R.id.iv_imageView);
Bitmap decodeResource = BitmapFactory.decodeResource(getResources(),
R.drawable.pressed);
mShuttersDrawable = new ShuttersDrawable(decodeResource, 5);
iv.setImageDrawable(mShuttersDrawable);
}

/** Button的点击事件 */
@SuppressLint("NewApi")
//TODO(suansuan):在这里,博主建议大家还是使用V7包下面的Animator,
//TODO(suansuan):博主是因为个人原因没用使用,为了更好的适配,大家请不要这样
public void click(View view){
ValueAnimator mValueAnimation = ValueAnimator.ofFloat(0f, 1f).
setDuration(10000);
mValueAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@SuppressLint("NewApi") @Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (Float) animation.getAnimatedValue();
mShuttersDrawable.setScale(animatedValue);
}
});
mValueAnimation.start();
}
}

效果呢就如开头那样所示。

上述就是如何通过自定义Drawable去实现百叶窗的效果,

2、通过自定义View去实现一些关于画图

接下来我们去探讨第二个问题,我们以前会去通过自定义View去实现一些关于画图的东西其实都可以通过自定义Drawable去实现,比如鸿洋大神可以通过自定义Drawable去实现圆角图片一样,这个时候我们想到的也许就是去自定义Imageview去实现,大家应该发现了,自定义Drawable就可以看做是自定义View的一种轻量级实现,下面我们看看一种效果,然后我们去自定义这种效果,这种效果叫什么呢,我也不知,总之我们看吧!

专辑盒子

这种类似于专辑盒的Icon,在各大音乐平台多的是,相信大家也都做过,我们可以类似于这样的专辑盒去做一个自定义Drawable,好了废话少说,我们开始干活。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package vadsdk.testdrawable.drawable;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;

import vadsdk.testdrawable.R;


public class AlbumDrawable extends Drawable {

/** 画笔对象 */
private Paint mPaint;

/** 右侧图片对象 */
private Bitmap mLeftDrawable;

/** 背景阴影图片 */
private Bitmap mBackgoundDrawable;

/** 内容图片 */
private Bitmap mContentBitmap;

/** 上下文环境 */
private Context mContext;

/** 构造方法 */
public AlbumDrawable(Bitmap bitmap, Context context) {
this.mContentBitmap = bitmap;
this.mContext = context;
init();
}

/** 初始化操作 */
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLeftDrawable = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.album_chart_left);
}


@Override
public void draw(Canvas canvas) {
onDrawLeftDrawable(canvas);
onDrawLeftBitmap(canvas);
onDrawDrawable(canvas);
}

/** 绘制小图片 */
private void onDrawLeftDrawable(Canvas canvas) {
canvas.save();
canvas.translate(mLeftDrawable.getWidth()/2f, mLeftDrawable.getWidth() * 3f/5f);
Bitmap zoomBitmap = zoomBitmap(mContentBitmap, 1/6f);
canvas.drawBitmap(zoomBitmap, 0, 0, mPaint);
canvas.restore();
}


/** 绘制海报Drawable */
private void onDrawDrawable(Canvas canvas) {
canvas.save();
canvas.translate(mLeftDrawable.getWidth(), 0);
Bitmap zoomBitmap = zoomBitmap(mContentBitmap , mLeftDrawable.getHeight()-3);
canvas.drawBitmap(zoomBitmap, 0, 0, mPaint);
canvas.restore();
}

/** 绘制左侧的图片 */
private void onDrawLeftBitmap(Canvas canvas) {
canvas.save();
canvas.drawBitmap(mLeftDrawable, 0, 0, mPaint);
canvas.restore();
}

/** 按照比例获取Bitmap */
private Bitmap zoomBitmap(Bitmap bitmap, int h){
int width = bitmap.getWidth();
int height = bitmap.getHeight();

float scaleHeight = ((float)h)/height;

Matrix matrix = new Matrix(); // 计算缩放比例
matrix.postScale(scaleHeight, scaleHeight);
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
}

/** 按照比例获取Bitmap */
private Bitmap zoomBitmap(Bitmap bitmap, float scale){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix(); // 计算缩放比例
matrix.postScale(scale, scale);
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
}

@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}

@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}

@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
}

我们现在自己定义了一个Drawable,我说一下思路,大体的思路就是对Bitmap做一定的操作,然后根据宽高去摆放,使用Bitmap去缩放小的海报,然后使用左侧的图片去盖住右侧的图片,主要就是计算方面,博主写的计算方法也有偏差,可以自己明确计算规则写自己的逻辑。

下来在我们的Activity当中使用我们定义的Drawable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainActivity extends AppCompatActivity {

private ImageView iv;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv_imageView);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.album);
AlbumDrawable albumDrawable = new AlbumDrawable(bitmap, this);
iv.setImageDrawable(albumDrawable);
}
}

效果如下:

专辑盒子

以上就是自定义Drawable