├── .gitignore
├── CHANGELOG.md
├── README.md
├── library
├── .classpath
├── .project
├── AndroidManifest.xml
├── project.properties
├── res
│ └── values
│ │ └── attrs.xml
└── src
│ └── com
│ └── joooonho
│ └── SelectableRoundedImageView.java
└── sample
├── .classpath
├── .project
├── AndroidManifest.xml
├── libs
├── android-support-v4.jar
└── picasso-2.4.0.jar
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ └── photo_cheetah.jpg
├── drawable-xhdpi
│ ├── photo1.jpg
│ ├── photo2.jpg
│ └── photo3.jpg
├── drawable-xxhdpi
│ └── ic_launcher.png
├── layout
│ └── activity_main.xml
└── values
│ └── strings.xml
└── src
└── com
└── joooonho
└── MainActivity.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Change Log
2 | ===
3 |
4 | v1.0.1 *(02.08.2015)*
5 | ---
6 | * Method name modification. "Radiuses" to "Radii"
7 | * Remove unused resource attribute 'sriv_mutate_background'
8 | * Fix a bug:
9 | * Getting rounded accumulatively when recycled by adapter.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SelectableRoundedImageView
2 | ==========================
3 | [](https://android-arsenal.com/details/1/1234)
4 |
5 |
6 | Note that this project is no longer maintained.
7 |
8 | Android ImageView
that supports different radii on each corner. It also
9 | supports oval(and circle) shape and border. This would be especially useful for
10 | being used inside CardView
which should be rounded only top left and
11 | top right corners(Don't forget to call setPreventCornerOverlap(false)
on your cardview).
12 |
13 | I referred to the [RoundedImageView][6], developed by Vince, in developing this new one, and I really appreciate him. Also, I wrote a short article about how I made this library and my thoughts on CardView, check [my blog post][5].
14 |
15 | Get the sample app on Play Store.
[](https://play.google.com/store/apps/details?id=com.joooonho)
16 |
17 | ![SelectableRoundedImageView Sample Screenshots][1]
18 |
19 | Note: When using with [Glide][9], be sure to add asBitmap()
chain, like below.
20 | ```java
21 | Glide.with(context)
22 | .load(src)
23 | .asBitmap()
24 | .listener(l)
25 | .into(imageView)
26 | ```
27 | Note: When using with [Android-Universal-Image-Loader][7], be sure to use SimpleBitmapDisplayer
or FadeInBitmapDisplayer
rather than RoundedBitmapDisplayer
(or RoundedVignetteBitmapDisplayer
) when building DisplayImageOptions
. See below code.
28 |
29 | ```java
30 | options = new DisplayImageOptions.Builder()
31 | .showImageOnLoading(R.drawable.ic_stub)
32 | .showImageForEmptyUri(R.drawable.ic_empty)
33 | .showImageOnFail(R.drawable.ic_error)
34 | .cacheInMemory(true)
35 | .cacheOnDisk(true)
36 | .considerExifParams(true)
37 | // .displayer(new RoundedBitmapDisplayer(20))
38 | // DO NOT USE RoundedBitmapDisplayer. Use SimpleBitmapDisplayer!
39 | .displayer(new SimpleBitmapDisplayer())
40 | .build();
41 | ```
42 |
43 | Usage
44 | ----
45 | Define in xml:
46 |
47 | ```xml
48 |
60 | ```
61 |
62 | Or in code:
63 |
64 | ```java
65 | SelectableRoundedImageView sriv = new SelectableRoundedImageView(context);
66 | sriv.setScaleType(ScaleType.CENTER_CROP);
67 | sriv.setCornerRadiiDP(4, 4, 0, 0);
68 | sriv.setBorderWidthDP(4);
69 | sriv.setBorderColor(Color.BLUE);
70 | sriv.setImageDrawable(drawable);
71 | sriv.setOval(true);
72 | ```
73 |
74 | Including In Your Project
75 | -------------------------
76 |
77 | If you are using Android Studio, SelectableRoundedImageView is available through Gradle.
78 | ```
79 | dependencies {
80 | compile 'com.joooonho:selectableroundedimageview:1.0.1'
81 | }
82 | ```
83 |
84 | Also SelectableRoundedImageView is presented as a [library project][3]. You can include
85 | this project by [referencing it as a library project][4] in Eclipse or ant(A standalone JAR
86 | is not possible due to the custom attributes).
87 |
88 |
89 | Developed By
90 | ==========================
91 |
92 | * Joonho Kim -
93 |
94 | License
95 | -------------------------
96 |
97 | Copyright 2014 Joonho Kim
98 |
99 | Licensed under the Apache License, Version 2.0 (the "License");
100 | you may not use this file except in compliance with the License.
101 | You may obtain a copy of the License at
102 |
103 | http://www.apache.org/licenses/LICENSE-2.0
104 |
105 | Unless required by applicable law or agreed to in writing, software
106 | distributed under the License is distributed on an "AS IS" BASIS,
107 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
108 | See the License for the specific language governing permissions and
109 | limitations under the License.
110 |
111 |
112 | [1]: http://i.imgur.com/iSizH82.png
113 | [2]: https://play.google.com/store/apps/details?id=com.joooonho
114 | [3]: http://developer.android.com/guide/developing/projects/projects-eclipse.html
115 | [4]: http://developer.android.com/guide/developing/projects/projects-eclipse.html#ReferencingLibraryProject
116 | [5]: http://www.joooooooooonhokim.com/?p=289
117 | [6]: http://github.com/vinc3m1/RoundedImageView
118 | [7]: https://github.com/nostra13/Android-Universal-Image-Loader
119 | [8]: https://github.com/square/picasso
120 | [9]: https://github.com/bumptech/glide
121 |
--------------------------------------------------------------------------------
/library/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/library/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | SelectableRoundedImageViewLib
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/library/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/library/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-14
15 | android.library=true
16 |
--------------------------------------------------------------------------------
/library/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/library/src/com/joooonho/SelectableRoundedImageView.java:
--------------------------------------------------------------------------------
1 | package com.joooonho;
2 |
3 | import com.joooonho.R;
4 |
5 | import android.content.Context;
6 | import android.content.res.ColorStateList;
7 | import android.content.res.Resources;
8 | import android.content.res.Resources.NotFoundException;
9 | import android.content.res.TypedArray;
10 | import android.graphics.Bitmap;
11 | import android.graphics.Bitmap.Config;
12 | import android.graphics.BitmapShader;
13 | import android.graphics.Canvas;
14 | import android.graphics.Color;
15 | import android.graphics.ColorFilter;
16 | import android.graphics.Matrix;
17 | import android.graphics.Paint;
18 | import android.graphics.Path;
19 | import android.graphics.PixelFormat;
20 | import android.graphics.Rect;
21 | import android.graphics.RectF;
22 | import android.graphics.Shader;
23 | import android.graphics.drawable.BitmapDrawable;
24 | import android.graphics.drawable.Drawable;
25 | import android.graphics.drawable.LayerDrawable;
26 | import android.net.Uri;
27 | import android.util.AttributeSet;
28 | import android.util.Log;
29 | import android.widget.ImageView;
30 |
31 | public class SelectableRoundedImageView extends ImageView {
32 |
33 | public static final String TAG = "SelectableRoundedImageView";
34 |
35 | private int mResource = 0;
36 |
37 | private static final ScaleType[] sScaleTypeArray = {
38 | ScaleType.MATRIX,
39 | ScaleType.FIT_XY,
40 | ScaleType.FIT_START,
41 | ScaleType.FIT_CENTER,
42 | ScaleType.FIT_END,
43 | ScaleType.CENTER,
44 | ScaleType.CENTER_CROP,
45 | ScaleType.CENTER_INSIDE
46 | };
47 |
48 | // Set default scale type to FIT_CENTER, which is default scale type of
49 | // original ImageView.
50 | private ScaleType mScaleType = ScaleType.FIT_CENTER;
51 |
52 | private float mLeftTopCornerRadius = 0.0f;
53 | private float mRightTopCornerRadius = 0.0f;
54 | private float mLeftBottomCornerRadius = 0.0f;
55 | private float mRightBottomCornerRadius = 0.0f;
56 |
57 | private float mBorderWidth = 0.0f;
58 | private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
59 | private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
60 |
61 | private boolean isOval = false;
62 |
63 | private Drawable mDrawable;
64 |
65 | private float[] mRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 };
66 |
67 | public SelectableRoundedImageView(Context context) {
68 | super(context);
69 | }
70 |
71 | public SelectableRoundedImageView(Context context, AttributeSet attrs) {
72 | this(context, attrs, 0);
73 | }
74 |
75 | public SelectableRoundedImageView(Context context, AttributeSet attrs, int defStyle) {
76 | super(context, attrs, defStyle);
77 |
78 | TypedArray a = context.obtainStyledAttributes(attrs,
79 | R.styleable.SelectableRoundedImageView, defStyle, 0);
80 |
81 | final int index = a.getInt(R.styleable.SelectableRoundedImageView_android_scaleType, -1);
82 | if (index >= 0) {
83 | setScaleType(sScaleTypeArray[index]);
84 | }
85 |
86 | mLeftTopCornerRadius = a.getDimensionPixelSize(
87 | R.styleable.SelectableRoundedImageView_sriv_left_top_corner_radius, 0);
88 | mRightTopCornerRadius = a.getDimensionPixelSize(
89 | R.styleable.SelectableRoundedImageView_sriv_right_top_corner_radius, 0);
90 | mLeftBottomCornerRadius = a.getDimensionPixelSize(
91 | R.styleable.SelectableRoundedImageView_sriv_left_bottom_corner_radius, 0);
92 | mRightBottomCornerRadius = a.getDimensionPixelSize(
93 | R.styleable.SelectableRoundedImageView_sriv_right_bottom_corner_radius, 0);
94 |
95 | if (mLeftTopCornerRadius < 0.0f || mRightTopCornerRadius < 0.0f
96 | || mLeftBottomCornerRadius < 0.0f || mRightBottomCornerRadius < 0.0f) {
97 | throw new IllegalArgumentException("radius values cannot be negative.");
98 | }
99 |
100 | mRadii = new float[] {
101 | mLeftTopCornerRadius, mLeftTopCornerRadius,
102 | mRightTopCornerRadius, mRightTopCornerRadius,
103 | mRightBottomCornerRadius, mRightBottomCornerRadius,
104 | mLeftBottomCornerRadius, mLeftBottomCornerRadius };
105 |
106 | mBorderWidth = a.getDimensionPixelSize(
107 | R.styleable.SelectableRoundedImageView_sriv_border_width, 0);
108 | if (mBorderWidth < 0) {
109 | throw new IllegalArgumentException("border width cannot be negative.");
110 | }
111 |
112 | mBorderColor = a
113 | .getColorStateList(R.styleable.SelectableRoundedImageView_sriv_border_color);
114 | if (mBorderColor == null) {
115 | mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
116 | }
117 |
118 | isOval = a.getBoolean(R.styleable.SelectableRoundedImageView_sriv_oval, false);
119 | a.recycle();
120 |
121 | updateDrawable();
122 | }
123 |
124 | @Override
125 | protected void drawableStateChanged() {
126 | super.drawableStateChanged();
127 | invalidate();
128 | }
129 |
130 | @Override
131 | public ScaleType getScaleType() {
132 | return mScaleType;
133 | }
134 |
135 | @Override
136 | public void setScaleType(ScaleType scaleType) {
137 | super.setScaleType(scaleType);
138 | mScaleType = scaleType;
139 | updateDrawable();
140 | }
141 |
142 | @Override
143 | public void setImageDrawable(Drawable drawable) {
144 | mResource = 0;
145 | mDrawable = SelectableRoundedCornerDrawable.fromDrawable(drawable, getResources());
146 | super.setImageDrawable(mDrawable);
147 | updateDrawable();
148 | }
149 |
150 | @Override
151 | public void setImageBitmap(Bitmap bm) {
152 | mResource = 0;
153 | mDrawable = SelectableRoundedCornerDrawable.fromBitmap(bm, getResources());
154 | super.setImageDrawable(mDrawable);
155 | updateDrawable();
156 | }
157 |
158 | @Override
159 | public void setImageResource(int resId) {
160 | if (mResource != resId) {
161 | mResource = resId;
162 | mDrawable = resolveResource();
163 | super.setImageDrawable(mDrawable);
164 | updateDrawable();
165 | }
166 | }
167 |
168 | @Override
169 | public void setImageURI(Uri uri) {
170 | super.setImageURI(uri);
171 | setImageDrawable(getDrawable());
172 | }
173 |
174 | private Drawable resolveResource() {
175 | Resources rsrc = getResources();
176 | if (rsrc == null) {
177 | return null;
178 | }
179 |
180 | Drawable d = null;
181 |
182 | if (mResource != 0) {
183 | try {
184 | d = rsrc.getDrawable(mResource);
185 | } catch (NotFoundException e) {
186 | Log.w(TAG, "Unable to find resource: " + mResource, e);
187 | // Don't try again.
188 | mResource = 0;
189 | }
190 | }
191 | return SelectableRoundedCornerDrawable.fromDrawable(d, getResources());
192 | }
193 |
194 | private void updateDrawable() {
195 | if (mDrawable == null) {
196 | return;
197 | }
198 |
199 | ((SelectableRoundedCornerDrawable) mDrawable).setScaleType(mScaleType);
200 | ((SelectableRoundedCornerDrawable) mDrawable).setCornerRadii(mRadii);
201 | ((SelectableRoundedCornerDrawable) mDrawable).setBorderWidth(mBorderWidth);
202 | ((SelectableRoundedCornerDrawable) mDrawable).setBorderColor(mBorderColor);
203 | ((SelectableRoundedCornerDrawable) mDrawable).setOval(isOval);
204 | }
205 |
206 | public float getCornerRadius() {
207 | return mLeftTopCornerRadius;
208 | }
209 |
210 | /**
211 | * Set radii for each corner.
212 | *
213 | * @param leftTop The desired radius for left-top corner in dip.
214 | * @param rightTop The desired desired radius for right-top corner in dip.
215 | * @param leftBottom The desired radius for left-bottom corner in dip.
216 | * @param rightBottom The desired radius for right-bottom corner in dip.
217 | *
218 | */
219 | public void setCornerRadiiDP(float leftTop, float rightTop, float leftBottom, float rightBottom) {
220 | final float density = getResources().getDisplayMetrics().density;
221 |
222 | final float lt = leftTop * density;
223 | final float rt = rightTop * density;
224 | final float lb = leftBottom * density;
225 | final float rb = rightBottom * density;
226 |
227 | mRadii = new float[] { lt, lt, rt, rt, rb, rb, lb, lb };
228 | updateDrawable();
229 | }
230 |
231 | public float getBorderWidth() {
232 | return mBorderWidth;
233 | }
234 |
235 | /**
236 | * Set border width.
237 | *
238 | * @param width
239 | * The desired width in dip.
240 | */
241 | public void setBorderWidthDP(float width) {
242 | float scaledWidth = getResources().getDisplayMetrics().density * width;
243 | if (mBorderWidth == scaledWidth) {
244 | return;
245 | }
246 |
247 | mBorderWidth = scaledWidth;
248 | updateDrawable();
249 | invalidate();
250 | }
251 |
252 | public int getBorderColor() {
253 | return mBorderColor.getDefaultColor();
254 | }
255 |
256 | public void setBorderColor(int color) {
257 | setBorderColor(ColorStateList.valueOf(color));
258 | }
259 |
260 | public ColorStateList getBorderColors() {
261 | return mBorderColor;
262 | }
263 |
264 | public void setBorderColor(ColorStateList colors) {
265 | if (mBorderColor.equals(colors)) {
266 | return;
267 | }
268 |
269 | mBorderColor = (colors != null) ? colors : ColorStateList
270 | .valueOf(DEFAULT_BORDER_COLOR);
271 | updateDrawable();
272 | if (mBorderWidth > 0) {
273 | invalidate();
274 | }
275 | }
276 |
277 | public boolean isOval() {
278 | return isOval;
279 | }
280 |
281 | public void setOval(boolean oval) {
282 | isOval = oval;
283 | updateDrawable();
284 | invalidate();
285 | }
286 |
287 | static class SelectableRoundedCornerDrawable extends Drawable {
288 |
289 | private static final String TAG = "SelectableRoundedCornerDrawable";
290 | private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
291 |
292 | private RectF mBounds = new RectF();
293 | private RectF mBorderBounds = new RectF();
294 |
295 | private final RectF mBitmapRect = new RectF();
296 | private final int mBitmapWidth;
297 | private final int mBitmapHeight;
298 |
299 | private final Paint mBitmapPaint;
300 | private final Paint mBorderPaint;
301 |
302 | private BitmapShader mBitmapShader;
303 |
304 | private float[] mRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 };
305 | private float[] mBorderRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 };
306 |
307 | private boolean mOval = false;
308 |
309 | private float mBorderWidth = 0;
310 | private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
311 | // Set default scale type to FIT_CENTER, which is default scale type of
312 | // original ImageView.
313 | private ScaleType mScaleType = ScaleType.FIT_CENTER;
314 |
315 | private Path mPath = new Path();
316 | private Bitmap mBitmap;
317 | private boolean mBoundsConfigured = false;
318 |
319 | public SelectableRoundedCornerDrawable(Bitmap bitmap, Resources r) {
320 | mBitmap = bitmap;
321 | mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
322 |
323 | if (bitmap != null) {
324 | mBitmapWidth = bitmap.getScaledWidth(r.getDisplayMetrics());
325 | mBitmapHeight = bitmap.getScaledHeight(r.getDisplayMetrics());
326 | } else {
327 | mBitmapWidth = mBitmapHeight = -1;
328 | }
329 |
330 | mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight);
331 |
332 | mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
333 | mBitmapPaint.setStyle(Paint.Style.FILL);
334 | mBitmapPaint.setShader(mBitmapShader);
335 |
336 | mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
337 | mBorderPaint.setStyle(Paint.Style.STROKE);
338 | mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));
339 | mBorderPaint.setStrokeWidth(mBorderWidth);
340 | }
341 |
342 | public static SelectableRoundedCornerDrawable fromBitmap(Bitmap bitmap, Resources r) {
343 | if (bitmap != null) {
344 | return new SelectableRoundedCornerDrawable(bitmap, r);
345 | } else {
346 | return null;
347 | }
348 | }
349 |
350 | public static Drawable fromDrawable(Drawable drawable, Resources r) {
351 | if (drawable != null) {
352 | if (drawable instanceof SelectableRoundedCornerDrawable) {
353 | return drawable;
354 | } else if (drawable instanceof LayerDrawable) {
355 | LayerDrawable ld = (LayerDrawable) drawable;
356 | final int num = ld.getNumberOfLayers();
357 | for (int i = 0; i < num; i++) {
358 | Drawable d = ld.getDrawable(i);
359 | ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d, r));
360 | }
361 | return ld;
362 | }
363 |
364 | Bitmap bm = drawableToBitmap(drawable);
365 | if (bm != null) {
366 | return new SelectableRoundedCornerDrawable(bm, r);
367 | } else {
368 | Log.w(TAG, "Failed to create bitmap from drawable!");
369 | }
370 | }
371 | return drawable;
372 | }
373 |
374 | public static Bitmap drawableToBitmap(Drawable drawable) {
375 | if (drawable == null) {
376 | return null;
377 | }
378 |
379 | if (drawable instanceof BitmapDrawable) {
380 | return ((BitmapDrawable) drawable).getBitmap();
381 | }
382 |
383 | Bitmap bitmap;
384 | int width = Math.max(drawable.getIntrinsicWidth(), 2);
385 | int height = Math.max(drawable.getIntrinsicHeight(), 2);
386 | try {
387 | bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
388 | Canvas canvas = new Canvas(bitmap);
389 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
390 | drawable.draw(canvas);
391 | } catch (IllegalArgumentException e) {
392 | e.printStackTrace();
393 | bitmap = null;
394 | }
395 | return bitmap;
396 | }
397 |
398 | @Override
399 | public boolean isStateful() {
400 | return mBorderColor.isStateful();
401 | }
402 |
403 | @Override
404 | protected boolean onStateChange(int[] state) {
405 | int newColor = mBorderColor.getColorForState(state, 0);
406 | if (mBorderPaint.getColor() != newColor) {
407 | mBorderPaint.setColor(newColor);
408 | return true;
409 | } else {
410 | return super.onStateChange(state);
411 | }
412 | }
413 |
414 | private void configureBounds(Canvas canvas) {
415 | // I have discovered a truly marvelous explanation of this,
416 | // which this comment space is too narrow to contain. :)
417 | // If you want to understand what's going on here,
418 | // See http://www.joooooooooonhokim.com/?p=289
419 | Rect clipBounds = canvas.getClipBounds();
420 | Matrix canvasMatrix = canvas.getMatrix();
421 |
422 | if (ScaleType.CENTER == mScaleType) {
423 | mBounds.set(clipBounds);
424 | } else if (ScaleType.CENTER_CROP == mScaleType) {
425 | applyScaleToRadii(canvasMatrix);
426 | mBounds.set(clipBounds);
427 | } else if (ScaleType.FIT_XY == mScaleType) {
428 | Matrix m = new Matrix();
429 | m.setRectToRect(mBitmapRect, new RectF(clipBounds), Matrix.ScaleToFit.FILL);
430 | mBitmapShader.setLocalMatrix(m);
431 | mBounds.set(clipBounds);
432 | } else if (ScaleType.FIT_START == mScaleType || ScaleType.FIT_END == mScaleType
433 | || ScaleType.FIT_CENTER == mScaleType || ScaleType.CENTER_INSIDE == mScaleType) {
434 | applyScaleToRadii(canvasMatrix);
435 | mBounds.set(mBitmapRect);
436 | } else if (ScaleType.MATRIX == mScaleType) {
437 | applyScaleToRadii(canvasMatrix);
438 | mBounds.set(mBitmapRect);
439 | }
440 | }
441 |
442 | private void applyScaleToRadii(Matrix m) {
443 | float[] values = new float[9];
444 | m.getValues(values);
445 | for (int i = 0; i < mRadii.length; i++) {
446 | mRadii[i] = mRadii[i] / values[0];
447 | }
448 | }
449 |
450 | private void adjustCanvasForBorder(Canvas canvas) {
451 | Matrix canvasMatrix = canvas.getMatrix();
452 | final float[] values = new float[9];
453 | canvasMatrix.getValues(values);
454 |
455 | final float scaleFactorX = values[0];
456 | final float scaleFactorY = values[4];
457 | final float translateX = values[2];
458 | final float translateY = values[5];
459 |
460 | final float newScaleX = mBounds.width()
461 | / (mBounds.width() + mBorderWidth + mBorderWidth);
462 | final float newScaleY = mBounds.height()
463 | / (mBounds.height() + mBorderWidth + mBorderWidth);
464 |
465 | canvas.scale(newScaleX, newScaleY);
466 | if (ScaleType.FIT_START == mScaleType || ScaleType.FIT_END == mScaleType
467 | || ScaleType.FIT_XY == mScaleType || ScaleType.FIT_CENTER == mScaleType
468 | || ScaleType.CENTER_INSIDE == mScaleType || ScaleType.MATRIX == mScaleType) {
469 | canvas.translate(mBorderWidth, mBorderWidth);
470 | } else if (ScaleType.CENTER == mScaleType || ScaleType.CENTER_CROP == mScaleType) {
471 | // First, make translate values to 0
472 | canvas.translate(
473 | -translateX / (newScaleX * scaleFactorX),
474 | -translateY / (newScaleY * scaleFactorY));
475 | // Then, set the final translate values.
476 | canvas.translate(-(mBounds.left - mBorderWidth), -(mBounds.top - mBorderWidth));
477 | }
478 | }
479 |
480 | private void adjustBorderWidthAndBorderBounds(Canvas canvas) {
481 | Matrix canvasMatrix = canvas.getMatrix();
482 | final float[] values = new float[9];
483 | canvasMatrix.getValues(values);
484 |
485 | final float scaleFactor = values[0];
486 |
487 | float viewWidth = mBounds.width() * scaleFactor;
488 | mBorderWidth = (mBorderWidth * mBounds.width()) / (viewWidth - (2 * mBorderWidth));
489 | mBorderPaint.setStrokeWidth(mBorderWidth);
490 |
491 | mBorderBounds.set(mBounds);
492 | mBorderBounds.inset(- mBorderWidth / 2, - mBorderWidth / 2);
493 | }
494 |
495 | private void setBorderRadii() {
496 | for (int i = 0; i < mRadii.length; i++) {
497 | if (mRadii[i] > 0) {
498 | mBorderRadii[i] = mRadii[i];
499 | mRadii[i] = mRadii[i] - mBorderWidth;
500 | }
501 | }
502 | }
503 |
504 | @Override
505 | public void draw(Canvas canvas) {
506 | canvas.save();
507 | if (!mBoundsConfigured) {
508 | configureBounds(canvas);
509 | if (mBorderWidth > 0) {
510 | adjustBorderWidthAndBorderBounds(canvas);
511 | setBorderRadii();
512 | }
513 | mBoundsConfigured = true;
514 | }
515 |
516 | if (mOval) {
517 | if (mBorderWidth > 0) {
518 | adjustCanvasForBorder(canvas);
519 | mPath.addOval(mBounds, Path.Direction.CW);
520 | canvas.drawPath(mPath, mBitmapPaint);
521 | mPath.reset();
522 | mPath.addOval(mBorderBounds, Path.Direction.CW);
523 | canvas.drawPath(mPath, mBorderPaint);
524 | } else {
525 | mPath.addOval(mBounds, Path.Direction.CW);
526 | canvas.drawPath(mPath, mBitmapPaint);
527 | }
528 | } else {
529 | if (mBorderWidth > 0) {
530 | adjustCanvasForBorder(canvas);
531 | mPath.addRoundRect(mBounds, mRadii, Path.Direction.CW);
532 | canvas.drawPath(mPath, mBitmapPaint);
533 | mPath.reset();
534 | mPath.addRoundRect(mBorderBounds, mBorderRadii, Path.Direction.CW);
535 | canvas.drawPath(mPath, mBorderPaint);
536 | } else {
537 | mPath.addRoundRect(mBounds, mRadii, Path.Direction.CW);
538 | canvas.drawPath(mPath, mBitmapPaint);
539 | }
540 | }
541 | canvas.restore();
542 | }
543 |
544 | public void setCornerRadii(float[] radii) {
545 | if (radii == null)
546 | return;
547 |
548 | if (radii.length != 8) {
549 | throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
550 | }
551 |
552 | for (int i = 0; i < radii.length; i++) {
553 | mRadii[i] = radii[i];
554 | }
555 | }
556 |
557 | @Override
558 | public int getOpacity() {
559 | return (mBitmap == null || mBitmap.hasAlpha() || mBitmapPaint.getAlpha() < 255) ? PixelFormat.TRANSLUCENT
560 | : PixelFormat.OPAQUE;
561 | }
562 |
563 | @Override
564 | public void setAlpha(int alpha) {
565 | mBitmapPaint.setAlpha(alpha);
566 | invalidateSelf();
567 | }
568 |
569 | @Override
570 | public void setColorFilter(ColorFilter cf) {
571 | mBitmapPaint.setColorFilter(cf);
572 | invalidateSelf();
573 | }
574 |
575 | @Override
576 | public void setDither(boolean dither) {
577 | mBitmapPaint.setDither(dither);
578 | invalidateSelf();
579 | }
580 |
581 | @Override
582 | public void setFilterBitmap(boolean filter) {
583 | mBitmapPaint.setFilterBitmap(filter);
584 | invalidateSelf();
585 | }
586 |
587 | @Override
588 | public int getIntrinsicWidth() {
589 | return mBitmapWidth;
590 | }
591 |
592 | @Override
593 | public int getIntrinsicHeight() {
594 | return mBitmapHeight;
595 | }
596 |
597 | public float getBorderWidth() {
598 | return mBorderWidth;
599 | }
600 |
601 | public void setBorderWidth(float width) {
602 | mBorderWidth = width;
603 | mBorderPaint.setStrokeWidth(width);
604 | }
605 |
606 | public int getBorderColor() {
607 | return mBorderColor.getDefaultColor();
608 | }
609 |
610 | public void setBorderColor(int color) {
611 | setBorderColor(ColorStateList.valueOf(color));
612 | }
613 |
614 | public ColorStateList getBorderColors() {
615 | return mBorderColor;
616 | }
617 |
618 | /**
619 | * Controls border color of this ImageView.
620 | *
621 | * @param colors
622 | * The desired border color. If it's null, no border will be
623 | * drawn.
624 | *
625 | */
626 | public void setBorderColor(ColorStateList colors) {
627 | if (colors == null) {
628 | mBorderWidth = 0;
629 | mBorderColor = ColorStateList.valueOf(Color.TRANSPARENT);
630 | mBorderPaint.setColor(Color.TRANSPARENT);
631 | } else {
632 | mBorderColor = colors;
633 | mBorderPaint.setColor(mBorderColor.getColorForState(getState(),
634 | DEFAULT_BORDER_COLOR));
635 | }
636 | }
637 |
638 | public boolean isOval() {
639 | return mOval;
640 | }
641 |
642 | public void setOval(boolean oval) {
643 | mOval = oval;
644 | }
645 |
646 | public ScaleType getScaleType() {
647 | return mScaleType;
648 | }
649 |
650 | public void setScaleType(ScaleType scaleType) {
651 | if (scaleType == null) {
652 | return;
653 | }
654 | mScaleType = scaleType;
655 | }
656 | }
657 |
658 | }
--------------------------------------------------------------------------------
/sample/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | SelectableRoundedImageViewSample
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/sample/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/sample/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pungrue26/SelectableRoundedImageView/86dcc45b4159d39440353997cdfefa5e57626c81/sample/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/sample/libs/picasso-2.4.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pungrue26/SelectableRoundedImageView/86dcc45b4159d39440353997cdfefa5e57626c81/sample/libs/picasso-2.4.0.jar
--------------------------------------------------------------------------------
/sample/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/sample/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-14
15 | android.library.reference.1=../library
16 |
--------------------------------------------------------------------------------
/sample/res/drawable-hdpi/photo_cheetah.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pungrue26/SelectableRoundedImageView/86dcc45b4159d39440353997cdfefa5e57626c81/sample/res/drawable-hdpi/photo_cheetah.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xhdpi/photo1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pungrue26/SelectableRoundedImageView/86dcc45b4159d39440353997cdfefa5e57626c81/sample/res/drawable-xhdpi/photo1.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xhdpi/photo2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pungrue26/SelectableRoundedImageView/86dcc45b4159d39440353997cdfefa5e57626c81/sample/res/drawable-xhdpi/photo2.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xhdpi/photo3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pungrue26/SelectableRoundedImageView/86dcc45b4159d39440353997cdfefa5e57626c81/sample/res/drawable-xhdpi/photo3.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pungrue26/SelectableRoundedImageView/86dcc45b4159d39440353997cdfefa5e57626c81/sample/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
26 |
27 |
35 |
36 |
42 |
43 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/sample/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Selectable Rounded ImageView
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/com/joooonho/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.joooonho;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.widget.ImageView.ScaleType;
6 |
7 | import com.squareup.picasso.Picasso;
8 |
9 | public class MainActivity extends Activity {
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | setContentView(R.layout.activity_main);
15 |
16 | // All properties can be set in xml.
17 | SelectableRoundedImageView iv0 = (SelectableRoundedImageView) findViewById(R.id.image0);
18 |
19 | // You can set image with resource id.
20 | SelectableRoundedImageView iv1 = (SelectableRoundedImageView) findViewById(R.id.image1);
21 | iv1.setScaleType(ScaleType.CENTER_CROP);
22 | iv1.setOval(true);
23 | iv1.setImageResource(R.drawable.photo_cheetah);
24 |
25 | // Also, You can set image with Picasso.
26 | // This is a normal rectangle imageview.
27 | SelectableRoundedImageView iv2 = (SelectableRoundedImageView) findViewById(R.id.image2);
28 | iv1.setScaleType(ScaleType.CENTER);
29 | Picasso.with(this).load(R.drawable.photo2).into(iv2);
30 |
31 | // Of course, you can set round radius in code.
32 | SelectableRoundedImageView iv3 = (SelectableRoundedImageView) findViewById(R.id.image3);
33 | iv3.setImageDrawable(getResources().getDrawable(R.drawable.photo3));
34 | ((SelectableRoundedImageView)iv3).setCornerRadiiDP(4, 4, 0, 0);
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------