├── .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 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.makeramen/roundedimageview/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.makeramen/roundedimageview) 30 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-RoundedImageView-brightgreen.svg?style=flat)](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 | ![RoundedImageView screenshot](https://raw.github.com/makeramen/RoundedImageView/master/screenshot.png) 35 | ![RoundedImageView screenshot with ovals](https://raw.github.com/makeramen/RoundedImageView/master/screenshot-oval.png) 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 | --------------------------------------------------------------------------------