图形绘制

下面是Android ApiDemo里的“Xfermodes”实例,效果图。

\

Xfermode有三个子类,结构如下:<喎�"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">public classXfermodeextends Objectjava.lang.Object ? android.graphics.XfermodeKnown Direct SubclassesAvoidXfermode, PixelXorXfermode, PorterDuffXfermode

AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。

PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素异或操作。

PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。


上面图片种显示的16种模式介绍如下:

1.PorterDuff.Mode.CLEAR

所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC

显示上层绘制图片
3.PorterDuff.Mode.DST

显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER

正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER

上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN

取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN

取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT

取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT

取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP

取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP

取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR

异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN

取两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN

取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY

取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN

取两图层全部区域,交集部分变为透明色


了解了上面的知识点后,我们根据上面的知识点先来实现第一种圆角图片制作方式:

原图:

\

private ImageView mImg;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         mImg = (ImageView) findViewById(R.id.img);
         
         //获得imageview中设置的图片
         BitmapDrawable drawable = (BitmapDrawable) mImg.getDrawable();
         Bitmap bmp = drawable.getBitmap();
         //获得图片的宽,并创建结果bitmap
         int width = bmp.getWidth();
         Bitmap resultBmp = Bitmap.createBitmap(width, width,
                 Bitmap.Config.ARGB_8888);
         Paint paint = new Paint();
         Canvas canvas = new Canvas(resultBmp);
         //画圆
         canvas.drawCircle(width / 2 , width / 2 , width / 2 , paint);
         paint.setXfermode( new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // 选择交集去上层图片
         canvas.drawBitmap(bmp, 0 , 0 , paint);
         mImg.setImageBitmap(resultBmp);
         bmp.recycle();
 
     }

通过运行上面的代码,我们得出的结果如下:

\

大家看到这是我们需要的结果。可是这样做可能导致OutOfMomery异常。假如图片很大或者你可能并非通过ImageView的getDrawable获得图像,而是直接Decode一张很大的图片加载到内存,你会发现可能会出现异常。我们做一下改变。


?
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
private static final String TAG = "RoundImage" ;
     private ImageView mImg;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         mImg = (ImageView) findViewById(R.id.img);
         // 裁剪图片
         BitmapFactory.Options options = new BitmapFactory.Options();
         options.inJustDecodeBounds = true ;
         BitmapFactory
                 .decodeResource(getResources(), R.drawable.avatar, options);
         Log.d(TAG, "original outwidth: " + options.outWidth);
         // 此宽度是目标ImageView希望的大小,你可以自定义ImageView,然后获得ImageView的宽度。
         int dstWidth = 150 ;
         // 我们需要加载的图片可能很大,我们先对原有的图片进行裁剪
         int sampleSize = calculateInSampleSize(options, dstWidth, dstWidth);
         options.inSampleSize = sampleSize;
         options.inJustDecodeBounds = false ;
         Log.d(TAG, "sample size: " + sampleSize);
         Bitmap bmp = BitmapFactory.decodeResource(getResources(),
                 R.drawable.avatar, options);
 
         // 绘制图片
         Bitmap resultBmp = Bitmap.createBitmap(dstWidth, dstWidth,
                 Bitmap.Config.ARGB_8888);
         Paint paint = new Paint();
         paint.setAntiAlias( true );
         Canvas canvas = new Canvas(resultBmp);
         // 画圆
         canvas.drawCircle(dstWidth / 2 , dstWidth / 2 , dstWidth / 2 , paint);
         // 选择交集去上层图片
         paint.setXfermode( new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
         canvas.drawBitmap(bmp, new Rect( 0 , 0 , bmp.getWidth(), bmp.getWidth()),
                 new Rect( 0 , 0 , dstWidth, dstWidth), paint);
         mImg.setImageBitmap(resultBmp);
         bmp.recycle();
     }
 
     private int calculateInSampleSize(BitmapFactory.Options options,
             int reqWidth, int reqHeight) {
         // Raw height and width of image
         final int height = options.outHeight;
         final int width = options.outWidth;
         int inSampleSize = 1 ;
 
         if (height > reqHeight || width > reqWidth) {
 
             final int halfHeight = height / 2 ;
             final int halfWidth = width / 2 ;
 
             // Calculate the largest inSampleSize value that is a power of 2 and
             // keeps both
             // height and width larger than the requested height and width.
             while ((halfHeight / inSampleSize) > reqHeight
                     && (halfWidth / inSampleSize) > reqWidth) {
                 inSampleSize *= 2 ;
             }
         }
         return inSampleSize;
     }

再来看一下效果:

\

上面提供了一种方式,更多细节,需要你自己去优化,下面介绍第二种绘制圆角图片的方式。


首先我们需要了解一个类BitmapShader

引用的介绍如下:

public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY)

调用这个方法来产生一个画有一个位图的渲染器(Shader)。

bitmap 在渲染器内使用的位图

tileX The tiling mode for x to draw the bitmap in. 在位图上X方向花砖模式

tileY The tiling mode for y to draw the bitmap in. 在位图上Y方向花砖模式

TileMode:(一共有三种)

CLAMP :如果渲染器超出原始边界范围,会复制范围内边缘染色。

REPEAT :横向和纵向的重复渲染器图片,平铺。

MIRROR :横向和纵向的重复渲染器图片,这个和REPEAT 重复方式不一样,他是以镜像方式平铺。

知道这个原理后,我们贴出对应的代码:
?
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
public class CircleImageView extends ImageView {
 
     private static final String TAG = CircleImageView. class .getSimpleName();
     private Paint mBitmapPaint = new Paint();
     private int mRadius;
 
     public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
         super (context, attrs, defStyleAttr);
         init();
     }
 
     public CircleImageView(Context context, AttributeSet attrs) {
         super (context, attrs);
         init();
     }
 
     public CircleImageView(Context context) {
         super (context);
         init();
     }
 
     private void init() {
         BitmapDrawable drawable = (BitmapDrawable) getDrawable();
         if (drawable == null ) {
             Log.i(TAG, "drawable: null" );
             return ;
         }
         Bitmap bmp = drawable.getBitmap();
         BitmapShader shader = new BitmapShader(bmp, TileMode.CLAMP,
                 TileMode.CLAMP);
         mBitmapPaint.setShader(shader);
         mBitmapPaint.setAntiAlias( true );
         invalidate();
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
         if (getDrawable() == null ) {
             return ;
         }
         mRadius = Math.min(getWidth()/ 2 , getHeight()/ 2 );
         canvas.drawCircle(getWidth() / 2 , getHeight() / 2 , mRadius,
                 mBitmapPaint);
     }
 
}

是不是挺简单的

结果我就不显示了,跟上面的一样。上面也是最原始的代码,文章的结尾贴出一份完整优化过的代码共大家参考如下:


?
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
public class CircleImageView extends ImageView {
 
     private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
 
     private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
     private static final int COLORDRAWABLE_DIMENSION = 1 ;
 
     private static final int DEFAULT_BORDER_WIDTH = 0 ;
     private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
 
     private final RectF mDrawableRect = new RectF();
     private final RectF mBorderRect = new RectF();
 
     private final Matrix mShaderMatrix = new Matrix();
     private final Paint mBitmapPaint = new Paint();
     private final Paint mBorderPaint = new Paint();
 
     private int mBorderColor = DEFAULT_BORDER_COLOR;
     private int mBorderWidth = DEFAULT_BORDER_WIDTH;
 
     private Bitmap mBitmap;
     private BitmapShader mBitmapShader;
     private int mBitmapWidth;
     private int mBitmapHeight;
 
     private float mDrawableRadius;
     private float mBorderRadius;
 
     private boolean mReady;
     private boolean mSetupPending;
 
     public CircleImageView(Context context) {
         super (context);
 
         init();
     }
 
     public CircleImageView(Context context, AttributeSet attrs) {
         this (context, attrs, 0 );
     }
 
     public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
         super (context, attrs, defStyle);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0 );
 
         mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
         mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
 
         a.recycle();
 
         init();
     }
 
     private void init() {
         super .setScaleType(SCALE_TYPE);
         mReady = true ;
 
         if (mSetupPending) {
             setup();
             mSetupPending = false ;
         }
     }
 
     @Override
     public ScaleType getScaleType() {
         return SCALE_TYPE;
     }
 
     @Override
     public void setScaleType(ScaleType scaleType) {
         if (scaleType != SCALE_TYPE) {
             throw new IllegalArgumentException(String.format( "ScaleType %s not supported." , scaleType));
         }
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
         if (getDrawable() == null ) {
             return ;
         }
 
         canvas.drawCircle(getWidth() / 2 , getHeight() / 2 , mDrawableRadius, mBitmapPaint);
         if (mBorderWidth != 0 ) {
             canvas.drawCircle(getWidth() / 2 , getHeight() / 2 , mBorderRadius, mBorderPaint);
         }
     }
 
     @Override
     protected void onSizeChanged( int w, int h, int oldw, int oldh) {
         super .onSizeChanged(w, h, oldw, oldh);
         setup();
     }
 
     public int getBorderColor() {
         return mBorderColor;
     }
 
     public void setBorderColor( int borderColor) {
         if (borderColor == mBorderColor) {
             return ;
         }
 
         mBorderColor = borderColor;
         mBorderPaint.setColor(mBorderColor);
         invalidate();
     }
 
     public int getBorderWidth() {
         return mBorderWidth;
     }
 
     public void setBorderWidth( int borderWidth) {
         if (borderWidth == mBorderWidth) {
             return ;
         }
 
         mBorderWidth = borderWidth;
         setup();
     }
 
     @Override
     public void setImageBitmap(Bitmap bm) {
         super .setImageBitmap(bm);
         mBitmap = bm;
         setup();
     }
 
     @Override
     public void setImageDrawable(Drawable drawable) {
         super .setImageDrawable(drawable);
         mBitmap = getBitmapFromDrawable(drawable);
         setup();
     }
 
     @Override
     public void setImageResource( int resId) {
         super .setImageResource(resId);
         mBitmap = getBitmapFromDrawable(getDrawable());
         setup();
     }
 
     @Override
     public void setImageURI(Uri uri) {
         super .setImageURI(uri);
         mBitmap = getBitmapFromDrawable(getDrawable());
         setup();
     }
 
     private Bitmap getBitmapFromDrawable(Drawable drawable) {
         if (drawable == null ) {
             return null ;
         }
 
         if (drawable instanceof BitmapDrawable) {
             return ((BitmapDrawable) drawable).getBitmap();
         }
 
         try {
             Bitmap bitmap;
 
             if (drawable instanceof ColorDrawable) {
                 bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
             } else {
                 bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
             }
 
             Canvas canvas = new Canvas(bitmap);
             drawable.setBounds( 0 , 0 , canvas.getWidth(), canvas.getHeight());
             drawable.draw(canvas);
             return bitmap;
         } catch (OutOfMemoryError e) {
             return null ;
         }
     }
 
     private void setup() {
         if (!mReady) {
             mSetupPending = true ;
             return ;
         }
 
         if (mBitmap == null ) {
             return ;
         }
 
         mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
 
         mBitmapPaint.setAntiAlias( true );
         mBitmapPaint.setShader(mBitmapShader);
 
         mBorderPaint.setStyle(Paint.Style.STROKE);
         mBorderPaint.setAntiAlias( true );
         mBorderPaint.setColor(mBorderColor);
         mBorderPaint.setStrokeWidth(mBorderWidth);
 
         mBitmapHeight = mBitmap.getHeight();
         mBitmapWidth = mBitmap.getWidth();
 
         mBorderRect.set( 0 , 0 , getWidth(), getHeight());
         mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2 , (mBorderRect.width() - mBorderWidth) / 2 );
 
         mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
         mDrawableRadius = Math.min(mDrawableRect.height() / 2 , mDrawableRect.width() / 2 );
 
         updateShaderMatrix();
         invalidate();
     }
 
     private void updateShaderMatrix() {
         float scale;
         float dx = 0 ;
         float dy = 0 ;
 
         mShaderMatrix.set( null );
 
         if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
             scale = mDrawableRect.height() / ( float ) mBitmapHeight;
             dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0 .5f;
         } else {
             scale = mDrawableRect.width() / ( float ) mBitmapWidth;
             dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0 .5f;
         }
 
         mShaderMatrix.setScale(scale, scale);
         mShaderMatrix.postTranslate(( int ) (dx + 0 .5f) + mBorderWidth, ( int ) (dy + 0 .5f) + mBorderWidth);
 
         mBitmapShader.setLocalMatrix(mShaderMatrix);
     }
 
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值