├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── example
├── build.gradle
├── ic_launcher-web.png
├── lint.xml
├── proguard-project.txt
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── makeramen
│ │ └── roundedimageview
│ │ └── example
│ │ ├── ColorFragment.java
│ │ ├── ExampleActivity.java
│ │ ├── PicassoFragment.java
│ │ └── RoundedFragment.java
│ └── res
│ ├── color
│ └── border.xml
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-ldpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-nodpi
│ ├── photo1.jpg
│ ├── photo2.jpg
│ ├── photo3.jpg
│ ├── photo4.jpg
│ ├── photo5.jpg
│ ├── photo6.jpg
│ └── photo7.jpg
│ ├── drawable-xhdpi
│ ├── black_white_tile.png
│ └── ic_launcher.png
│ ├── layout
│ ├── example_activity.xml
│ ├── fragment_rounded.xml
│ ├── picasso_item.xml
│ ├── rounded_background_item.xml
│ ├── rounded_item.xml
│ └── rounded_item_select.xml
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── roundedimageview
├── build.gradle
├── release-proguard.cfg
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── makeramen
│ │ └── roundedimageview
│ │ ├── Corner.java
│ │ ├── RoundedDrawable.java
│ │ ├── RoundedImageView.java
│ │ └── RoundedTransformationBuilder.java
│ └── res
│ └── values
│ ├── about_libraries_def.xml
│ └── attrs.xml
├── screenshot-oval.png
├── screenshot-round-specific-corners.png
├── screenshot.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Gradle files
12 | .gradle
13 |
14 | # generated files
15 | bin/
16 | libs/libray/bin/
17 | gen/
18 | libs/library/gen/
19 | build/
20 |
21 | # Local configuration file (sdk path, etc)
22 | project.properties
23 | local.properties
24 | *.DS_Store
25 |
26 | # IDE
27 |
28 | ## eclipse
29 | *.metadata
30 | *.settings
31 |
32 | ## idea
33 | *.idea
34 | *.iml
35 | */target
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017, Vincent Mi
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 |
6 | Unless required by applicable law or agreed to in writing, software
7 | distributed under the License is distributed on an "AS IS" BASIS,
8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | See the License for the specific language governing permissions and
10 | limitations under the License.
11 |
12 | Apache License
13 | Version 2.0, January 2004
14 | http://www.apache.org/licenses/
15 |
16 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
17 |
18 | 1. Definitions.
19 |
20 | "License" shall mean the terms and conditions for use, reproduction,
21 | and distribution as defined by Sections 1 through 9 of this document.
22 |
23 | "Licensor" shall mean the copyright owner or entity authorized by
24 | the copyright owner that is granting the License.
25 |
26 | "Legal Entity" shall mean the union of the acting entity and all
27 | other entities that control, are controlled by, or are under common
28 | control with that entity. For the purposes of this definition,
29 | "control" means (i) the power, direct or indirect, to cause the
30 | direction or management of such entity, whether by contract or
31 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
32 | outstanding shares, or (iii) beneficial ownership of such entity.
33 |
34 | "You" (or "Your") shall mean an individual or Legal Entity
35 | exercising permissions granted by this License.
36 |
37 | "Source" form shall mean the preferred form for making modifications,
38 | including but not limited to software source code, documentation
39 | source, and configuration files.
40 |
41 | "Object" form shall mean any form resulting from mechanical
42 | transformation or translation of a Source form, including but
43 | not limited to compiled object code, generated documentation,
44 | and conversions to other media types.
45 |
46 | "Work" shall mean the work of authorship, whether in Source or
47 | Object form, made available under the License, as indicated by a
48 | copyright notice that is included in or attached to the work
49 | (an example is provided in the Appendix below).
50 |
51 | "Derivative Works" shall mean any work, whether in Source or Object
52 | form, that is based on (or derived from) the Work and for which the
53 | editorial revisions, annotations, elaborations, or other modifications
54 | represent, as a whole, an original work of authorship. For the purposes
55 | of this License, Derivative Works shall not include works that remain
56 | separable from, or merely link (or bind by name) to the interfaces of,
57 | the Work and Derivative Works thereof.
58 |
59 | "Contribution" shall mean any work of authorship, including
60 | the original version of the Work and any modifications or additions
61 | to that Work or Derivative Works thereof, that is intentionally
62 | submitted to Licensor for inclusion in the Work by the copyright owner
63 | or by an individual or Legal Entity authorized to submit on behalf of
64 | the copyright owner. For the purposes of this definition, "submitted"
65 | means any form of electronic, verbal, or written communication sent
66 | to the Licensor or its representatives, including but not limited to
67 | communication on electronic mailing lists, source code control systems,
68 | and issue tracking systems that are managed by, or on behalf of, the
69 | Licensor for the purpose of discussing and improving the Work, but
70 | excluding communication that is conspicuously marked or otherwise
71 | designated in writing by the copyright owner as "Not a Contribution."
72 |
73 | "Contributor" shall mean Licensor and any individual or Legal Entity
74 | on behalf of whom a Contribution has been received by Licensor and
75 | subsequently incorporated within the Work.
76 |
77 | 2. Grant of Copyright License. Subject to the terms and conditions of
78 | this License, each Contributor hereby grants to You a perpetual,
79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
80 | copyright license to reproduce, prepare Derivative Works of,
81 | publicly display, publicly perform, sublicense, and distribute the
82 | Work and such Derivative Works in Source or Object form.
83 |
84 | 3. Grant of Patent License. Subject to the terms and conditions of
85 | this License, each Contributor hereby grants to You a perpetual,
86 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
87 | (except as stated in this section) patent license to make, have made,
88 | use, offer to sell, sell, import, and otherwise transfer the Work,
89 | where such license applies only to those patent claims licensable
90 | by such Contributor that are necessarily infringed by their
91 | Contribution(s) alone or by combination of their Contribution(s)
92 | with the Work to which such Contribution(s) was submitted. If You
93 | institute patent litigation against any entity (including a
94 | cross-claim or counterclaim in a lawsuit) alleging that the Work
95 | or a Contribution incorporated within the Work constitutes direct
96 | or contributory patent infringement, then any patent licenses
97 | granted to You under this License for that Work shall terminate
98 | as of the date such litigation is filed.
99 |
100 | 4. Redistribution. You may reproduce and distribute copies of the
101 | Work or Derivative Works thereof in any medium, with or without
102 | modifications, and in Source or Object form, provided that You
103 | meet the following conditions:
104 |
105 | (a) You must give any other recipients of the Work or
106 | Derivative Works a copy of this License; and
107 |
108 | (b) You must cause any modified files to carry prominent notices
109 | stating that You changed the files; and
110 |
111 | (c) You must retain, in the Source form of any Derivative Works
112 | that You distribute, all copyright, patent, trademark, and
113 | attribution notices from the Source form of the Work,
114 | excluding those notices that do not pertain to any part of
115 | the Derivative Works; and
116 |
117 | (d) If the Work includes a "NOTICE" text file as part of its
118 | distribution, then any Derivative Works that You distribute must
119 | include a readable copy of the attribution notices contained
120 | within such NOTICE file, excluding those notices that do not
121 | pertain to any part of the Derivative Works, in at least one
122 | of the following places: within a NOTICE text file distributed
123 | as part of the Derivative Works; within the Source form or
124 | documentation, if provided along with the Derivative Works; or,
125 | within a display generated by the Derivative Works, if and
126 | wherever such third-party notices normally appear. The contents
127 | of the NOTICE file are for informational purposes only and
128 | do not modify the License. You may add Your own attribution
129 | notices within Derivative Works that You distribute, alongside
130 | or as an addendum to the NOTICE text from the Work, provided
131 | that such additional attribution notices cannot be construed
132 | as modifying the License.
133 |
134 | You may add Your own copyright statement to Your modifications and
135 | may provide additional or different license terms and conditions
136 | for use, reproduction, or distribution of Your modifications, or
137 | for any such Derivative Works as a whole, provided Your use,
138 | reproduction, and distribution of the Work otherwise complies with
139 | the conditions stated in this License.
140 |
141 | 5. Submission of Contributions. Unless You explicitly state otherwise,
142 | any Contribution intentionally submitted for inclusion in the Work
143 | by You to the Licensor shall be under the terms and conditions of
144 | this License, without any additional terms or conditions.
145 | Notwithstanding the above, nothing herein shall supersede or modify
146 | the terms of any separate license agreement you may have executed
147 | with Licensor regarding such Contributions.
148 |
149 | 6. Trademarks. This License does not grant permission to use the trade
150 | names, trademarks, service marks, or product names of the Licensor,
151 | except as required for reasonable and customary use in describing the
152 | origin of the Work and reproducing the content of the NOTICE file.
153 |
154 | 7. Disclaimer of Warranty. Unless required by applicable law or
155 | agreed to in writing, Licensor provides the Work (and each
156 | Contributor provides its Contributions) on an "AS IS" BASIS,
157 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
158 | implied, including, without limitation, any warranties or conditions
159 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
160 | PARTICULAR PURPOSE. You are solely responsible for determining the
161 | appropriateness of using or redistributing the Work and assume any
162 | risks associated with Your exercise of permissions under this License.
163 |
164 | 8. Limitation of Liability. In no event and under no legal theory,
165 | whether in tort (including negligence), contract, or otherwise,
166 | unless required by applicable law (such as deliberate and grossly
167 | negligent acts) or agreed to in writing, shall any Contributor be
168 | liable to You for damages, including any direct, indirect, special,
169 | incidental, or consequential damages of any character arising as a
170 | result of this License or out of the use or inability to use the
171 | Work (including but not limited to damages for loss of goodwill,
172 | work stoppage, computer failure or malfunction, or any and all
173 | other commercial damages or losses), even if such Contributor
174 | has been advised of the possibility of such damages.
175 |
176 | 9. Accepting Warranty or Additional Liability. While redistributing
177 | the Work or Derivative Works thereof, You may choose to offer,
178 | and charge a fee for, acceptance of support, warranty, indemnity,
179 | or other liability obligations and/or rights consistent with this
180 | License. However, in accepting such obligations, You may act only
181 | on Your own behalf and on Your sole responsibility, not on behalf
182 | of any other Contributor, and only if You agree to indemnify,
183 | defend, and hold each Contributor harmless for any liability
184 | incurred by, or claims asserted against, such Contributor by reason
185 | of your accepting any such warranty or additional liability.
186 |
187 | END OF TERMS AND CONDITIONS
188 |
189 | APPENDIX: How to apply the Apache License to your work.
190 |
191 | To apply the Apache License to your work, attach the following
192 | boilerplate notice, with the fields enclosed by brackets "[]"
193 | replaced with your own identifying information. (Don't include
194 | the brackets!) The text should be enclosed in the appropriate
195 | comment syntax for the file format. We also recommend that a
196 | file or class name and description of purpose be included on the
197 | same "printed page" as the copyright notice for easier
198 | identification within third-party archives.
199 |
200 | Copyright [yyyy] [name of copyright owner]
201 |
202 | Licensed under the Apache License, Version 2.0 (the "License");
203 | you may not use this file except in compliance with the License.
204 | You may obtain a copy of the License at
205 |
206 | http://www.apache.org/licenses/LICENSE-2.0
207 |
208 | Unless required by applicable law or agreed to in writing, software
209 | distributed under the License is distributed on an "AS IS" BASIS,
210 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
211 | See the License for the specific language governing permissions and
212 | limitations under the License.
213 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [ARCHIVED] RoundedImageView is no longer actively maintained as there are better alternatives available
2 |
3 | RoundedImageView was originally made with a primary goal of memory and rendering performance, and specifically only worked well on Bitmaps. It had some very poorly performing hacks built in for non-bitmaps, along with a lot of hard to maintain synchronization of ScaleTypes and sizing between the ImageView and the Drawable itself.
4 |
5 | RoundedImageView has also never supported image loaders like Picasso and Glide well, especially when they leverage TransitionDrawables for fade effects. These use cases are always best served by using the respective library's own Transformation APIs for image manipulation inside the async pipelines where the performance impact is less apparent.
6 |
7 | As the ImageView and Drawable APIs evolved to support even more features like tinting, and Android's hardware acceleration support improved, the need for a library like RoundedImageView has dwindled while the maintenance cost remained. Finally with the inclusion of [RoundedBitmapDrawable](https://developer.android.com/reference/kotlin/androidx/core/graphics/drawable/RoundedBitmapDrawable) in androidx which has a much simpler (and better IMO) API, I have recommended that as the best solution for everyone considering RoundedImageView.
8 |
9 |
10 | ## Here are my recommended alternatives:
11 |
12 | ### 1. For rounding bitmaps bundled in your APK (not loaded from internet or disk) use [RoundedBitmapDrawable](https://developer.android.com/reference/kotlin/androidx/core/graphics/drawable/RoundedBitmapDrawable)
13 |
14 | RoundedBitmapDrawable avoids the many pitfalls of RoundedImageView by:
15 | 1. Working only on Bitmaps and BitmapDrawables which is what RoundedImageView was primrarily built for.
16 | 2. Decoupling the Drawable from the ImageView, which could have different size and ScaleType constraints and was always complicated to synchronize in RoundedImageView.
17 | 3. Decoupling the Drawable from other Drawable wrappers like [TransitionDrawable](https://developer.android.com/reference/android/graphics/drawable/TransitionDrawable) and other [LayerDrawable](https://developer.android.com/reference/android/graphics/drawable/LayerDrawable) and [DrawableContainer](https://developer.android.com/reference/android/graphics/drawable/DrawableContainer) types. This was always hard to manage in RoundedImageView because it was hard to guess the intent of the author whether
18 |
19 | ### 2. For loading from network or disk, use the library's transformation API, for example [wasabeef/glide-transformations](https://github.com/wasabeef/glide-transformations) can round images loaded by [Glide](https://github.com/bumptech/glide), and [wasabeef/picasso-transformations](https://github.com/wasabeef/picasso-transformations) can round images loaded by [Picasso](https://github.com/square/picasso).
20 |
21 | ### 3. For vector drawables and other non-Bitmap drawables, modify the vector or drawable to include rounding for best performance.
22 |
23 | RoundedImageView would try its best to rasterize these to a Bitmap and round them, at a rather high cost to memory (creating an extra bitmap) and fidelity (scaling rasterized bitmaps leads to artifacts). While rasterizing these drawables technically works, the resulting performance and quality impact is in direct contrast with the original goals of RoundedImageView around memory efficiency and image quality and probably should have been left unsupported completely.
24 |
25 | ---
26 |
27 | # RoundedImageView
28 |
29 | [](https://maven-badges.herokuapp.com/maven-central/com.makeramen/roundedimageview)
30 | [](https://android-arsenal.com/details/1/680)
31 |
32 | A fast ImageView (and Drawable) that supports rounded corners (and ovals or circles) based on the original [example from Romain Guy](http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/). It supports many additional features including ovals, rounded rectangles, ScaleTypes and TileModes.
33 |
34 | 
35 | 
36 |
37 | There are many ways to create rounded corners in android, but this is the fastest and best one that I know of because it:
38 | * does **not** create a copy of the original bitmap
39 | * does **not** use a clipPath which is not hardware accelerated and not anti-aliased.
40 | * does **not** use setXfermode to clip the bitmap and draw twice to the canvas.
41 |
42 | If you know of a better method, let me know (or even better open a pull request)!
43 |
44 | Also has proper support for:
45 | * Borders (with Colors and ColorStateLists)
46 | * Ovals and Circles
47 | * All `ScaleType`s
48 | * Borders are drawn at view edge, not bitmap edge
49 | * Except on edges where the bitmap is smaller than the view
50 | * Borders are **not** scaled up/down with the image (correct width and radius are maintained)
51 | * Anti-aliasing
52 | * Transparent backgrounds
53 | * Hardware acceleration
54 | * Support for LayerDrawables (including TransitionDrawables)
55 | * TileModes for repeating drawables
56 |
57 | ## Known Issues
58 |
59 | - VectorDrawables are **not** supported. This library is designed for BitmapDrawables only. Other drawables will likely fail or cause high memory usage.
60 | - ColorDrawables are poorly supported, use your own rounded VectorDrawables instead if you want less memory pressure.
61 | - Glide transforms are **not** supported, please use [wasabeef/glide-transformations](https://github.com/wasabeef/glide-transformations) if you want to round images loaded from Glide.
62 |
63 | ## Gradle
64 |
65 | RoundedImageView is available in [Maven Central](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.makeramen%22%20AND%20a%3A%22roundedimageview%22).
66 |
67 | Add the following to your `build.gradle` to use:
68 | ```
69 | repositories {
70 | mavenCentral()
71 | }
72 |
73 | dependencies {
74 | implementation 'com.makeramen:roundedimageview:2.3.0'
75 | }
76 | ```
77 |
78 |
79 | ## Usage
80 |
81 | Define in xml:
82 |
83 | ```xml
84 |
95 | ```
96 |
97 | Or in code:
98 |
99 | ```java
100 | RoundedImageView riv = new RoundedImageView(context);
101 | riv.setScaleType(ScaleType.CENTER_CROP);
102 | riv.setCornerRadius((float) 10);
103 | riv.setBorderWidth((float) 2);
104 | riv.setBorderColor(Color.DKGRAY);
105 | riv.mutateBackground(true);
106 | riv.setImageDrawable(drawable);
107 | riv.setBackground(backgroundDrawable);
108 | riv.setOval(true);
109 | riv.setTileModeX(Shader.TileMode.REPEAT);
110 | riv.setTileModeY(Shader.TileMode.REPEAT);
111 | ```
112 |
113 | ### Picasso
114 |
115 | To make a Transformation for Picasso:
116 |
117 | ```java
118 | Transformation transformation = new RoundedTransformationBuilder()
119 | .borderColor(Color.BLACK)
120 | .borderWidthDp(3)
121 | .cornerRadiusDp(30)
122 | .oval(false)
123 | .build();
124 |
125 | Picasso.with(context)
126 | .load(url)
127 | .fit()
128 | .transform(transformation)
129 | .into(imageView);
130 | ```
131 |
132 | ## Changelog
133 |
134 | see [Releases](https://github.com/vinc3m1/RoundedImageView/releases)
135 |
136 |
137 |
138 |
139 | ## License
140 |
141 | Copyright 2017 Vincent Mi
142 |
143 | Licensed under the Apache License, Version 2.0 (the "License");
144 | you may not use this file except in compliance with the License.
145 | You may obtain a copy of the License at
146 |
147 | http://www.apache.org/licenses/LICENSE-2.0
148 |
149 | Unless required by applicable law or agreed to in writing, software
150 | distributed under the License is distributed on an "AS IS" BASIS,
151 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
152 | See the License for the specific language governing permissions and
153 | limitations under the License.
154 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | group 'com.makeramen'
14 | version '2.3.0'
15 | repositories {
16 | mavenCentral()
17 | jcenter()
18 | }
19 | }
20 |
21 | ext {
22 | compileSdkVersion = 25
23 | buildToolsVersion = "25.0.2"
24 | }
--------------------------------------------------------------------------------
/example/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | repositories {
4 | mavenCentral()
5 | // maven {
6 | // url 'https://oss.sonatype.org/content/repositories/snapshots/'
7 | // url 'https://oss.sonatype.org/content/repositories/releases/'
8 | // }
9 | }
10 |
11 | dependencies {
12 | //compile 'com.makeramen:roundedimageview:2.0.0-SNAPSHOT'
13 | compile project(':roundedimageview')
14 | compile 'com.squareup.picasso:picasso:2.5.2'
15 | compile 'com.github.bumptech.glide:glide:3.7.0'
16 | compile 'com.android.support:support-v4:25.3.1'
17 | compile 'com.android.support:appcompat-v7:25.3.1'
18 | }
19 |
20 | android {
21 | compileSdkVersion rootProject.ext.compileSdkVersion
22 | buildToolsVersion rootProject.ext.buildToolsVersion
23 |
24 | defaultConfig {
25 | applicationId "com.makeramen.roundedimageview.example"
26 | minSdkVersion 14
27 | targetSdkVersion 25
28 | versionCode 1
29 | versionName version
30 | }
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_7
34 | targetCompatibility JavaVersion.VERSION_1_7
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/ic_launcher-web.png
--------------------------------------------------------------------------------
/example/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/example/src/main/java/com/makeramen/roundedimageview/example/ColorFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Vincent Mi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.makeramen.roundedimageview.example;
18 |
19 | import android.content.Context;
20 | import android.os.Bundle;
21 | import android.support.v4.app.Fragment;
22 | import android.view.LayoutInflater;
23 | import android.view.View;
24 | import android.view.ViewGroup;
25 | import android.widget.ArrayAdapter;
26 | import android.widget.ImageView;
27 | import android.widget.ImageView.ScaleType;
28 | import android.widget.ListView;
29 | import android.widget.TextView;
30 |
31 | public class ColorFragment extends Fragment {
32 |
33 | @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
34 | Bundle savedInstanceState) {
35 |
36 | View view = inflater.inflate(R.layout.fragment_rounded, container, false);
37 |
38 | StreamAdapter adapter = new StreamAdapter(getActivity());
39 | ((ListView) view.findViewById(R.id.main_list)).setAdapter(adapter);
40 |
41 | adapter.add(
42 | new ColorItem(android.R.color.darker_gray, "Tufa at night", "Mono Lake, CA",
43 | ScaleType.CENTER));
44 | adapter.add(
45 | new ColorItem(android.R.color.holo_orange_dark, "Starry night", "Lake Powell, AZ",
46 | ScaleType.CENTER_CROP));
47 | adapter.add(
48 | new ColorItem(android.R.color.holo_blue_dark, "Racetrack playa", "Death Valley, CA",
49 | ScaleType.CENTER_INSIDE));
50 | adapter.add(
51 | new ColorItem(android.R.color.holo_green_dark, "Napali coast", "Kauai, HI",
52 | ScaleType.FIT_CENTER));
53 | adapter.add(
54 | new ColorItem(android.R.color.holo_red_dark, "Delicate Arch", "Arches, UT",
55 | ScaleType.FIT_END));
56 | adapter.add(
57 | new ColorItem(android.R.color.holo_purple, "Sierra sunset", "Lone Pine, CA",
58 | ScaleType.FIT_START));
59 | adapter.add(
60 | new ColorItem(android.R.color.white, "Majestic", "Grand Teton, WY",
61 | ScaleType.FIT_XY));
62 |
63 | return view;
64 | }
65 |
66 | class ColorItem {
67 | final int mResId;
68 | final String mLine1;
69 | final String mLine2;
70 | final ScaleType mScaleType;
71 |
72 | ColorItem(int resid, String line1, String line2, ScaleType scaleType) {
73 | mResId = resid;
74 | mLine1 = line1;
75 | mLine2 = line2;
76 | mScaleType = scaleType;
77 | }
78 | }
79 |
80 | class StreamAdapter extends ArrayAdapter {
81 | private final LayoutInflater mInflater;
82 |
83 | public StreamAdapter(Context context) {
84 | super(context, 0);
85 | mInflater = LayoutInflater.from(getContext());
86 | }
87 |
88 | @Override
89 | public View getView(int position, View convertView, ViewGroup parent) {
90 | ViewGroup view;
91 | if (convertView == null) {
92 | view = (ViewGroup) mInflater.inflate(R.layout.rounded_item, parent, false);
93 | } else {
94 | view = (ViewGroup) convertView;
95 | }
96 |
97 | ColorItem item = getItem(position);
98 |
99 | ((ImageView) view.findViewById(R.id.imageView1)).setImageResource(item.mResId);
100 | ((ImageView) view.findViewById(R.id.imageView1)).setScaleType(item.mScaleType);
101 | ((TextView) view.findViewById(R.id.textView1)).setText(item.mLine1);
102 | ((TextView) view.findViewById(R.id.textView2)).setText(item.mLine2);
103 | ((TextView) view.findViewById(R.id.textView3)).setText(item.mScaleType.toString());
104 | return view;
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/example/src/main/java/com/makeramen/roundedimageview/example/ExampleActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Vincent Mi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.makeramen.roundedimageview.example;
18 |
19 | import android.os.Bundle;
20 | import android.support.v4.app.Fragment;
21 | import android.support.v7.app.AppCompatActivity;
22 | import android.support.v7.widget.Toolbar;
23 | import android.view.View;
24 | import android.widget.AdapterView;
25 | import android.widget.ArrayAdapter;
26 | import android.widget.Spinner;
27 |
28 | public class ExampleActivity extends AppCompatActivity
29 | implements AdapterView.OnItemSelectedListener {
30 |
31 | @Override protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 |
34 | setContentView(R.layout.example_activity);
35 |
36 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
37 | Spinner navSpinner = (Spinner) findViewById(R.id.spinner_nav);
38 |
39 | navSpinner.setAdapter(ArrayAdapter.createFromResource(
40 | navSpinner.getContext(),
41 | R.array.action_list,
42 | android.R.layout.simple_spinner_dropdown_item));
43 |
44 | navSpinner.setOnItemSelectedListener(this);
45 |
46 | if (savedInstanceState == null) {
47 | navSpinner.setSelection(0);
48 | }
49 | }
50 |
51 | @Override public void onItemSelected(AdapterView> parent, View view, int position, long id) {
52 | Fragment newFragment;
53 | switch (position) {
54 | default:
55 | case 0:
56 | // bitmap
57 | newFragment = RoundedFragment.getInstance(RoundedFragment.ExampleType.DEFAULT);
58 | break;
59 | case 1:
60 | // oval
61 | newFragment = RoundedFragment.getInstance(RoundedFragment.ExampleType.OVAL);
62 | break;
63 | case 2:
64 | // select
65 | newFragment = RoundedFragment.getInstance(RoundedFragment.ExampleType.SELECT_CORNERS);
66 | break;
67 | case 3:
68 | // picasso
69 | newFragment = new PicassoFragment();
70 | break;
71 | case 4:
72 | // color
73 | newFragment = new ColorFragment();
74 | break;
75 | case 5:
76 | // background
77 | newFragment = RoundedFragment.getInstance(RoundedFragment.ExampleType.BACKGROUND);
78 | break;
79 | }
80 |
81 | getSupportFragmentManager().beginTransaction()
82 | .replace(R.id.fragment_container, newFragment)
83 | .commit();
84 | }
85 |
86 | @Override public void onNothingSelected(AdapterView> parent) { }
87 | }
88 |
--------------------------------------------------------------------------------
/example/src/main/java/com/makeramen/roundedimageview/example/PicassoFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Vincent Mi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.makeramen.roundedimageview.example;
18 |
19 | import android.content.Context;
20 | import android.graphics.Color;
21 | import android.os.Bundle;
22 | import android.support.annotation.NonNull;
23 | import android.support.v4.app.Fragment;
24 | import android.view.LayoutInflater;
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 | import android.widget.ArrayAdapter;
28 | import android.widget.ImageView;
29 | import android.widget.ImageView.ScaleType;
30 | import android.widget.ListView;
31 | import android.widget.TextView;
32 | import com.makeramen.roundedimageview.RoundedTransformationBuilder;
33 | import com.squareup.picasso.Picasso;
34 | import com.squareup.picasso.Transformation;
35 |
36 | public class PicassoFragment extends Fragment {
37 |
38 | @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
39 | Bundle savedInstanceState) {
40 |
41 | View view = inflater.inflate(R.layout.fragment_rounded, container, false);
42 |
43 | PicassoAdapter adapter = new PicassoAdapter(getActivity());
44 | ((ListView) view.findViewById(R.id.main_list)).setAdapter(adapter);
45 |
46 | adapter.add(new PicassoItem("http://24.media.tumblr.com/2176464a507f8a34f09d58ee7fcf105a/tumblr_mzgzd79XMY1st5lhmo1_1280.jpg", ScaleType.CENTER));
47 | adapter.add(new PicassoItem("http://25.media.tumblr.com/af50758346e388e6e69f4c378c4f264f/tumblr_mzgzcdEDTL1st5lhmo1_1280.jpg", ScaleType.CENTER_CROP));
48 | adapter.add(new PicassoItem("http://24.media.tumblr.com/5f97f94756bf706bf41ac0dd37b585cf/tumblr_mzgzbdYBht1st5lhmo1_1280.jpg", ScaleType.CENTER_INSIDE));
49 | adapter.add(new PicassoItem("http://24.media.tumblr.com/6ddffd6a6036f61a1f2b1744bad77730/tumblr_mzgz9vJ1CK1st5lhmo1_1280.jpg", ScaleType.FIT_CENTER));
50 | adapter.add(new PicassoItem("http://25.media.tumblr.com/104330dfee76bb4713ea6c424a339b31/tumblr_mzgz92BX471st5lhmo1_1280.jpg", ScaleType.FIT_END));
51 | adapter.add(new PicassoItem("http://25.media.tumblr.com/c2aa498a075ab4b0c1b7c56120c140ab/tumblr_mzgz8arzYo1st5lhmo1_1280.jpg", ScaleType.FIT_START));
52 | adapter.add(new PicassoItem("http://25.media.tumblr.com/e886622da66651f4818f441e3120127d/tumblr_mzgz6yFP0u1st5lhmo1_1280.jpg", ScaleType.FIT_XY));
53 |
54 | return view;
55 | }
56 |
57 | static class PicassoItem {
58 | final String mUrl;
59 |
60 | final ScaleType mScaleType;
61 | PicassoItem(String url, ScaleType scaleType) {
62 | this.mUrl = url;
63 | mScaleType = scaleType;
64 | }
65 |
66 | }
67 |
68 | class PicassoAdapter extends ArrayAdapter {
69 | private final LayoutInflater mInflater;
70 | private final Transformation mTransformation;
71 |
72 | public PicassoAdapter(Context context) {
73 | super(context, 0);
74 | mInflater = LayoutInflater.from(getContext());
75 | mTransformation = new RoundedTransformationBuilder()
76 | .cornerRadiusDp(30)
77 | .borderColor(Color.BLACK)
78 | .borderWidthDp(3)
79 | .oval(false)
80 | .build();
81 | }
82 |
83 | @Override public View getView(int position, View convertView, @NonNull ViewGroup parent) {
84 | ViewGroup view;
85 | if (convertView == null) {
86 | view = (ViewGroup) mInflater.inflate(R.layout.picasso_item, parent, false);
87 | } else {
88 | view = (ViewGroup) convertView;
89 | }
90 |
91 | PicassoItem item = getItem(position);
92 |
93 | ImageView imageView = ((ImageView) view.findViewById(R.id.imageView1));
94 | imageView.setScaleType(item.mScaleType);
95 |
96 | Picasso.with(getContext())
97 | .load(item.mUrl)
98 | .fit()
99 | .transform(mTransformation)
100 | .into(imageView);
101 |
102 | ((TextView) view.findViewById(R.id.textView3)).setText(item.mScaleType.toString());
103 | return view;
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/example/src/main/java/com/makeramen/roundedimageview/example/RoundedFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Vincent Mi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.makeramen.roundedimageview.example;
18 |
19 | import android.content.Context;
20 | import android.graphics.Bitmap;
21 | import android.graphics.BitmapFactory;
22 | import android.graphics.Shader;
23 | import android.os.Bundle;
24 | import android.support.annotation.NonNull;
25 | import android.support.v4.app.Fragment;
26 | import android.view.LayoutInflater;
27 | import android.view.View;
28 | import android.view.ViewGroup;
29 | import android.widget.ArrayAdapter;
30 | import android.widget.ImageView.ScaleType;
31 | import android.widget.ListView;
32 | import android.widget.TextView;
33 |
34 | import com.makeramen.roundedimageview.RoundedImageView;
35 |
36 | public class RoundedFragment extends Fragment {
37 |
38 | static final String ARG_EXAMPLE_TYPE = "example_type";
39 |
40 | private ExampleType exampleType;
41 |
42 | public static RoundedFragment getInstance(ExampleType exampleType) {
43 | RoundedFragment f = new RoundedFragment();
44 | Bundle args = new Bundle();
45 | args.putString(ARG_EXAMPLE_TYPE, exampleType.name());
46 | f.setArguments(args);
47 | return f;
48 | }
49 |
50 | @Override public void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 |
53 | if (getArguments() != null) {
54 | exampleType = ExampleType.valueOf(getArguments().getString(ARG_EXAMPLE_TYPE));
55 | }
56 | }
57 |
58 | @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
59 | Bundle savedInstanceState) {
60 | View view = inflater.inflate(R.layout.fragment_rounded, container, false);
61 |
62 | StreamAdapter adapter = new StreamAdapter(getActivity());
63 |
64 | adapter.add(new StreamItem(getActivity(),
65 | R.drawable.photo1, "Tufa at night", "Mono Lake, CA", ScaleType.CENTER));
66 | adapter.add(new StreamItem(getActivity(),
67 | R.drawable.photo2, "Starry night", "Lake Powell, AZ", ScaleType.CENTER_CROP));
68 | adapter.add(new StreamItem(getActivity(),
69 | R.drawable.photo3, "Racetrack playa", "Death Valley, CA", ScaleType.CENTER_INSIDE));
70 | adapter.add(new StreamItem(getActivity(),
71 | R.drawable.photo4, "Napali coast", "Kauai, HI", ScaleType.FIT_CENTER));
72 | adapter.add(new StreamItem(getActivity(),
73 | R.drawable.photo5, "Delicate Arch", "Arches, UT", ScaleType.FIT_END));
74 | adapter.add(new StreamItem(getActivity(),
75 | R.drawable.photo6, "Sierra sunset", "Lone Pine, CA", ScaleType.FIT_START));
76 | adapter.add(new StreamItem(getActivity(),
77 | R.drawable.photo7, "Majestic", "Grand Teton, WY", ScaleType.FIT_XY));
78 | adapter.add(new StreamItem(getActivity(),
79 | R.drawable.black_white_tile, "TileMode", "REPEAT", ScaleType.FIT_XY,
80 | Shader.TileMode.REPEAT));
81 | adapter.add(new StreamItem(getActivity(),
82 | R.drawable.black_white_tile, "TileMode", "CLAMP", ScaleType.FIT_XY,
83 | Shader.TileMode.CLAMP));
84 | adapter.add(new StreamItem(getActivity(),
85 | R.drawable.black_white_tile, "TileMode", "MIRROR", ScaleType.FIT_XY,
86 | Shader.TileMode.MIRROR));
87 |
88 | ((ListView) view.findViewById(R.id.main_list)).setAdapter(adapter);
89 | return view;
90 | }
91 |
92 | class StreamItem {
93 | final Bitmap mBitmap;
94 | final String mLine1;
95 | final String mLine2;
96 | final ScaleType mScaleType;
97 | final Shader.TileMode mTileMode;
98 |
99 | StreamItem(Context c, int resid, String line1, String line2, ScaleType scaleType) {
100 | this(c, resid, line1, line2, scaleType, Shader.TileMode.CLAMP);
101 | }
102 |
103 | StreamItem(Context c, int resid, String line1, String line2, ScaleType scaleType,
104 | Shader.TileMode tileMode) {
105 | mBitmap = BitmapFactory.decodeResource(c.getResources(), resid);
106 | mLine1 = line1;
107 | mLine2 = line2;
108 | mScaleType = scaleType;
109 | mTileMode = tileMode;
110 | }
111 | }
112 |
113 | class StreamAdapter extends ArrayAdapter {
114 | private final LayoutInflater mInflater;
115 |
116 | public StreamAdapter(Context context) {
117 | super(context, 0);
118 | mInflater = LayoutInflater.from(getContext());
119 | }
120 |
121 | @Override
122 | public View getView(int position, View convertView, @NonNull ViewGroup parent) {
123 | ViewGroup view;
124 | if (convertView == null) {
125 | if (exampleType == ExampleType.SELECT_CORNERS) {
126 | view = (ViewGroup) mInflater.inflate(R.layout.rounded_item_select, parent, false);
127 | } else if (exampleType == ExampleType.BACKGROUND) {
128 | view = (ViewGroup) mInflater.inflate(R.layout.rounded_background_item, parent, false);
129 | } else {
130 | view = (ViewGroup) mInflater.inflate(R.layout.rounded_item, parent, false);
131 | }
132 | } else {
133 | view = (ViewGroup) convertView;
134 | }
135 |
136 | StreamItem item = getItem(position);
137 |
138 | RoundedImageView iv = ((RoundedImageView) view.findViewById(R.id.imageView1));
139 | iv.setOval(exampleType == ExampleType.OVAL);
140 | iv.setImageBitmap(item.mBitmap);
141 | iv.setScaleType(item.mScaleType);
142 | iv.setTileModeX(item.mTileMode);
143 | iv.setTileModeY(item.mTileMode);
144 |
145 | ((TextView) view.findViewById(R.id.textView1)).setText(item.mLine1);
146 | ((TextView) view.findViewById(R.id.textView2)).setText(item.mLine2);
147 | ((TextView) view.findViewById(R.id.textView3)).setText(item.mScaleType.toString());
148 |
149 | return view;
150 | }
151 | }
152 |
153 | public enum ExampleType {
154 | DEFAULT,
155 | OVAL,
156 | SELECT_CORNERS,
157 | BACKGROUND
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/example/src/main/res/color/border.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/photo1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-nodpi/photo1.jpg
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/photo2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-nodpi/photo2.jpg
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/photo3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-nodpi/photo3.jpg
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/photo4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-nodpi/photo4.jpg
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/photo5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-nodpi/photo5.jpg
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/photo6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-nodpi/photo6.jpg
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/photo7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-nodpi/photo7.jpg
--------------------------------------------------------------------------------
/example/src/main/res/drawable-xhdpi/black_white_tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-xhdpi/black_white_tile.png
--------------------------------------------------------------------------------
/example/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/example/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/layout/example_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
16 |
21 |
22 |
27 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/fragment_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/picasso_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
21 |
22 |
37 |
38 |
51 |
52 |
65 |
66 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/rounded_background_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
29 |
30 |
45 |
46 |
59 |
60 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/rounded_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
26 |
27 |
42 |
43 |
56 |
57 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/rounded_item_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
27 |
28 |
43 |
44 |
57 |
58 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/example/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #8BC34A
4 | #689F38
5 | #4CAF50
6 |
--------------------------------------------------------------------------------
/example/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | RoundedImageView
4 | RoundedPicassoView
5 |
6 |
7 | - Bitmap
8 | - Ovals
9 | - Select Corners
10 | - Picasso
11 | - Color
12 | - Background
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Apr 03 18:53:38 PDT 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/roundedimageview/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'maven'
3 | apply plugin: 'signing'
4 |
5 | def sonatypeUsername = project.hasProperty('sonatypeUsername') ? sonatypeUsername : ""
6 | def sonatypePassword = project.hasProperty('sonatypePassword') ? sonatypePassword : ""
7 |
8 | android {
9 | compileSdkVersion rootProject.ext.compileSdkVersion
10 | buildToolsVersion rootProject.ext.buildToolsVersion
11 |
12 | defaultConfig {
13 | minSdkVersion 8
14 | consumerProguardFiles 'release-proguard.cfg'
15 | versionCode 1
16 | versionName version
17 | }
18 |
19 | compileOptions {
20 | sourceCompatibility JavaVersion.VERSION_1_7
21 | targetCompatibility JavaVersion.VERSION_1_7
22 | }
23 | }
24 |
25 | dependencies {
26 | provided 'com.squareup.picasso:picasso:2.5.2'
27 | provided 'com.android.support:support-annotations:25.3.1'
28 | }
29 |
30 | task androidJavadocs(type: Javadoc) {
31 | source = android.sourceSets.main.java.source
32 | }
33 |
34 | task androidJavadocsJar(type: Jar) {
35 | classifier = 'javadoc'
36 | //basename = artifact_id
37 | from androidJavadocs.destinationDir
38 | }
39 |
40 | task androidSourcesJar(type: Jar) {
41 | classifier = 'sources'
42 | //basename = artifact_id
43 | from android.sourceSets.main.java.source
44 | }
45 |
46 | artifacts {
47 | archives androidSourcesJar
48 | archives androidJavadocsJar
49 | }
50 |
51 | signing {
52 | required { has("release") && gradle.taskGraph.hasTask("uploadArchives") }
53 | sign configurations.archives
54 | }
55 |
56 | uploadArchives {
57 | repositories.mavenDeployer {
58 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
59 |
60 | repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2/') {
61 | authentication(userName: sonatypeUsername, password: sonatypePassword)
62 | }
63 |
64 | snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') {
65 | authentication(userName: sonatypeUsername, password: sonatypePassword)
66 | }
67 |
68 | pom {
69 | project {
70 | name 'RoundedImageView'
71 | packaging 'aar'
72 |
73 | description 'Fast ImageView with support for rounded corners and borders'
74 | url 'https://github.com/vinc3m1/RoundedImageView'
75 |
76 | scm {
77 | url 'scm:git@github.com:vinc3m1/RoundedImageView.git'
78 | connection 'scm:git@github.com:vinc3m1/RoundedImageView.git'
79 | developerConnection 'scm:git@github.com:vinc3m1/RoundedImageView.git'
80 | }
81 |
82 | licenses {
83 | license {
84 | name 'The Apache Software License, Version 2.0'
85 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
86 | distribution 'repo'
87 | }
88 | }
89 |
90 | developers {
91 | developer {
92 | id 'vinc3m1'
93 | name 'Vince Mi'
94 | email 'vince@makeramen.com'
95 | }
96 | }
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/roundedimageview/release-proguard.cfg:
--------------------------------------------------------------------------------
1 | # Proguard configuration.
2 | -dontwarn com.squareup.okhttp.**
3 |
4 | # References to Picasso are okay if the consuming app doesn't use it
5 | -dontwarn com.squareup.picasso.Transformation
6 |
--------------------------------------------------------------------------------
/roundedimageview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/roundedimageview/src/main/java/com/makeramen/roundedimageview/Corner.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Vincent Mi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.makeramen.roundedimageview;
18 |
19 | import android.support.annotation.IntDef;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 |
23 | @Retention(RetentionPolicy.SOURCE)
24 | @IntDef({
25 | Corner.TOP_LEFT, Corner.TOP_RIGHT,
26 | Corner.BOTTOM_LEFT, Corner.BOTTOM_RIGHT
27 | })
28 | public @interface Corner {
29 | int TOP_LEFT = 0;
30 | int TOP_RIGHT = 1;
31 | int BOTTOM_RIGHT = 2;
32 | int BOTTOM_LEFT = 3;
33 | }
34 |
--------------------------------------------------------------------------------
/roundedimageview/src/main/java/com/makeramen/roundedimageview/RoundedDrawable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Vincent Mi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.makeramen.roundedimageview;
18 |
19 | import android.content.res.ColorStateList;
20 | import android.graphics.Bitmap;
21 | import android.graphics.Bitmap.Config;
22 | import android.graphics.BitmapShader;
23 | import android.graphics.Canvas;
24 | import android.graphics.Color;
25 | import android.graphics.ColorFilter;
26 | import android.graphics.Matrix;
27 | import android.graphics.Paint;
28 | import android.graphics.PixelFormat;
29 | import android.graphics.Rect;
30 | import android.graphics.RectF;
31 | import android.graphics.Shader;
32 | import android.graphics.drawable.BitmapDrawable;
33 | import android.graphics.drawable.Drawable;
34 | import android.graphics.drawable.LayerDrawable;
35 | import android.support.annotation.ColorInt;
36 | import android.support.annotation.NonNull;
37 | import android.util.Log;
38 | import android.widget.ImageView.ScaleType;
39 | import java.util.HashSet;
40 | import java.util.Set;
41 | import java.lang.Throwable;
42 |
43 | @SuppressWarnings("UnusedDeclaration")
44 | public class RoundedDrawable extends Drawable {
45 |
46 | public static final String TAG = "RoundedDrawable";
47 | public static final int DEFAULT_BORDER_COLOR = Color.BLACK;
48 |
49 | private final RectF mBounds = new RectF();
50 | private final RectF mDrawableRect = new RectF();
51 | private final RectF mBitmapRect = new RectF();
52 | private final Bitmap mBitmap;
53 | private final Paint mBitmapPaint;
54 | private final int mBitmapWidth;
55 | private final int mBitmapHeight;
56 | private final RectF mBorderRect = new RectF();
57 | private final Paint mBorderPaint;
58 | private final Matrix mShaderMatrix = new Matrix();
59 | private final RectF mSquareCornersRect = new RectF();
60 |
61 | private Shader.TileMode mTileModeX = Shader.TileMode.CLAMP;
62 | private Shader.TileMode mTileModeY = Shader.TileMode.CLAMP;
63 | private boolean mRebuildShader = true;
64 |
65 | private float mCornerRadius = 0f;
66 | // [ topLeft, topRight, bottomLeft, bottomRight ]
67 | private final boolean[] mCornersRounded = new boolean[] { true, true, true, true };
68 |
69 | private boolean mOval = false;
70 | private float mBorderWidth = 0;
71 | private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
72 | private ScaleType mScaleType = ScaleType.FIT_CENTER;
73 |
74 | public RoundedDrawable(Bitmap bitmap) {
75 | mBitmap = bitmap;
76 |
77 | mBitmapWidth = bitmap.getWidth();
78 | mBitmapHeight = bitmap.getHeight();
79 | mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight);
80 |
81 | mBitmapPaint = new Paint();
82 | mBitmapPaint.setStyle(Paint.Style.FILL);
83 | mBitmapPaint.setAntiAlias(true);
84 |
85 | mBorderPaint = new Paint();
86 | mBorderPaint.setStyle(Paint.Style.STROKE);
87 | mBorderPaint.setAntiAlias(true);
88 | mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));
89 | mBorderPaint.setStrokeWidth(mBorderWidth);
90 | }
91 |
92 | public static RoundedDrawable fromBitmap(Bitmap bitmap) {
93 | if (bitmap != null) {
94 | return new RoundedDrawable(bitmap);
95 | } else {
96 | return null;
97 | }
98 | }
99 |
100 | public static Drawable fromDrawable(Drawable drawable) {
101 | if (drawable != null) {
102 | if (drawable instanceof RoundedDrawable) {
103 | // just return if it's already a RoundedDrawable
104 | return drawable;
105 | } else if (drawable instanceof LayerDrawable) {
106 | ConstantState cs = drawable.mutate().getConstantState();
107 | LayerDrawable ld = (LayerDrawable) (cs != null ? cs.newDrawable() : drawable);
108 |
109 | int num = ld.getNumberOfLayers();
110 |
111 | // loop through layers to and change to RoundedDrawables if possible
112 | for (int i = 0; i < num; i++) {
113 | Drawable d = ld.getDrawable(i);
114 | ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d));
115 | }
116 | return ld;
117 | }
118 |
119 | // try to get a bitmap from the drawable and
120 | Bitmap bm = drawableToBitmap(drawable);
121 | if (bm != null) {
122 | return new RoundedDrawable(bm);
123 | }
124 | }
125 | return drawable;
126 | }
127 |
128 | public static Bitmap drawableToBitmap(Drawable drawable) {
129 | if (drawable instanceof BitmapDrawable) {
130 | return ((BitmapDrawable) drawable).getBitmap();
131 | }
132 |
133 | Bitmap bitmap;
134 | int width = Math.max(drawable.getIntrinsicWidth(), 2);
135 | int height = Math.max(drawable.getIntrinsicHeight(), 2);
136 | try {
137 | bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
138 | Canvas canvas = new Canvas(bitmap);
139 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
140 | drawable.draw(canvas);
141 | } catch (Throwable e) {
142 | e.printStackTrace();
143 | Log.w(TAG, "Failed to create bitmap from drawable!");
144 | bitmap = null;
145 | }
146 |
147 | return bitmap;
148 | }
149 |
150 | public Bitmap getSourceBitmap() {
151 | return mBitmap;
152 | }
153 |
154 | @Override
155 | public boolean isStateful() {
156 | return mBorderColor.isStateful();
157 | }
158 |
159 | @Override
160 | protected boolean onStateChange(int[] state) {
161 | int newColor = mBorderColor.getColorForState(state, 0);
162 | if (mBorderPaint.getColor() != newColor) {
163 | mBorderPaint.setColor(newColor);
164 | return true;
165 | } else {
166 | return super.onStateChange(state);
167 | }
168 | }
169 |
170 | private void updateShaderMatrix() {
171 | float scale;
172 | float dx;
173 | float dy;
174 |
175 | switch (mScaleType) {
176 | case CENTER:
177 | mBorderRect.set(mBounds);
178 | mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
179 |
180 | mShaderMatrix.reset();
181 | mShaderMatrix.setTranslate((int) ((mBorderRect.width() - mBitmapWidth) * 0.5f + 0.5f),
182 | (int) ((mBorderRect.height() - mBitmapHeight) * 0.5f + 0.5f));
183 | break;
184 |
185 | case CENTER_CROP:
186 | mBorderRect.set(mBounds);
187 | mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
188 |
189 | mShaderMatrix.reset();
190 |
191 | dx = 0;
192 | dy = 0;
193 |
194 | if (mBitmapWidth * mBorderRect.height() > mBorderRect.width() * mBitmapHeight) {
195 | scale = mBorderRect.height() / (float) mBitmapHeight;
196 | dx = (mBorderRect.width() - mBitmapWidth * scale) * 0.5f;
197 | } else {
198 | scale = mBorderRect.width() / (float) mBitmapWidth;
199 | dy = (mBorderRect.height() - mBitmapHeight * scale) * 0.5f;
200 | }
201 |
202 | mShaderMatrix.setScale(scale, scale);
203 | mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth / 2,
204 | (int) (dy + 0.5f) + mBorderWidth / 2);
205 | break;
206 |
207 | case CENTER_INSIDE:
208 | mShaderMatrix.reset();
209 |
210 | if (mBitmapWidth <= mBounds.width() && mBitmapHeight <= mBounds.height()) {
211 | scale = 1.0f;
212 | } else {
213 | scale = Math.min(mBounds.width() / (float) mBitmapWidth,
214 | mBounds.height() / (float) mBitmapHeight);
215 | }
216 |
217 | dx = (int) ((mBounds.width() - mBitmapWidth * scale) * 0.5f + 0.5f);
218 | dy = (int) ((mBounds.height() - mBitmapHeight * scale) * 0.5f + 0.5f);
219 |
220 | mShaderMatrix.setScale(scale, scale);
221 | mShaderMatrix.postTranslate(dx, dy);
222 |
223 | mBorderRect.set(mBitmapRect);
224 | mShaderMatrix.mapRect(mBorderRect);
225 | mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
226 | mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL);
227 | break;
228 |
229 | default:
230 | case FIT_CENTER:
231 | mBorderRect.set(mBitmapRect);
232 | mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.CENTER);
233 | mShaderMatrix.mapRect(mBorderRect);
234 | mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
235 | mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL);
236 | break;
237 |
238 | case FIT_END:
239 | mBorderRect.set(mBitmapRect);
240 | mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.END);
241 | mShaderMatrix.mapRect(mBorderRect);
242 | mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
243 | mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL);
244 | break;
245 |
246 | case FIT_START:
247 | mBorderRect.set(mBitmapRect);
248 | mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.START);
249 | mShaderMatrix.mapRect(mBorderRect);
250 | mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
251 | mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL);
252 | break;
253 |
254 | case FIT_XY:
255 | mBorderRect.set(mBounds);
256 | mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
257 | mShaderMatrix.reset();
258 | mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL);
259 | break;
260 | }
261 |
262 | mDrawableRect.set(mBorderRect);
263 | mRebuildShader = true;
264 | }
265 |
266 | @Override
267 | protected void onBoundsChange(@NonNull Rect bounds) {
268 | super.onBoundsChange(bounds);
269 |
270 | mBounds.set(bounds);
271 |
272 | updateShaderMatrix();
273 | }
274 |
275 | @Override
276 | public void draw(@NonNull Canvas canvas) {
277 | if (mRebuildShader) {
278 | BitmapShader bitmapShader = new BitmapShader(mBitmap, mTileModeX, mTileModeY);
279 | if (mTileModeX == Shader.TileMode.CLAMP && mTileModeY == Shader.TileMode.CLAMP) {
280 | bitmapShader.setLocalMatrix(mShaderMatrix);
281 | }
282 | mBitmapPaint.setShader(bitmapShader);
283 | mRebuildShader = false;
284 | }
285 |
286 | if (mOval) {
287 | if (mBorderWidth > 0) {
288 | canvas.drawOval(mDrawableRect, mBitmapPaint);
289 | canvas.drawOval(mBorderRect, mBorderPaint);
290 | } else {
291 | canvas.drawOval(mDrawableRect, mBitmapPaint);
292 | }
293 | } else {
294 | if (any(mCornersRounded)) {
295 | float radius = mCornerRadius;
296 | if (mBorderWidth > 0) {
297 | canvas.drawRoundRect(mDrawableRect, radius, radius, mBitmapPaint);
298 | canvas.drawRoundRect(mBorderRect, radius, radius, mBorderPaint);
299 | redrawBitmapForSquareCorners(canvas);
300 | redrawBorderForSquareCorners(canvas);
301 | } else {
302 | canvas.drawRoundRect(mDrawableRect, radius, radius, mBitmapPaint);
303 | redrawBitmapForSquareCorners(canvas);
304 | }
305 | } else {
306 | canvas.drawRect(mDrawableRect, mBitmapPaint);
307 | if (mBorderWidth > 0) {
308 | canvas.drawRect(mBorderRect, mBorderPaint);
309 | }
310 | }
311 | }
312 | }
313 |
314 | private void redrawBitmapForSquareCorners(Canvas canvas) {
315 | if (all(mCornersRounded)) {
316 | // no square corners
317 | return;
318 | }
319 |
320 | if (mCornerRadius == 0) {
321 | return; // no round corners
322 | }
323 |
324 | float left = mDrawableRect.left;
325 | float top = mDrawableRect.top;
326 | float right = left + mDrawableRect.width();
327 | float bottom = top + mDrawableRect.height();
328 | float radius = mCornerRadius;
329 |
330 | if (!mCornersRounded[Corner.TOP_LEFT]) {
331 | mSquareCornersRect.set(left, top, left + radius, top + radius);
332 | canvas.drawRect(mSquareCornersRect, mBitmapPaint);
333 | }
334 |
335 | if (!mCornersRounded[Corner.TOP_RIGHT]) {
336 | mSquareCornersRect.set(right - radius, top, right, radius);
337 | canvas.drawRect(mSquareCornersRect, mBitmapPaint);
338 | }
339 |
340 | if (!mCornersRounded[Corner.BOTTOM_RIGHT]) {
341 | mSquareCornersRect.set(right - radius, bottom - radius, right, bottom);
342 | canvas.drawRect(mSquareCornersRect, mBitmapPaint);
343 | }
344 |
345 | if (!mCornersRounded[Corner.BOTTOM_LEFT]) {
346 | mSquareCornersRect.set(left, bottom - radius, left + radius, bottom);
347 | canvas.drawRect(mSquareCornersRect, mBitmapPaint);
348 | }
349 | }
350 |
351 | private void redrawBorderForSquareCorners(Canvas canvas) {
352 | if (all(mCornersRounded)) {
353 | // no square corners
354 | return;
355 | }
356 |
357 | if (mCornerRadius == 0) {
358 | return; // no round corners
359 | }
360 |
361 | float left = mDrawableRect.left;
362 | float top = mDrawableRect.top;
363 | float right = left + mDrawableRect.width();
364 | float bottom = top + mDrawableRect.height();
365 | float radius = mCornerRadius;
366 | float offset = mBorderWidth / 2;
367 |
368 | if (!mCornersRounded[Corner.TOP_LEFT]) {
369 | canvas.drawLine(left - offset, top, left + radius, top, mBorderPaint);
370 | canvas.drawLine(left, top - offset, left, top + radius, mBorderPaint);
371 | }
372 |
373 | if (!mCornersRounded[Corner.TOP_RIGHT]) {
374 | canvas.drawLine(right - radius - offset, top, right, top, mBorderPaint);
375 | canvas.drawLine(right, top - offset, right, top + radius, mBorderPaint);
376 | }
377 |
378 | if (!mCornersRounded[Corner.BOTTOM_RIGHT]) {
379 | canvas.drawLine(right - radius - offset, bottom, right + offset, bottom, mBorderPaint);
380 | canvas.drawLine(right, bottom - radius, right, bottom, mBorderPaint);
381 | }
382 |
383 | if (!mCornersRounded[Corner.BOTTOM_LEFT]) {
384 | canvas.drawLine(left - offset, bottom, left + radius, bottom, mBorderPaint);
385 | canvas.drawLine(left, bottom - radius, left, bottom, mBorderPaint);
386 | }
387 | }
388 |
389 | @Override
390 | public int getOpacity() {
391 | return PixelFormat.TRANSLUCENT;
392 | }
393 |
394 | @Override
395 | public int getAlpha() {
396 | return mBitmapPaint.getAlpha();
397 | }
398 |
399 | @Override
400 | public void setAlpha(int alpha) {
401 | mBitmapPaint.setAlpha(alpha);
402 | invalidateSelf();
403 | }
404 |
405 | @Override
406 | public ColorFilter getColorFilter() {
407 | return mBitmapPaint.getColorFilter();
408 | }
409 |
410 | @Override
411 | public void setColorFilter(ColorFilter cf) {
412 | mBitmapPaint.setColorFilter(cf);
413 | invalidateSelf();
414 | }
415 |
416 | @Override
417 | public void setDither(boolean dither) {
418 | mBitmapPaint.setDither(dither);
419 | invalidateSelf();
420 | }
421 |
422 | @Override
423 | public void setFilterBitmap(boolean filter) {
424 | mBitmapPaint.setFilterBitmap(filter);
425 | invalidateSelf();
426 | }
427 |
428 | @Override
429 | public int getIntrinsicWidth() {
430 | return mBitmapWidth;
431 | }
432 |
433 | @Override
434 | public int getIntrinsicHeight() {
435 | return mBitmapHeight;
436 | }
437 |
438 | /**
439 | * @return the corner radius.
440 | */
441 | public float getCornerRadius() {
442 | return mCornerRadius;
443 | }
444 |
445 | /**
446 | * @param corner the specific corner to get radius of.
447 | * @return the corner radius of the specified corner.
448 | */
449 | public float getCornerRadius(@Corner int corner) {
450 | return mCornersRounded[corner] ? mCornerRadius : 0f;
451 | }
452 |
453 | /**
454 | * Sets all corners to the specified radius.
455 | *
456 | * @param radius the radius.
457 | * @return the {@link RoundedDrawable} for chaining.
458 | */
459 | public RoundedDrawable setCornerRadius(float radius) {
460 | setCornerRadius(radius, radius, radius, radius);
461 | return this;
462 | }
463 |
464 | /**
465 | * Sets the corner radius of one specific corner.
466 | *
467 | * @param corner the corner.
468 | * @param radius the radius.
469 | * @return the {@link RoundedDrawable} for chaining.
470 | */
471 | public RoundedDrawable setCornerRadius(@Corner int corner, float radius) {
472 | if (radius != 0 && mCornerRadius != 0 && mCornerRadius != radius) {
473 | throw new IllegalArgumentException("Multiple nonzero corner radii not yet supported.");
474 | }
475 |
476 | if (radius == 0) {
477 | if (only(corner, mCornersRounded)) {
478 | mCornerRadius = 0;
479 | }
480 | mCornersRounded[corner] = false;
481 | } else {
482 | if (mCornerRadius == 0) {
483 | mCornerRadius = radius;
484 | }
485 | mCornersRounded[corner] = true;
486 | }
487 |
488 | return this;
489 | }
490 |
491 | /**
492 | * Sets the corner radii of all the corners.
493 | *
494 | * @param topLeft top left corner radius.
495 | * @param topRight top right corner radius
496 | * @param bottomRight bototm right corner radius.
497 | * @param bottomLeft bottom left corner radius.
498 | * @return the {@link RoundedDrawable} for chaining.
499 | */
500 | public RoundedDrawable setCornerRadius(float topLeft, float topRight, float bottomRight,
501 | float bottomLeft) {
502 | Set radiusSet = new HashSet<>(4);
503 | radiusSet.add(topLeft);
504 | radiusSet.add(topRight);
505 | radiusSet.add(bottomRight);
506 | radiusSet.add(bottomLeft);
507 |
508 | radiusSet.remove(0f);
509 |
510 | if (radiusSet.size() > 1) {
511 | throw new IllegalArgumentException("Multiple nonzero corner radii not yet supported.");
512 | }
513 |
514 | if (!radiusSet.isEmpty()) {
515 | float radius = radiusSet.iterator().next();
516 | if (Float.isInfinite(radius) || Float.isNaN(radius) || radius < 0) {
517 | throw new IllegalArgumentException("Invalid radius value: " + radius);
518 | }
519 | mCornerRadius = radius;
520 | } else {
521 | mCornerRadius = 0f;
522 | }
523 |
524 | mCornersRounded[Corner.TOP_LEFT] = topLeft > 0;
525 | mCornersRounded[Corner.TOP_RIGHT] = topRight > 0;
526 | mCornersRounded[Corner.BOTTOM_RIGHT] = bottomRight > 0;
527 | mCornersRounded[Corner.BOTTOM_LEFT] = bottomLeft > 0;
528 | return this;
529 | }
530 |
531 | public float getBorderWidth() {
532 | return mBorderWidth;
533 | }
534 |
535 | public RoundedDrawable setBorderWidth(float width) {
536 | mBorderWidth = width;
537 | mBorderPaint.setStrokeWidth(mBorderWidth);
538 | return this;
539 | }
540 |
541 | public int getBorderColor() {
542 | return mBorderColor.getDefaultColor();
543 | }
544 |
545 | public RoundedDrawable setBorderColor(@ColorInt int color) {
546 | return setBorderColor(ColorStateList.valueOf(color));
547 | }
548 |
549 | public ColorStateList getBorderColors() {
550 | return mBorderColor;
551 | }
552 |
553 | public RoundedDrawable setBorderColor(ColorStateList colors) {
554 | mBorderColor = colors != null ? colors : ColorStateList.valueOf(0);
555 | mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));
556 | return this;
557 | }
558 |
559 | public boolean isOval() {
560 | return mOval;
561 | }
562 |
563 | public RoundedDrawable setOval(boolean oval) {
564 | mOval = oval;
565 | return this;
566 | }
567 |
568 | public ScaleType getScaleType() {
569 | return mScaleType;
570 | }
571 |
572 | public RoundedDrawable setScaleType(ScaleType scaleType) {
573 | if (scaleType == null) {
574 | scaleType = ScaleType.FIT_CENTER;
575 | }
576 | if (mScaleType != scaleType) {
577 | mScaleType = scaleType;
578 | updateShaderMatrix();
579 | }
580 | return this;
581 | }
582 |
583 | public Shader.TileMode getTileModeX() {
584 | return mTileModeX;
585 | }
586 |
587 | public RoundedDrawable setTileModeX(Shader.TileMode tileModeX) {
588 | if (mTileModeX != tileModeX) {
589 | mTileModeX = tileModeX;
590 | mRebuildShader = true;
591 | invalidateSelf();
592 | }
593 | return this;
594 | }
595 |
596 | public Shader.TileMode getTileModeY() {
597 | return mTileModeY;
598 | }
599 |
600 | public RoundedDrawable setTileModeY(Shader.TileMode tileModeY) {
601 | if (mTileModeY != tileModeY) {
602 | mTileModeY = tileModeY;
603 | mRebuildShader = true;
604 | invalidateSelf();
605 | }
606 | return this;
607 | }
608 |
609 | private static boolean only(int index, boolean[] booleans) {
610 | for (int i = 0, len = booleans.length; i < len; i++) {
611 | if (booleans[i] != (i == index)) {
612 | return false;
613 | }
614 | }
615 | return true;
616 | }
617 |
618 | private static boolean any(boolean[] booleans) {
619 | for (boolean b : booleans) {
620 | if (b) { return true; }
621 | }
622 | return false;
623 | }
624 |
625 | private static boolean all(boolean[] booleans) {
626 | for (boolean b : booleans) {
627 | if (b) { return false; }
628 | }
629 | return true;
630 | }
631 |
632 | public Bitmap toBitmap() {
633 | return drawableToBitmap(this);
634 | }
635 | }
636 |
--------------------------------------------------------------------------------
/roundedimageview/src/main/java/com/makeramen/roundedimageview/RoundedImageView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Vincent Mi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.makeramen.roundedimageview;
18 |
19 | import android.content.Context;
20 | import android.content.res.ColorStateList;
21 | import android.content.res.Resources;
22 | import android.content.res.TypedArray;
23 | import android.graphics.Bitmap;
24 | import android.graphics.ColorFilter;
25 | import android.graphics.Shader;
26 | import android.graphics.drawable.ColorDrawable;
27 | import android.graphics.drawable.Drawable;
28 | import android.graphics.drawable.LayerDrawable;
29 | import android.net.Uri;
30 | import android.support.annotation.ColorInt;
31 | import android.support.annotation.DimenRes;
32 | import android.support.annotation.DrawableRes;
33 | import android.util.AttributeSet;
34 | import android.util.Log;
35 | import android.widget.ImageView;
36 |
37 | @SuppressWarnings("UnusedDeclaration")
38 | public class RoundedImageView extends ImageView {
39 |
40 | // Constants for tile mode attributes
41 | private static final int TILE_MODE_UNDEFINED = -2;
42 | private static final int TILE_MODE_CLAMP = 0;
43 | private static final int TILE_MODE_REPEAT = 1;
44 | private static final int TILE_MODE_MIRROR = 2;
45 |
46 | public static final String TAG = "RoundedImageView";
47 | public static final float DEFAULT_RADIUS = 0f;
48 | public static final float DEFAULT_BORDER_WIDTH = 0f;
49 | public static final Shader.TileMode DEFAULT_TILE_MODE = Shader.TileMode.CLAMP;
50 | private static final ScaleType[] SCALE_TYPES = {
51 | ScaleType.MATRIX,
52 | ScaleType.FIT_XY,
53 | ScaleType.FIT_START,
54 | ScaleType.FIT_CENTER,
55 | ScaleType.FIT_END,
56 | ScaleType.CENTER,
57 | ScaleType.CENTER_CROP,
58 | ScaleType.CENTER_INSIDE
59 | };
60 |
61 | private final float[] mCornerRadii =
62 | new float[] { DEFAULT_RADIUS, DEFAULT_RADIUS, DEFAULT_RADIUS, DEFAULT_RADIUS };
63 |
64 | private Drawable mBackgroundDrawable;
65 | private ColorStateList mBorderColor =
66 | ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR);
67 | private float mBorderWidth = DEFAULT_BORDER_WIDTH;
68 | private ColorFilter mColorFilter = null;
69 | private boolean mColorMod = false;
70 | private Drawable mDrawable;
71 | private boolean mHasColorFilter = false;
72 | private boolean mIsOval = false;
73 | private boolean mMutateBackground = false;
74 | private int mResource;
75 | private int mBackgroundResource;
76 | private ScaleType mScaleType;
77 | private Shader.TileMode mTileModeX = DEFAULT_TILE_MODE;
78 | private Shader.TileMode mTileModeY = DEFAULT_TILE_MODE;
79 |
80 | public RoundedImageView(Context context) {
81 | super(context);
82 | }
83 |
84 | public RoundedImageView(Context context, AttributeSet attrs) {
85 | this(context, attrs, 0);
86 | }
87 |
88 | public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
89 | super(context, attrs, defStyle);
90 |
91 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundedImageView, defStyle, 0);
92 |
93 | int index = a.getInt(R.styleable.RoundedImageView_android_scaleType, -1);
94 | if (index >= 0) {
95 | setScaleType(SCALE_TYPES[index]);
96 | } else {
97 | // default scaletype to FIT_CENTER
98 | setScaleType(ScaleType.FIT_CENTER);
99 | }
100 |
101 | float cornerRadiusOverride =
102 | a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius, -1);
103 |
104 | mCornerRadii[Corner.TOP_LEFT] =
105 | a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius_top_left, -1);
106 | mCornerRadii[Corner.TOP_RIGHT] =
107 | a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius_top_right, -1);
108 | mCornerRadii[Corner.BOTTOM_RIGHT] =
109 | a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius_bottom_right, -1);
110 | mCornerRadii[Corner.BOTTOM_LEFT] =
111 | a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius_bottom_left, -1);
112 |
113 | boolean any = false;
114 | for (int i = 0, len = mCornerRadii.length; i < len; i++) {
115 | if (mCornerRadii[i] < 0) {
116 | mCornerRadii[i] = 0f;
117 | } else {
118 | any = true;
119 | }
120 | }
121 |
122 | if (!any) {
123 | if (cornerRadiusOverride < 0) {
124 | cornerRadiusOverride = DEFAULT_RADIUS;
125 | }
126 | for (int i = 0, len = mCornerRadii.length; i < len; i++) {
127 | mCornerRadii[i] = cornerRadiusOverride;
128 | }
129 | }
130 |
131 | mBorderWidth = a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_border_width, -1);
132 | if (mBorderWidth < 0) {
133 | mBorderWidth = DEFAULT_BORDER_WIDTH;
134 | }
135 |
136 | mBorderColor = a.getColorStateList(R.styleable.RoundedImageView_riv_border_color);
137 | if (mBorderColor == null) {
138 | mBorderColor = ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR);
139 | }
140 |
141 | mMutateBackground = a.getBoolean(R.styleable.RoundedImageView_riv_mutate_background, false);
142 | mIsOval = a.getBoolean(R.styleable.RoundedImageView_riv_oval, false);
143 |
144 | final int tileMode = a.getInt(R.styleable.RoundedImageView_riv_tile_mode, TILE_MODE_UNDEFINED);
145 | if (tileMode != TILE_MODE_UNDEFINED) {
146 | setTileModeX(parseTileMode(tileMode));
147 | setTileModeY(parseTileMode(tileMode));
148 | }
149 |
150 | final int tileModeX =
151 | a.getInt(R.styleable.RoundedImageView_riv_tile_mode_x, TILE_MODE_UNDEFINED);
152 | if (tileModeX != TILE_MODE_UNDEFINED) {
153 | setTileModeX(parseTileMode(tileModeX));
154 | }
155 |
156 | final int tileModeY =
157 | a.getInt(R.styleable.RoundedImageView_riv_tile_mode_y, TILE_MODE_UNDEFINED);
158 | if (tileModeY != TILE_MODE_UNDEFINED) {
159 | setTileModeY(parseTileMode(tileModeY));
160 | }
161 |
162 | updateDrawableAttrs();
163 | updateBackgroundDrawableAttrs(true);
164 |
165 | if (mMutateBackground) {
166 | //noinspection deprecation
167 | super.setBackgroundDrawable(mBackgroundDrawable);
168 | }
169 |
170 | a.recycle();
171 | }
172 |
173 | private static Shader.TileMode parseTileMode(int tileMode) {
174 | switch (tileMode) {
175 | case TILE_MODE_CLAMP:
176 | return Shader.TileMode.CLAMP;
177 | case TILE_MODE_REPEAT:
178 | return Shader.TileMode.REPEAT;
179 | case TILE_MODE_MIRROR:
180 | return Shader.TileMode.MIRROR;
181 | default:
182 | return null;
183 | }
184 | }
185 |
186 | @Override
187 | protected void drawableStateChanged() {
188 | super.drawableStateChanged();
189 | invalidate();
190 | }
191 |
192 | @Override
193 | public ScaleType getScaleType() {
194 | return mScaleType;
195 | }
196 |
197 | @Override
198 | public void setScaleType(ScaleType scaleType) {
199 | assert scaleType != null;
200 |
201 | if (mScaleType != scaleType) {
202 | mScaleType = scaleType;
203 |
204 | switch (scaleType) {
205 | case CENTER:
206 | case CENTER_CROP:
207 | case CENTER_INSIDE:
208 | case FIT_CENTER:
209 | case FIT_START:
210 | case FIT_END:
211 | case FIT_XY:
212 | super.setScaleType(ScaleType.FIT_XY);
213 | break;
214 | default:
215 | super.setScaleType(scaleType);
216 | break;
217 | }
218 |
219 | updateDrawableAttrs();
220 | updateBackgroundDrawableAttrs(false);
221 | invalidate();
222 | }
223 | }
224 |
225 | @Override
226 | public void setImageDrawable(Drawable drawable) {
227 | mResource = 0;
228 | mDrawable = RoundedDrawable.fromDrawable(drawable);
229 | updateDrawableAttrs();
230 | super.setImageDrawable(mDrawable);
231 | }
232 |
233 | @Override
234 | public void setImageBitmap(Bitmap bm) {
235 | mResource = 0;
236 | mDrawable = RoundedDrawable.fromBitmap(bm);
237 | updateDrawableAttrs();
238 | super.setImageDrawable(mDrawable);
239 | }
240 |
241 | @Override
242 | public void setImageResource(@DrawableRes int resId) {
243 | if (mResource != resId) {
244 | mResource = resId;
245 | mDrawable = resolveResource();
246 | updateDrawableAttrs();
247 | super.setImageDrawable(mDrawable);
248 | }
249 | }
250 |
251 | @Override public void setImageURI(Uri uri) {
252 | super.setImageURI(uri);
253 | setImageDrawable(getDrawable());
254 | }
255 |
256 | private Drawable resolveResource() {
257 | Resources rsrc = getResources();
258 | if (rsrc == null) { return null; }
259 |
260 | Drawable d = null;
261 |
262 | if (mResource != 0) {
263 | try {
264 | d = rsrc.getDrawable(mResource);
265 | } catch (Exception e) {
266 | Log.w(TAG, "Unable to find resource: " + mResource, e);
267 | // Don't try again.
268 | mResource = 0;
269 | }
270 | }
271 | return RoundedDrawable.fromDrawable(d);
272 | }
273 |
274 | @Override
275 | public void setBackground(Drawable background) {
276 | setBackgroundDrawable(background);
277 | }
278 |
279 | @Override
280 | public void setBackgroundResource(@DrawableRes int resId) {
281 | if (mBackgroundResource != resId) {
282 | mBackgroundResource = resId;
283 | mBackgroundDrawable = resolveBackgroundResource();
284 | setBackgroundDrawable(mBackgroundDrawable);
285 | }
286 | }
287 |
288 | @Override
289 | public void setBackgroundColor(int color) {
290 | mBackgroundDrawable = new ColorDrawable(color);
291 | setBackgroundDrawable(mBackgroundDrawable);
292 | }
293 |
294 | private Drawable resolveBackgroundResource() {
295 | Resources rsrc = getResources();
296 | if (rsrc == null) { return null; }
297 |
298 | Drawable d = null;
299 |
300 | if (mBackgroundResource != 0) {
301 | try {
302 | d = rsrc.getDrawable(mBackgroundResource);
303 | } catch (Exception e) {
304 | Log.w(TAG, "Unable to find resource: " + mBackgroundResource, e);
305 | // Don't try again.
306 | mBackgroundResource = 0;
307 | }
308 | }
309 | return RoundedDrawable.fromDrawable(d);
310 | }
311 |
312 | private void updateDrawableAttrs() {
313 | updateAttrs(mDrawable, mScaleType);
314 | }
315 |
316 | private void updateBackgroundDrawableAttrs(boolean convert) {
317 | if (mMutateBackground) {
318 | if (convert) {
319 | mBackgroundDrawable = RoundedDrawable.fromDrawable(mBackgroundDrawable);
320 | }
321 | updateAttrs(mBackgroundDrawable, ScaleType.FIT_XY);
322 | }
323 | }
324 |
325 | @Override public void setColorFilter(ColorFilter cf) {
326 | if (mColorFilter != cf) {
327 | mColorFilter = cf;
328 | mHasColorFilter = true;
329 | mColorMod = true;
330 | applyColorMod();
331 | invalidate();
332 | }
333 | }
334 |
335 | private void applyColorMod() {
336 | // Only mutate and apply when modifications have occurred. This should
337 | // not reset the mColorMod flag, since these filters need to be
338 | // re-applied if the Drawable is changed.
339 | if (mDrawable != null && mColorMod) {
340 | mDrawable = mDrawable.mutate();
341 | if (mHasColorFilter) {
342 | mDrawable.setColorFilter(mColorFilter);
343 | }
344 | // TODO: support, eventually...
345 | //mDrawable.setXfermode(mXfermode);
346 | //mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
347 | }
348 | }
349 |
350 | private void updateAttrs(Drawable drawable, ScaleType scaleType) {
351 | if (drawable == null) { return; }
352 |
353 | if (drawable instanceof RoundedDrawable) {
354 | ((RoundedDrawable) drawable)
355 | .setScaleType(scaleType)
356 | .setBorderWidth(mBorderWidth)
357 | .setBorderColor(mBorderColor)
358 | .setOval(mIsOval)
359 | .setTileModeX(mTileModeX)
360 | .setTileModeY(mTileModeY);
361 |
362 | if (mCornerRadii != null) {
363 | ((RoundedDrawable) drawable).setCornerRadius(
364 | mCornerRadii[Corner.TOP_LEFT],
365 | mCornerRadii[Corner.TOP_RIGHT],
366 | mCornerRadii[Corner.BOTTOM_RIGHT],
367 | mCornerRadii[Corner.BOTTOM_LEFT]);
368 | }
369 |
370 | applyColorMod();
371 | } else if (drawable instanceof LayerDrawable) {
372 | // loop through layers to and set drawable attrs
373 | LayerDrawable ld = ((LayerDrawable) drawable);
374 | for (int i = 0, layers = ld.getNumberOfLayers(); i < layers; i++) {
375 | updateAttrs(ld.getDrawable(i), scaleType);
376 | }
377 | }
378 | }
379 |
380 | @Override
381 | @Deprecated
382 | public void setBackgroundDrawable(Drawable background) {
383 | mBackgroundDrawable = background;
384 | updateBackgroundDrawableAttrs(true);
385 | //noinspection deprecation
386 | super.setBackgroundDrawable(mBackgroundDrawable);
387 | }
388 |
389 | /**
390 | * @return the largest corner radius.
391 | */
392 | public float getCornerRadius() {
393 | return getMaxCornerRadius();
394 | }
395 |
396 | /**
397 | * @return the largest corner radius.
398 | */
399 | public float getMaxCornerRadius() {
400 | float maxRadius = 0;
401 | for (float r : mCornerRadii) {
402 | maxRadius = Math.max(r, maxRadius);
403 | }
404 | return maxRadius;
405 | }
406 |
407 | /**
408 | * Get the corner radius of a specified corner.
409 | *
410 | * @param corner the corner.
411 | * @return the radius.
412 | */
413 | public float getCornerRadius(@Corner int corner) {
414 | return mCornerRadii[corner];
415 | }
416 |
417 | /**
418 | * Set all the corner radii from a dimension resource id.
419 | *
420 | * @param resId dimension resource id of radii.
421 | */
422 | public void setCornerRadiusDimen(@DimenRes int resId) {
423 | float radius = getResources().getDimension(resId);
424 | setCornerRadius(radius, radius, radius, radius);
425 | }
426 |
427 | /**
428 | * Set the corner radius of a specific corner from a dimension resource id.
429 | *
430 | * @param corner the corner to set.
431 | * @param resId the dimension resource id of the corner radius.
432 | */
433 | public void setCornerRadiusDimen(@Corner int corner, @DimenRes int resId) {
434 | setCornerRadius(corner, getResources().getDimensionPixelSize(resId));
435 | }
436 |
437 | /**
438 | * Set the corner radii of all corners in px.
439 | *
440 | * @param radius the radius to set.
441 | */
442 | public void setCornerRadius(float radius) {
443 | setCornerRadius(radius, radius, radius, radius);
444 | }
445 |
446 | /**
447 | * Set the corner radius of a specific corner in px.
448 | *
449 | * @param corner the corner to set.
450 | * @param radius the corner radius to set in px.
451 | */
452 | public void setCornerRadius(@Corner int corner, float radius) {
453 | if (mCornerRadii[corner] == radius) {
454 | return;
455 | }
456 | mCornerRadii[corner] = radius;
457 |
458 | updateDrawableAttrs();
459 | updateBackgroundDrawableAttrs(false);
460 | invalidate();
461 | }
462 |
463 | /**
464 | * Set the corner radii of each corner individually. Currently only one unique nonzero value is
465 | * supported.
466 | *
467 | * @param topLeft radius of the top left corner in px.
468 | * @param topRight radius of the top right corner in px.
469 | * @param bottomRight radius of the bottom right corner in px.
470 | * @param bottomLeft radius of the bottom left corner in px.
471 | */
472 | public void setCornerRadius(float topLeft, float topRight, float bottomLeft, float bottomRight) {
473 | if (mCornerRadii[Corner.TOP_LEFT] == topLeft
474 | && mCornerRadii[Corner.TOP_RIGHT] == topRight
475 | && mCornerRadii[Corner.BOTTOM_RIGHT] == bottomRight
476 | && mCornerRadii[Corner.BOTTOM_LEFT] == bottomLeft) {
477 | return;
478 | }
479 |
480 | mCornerRadii[Corner.TOP_LEFT] = topLeft;
481 | mCornerRadii[Corner.TOP_RIGHT] = topRight;
482 | mCornerRadii[Corner.BOTTOM_LEFT] = bottomLeft;
483 | mCornerRadii[Corner.BOTTOM_RIGHT] = bottomRight;
484 |
485 | updateDrawableAttrs();
486 | updateBackgroundDrawableAttrs(false);
487 | invalidate();
488 | }
489 |
490 | public float getBorderWidth() {
491 | return mBorderWidth;
492 | }
493 |
494 | public void setBorderWidth(@DimenRes int resId) {
495 | setBorderWidth(getResources().getDimension(resId));
496 | }
497 |
498 | public void setBorderWidth(float width) {
499 | if (mBorderWidth == width) { return; }
500 |
501 | mBorderWidth = width;
502 | updateDrawableAttrs();
503 | updateBackgroundDrawableAttrs(false);
504 | invalidate();
505 | }
506 |
507 | @ColorInt
508 | public int getBorderColor() {
509 | return mBorderColor.getDefaultColor();
510 | }
511 |
512 | public void setBorderColor(@ColorInt int color) {
513 | setBorderColor(ColorStateList.valueOf(color));
514 | }
515 |
516 | public ColorStateList getBorderColors() {
517 | return mBorderColor;
518 | }
519 |
520 | public void setBorderColor(ColorStateList colors) {
521 | if (mBorderColor.equals(colors)) { return; }
522 |
523 | mBorderColor =
524 | (colors != null) ? colors : ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR);
525 | updateDrawableAttrs();
526 | updateBackgroundDrawableAttrs(false);
527 | if (mBorderWidth > 0) {
528 | invalidate();
529 | }
530 | }
531 |
532 | /**
533 | * Return true if this view should be oval and always set corner radii to half the height or
534 | * width.
535 | *
536 | * @return if this {@link RoundedImageView} is set to oval.
537 | */
538 | public boolean isOval() {
539 | return mIsOval;
540 | }
541 |
542 | /**
543 | * Set if the drawable should ignore the corner radii set and always round the source to
544 | * exactly half the height or width.
545 | *
546 | * @param oval if this {@link RoundedImageView} should be oval.
547 | */
548 | public void setOval(boolean oval) {
549 | mIsOval = oval;
550 | updateDrawableAttrs();
551 | updateBackgroundDrawableAttrs(false);
552 | invalidate();
553 | }
554 |
555 | public Shader.TileMode getTileModeX() {
556 | return mTileModeX;
557 | }
558 |
559 | public void setTileModeX(Shader.TileMode tileModeX) {
560 | if (this.mTileModeX == tileModeX) { return; }
561 |
562 | this.mTileModeX = tileModeX;
563 | updateDrawableAttrs();
564 | updateBackgroundDrawableAttrs(false);
565 | invalidate();
566 | }
567 |
568 | public Shader.TileMode getTileModeY() {
569 | return mTileModeY;
570 | }
571 |
572 | public void setTileModeY(Shader.TileMode tileModeY) {
573 | if (this.mTileModeY == tileModeY) { return; }
574 |
575 | this.mTileModeY = tileModeY;
576 | updateDrawableAttrs();
577 | updateBackgroundDrawableAttrs(false);
578 | invalidate();
579 | }
580 |
581 | /**
582 | * If {@code true}, we will also round the background drawable according to the settings on this
583 | * ImageView.
584 | *
585 | * @return whether the background is mutated.
586 | */
587 | public boolean mutatesBackground() {
588 | return mMutateBackground;
589 | }
590 |
591 | /**
592 | * Set whether the {@link RoundedImageView} should round the background drawable according to
593 | * the settings in addition to the source drawable.
594 | *
595 | * @param mutate true if this view should mutate the background drawable.
596 | */
597 | public void mutateBackground(boolean mutate) {
598 | if (mMutateBackground == mutate) { return; }
599 |
600 | mMutateBackground = mutate;
601 | updateBackgroundDrawableAttrs(true);
602 | invalidate();
603 | }
604 | }
605 |
--------------------------------------------------------------------------------
/roundedimageview/src/main/java/com/makeramen/roundedimageview/RoundedTransformationBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Vincent Mi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.makeramen.roundedimageview;
18 |
19 | import android.content.res.ColorStateList;
20 | import android.content.res.Resources;
21 | import android.graphics.Bitmap;
22 | import android.util.DisplayMetrics;
23 | import android.util.TypedValue;
24 | import android.widget.ImageView;
25 | import com.squareup.picasso.Transformation;
26 | import java.util.Arrays;
27 |
28 | public final class RoundedTransformationBuilder {
29 |
30 | private final DisplayMetrics mDisplayMetrics;
31 |
32 | private float[] mCornerRadii = new float[] { 0, 0, 0, 0 };
33 |
34 | private boolean mOval = false;
35 | private float mBorderWidth = 0;
36 | private ColorStateList mBorderColor =
37 | ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR);
38 | private ImageView.ScaleType mScaleType = ImageView.ScaleType.FIT_CENTER;
39 |
40 | public RoundedTransformationBuilder() {
41 | mDisplayMetrics = Resources.getSystem().getDisplayMetrics();
42 | }
43 |
44 | public RoundedTransformationBuilder scaleType(ImageView.ScaleType scaleType) {
45 | mScaleType = scaleType;
46 | return this;
47 | }
48 |
49 | /**
50 | * Set corner radius for all corners in px.
51 | *
52 | * @param radius the radius in px
53 | * @return the builder for chaining.
54 | */
55 | public RoundedTransformationBuilder cornerRadius(float radius) {
56 | mCornerRadii[Corner.TOP_LEFT] = radius;
57 | mCornerRadii[Corner.TOP_RIGHT] = radius;
58 | mCornerRadii[Corner.BOTTOM_RIGHT] = radius;
59 | mCornerRadii[Corner.BOTTOM_LEFT] = radius;
60 | return this;
61 | }
62 |
63 | /**
64 | * Set corner radius for a specific corner in px.
65 | *
66 | * @param corner the corner to set.
67 | * @param radius the radius in px.
68 | * @return the builder for chaning.
69 | */
70 | public RoundedTransformationBuilder cornerRadius(@Corner int corner, float radius) {
71 | mCornerRadii[corner] = radius;
72 | return this;
73 | }
74 |
75 | /**
76 | * Set corner radius for all corners in density independent pixels.
77 | *
78 | * @param radius the radius in density independent pixels.
79 | * @return the builder for chaining.
80 | */
81 | public RoundedTransformationBuilder cornerRadiusDp(float radius) {
82 | return cornerRadius(
83 | TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, radius, mDisplayMetrics));
84 | }
85 |
86 | /**
87 | * Set corner radius for a specific corner in density independent pixels.
88 | *
89 | * @param corner the corner to set
90 | * @param radius the radius in density independent pixels.
91 | * @return the builder for chaining.
92 | */
93 | public RoundedTransformationBuilder cornerRadiusDp(@Corner int corner, float radius) {
94 | return cornerRadius(corner,
95 | TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, radius, mDisplayMetrics));
96 | }
97 |
98 | /**
99 | * Set the border width in pixels.
100 | *
101 | * @param width border width in pixels.
102 | * @return the builder for chaining.
103 | */
104 | public RoundedTransformationBuilder borderWidth(float width) {
105 | mBorderWidth = width;
106 | return this;
107 | }
108 |
109 | /**
110 | * Set the border width in density independent pixels.
111 | *
112 | * @param width border width in density independent pixels.
113 | * @return the builder for chaining.
114 | */
115 | public RoundedTransformationBuilder borderWidthDp(float width) {
116 | mBorderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, mDisplayMetrics);
117 | return this;
118 | }
119 |
120 | /**
121 | * Set the border color.
122 | *
123 | * @param color the color to set.
124 | * @return the builder for chaining.
125 | */
126 | public RoundedTransformationBuilder borderColor(int color) {
127 | mBorderColor = ColorStateList.valueOf(color);
128 | return this;
129 | }
130 |
131 | /**
132 | * Set the border color as a {@link ColorStateList}.
133 | *
134 | * @param colors the {@link ColorStateList} to set.
135 | * @return the builder for chaining.
136 | */
137 | public RoundedTransformationBuilder borderColor(ColorStateList colors) {
138 | mBorderColor = colors;
139 | return this;
140 | }
141 |
142 | /**
143 | * Sets whether the image should be oval or not.
144 | *
145 | * @param oval if the image should be oval.
146 | * @return the builder for chaining.
147 | */
148 | public RoundedTransformationBuilder oval(boolean oval) {
149 | mOval = oval;
150 | return this;
151 | }
152 |
153 | /**
154 | * Creates a {@link Transformation} for use with picasso.
155 | *
156 | * @return the {@link Transformation}
157 | */
158 | public Transformation build() {
159 | return new Transformation() {
160 | @Override public Bitmap transform(Bitmap source) {
161 | Bitmap transformed = RoundedDrawable.fromBitmap(source)
162 | .setScaleType(mScaleType)
163 | .setCornerRadius(mCornerRadii[0], mCornerRadii[1], mCornerRadii[2], mCornerRadii[3])
164 | .setBorderWidth(mBorderWidth)
165 | .setBorderColor(mBorderColor)
166 | .setOval(mOval)
167 | .toBitmap();
168 | if (!source.equals(transformed)) {
169 | source.recycle();
170 | }
171 | return transformed;
172 | }
173 |
174 | @Override public String key() {
175 | return "r:" + Arrays.toString(mCornerRadii)
176 | + "b:" + mBorderWidth
177 | + "c:" + mBorderColor
178 | + "o:" + mOval;
179 | }
180 | };
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/roundedimageview/src/main/res/values/about_libraries_def.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Vince Mi
5 | https://github.com/vinc3m1
6 | RoundedImageView
7 | A fast ImageView (and Drawable) that supports rounded corners (and ovals or circles) based on the original example from Romain Guy.
8 | 1.3.0
9 | https://github.com/vinc3m1/RoundedImageView
10 | apache_2_0
11 | true
12 | https://github.com/vinc3m1/RoundedImageView.git
13 |
14 |
--------------------------------------------------------------------------------
/roundedimageview/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/screenshot-oval.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/screenshot-oval.png
--------------------------------------------------------------------------------
/screenshot-round-specific-corners.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/screenshot-round-specific-corners.png
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinc3m1/RoundedImageView/37ab3227182b5feac8bce29e91de3b0cee34474b/screenshot.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':roundedimageview'
2 | include ':example'
3 |
--------------------------------------------------------------------------------