├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ruffian │ │ └── library │ │ └── demo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── fonts │ │ │ ├── Roboto-Black.ttf │ │ │ ├── Roboto-BlackItalic.ttf │ │ │ ├── Roboto-Bold.ttf │ │ │ ├── Roboto-BoldItalic.ttf │ │ │ ├── Roboto-Italic.ttf │ │ │ ├── Roboto-Light.ttf │ │ │ ├── Roboto-LightItalic.ttf │ │ │ ├── Roboto-Medium.ttf │ │ │ ├── Roboto-MediumItalic.ttf │ │ │ ├── Roboto-Regular.ttf │ │ │ ├── Roboto-Thin.ttf │ │ │ ├── Roboto-ThinItalic.ttf │ │ │ ├── RobotoCondensed-Bold.ttf │ │ │ ├── RobotoCondensed-BoldItalic.ttf │ │ │ ├── RobotoCondensed-Italic.ttf │ │ │ ├── RobotoCondensed-Light.ttf │ │ │ ├── RobotoCondensed-LightItalic.ttf │ │ │ ├── RobotoCondensed-Regular.ttf │ │ │ ├── RobotoMono-Bold.ttf │ │ │ ├── RobotoMono-BoldItalic.ttf │ │ │ ├── RobotoMono-Italic.ttf │ │ │ ├── RobotoMono-Light.ttf │ │ │ ├── RobotoMono-LightItalic.ttf │ │ │ ├── RobotoMono-Medium.ttf │ │ │ ├── RobotoMono-MediumItalic.ttf │ │ │ ├── RobotoMono-Regular.ttf │ │ │ ├── RobotoMono-Thin.ttf │ │ │ ├── RobotoMono-ThinItalic.ttf │ │ │ ├── RobotoSlab-Bold.ttf │ │ │ ├── RobotoSlab-Light.ttf │ │ │ ├── RobotoSlab-Regular.ttf │ │ │ ├── RobotoSlab-Thin.ttf │ │ │ └── huakangshaonv.ttf │ ├── java │ │ └── com │ │ │ └── ruffian │ │ │ └── library │ │ │ └── demo │ │ │ ├── App.java │ │ │ ├── CheckedActivity.java │ │ │ ├── FragmentCorner.java │ │ │ ├── FragmentState.java │ │ │ ├── FragmentVersion.java │ │ │ ├── GradientActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── REditTextActivity.java │ │ │ ├── RImageViewActivity.java │ │ │ ├── RTextViewActivity.java │ │ │ ├── RViewActivity.java │ │ │ ├── RViewGroupActivity.java │ │ │ ├── RipperActivity.java │ │ │ └── ShadowActivity.java │ └── res │ │ ├── drawable │ │ ├── bg_selector.xml │ │ └── tv_selected.xml │ │ ├── layout │ │ ├── activity_checked.xml │ │ ├── activity_edittext.xml │ │ ├── activity_gradient.xml │ │ ├── activity_imageview.xml │ │ ├── activity_list.xml │ │ ├── activity_main.xml │ │ ├── activity_ripper.xml │ │ ├── activity_shadow.xml │ │ ├── activity_textview_main.xml │ │ ├── activity_view.xml │ │ ├── activity_viewgroup.xml │ │ ├── fmt_textview_corner.xml │ │ ├── fmt_textview_state.xml │ │ ├── fmt_textview_version.xml │ │ └── item_list.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ ├── food.jpg │ │ ├── ic_launcher.png │ │ ├── icon_account.png │ │ ├── icon_bg.png │ │ ├── icon_bg_normal.png │ │ ├── icon_bg_pressed.png │ │ ├── icon_heart.png │ │ ├── icon_help.png │ │ ├── icon_man.png │ │ ├── icon_man_pressed.jpg │ │ ├── icon_phone.png │ │ ├── icon_phone_normal.png │ │ ├── icon_phone_pressed.png │ │ ├── icon_phone_unable.png │ │ ├── icon_psw.png │ │ ├── icon_rb_green_default.png │ │ ├── icon_rb_green_selected.png │ │ ├── icon_settings.png │ │ └── icon_star.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── array.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── ruffian │ └── library │ └── demo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ruffian │ │ └── library │ │ └── widget │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── ruffian │ │ │ └── library │ │ │ └── widget │ │ │ ├── RCheckBox.java │ │ │ ├── RConstraintLayout.java │ │ │ ├── REditText.java │ │ │ ├── RFrameLayout.java │ │ │ ├── RImageView.java │ │ │ ├── RLinearLayout.java │ │ │ ├── RRadioButton.java │ │ │ ├── RRelativeLayout.java │ │ │ ├── RTextView.java │ │ │ ├── RView.java │ │ │ ├── clip │ │ │ ├── ClipHelper.java │ │ │ ├── ClipPathManager.java │ │ │ └── IClip.java │ │ │ ├── helper │ │ │ ├── RBaseHelper.java │ │ │ ├── RCheckHelper.java │ │ │ ├── RImageViewHelper.java │ │ │ └── RTextViewHelper.java │ │ │ ├── iface │ │ │ ├── ITextViewFeature.java │ │ │ └── RHelper.java │ │ │ ├── rounded │ │ │ └── RoundDrawable.java │ │ │ ├── shadow │ │ │ ├── ShadowBitmapDrawable.java │ │ │ └── ShadowDrawable.java │ │ │ └── utils │ │ │ ├── ReflectUtils.java │ │ │ ├── RippleDrawableUtils.java │ │ │ └── TextViewUtils.java │ └── res │ │ └── values │ │ └── attr.xml │ └── test │ └── java │ └── com │ └── ruffian │ └── library │ └── widget │ └── ExampleUnitTest.java ├── picture ├── Gradient.png ├── REditText.png ├── RImageView.png ├── RTextView_corner.png ├── RTextView_state.gif ├── RTextView_typeface.png ├── RView.png ├── RViewGroup.png ├── RWidgetHelper.gif ├── all.gif ├── part1.png ├── part2.png ├── part3.png ├── part4.png ├── part5.png ├── ripple.gif └── shadow.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /.gradle 3 | /app/build 4 | /local.properties 5 | /gradle.properties 6 | /.idea 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RWidgetHelper 2 | 3 | ## 欢迎使用 RWidgetHelper >>> [iOS版本:ZTUIStyle](https://github.com/RuffianZhong/ZTUIStyle) 4 | 5 | ### 宗旨:专治原生控件各种不服 6 | 7 | ### 目标:Android UI 快速开发 8 | 9 | ### 说明 10 | 11 | > Android UI 开发常用:圆角,边框,Gradient背景渐变,控件State各个状态UI样式,阴影,水波纹 12 | > 13 | > 普通解决方案缺点:代码冗余,复用性差,自由度低 14 | > 15 | > RWidgetHelper优点:优化代码,简化使用,快速开发 16 | > 17 | 18 | ### 简介 19 | 20 | 通过继承原生控件,设置自定义属性,解决常用 Selector,Gradient,Shape, 阴影,水波纹等功能 21 | 22 | 原生控件都可实现 **基础功能** ,针对具体控件还有 **个性功能** 23 | 24 | 25 | ## 基础功能 26 | 27 | | 功能 | 属性值 | 可用State状态 | 特性 | 28 | | ------------- | ------------- | ------------- | ------------- | 29 | | 圆角 | 颜色 | 默认/按下/不可用/选中 | 四周圆角/单个方向圆角 | 30 | | 边框宽度 | 数值 | 默认/按下/不可用/选中 | 实线/虚线边框 | 31 | | 边框颜色 | 颜色 | 默认/按下/不可用/选中 | 实线/虚线边框 | 32 | | 背景 | 颜色/颜色数组/drawable | 默认/按下/不可用/选中 | 纯色/渐变/Drawable | 33 | | 水波纹 | -- | -- | 点击效果水波纹 | 34 | | 阴影 | -- | -- | 控件四周阴影 | 35 | | 裁剪布局 | -- | -- | 裁剪子控件内容 | 36 | #### 属性介绍 37 | 38 | | 属性 |说明 | 39 | | ------------- | :-------------| 40 | | corner_radius | 圆角 四周 >=正方形宽度/2实现圆形| 41 | | corner_radius_top_left | 圆角 左上 | 42 | | corner_radius_top_right | 圆角 右上 | 43 | | corner_radius_bottom_left | 圆角 左下 | 44 | | corner_radius_bottom_right | 圆角 右下 | 45 | | border_dash_width | 虚线边框 宽度 | 46 | | border_dash_gap | 虚线边框 间隔 | 47 | | border_width_normal | 边框宽度 默认 | 48 | | border_width_pressed | 边框宽度 按下 | 49 | | border_width_unable | 边框宽度 不可点击 | 50 | | border_width_checked | 边框宽度 选中 | 51 | | border_width_selected | 边框宽度 选择 | 52 | | border_color_normal | 边框颜色 默认 | 53 | | border_color_pressed | 边框颜色 按下 | 54 | | border_color_unable | 边框颜色 不可点击 | 55 | | border_color_checked | 边框颜色 选中 | 56 | | border_color_selected | 边框颜色 选择 | 57 | | background_normal | 背景 默认 | 58 | | background_pressed | 背景 按下 | 59 | | background_unable | 背景 不可点击 | 60 | | background_checked | 背景 选中 | 61 | | background_selected | 背景 选择 | 62 | | gradient_orientation | 渐变的方向 参考 GradientDrawable.Orientation:TOP_BOTTOM,TR_BL... | 63 | | gradient_type | 渐变的样式 linear线性,radial径向,sweep扫描式 默认:linear| 64 | | gradient_radius | 渐变半径 默认:(宽或高最小值)/ 2 | 65 | | gradient_centerX | 渐变中心点X坐标(0.0-1.0) 0.5表示中间 默认:0.5 | 66 | | gradient_centerY | 渐变中心点Y坐标(0.0-1.0) 0.5表示中间 默认:0.5 | 67 | | ripple | 是否开启水波纹效果,true/false | 68 | | ripple_color | 水波纹效果颜色 | 69 | | ripple_mask_style | 水波纹效果限制样式,none:无限制 normal:默认跟随控件背景形状 drawable:自定义形状 | 70 | | ripple_mask | 水波纹效果限制自定义drawable | 71 | | shadow_radius | 阴影大小 dp | 72 | | shadow_color | 阴影颜色 | 73 | | shadow_dx | 阴影水平方向偏移 负数向左,正数向右 dp | 74 | | shadow_dy | 阴影垂直方向偏移 负数向上,正数向下 dp | 75 | | clip_layout | 裁剪子控件内容 | 76 | 77 | > 1.background_xxx 纯色 渐变 drawable 78 | > 纯色: 颜色值 app:background_normal="#74EBD5" 79 | > 渐变: 颜色数组 >=2个 app:background_normal="@array/@colorArray" 80 | > drawable: 资源图片 app:background_normal="@mipmap/@drawable" 81 | > 82 | > 2.自定义属性对原生属性无效 83 | > 例如: `app:corner_radius="10dp"` 搭配 `app:background_normal="#74EBD5"` 而不是`background="#74EBD5"` 84 | > 85 | > 3.ripple 效果和 pressed 对立,启用 ripple 后,按下效果无效 86 | > 87 | > 4.shadow 内容需要开发者自行兼容,例如:`shadow_radius=10dp` , `shadow_dx=5dp` 则在水平方向需要 `padingLeft/right = 15 dp` 88 | > 89 | > 5.pressed/unable/checked/selected 等状态在未设置具体值时会默认设置 normal 值,方便开发者实现各种需求 90 | > 91 | > 6.clip_layout 是否裁剪控件,未设置此属性时,所有的圆角/圆形都是背景形状。 设置此属性后,在背景效果的同时真实裁剪控件 92 | 93 | 94 | 95 | #### 示例xml 96 | ``` 97 | 156 | ``` 157 | 158 | #### 示例java 159 | 160 | ``` 161 | RView view = (RView) findViewById(R.id.view); 162 | //获取Helper 163 | RBaseHelper helper = view.getHelper(); 164 | helper.setBackgroundColorNormal(getResources().getColor(R.color.blue)) 165 | .setBorderColorNormal(getResources().getColor(R.color.red)) 166 | .setBorderWidthNormal(12) 167 | .setCornerRadius(25); 168 | ``` 169 | 170 | #### 效果图 171 | ![](picture/all.gif) ![](picture/part5.png) 172 | ![](picture/part1.png) ![](picture/part2.png) 173 | ![](picture/part3.png) ![](picture/part4.png) 174 | ![](picture/ripple.gif) ![](picture/shadow.png) 175 | 176 | ## 个性功能 177 | 178 | #### RTextView 179 | 180 | - [x] `drawableLeft/Right/Top/Bottom icon大小` 181 | - [x] `drawableLeft/Right/Top/Bottom icon状态` 182 | - [x] `drawableLeft 和 text 一起居中` 183 | - [x] `文字根据状态变色 默认/按下/不可点击/选择` 184 | - [x] `设置字体样式` 185 | 186 | | 属性 |说明 | 187 | | ------------- | :-------------| 188 | | text_color_normal | 文字颜色 默认 | 189 | | text_color_pressed | 文字颜色 按下 | 190 | | text_color_unable | 文字颜色 不可点击 | 191 | | text_color_selected | 文字颜色 选择 | 192 | | icon_direction | drawable icon 位置{left,top,right,bottom} | 193 | | icon_with_text | 图片和文本一起居中 true/false | 194 | | text_typeface | 字体样式 | 195 | 196 | 老版本仅支持单一方向icon设置 197 | 198 | | | | 199 | | ------------- | :-------------| 200 | | icon_src_normal | drawable icon 默认 | 201 | | icon_src_pressed | drawable icon 按下 | 202 | | icon_src_unable | drawable icon 不可点击 | 203 | | icon_src_selected | drawable icon 选择 | 204 | | icon_height | drawable icon 高 | 205 | | icon_width | drawable icon 宽 | 206 | 207 | 新版本仅支持多个方向icon设置 208 | 209 | | | | 210 | | ------------- | :-------------| 211 | | icon_normal_left/right/top/bottom | 默认icon 各方向 | 212 | | icon_pressed_left/right/top/bottom | 按下icon 各方向 | 213 | | icon_unable_left/right/top/bottom | 不可用icon 各方向 | 214 | | icon_selected_left/right/top/bottom | 选中icon 各方向 | 215 | | icon_height_left/right/top/bottom | icon 各方向 高 | 216 | | icon_width_left/right/top/bottom | icon 各方向 宽 | 217 | 218 | > `RTextView` 控件新增支持多个方向同时设置 `icon`(老版本仅支持一个方向)为了兼容老版本用户,保留了原来字段 `icon_src_normal/pressed/unable/selected` 219 | > 220 | > 如果出现老版本属性默认开发者使用老版本逻辑(仅支持一个方向),新版请使用 `icon_normal_left/right/top/bottom` 等属性 221 | 222 | #### REditText 223 | 224 | > REditText 使用方法跟 RTextView 一致 225 | 226 | #### RLinearLayout / RRelativeLayout / RFrameLayout / RView / RConstraintLayout 227 | 228 | > 查看基础功能 229 | > 230 | > **重磅: ViewGroup 支持通过 clip_layout 设置是否裁剪子控件** 231 | 232 | #### RRadioButton / RCheckBox 233 | 234 | > 查看基础功能 235 | > 236 | > 查看 `RTextView` 所有功能 237 | > 238 | > 常使用选择属性 checked 239 | > 240 | > 支持 `RTextView` 的基础功能 自定义各个状态 `drawableLeft` 以及 icon与文本居中等 241 | 242 | | 属性 |说明 | 243 | | ------------- | :-------------| 244 | | border_width_checked | 边框宽度 选中 | 245 | | border_color_checked | 边框颜色 选中 | 246 | | background_checked | 背景 选中 | 247 | | text_color_checked | 文字颜色 选中 | 248 | 249 | 老版本仅支持单一方向icon设置 250 | 251 | | | | 252 | | ------------- | :-------------| 253 | | icon_src_normal | 图标 未选中 | 254 | | icon_src_checked | 图标 选中 | 255 | 256 | 新版本仅支持多个方向icon设置 257 | 258 | | | | 259 | | ------------- | :-------------| 260 | | icon_normal_left/right/top/bottom | 图标 未选中 各方向 | 261 | | icon_checked_left/right/top/bottom | 图标 选中 各方向 | 262 | 263 | > `RRadioButton / RCheckBox` 控件新增支持多个方向同时设置 `icon`(老版本仅支持一个方向)为了兼容老版本用户,保留了原来字段 `icon_src_checked` 264 | > 如果出现老版本属性默认开发者使用老版本逻辑(仅支持一个方向),新版请使用 `icon_checked_left/right/top/bottom` 等属性 265 | 266 | 267 | #### RImageView 268 | 269 | > RImageView 不提供state状态 270 | > 271 | > 1.圆形图片 272 | > 273 | > 2.圆角图片 274 | > 275 | > 3.指定某一方向圆角图片 276 | > 277 | > 4.边框 278 | > 279 | 280 | | 属性 |说明 | 281 | | ------------- | :-------------| 282 | | corner_radius | 圆角 四周 | 283 | | corner_radius_top_left | 圆角 左上 | 284 | | corner_radius_top_right | 圆角 右上 | 285 | | corner_radius_bottom_left | 圆角 左下 | 286 | | corner_radius_bottom_right | 圆角 右下 | 287 | | border_width | 边框宽度 | 288 | | border_color | 边框颜色 | 289 | | is_circle | 是否圆形图片 | 290 | 291 | 292 | ### 使用 (版本号根据更新历史使用最新版) 293 | #### JCenter仓库已经被关闭,新版本依赖方式 Jitpack 294 | ``` 295 | //项目级别 build.gradle 296 | allprojects { 297 | repositories { 298 | ... 299 | maven { url "https://jitpack.io" } 300 | } 301 | } 302 | 303 | 304 | //support 版本 305 | dependencies { 306 | implementation 'com.github.RuffianZhong:RWidgetHelper:support.v0.0.14' 307 | } 308 | 309 | //androidx 版本 310 | dependencies { 311 | implementation 'com.github.RuffianZhong:RWidgetHelper:androidx.v0.0.14' 312 | } 313 | ``` 314 | #### 老版本依赖方式 JCenter 2022 年 2 月 1 日开始无法使用 315 | ``` 316 | //support 版本 317 | compile 'com.ruffian.library:RWidgetHelper:1.1.18' 318 | 319 | //androidx 版本 320 | compile 'com.ruffian.library:RWidgetHelper-AndroidX:0.0.9' 321 | ``` 322 | 323 | ### 版本历史 324 | 325 | **support.v0.0.14/androidx.v0.0.14** `2023.05.08` Fix bug [issues#118](https://github.com/RuffianZhong/RWidgetHelper/issues/118) | [issues#121](https://github.com/RuffianZhong/RWidgetHelper/issues/121) 326 | 327 | **support.v0.0.13/androidx.v0.0.13** `2023.02.21` 支持 Android Studio Electric Eel | 2022.1.1 Patch 1 布局预览 328 | 329 | **support.v0.0.12/androidx.v0.0.12** `2023.02.21` 支持 Android Studio Chipmunk | 2021.2.1 Patch 1 布局预览 330 | 331 | **support.v0.0.11/androidx.v0.0.11** `2022.10.14` 优化性能,修改阴影实现方式 332 | 333 | **support.v0.0.10/androidx.v0.0.10** `2022.04.25` 圆角支持Right to left(从右到左布局) 334 | 335 | **support.v0.0.9/androidx.v0.0.9** `2022.03.17` 支持Right to left(从右到左布局)(left=left||start,right=right||end) 336 | 337 | **support.v0.0.7/androidx.v0.0.7|androidx.v0.0.8** `2021.09.23` 修复RTextView在5.0以下版本崩溃问题(v0.0.6修改不当导致) 338 | 339 | **support.v0.0.6/androidx.v0.0.6** `2021.09.15` 修复控件无法预览的问题 340 | 341 | **support.v0.0.5/androidx.v0.0.5** `2021.09.11` 适配原生drawableLeft/Right/Top/Bottom 342 | 343 | **support.v0.0.4/androidx.v0.0.4** `2021.08.18` 背景 || 边框 || 图标 ,未设置 "按下"/"选中"/"不可用"等状态时,使用默认状态的资源 344 | 345 | **support.v0.0.3/androidx.v0.0.3** `2021.07.19` bugfix:修复support.v0.0.1/androidx.v0.0.1版本引起的在代码中设置背景图不起效问题;修复重复设置背景图不起效问题 346 | 347 | **support.v0.0.2/androidx.v0.0.2** `2021.07.02` 基础控件继承自 Appcompat XX 348 | 349 | **support.v0.0.1/androidx.v0.0.1** `2021.05.25` 修改按下状态逻辑(背景/边框/文本颜色) 350 | 351 | **v1.1.18/v0.0.9** `2021.02.24`  修复 iconWithText Bug 352 | 353 | **v1.1.17/v0.0.8** `2021.01.30`  Fix bug [issues#68](https://github.com/RuffianZhong/RWidgetHelper/issues/68) | [issues#73](https://github.com/RuffianZhong/RWidgetHelper/issues/73) 354 | 355 | **v1.1.16/v0.0.7** `2020.11.05`  Fix bug:4.4.x版本上裁剪出现崩溃,内存泄漏[issues#78](https://github.com/RuffianZhong/RWidgetHelper/issues/78) 356 | 357 | **v1.1.15/v0.0.6** `2020.08.18`  重磅更新 ViewGroup 支持裁剪控件功能 358 | 359 | **v1.1.14/v0.0.5** `2020.07.23`  RTextView 支持多方向icon,优化按下状态(背景/字体颜色)默认值 360 | 361 | **v1.1.13/v0.0.4** `2020.05.05`  修复RImageView未设置宽高时出错,圆角最大值不超过高度的1/2 362 | 363 | **v1.1.12/v0.0.3** `2020.04.02`  修复RImageView未设置src时不能绘制边框 364 | 365 | **v1.1.11/v0.0.2** `2020.01.14`  ScaleType 的 center_inside 类型实现方式修改 366 | 367 | **v0.0.1** `2020.01.10`  单独支持 AndroidX ,方便开发者选择 (基于v1.1.10) 368 | 369 | **v1.1.10** `2020.01.10`  还原 Support 包支持,分离 AndroidX 370 | 371 | **v1.1.9** `2020.01.08`  `androidx.appcompat` 和 `androidx.constraintlayout` 的依赖方式从编译时依赖到直接依赖,避免开发者未引入时不能正确使用 372 | 373 | **v1.1.8** `2020.01.03`  RImageView重新实现,解决历史版本卡顿和内存溢出问题,新增RConstraintLayout控件,支持androidX 374 | 375 | **v1.1.7** `2019.12.03`  修复 Android 7.1 版本 Ripple 空指针问题 [issues#51](https://github.com/RuffianZhong/RWidgetHelper/issues/51) 376 | 377 | **v1.1.6** `2019.11.23`  修改 RImageView 实现方式。 Fix bug 378 | 379 | **v1.1.5** `2019.09.17`  修复 REditText 在Android 4.X 系统上无法正常使用 380 | 381 | **v1.1.4** `2019.09.17`  支持selected状态,Fix bug 382 | 383 | **v1.1.3** `2019.09.03`  支持阴影效果 384 | 385 | **v1.1.2** `2019.08.30`  RippleDrawable 与 unable / checked 状态兼容,ripper -> ripple (更名:1.1.1版本名称写错了) 386 | 387 | **v1.1.1** `2019.08.28`  5.0 以上版本,支持 RippleDrawable 水波纹效果 388 | 389 | **v1.1.0** `2019.06.27`  `RCheckBox` 和 `RRadioButton` 支持选中图片,支持继承 `RTextView` 的基础功能 390 | 391 | **v1.0.10** `2019.06.26` Fix bug [issues#33](https://github.com/RuffianZhong/RWidgetHelper/issues/33) 392 | 393 | **v1.0.9** `2019.06.21` RImageView支持ScaleType 394 | 395 | **v1.0.8** `2019.01.30` Fix bug [issues#25](https://github.com/RuffianZhong/RWidgetHelper/issues/25) and [issues#26](https://github.com/RuffianZhong/RWidgetHelper/issues/26) 396 | 397 | **v1.0.7** `2018.12.20` 优化代码,Fix bug 398 | 399 | **v1.0.6** `2018.12.18` 版本兼容,优化代码,`RFrameLayout` 400 | 401 | **v1.0.5** `2018.12.18` 1.DrawableWithText 图片和文本一起居中 2.Fix bug 402 | 403 | **v1.0.4** `2018.11.15` 1.背景支持图片类型 2.添加 `selector->checked`状态 添加 `RCheckBox` 和 `RRadioButton` 404 | 405 | **v1.0.3** `2018.11.09` Fix Bug [issues#17](https://github.com/RuffianZhong/RWidgetHelper/issues/17 "issues#17") 406 | 407 | **v1.0.2** `2018.10.09` 添加 Gradient 渐变功能 408 | 409 | **v1.0.1** `2018.08.20` Fix bug setEnabled [issues #7](https://github.com/RuffianZhong/RWidgetHelper/issues/7) 410 | 411 | **v1.0.0** `2018.04.26` 发布第一版本 412 | 413 | 414 | ## License 415 | 416 | ``` 417 | MIT License 418 | 419 | Copyright (c) 2018 Ruffian-痞子 420 | ``` 421 | 422 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 31 5 | defaultConfig { 6 | applicationId "com.ruffian.library.demo" 7 | minSdkVersion 14 8 | targetSdkVersion 31 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | android { 20 | lintOptions { 21 | abortOnError false 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 29 | exclude group: 'com.android.support', module: 'support-annotations' 30 | }) 31 | implementation 'com.android.support:appcompat-v7:28.0.0' 32 | implementation 'com.android.support:recyclerview-v7:28.0.0' 33 | implementation 'com.android.support:support-v4:28.0.0' 34 | implementation 'com.github.bumptech.glide:glide:4.3.0' 35 | annotationProcessor 'com.github.bumptech.glide:compiler:4.3.0' 36 | testImplementation 'junit:junit:4.12' 37 | implementation project(':library') 38 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 39 | } 40 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ruffian/library/demo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.ruffian.library.demo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoCondensed-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoCondensed-BoldItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoCondensed-Italic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoCondensed-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoCondensed-LightItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoCondensed-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-BoldItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-Italic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-LightItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-Medium.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-MediumItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-Thin.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoMono-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoMono-ThinItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoSlab-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoSlab-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoSlab-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoSlab-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoSlab-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoSlab-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoSlab-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/RobotoSlab-Thin.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/huakangshaonv.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/assets/fonts/huakangshaonv.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | public class App extends Application { 7 | 8 | @Override 9 | public void attachBaseContext(Context context) { 10 | super.attachBaseContext(context); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/CheckedActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * CheckedActivity 9 | * 10 | * @author ZhongDaFeng 11 | */ 12 | public class CheckedActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(@Nullable Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_checked); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/FragmentCorner.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | /** 10 | * 示例FragmentCorner 11 | * 备注:各种圆角使用示例 12 | * 13 | * @author ZhongDaFeng 14 | */ 15 | 16 | public class FragmentCorner extends Fragment { 17 | 18 | @Override 19 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 20 | return LayoutInflater.from(getActivity()).inflate(R.layout.fmt_textview_corner, null); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/FragmentState.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | /** 10 | * 示例FragmentState 11 | * 备注:各种边框,背景状态使用示例 12 | * 13 | * @author ZhongDaFeng 14 | */ 15 | 16 | public class FragmentState extends Fragment { 17 | 18 | @Override 19 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 20 | View view = LayoutInflater.from(getActivity()).inflate(R.layout.fmt_textview_state, null); 21 | return view; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/FragmentVersion.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | /** 10 | * 示例FragmentCorner 11 | * 12 | * @author ZhongDaFeng 13 | */ 14 | 15 | public class FragmentVersion extends Fragment { 16 | 17 | private View mView; 18 | 19 | @Override 20 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 21 | mView = LayoutInflater.from(getActivity()).inflate(R.layout.fmt_textview_version, null); 22 | return mView; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/GradientActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * Gradient相关演示 9 | * 10 | * @author ZhongDaFeng 11 | */ 12 | public class GradientActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(@Nullable Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_gradient); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_main); 14 | findViewById(R.id.tv).setOnClickListener(this); 15 | findViewById(R.id.iv).setOnClickListener(this); 16 | findViewById(R.id.et).setOnClickListener(this); 17 | findViewById(R.id.view_group).setOnClickListener(this); 18 | findViewById(R.id.view).setOnClickListener(this); 19 | findViewById(R.id.gradient).setOnClickListener(this); 20 | findViewById(R.id.checked).setOnClickListener(this); 21 | findViewById(R.id.ripper).setOnClickListener(this); 22 | findViewById(R.id.shadow).setOnClickListener(this); 23 | } 24 | 25 | 26 | @Override 27 | public void onClick(View view) { 28 | Intent intent = null; 29 | switch (view.getId()) { 30 | case R.id.tv: 31 | intent = new Intent(this, RTextViewActivity.class); 32 | break; 33 | case R.id.iv: 34 | intent = new Intent(this, RImageViewActivity.class); 35 | break; 36 | case R.id.et: 37 | intent = new Intent(this, REditTextActivity.class); 38 | break; 39 | case R.id.view_group: 40 | intent = new Intent(this, RViewGroupActivity.class); 41 | break; 42 | case R.id.view: 43 | intent = new Intent(this, RViewActivity.class); 44 | break; 45 | case R.id.gradient: 46 | intent = new Intent(this, GradientActivity.class); 47 | break; 48 | case R.id.checked: 49 | intent = new Intent(this, CheckedActivity.class); 50 | break; 51 | case R.id.ripper: 52 | intent = new Intent(this, RipperActivity.class); 53 | break; 54 | case R.id.shadow: 55 | intent = new Intent(this, ShadowActivity.class); 56 | break; 57 | } 58 | if (intent != null) { 59 | startActivity(intent); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/REditTextActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * EditText示例 9 | * 10 | * @author ZhongDaFeng 11 | */ 12 | public class REditTextActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(@Nullable Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_edittext); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/RImageViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * @author ZhongDaFeng 9 | */ 10 | public class RImageViewActivity extends AppCompatActivity { 11 | @Override 12 | protected void onCreate(@Nullable Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_imageview); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/RTextViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentPagerAdapter; 6 | import android.support.v4.view.ViewPager; 7 | import android.support.v7.app.AppCompatActivity; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author ZhongDaFeng 14 | */ 15 | public class RTextViewActivity extends AppCompatActivity { 16 | 17 | private ViewPager mViewPager; 18 | private FragmentPagerAdapter mAdapter; 19 | private List mFragmentList = new ArrayList(); 20 | 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_textview_main); 26 | initViews(); 27 | initData(); 28 | } 29 | 30 | 31 | private void initData() { 32 | 33 | mFragmentList.add(new FragmentCorner()); 34 | mFragmentList.add(new FragmentState()); 35 | mFragmentList.add(new FragmentVersion()); 36 | mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) { 37 | @Override 38 | public int getCount() { 39 | return mFragmentList.size(); 40 | } 41 | 42 | @Override 43 | public Fragment getItem(int position) { 44 | return mFragmentList.get(position); 45 | } 46 | }; 47 | 48 | mViewPager.setAdapter(mAdapter); 49 | } 50 | 51 | private void initViews() { 52 | mViewPager = (ViewPager) findViewById(R.id.vp_content); 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/RViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * @author ZhongDaFeng 9 | */ 10 | public class RViewActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(@Nullable Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_view); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/RViewGroupActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * @author ZhongDaFeng 9 | */ 10 | public class RViewGroupActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(@Nullable Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_viewgroup); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/RipperActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | import com.ruffian.library.widget.RTextView; 9 | 10 | 11 | /** 12 | * 水波纹效果展示 13 | * 14 | * @author ZhongDaFeng 15 | */ 16 | public class RipperActivity extends AppCompatActivity { 17 | private RTextView tvTag; 18 | private RTextView btnUpdate; 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_ripper); 24 | tvTag = (RTextView) findViewById(R.id.tv_tag); 25 | btnUpdate = (RTextView) findViewById(R.id.tv_update); 26 | btnUpdate.setOnClickListener(new View.OnClickListener() { 27 | @Override 28 | public void onClick(View v) { 29 | boolean enabled = tvTag.isEnabled(); 30 | tvTag.setEnabled(!enabled); 31 | btnUpdate.setText(String.valueOf(!enabled)); 32 | } 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/ruffian/library/demo/ShadowActivity.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.util.TypedValue; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | 19 | /** 20 | * 阴影效果展示 21 | * 22 | * @author ZhongDaFeng 23 | */ 24 | public class ShadowActivity extends AppCompatActivity { 25 | 26 | private RecyclerView recyclerView; 27 | private MyAdapter adapter; 28 | private final boolean isAuthor = false;//作者自测逻辑 29 | 30 | @Override 31 | protected void onCreate(@Nullable Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | //demo 示例布局 activity_shadow 34 | setContentView(R.layout.activity_shadow); 35 | 36 | if (!isAuthor) return; 37 | /** 38 | * 以下代码为作者自测请忽略test 39 | */ 40 | //test 41 | setContentView(R.layout.activity_list); 42 | 43 | List list = new ArrayList<>(); 44 | for (int i = 0; i < 20; i++) list.add("Item" + (i + 1)); 45 | 46 | recyclerView = (RecyclerView) findViewById(R.id.recycler_view); 47 | adapter = new MyAdapter(list); 48 | recyclerView.setAdapter(adapter); 49 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 50 | } 51 | 52 | class MyAdapter extends RecyclerView.Adapter { 53 | 54 | private List mList; 55 | 56 | public MyAdapter(List list) { 57 | this.mList = list; 58 | } 59 | 60 | @Override 61 | public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 62 | return new MyViewHolder(LayoutInflater.from(ShadowActivity.this).inflate(R.layout.item_list, parent, false)); 63 | } 64 | 65 | @Override 66 | public void onBindViewHolder(MyViewHolder holder, int position) { 67 | holder.textView.setText(mList.get(position)); 68 | //test 69 | int height = dp2px(150) + (position) * 20; 70 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height); 71 | holder.layout_ll.setLayoutParams(params); 72 | } 73 | 74 | @Override 75 | public int getItemCount() { 76 | return mList.size(); 77 | } 78 | } 79 | 80 | class MyViewHolder extends RecyclerView.ViewHolder { 81 | 82 | private TextView textView; 83 | private LinearLayout layout_ll; 84 | 85 | public MyViewHolder(View itemView) { 86 | super(itemView); 87 | textView = itemView.findViewById(R.id.tv_text); 88 | layout_ll = itemView.findViewById(R.id.layout_ll); 89 | } 90 | } 91 | 92 | protected int dp2px(int dp) { 93 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tv_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_checked.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 14 | 15 | 20 | 21 | 34 | 35 | 47 | 48 | 61 | 62 | 63 | 64 | 70 | 71 | 87 | 88 | 103 | 104 | 120 | 121 | 122 | 123 | 128 | 129 | 134 | 135 | 146 | 147 | 161 | 162 | 173 | 174 | 175 | 176 | 181 | 182 | 196 | 197 | 214 | 215 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_edittext.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 17 | 18 | 32 | 33 | 50 | 51 | 68 | 69 | 73 | 74 | 90 | 91 | 92 | 106 | 107 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 27 | 28 | 39 | 40 | 41 | 42 | 47 | 48 | 58 | 59 | 70 | 71 | 72 | 73 | 74 | 79 | 80 | 91 | 92 | 104 | 105 | 109 | 110 | 122 | 123 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_imageview.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 15 | 20 | 21 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 56 | 57 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 90 | 91 | 102 | 103 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 21 | 22 | 27 | 28 | 33 | 34 | 39 | 40 | 45 | 46 | 51 | 52 | 60 | 61 | 66 | 67 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_ripper.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 20 | 25 | 26 | 31 | 32 | 45 | 46 | 56 | 57 | 58 | 63 | 64 | 70 | 71 | 88 | 89 | 107 | 108 | 109 | 110 | 124 | 125 | 133 | 134 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 29 | 30 | 38 | 39 | 40 | 41 | 42 | 55 | 56 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_textview_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 47 | 48 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_viewgroup.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 28 | 29 | 30 | 31 | 40 | 41 | 49 | 50 | 51 | 52 | 63 | 64 | 72 | 73 | 74 | 75 | 83 | 84 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fmt_textview_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 19 | 20 | 21 | 25 | 26 | 34 | 35 | 36 | 46 | 47 | 48 | 49 | 54 | 55 | 63 | 64 | 65 | 75 | 76 | 77 | 78 | 83 | 84 | 93 | 94 | 104 | 105 | 116 | 117 | 118 | 119 | 120 | 125 | 126 | 134 | 135 | 146 | 147 | 157 | 158 | 159 | 160 | 161 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fmt_textview_state.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 27 | 28 | 43 | 44 | 45 | 46 | 52 | 53 | 67 | 68 | 84 | 85 | 86 | 87 | 93 | 94 | 110 | 111 | 133 | 134 | 135 | 136 | 142 | 143 | 154 | 155 | 156 | 157 | 174 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fmt_textview_version.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 19 | 20 | 29 | 30 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/food.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/food.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_account.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_bg.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_bg_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_bg_normal.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_bg_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_bg_pressed.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_heart.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_help.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_man.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_man.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_man_pressed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_man_pressed.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_phone.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_phone_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_phone_normal.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_phone_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_phone_pressed.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_phone_unable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_phone_unable.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_psw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_psw.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_rb_green_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_rb_green_default.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_rb_green_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_rb_green_selected.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_settings.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xhdpi/icon_star.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/array.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @color/purple 7 | @color/blue 8 | @color/navy 9 | @color/green 10 | @color/yellow 11 | @color/orange 12 | @color/red 13 | 14 | 15 | 16 | 17 | #FA8BFF 18 | #2BD2FF 19 | #2BFF88 20 | 21 | 22 | 23 | 24 | #74EBD5 25 | #9FACE6 26 | 27 | 28 | 29 | 30 | #D9AFD9 31 | #97D9E1 32 | 33 | 34 | 35 | 36 | #FF3CAC 37 | #784BA0 38 | #2B86C5 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #eff1f3 7 | #DCDCDD 8 | 9 | #FF0000 10 | #FF7F00 11 | #FFFF00 12 | #00FF00 13 | #00FFFF 14 | #0000FF 15 | #8B00FF 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RWidgetHelper 3 | 子控件自行兼容父控件\nshadow_radius + shadow_dx/shadow_dy 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/test/java/com/ruffian/library/demo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.demo; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | //serve to dependencies 6 | maven { url 'https://maven.aliyun.com/repository/central' } 7 | maven { url 'https://maven.aliyun.com/repository/public' } 8 | maven { url 'https://maven.aliyun.com/repository/google' } 9 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } 10 | 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath 'com.android.tools.build:gradle:7.2.1' 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | //serve to project 22 | maven { url 'https://maven.aliyun.com/repository/central' } 23 | maven { url 'https://maven.aliyun.com/repository/public' } 24 | maven { url 'https://maven.aliyun.com/repository/google' } 25 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } 26 | maven { url "https://jitpack.io" } 27 | google() 28 | } 29 | tasks.withType(Javadoc) { 30 | options { 31 | encoding "UTF-8" 32 | charSet 'UTF-8' 33 | links "http://docs.oracle.com/javase/7/docs/api" 34 | } 35 | } 36 | } 37 | 38 | task clean(type: Delete) { 39 | delete rootProject.buildDir 40 | } 41 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | #systemProp.https.proxyHost=127.0.0.1 19 | #systemProp.https.proxyPort=1080 20 | #systemProp.http.proxyHost=127.0.0.1 21 | #systemProp.http.proxyPort=1080 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Nov 05 16:59:03 CST 2020 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-7.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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 31 5 | 6 | defaultConfig { 7 | minSdkVersion 14 8 | targetSdkVersion 31 9 | versionCode 38 10 | versionName "support.v0.0.14" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | android { 20 | lintOptions { 21 | abortOnError false 22 | 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | api fileTree(dir: 'libs', include: ['*.jar']) 29 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | //直接依赖,避免开发者不能正常使用 33 | api 'com.android.support:appcompat-v7:28.0.0' 34 | api 'com.android.support.constraint:constraint-layout:1.1.3' 35 | testImplementation 'junit:junit:4.12' 36 | } -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/ruffian/library/widget/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.ruffian.library.widget.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RCheckBox.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.support.v7.widget.AppCompatCheckBox; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | 9 | import com.ruffian.library.widget.helper.RCheckHelper; 10 | import com.ruffian.library.widget.iface.RHelper; 11 | 12 | /** 13 | * RCheckBox 14 | * 15 | * @author ZhongDaFeng 16 | */ 17 | public class RCheckBox extends AppCompatCheckBox implements RHelper { 18 | 19 | private RCheckHelper mHelper; 20 | 21 | public RCheckBox(Context context) { 22 | this(context, null); 23 | } 24 | 25 | public RCheckBox(Context context, AttributeSet attrs) { 26 | super(context, attrs); 27 | mHelper = new RCheckHelper(context, this, attrs); 28 | } 29 | 30 | @Override 31 | public RCheckHelper getHelper() { 32 | return mHelper; 33 | } 34 | 35 | @Override 36 | public void setEnabled(boolean enabled) { 37 | super.setEnabled(enabled); 38 | if (mHelper != null) mHelper.setEnabled(enabled); 39 | } 40 | 41 | @Override 42 | public boolean onTouchEvent(MotionEvent event) { 43 | if (mHelper != null) mHelper.onTouchEvent(event); 44 | return super.onTouchEvent(event); 45 | } 46 | 47 | @Override 48 | public void setSelected(boolean selected) { 49 | if (mHelper != null) mHelper.setSelected(selected); 50 | super.setSelected(selected); 51 | } 52 | 53 | @Override 54 | public void setChecked(boolean checked) { 55 | if (mHelper != null) mHelper.setChecked(checked); 56 | super.setChecked(checked); 57 | } 58 | 59 | @Override 60 | protected void onDraw(Canvas canvas) { 61 | super.onDraw(canvas); 62 | if (mHelper != null) mHelper.drawIconWithText(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RConstraintLayout.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.support.constraint.ConstraintLayout; 6 | import android.util.AttributeSet; 7 | 8 | import com.ruffian.library.widget.helper.RBaseHelper; 9 | import com.ruffian.library.widget.iface.RHelper; 10 | 11 | /** 12 | * RConstraintLayout 13 | * 14 | * @author ZhongDaFeng 15 | */ 16 | public class RConstraintLayout extends ConstraintLayout implements RHelper { 17 | 18 | private RBaseHelper mHelper; 19 | 20 | public RConstraintLayout(Context context) { 21 | this(context, null); 22 | } 23 | 24 | public RConstraintLayout(Context context, AttributeSet attrs) { 25 | this(context, attrs, 0); 26 | } 27 | 28 | public RConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) { 29 | super(context, attrs, defStyleAttr); 30 | mHelper = new RBaseHelper(context, this, attrs); 31 | } 32 | 33 | @Override 34 | public RBaseHelper getHelper() { 35 | return mHelper; 36 | } 37 | 38 | @Override 39 | public void dispatchDraw(Canvas canvas) { 40 | super.dispatchDraw(canvas); 41 | mHelper.dispatchDraw(canvas); 42 | } 43 | 44 | @Override 45 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 46 | super.onLayout(changed, left, top, right, bottom); 47 | mHelper.onLayout(changed, left, top, right, bottom); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/REditText.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.AppCompatEditText; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | import com.ruffian.library.widget.helper.RTextViewHelper; 9 | import com.ruffian.library.widget.iface.RHelper; 10 | 11 | /** 12 | * REditText 13 | * 14 | * @author ZhongDaFeng 15 | */ 16 | public class REditText extends AppCompatEditText implements RHelper { 17 | 18 | private RTextViewHelper mHelper; 19 | 20 | public REditText(Context context) { 21 | this(context, null); 22 | } 23 | 24 | public REditText(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | mHelper = new RTextViewHelper(context, this, attrs); 27 | } 28 | 29 | @Override 30 | public RTextViewHelper getHelper() { 31 | return mHelper; 32 | } 33 | 34 | @Override 35 | public void setEnabled(boolean enabled) { 36 | super.setEnabled(enabled); 37 | if (mHelper != null) mHelper.setEnabled(enabled); 38 | } 39 | 40 | @Override 41 | public boolean onTouchEvent(MotionEvent event) { 42 | if (mHelper != null) mHelper.onTouchEvent(event); 43 | return super.onTouchEvent(event); 44 | } 45 | 46 | @Override 47 | public void setSelected(boolean selected) { 48 | if (mHelper != null) mHelper.setSelected(selected); 49 | super.setSelected(selected); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RFrameLayout.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.util.AttributeSet; 6 | import android.widget.FrameLayout; 7 | 8 | import com.ruffian.library.widget.helper.RBaseHelper; 9 | import com.ruffian.library.widget.iface.RHelper; 10 | 11 | /** 12 | * RFrameLayout 13 | * 14 | * @author ZhongDaFeng 15 | */ 16 | public class RFrameLayout extends FrameLayout implements RHelper { 17 | 18 | private RBaseHelper mHelper; 19 | 20 | public RFrameLayout(Context context) { 21 | this(context, null); 22 | } 23 | 24 | public RFrameLayout(Context context, AttributeSet attrs) { 25 | this(context, attrs, 0); 26 | } 27 | 28 | public RFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { 29 | super(context, attrs, defStyleAttr); 30 | mHelper = new RBaseHelper(context, this, attrs); 31 | } 32 | 33 | @Override 34 | public RBaseHelper getHelper() { 35 | return mHelper; 36 | } 37 | 38 | @Override 39 | public void dispatchDraw(Canvas canvas) { 40 | super.dispatchDraw(canvas); 41 | mHelper.dispatchDraw(canvas); 42 | } 43 | 44 | @Override 45 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 46 | super.onLayout(changed, left, top, right, bottom); 47 | mHelper.onLayout(changed, left, top, right, bottom); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RImageView.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.ColorStateList; 5 | import android.content.res.Resources; 6 | import android.content.res.TypedArray; 7 | import android.graphics.Bitmap; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.ColorFilter; 11 | import android.graphics.PorterDuff; 12 | import android.graphics.PorterDuffColorFilter; 13 | import android.graphics.drawable.Drawable; 14 | import android.graphics.drawable.LayerDrawable; 15 | import android.os.Build; 16 | import android.util.AttributeSet; 17 | 18 | import android.support.annotation.ColorInt; 19 | import android.support.annotation.DrawableRes; 20 | import android.support.annotation.Nullable; 21 | import android.support.v7.widget.AppCompatImageView; 22 | 23 | import com.ruffian.library.widget.rounded.RoundDrawable; 24 | 25 | /** 26 | * RImageView 27 | * 28 | * @author ZhongDaFeng 29 | */ 30 | public class RImageView extends AppCompatImageView { 31 | 32 | //圆角 33 | private float mCorner = -1; 34 | private float mCornerTopLeft = 0; 35 | private float mCornerTopRight = 0; 36 | private float mCornerBottomLeft = 0; 37 | private float mCornerBottomRight = 0; 38 | //边框 39 | private float mBorderWidth = 0; 40 | private int mBorderColor = Color.BLACK; 41 | //是否圆形 42 | private boolean mIsCircle = false; 43 | 44 | private Drawable mDrawable; 45 | private ScaleType mScaleType; 46 | private int mResource; 47 | private ColorFilter mColorFilter; 48 | private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_ATOP; 49 | 50 | public RImageView(Context context) { 51 | this(context, null); 52 | } 53 | 54 | public RImageView(Context context, @Nullable AttributeSet attrs) { 55 | super(context, attrs); 56 | initAttributeSet(context, attrs); 57 | } 58 | 59 | /** 60 | * 初始化自定义属性 61 | * 62 | * @param attrs 63 | */ 64 | private void initAttributeSet(Context context, AttributeSet attrs) { 65 | if (context == null || attrs == null) return; 66 | 67 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RImageView); 68 | mIsCircle = a.getBoolean(R.styleable.RImageView_is_circle, false); 69 | mCorner = a.getDimensionPixelSize(R.styleable.RImageView_corner_radius, -1); 70 | mCornerTopLeft = a.getDimensionPixelSize(R.styleable.RImageView_corner_radius_top_left, 0); 71 | mCornerTopRight = a.getDimensionPixelSize(R.styleable.RImageView_corner_radius_top_right, 0); 72 | mCornerBottomLeft = a.getDimensionPixelSize(R.styleable.RImageView_corner_radius_bottom_left, 0); 73 | mCornerBottomRight = a.getDimensionPixelSize(R.styleable.RImageView_corner_radius_bottom_right, 0); 74 | mBorderWidth = a.getDimensionPixelSize(R.styleable.RImageView_border_width, 0); 75 | mBorderColor = a.getColor(R.styleable.RImageView_border_color, Color.BLACK); 76 | 77 | //get system attrs 78 | String namespace = "http://schemas.android.com/apk/res/android";//android的命名空间 79 | int tintColor = attrs.getAttributeResourceValue(namespace, "tint", 0); 80 | 81 | int tintMode = attrs.getAttributeIntValue(namespace, "tintMode", 0); 82 | if (tintMode != 0) mTintMode = wrapTintMode(tintMode); 83 | //android studio 预览:需要区分版本或者使用 isInEditMode() 判断区分 84 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 85 | setImageTintList(getImageTintList()); 86 | } else { 87 | if (tintColor != 0) { 88 | mColorFilter = new PorterDuffColorFilter(getResources().getColor(tintColor), mTintMode); 89 | } 90 | } 91 | 92 | a.recycle(); 93 | updateDrawableAttrs(); 94 | } 95 | 96 | @Override 97 | protected void drawableStateChanged() { 98 | super.drawableStateChanged(); 99 | invalidate(); 100 | } 101 | 102 | @Override 103 | public void setScaleType(ScaleType scaleType) { 104 | super.setScaleType(scaleType); 105 | if (mScaleType != scaleType) { 106 | mScaleType = scaleType; 107 | updateDrawableAttrs(); 108 | invalidate(); 109 | } 110 | } 111 | 112 | @Override 113 | public void setImageBitmap(Bitmap bm) { 114 | mResource = 0; 115 | mDrawable = RoundDrawable.fromBitmap(bm); 116 | updateDrawableAttrs(); 117 | super.setImageDrawable(mDrawable); 118 | } 119 | 120 | @Override 121 | public void setImageDrawable(Drawable drawable) { 122 | mResource = 0; 123 | mDrawable = RoundDrawable.fromDrawable(drawable); 124 | updateDrawableAttrs(); 125 | super.setImageDrawable(mDrawable); 126 | } 127 | 128 | @Override 129 | public void setImageResource(@DrawableRes int resId) { 130 | if (mResource != resId) { 131 | mResource = resId; 132 | mDrawable = resolveResource(); 133 | updateDrawableAttrs(); 134 | super.setImageDrawable(mDrawable); 135 | } 136 | } 137 | 138 | @Override 139 | public void setImageTintList(@Nullable ColorStateList tint) { 140 | super.setImageTintList(tint); 141 | if (tint != null) { 142 | this.mColorFilter = new PorterDuffColorFilter(tint.getDefaultColor(), mTintMode); 143 | } 144 | setColorFilter(); 145 | } 146 | 147 | @Override 148 | public void setImageTintMode(@Nullable PorterDuff.Mode tintMode) { 149 | super.setImageTintMode(tintMode); 150 | this.mTintMode = tintMode; 151 | setColorFilter(); 152 | } 153 | 154 | public void setColorFilter() { 155 | if (mColorFilter != null && mDrawable != null) { 156 | //ColorFilter filter = new PorterDuffColorFilter(Color.parseColor("#00FF00"), PorterDuff.Mode.SRC_ATOP); 157 | mDrawable.setColorFilter(mColorFilter); 158 | } 159 | } 160 | 161 | @Override 162 | protected void onDraw(Canvas canvas) { 163 | super.onDraw(canvas); 164 | drawEmptyBitmap(); 165 | } 166 | 167 | private Drawable resolveResource() { 168 | Resources resources = getResources(); 169 | if (resources == null) { 170 | return null; 171 | } 172 | Drawable d = null; 173 | if (mResource != 0) { 174 | try { 175 | d = resources.getDrawable(mResource); 176 | } catch (Exception e) { 177 | mResource = 0; 178 | } 179 | } 180 | return RoundDrawable.fromDrawable(d); 181 | } 182 | 183 | private void updateDrawableAttrs() { 184 | updateAttrs(mDrawable, mScaleType); 185 | setColorFilter(); 186 | } 187 | 188 | private void updateAttrs(Drawable drawable, ScaleType scaleType) { 189 | if (drawable == null) return; 190 | if (drawable instanceof RoundDrawable) { 191 | ((RoundDrawable) drawable).setParams(scaleType, mBorderWidth, mBorderColor, mIsCircle, mCorner, mCornerTopLeft, mCornerTopRight, mCornerBottomLeft, mCornerBottomRight); 192 | } else if (drawable instanceof LayerDrawable) { 193 | LayerDrawable ld = ((LayerDrawable) drawable); 194 | for (int i = 0, layers = ld.getNumberOfLayers(); i < layers; i++) { 195 | updateAttrs(ld.getDrawable(i), scaleType); 196 | } 197 | } 198 | } 199 | 200 | /** 201 | * 绘制空Bitmap 202 | */ 203 | private void drawEmptyBitmap() { 204 | if (mDrawable == null) {//未设置Bitmap 205 | int width = getMeasuredWidth(); 206 | int height = getMeasuredHeight(); 207 | if (width > 0 && height > 0) { 208 | Drawable background = getBackground(); 209 | if (background != null) { 210 | background.setBounds(0, 0, width, height); 211 | setImageDrawable(background); 212 | } else { 213 | setImageBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)); 214 | } 215 | } 216 | } 217 | } 218 | 219 | public RImageView isCircle(boolean isCircle) { 220 | this.mIsCircle = isCircle; 221 | updateDrawableAttrs(); 222 | return this; 223 | } 224 | 225 | /************************ 226 | * Border 227 | ************************/ 228 | public float getBorderWidth() { 229 | return mBorderWidth; 230 | } 231 | 232 | public RImageView setBorderWidth(int borderWidth) { 233 | this.mBorderWidth = borderWidth; 234 | updateDrawableAttrs(); 235 | return this; 236 | } 237 | 238 | public int getBorderColor() { 239 | return mBorderColor; 240 | } 241 | 242 | public RImageView setBorderColor(@ColorInt int borderColor) { 243 | this.mBorderColor = borderColor; 244 | updateDrawableAttrs(); 245 | return this; 246 | } 247 | 248 | /************************ 249 | * Corner 250 | ************************/ 251 | public float getCorner() { 252 | return mCorner; 253 | } 254 | 255 | public RImageView setCorner(float corner) { 256 | this.mCorner = corner; 257 | updateDrawableAttrs(); 258 | return this; 259 | } 260 | 261 | public float getCornerTopLeft() { 262 | return mCornerTopLeft; 263 | } 264 | 265 | public RImageView setCornerTopLeft(float cornerTopLeft) { 266 | this.mCorner = -1; 267 | this.mCornerTopLeft = cornerTopLeft; 268 | updateDrawableAttrs(); 269 | return this; 270 | } 271 | 272 | public float getCornerTopRight() { 273 | return mCornerTopRight; 274 | } 275 | 276 | public RImageView setCornerTopRight(float cornerTopRight) { 277 | this.mCorner = -1; 278 | this.mCornerTopRight = cornerTopRight; 279 | updateDrawableAttrs(); 280 | return this; 281 | } 282 | 283 | public float getCornerBottomLeft() { 284 | return mCornerBottomLeft; 285 | } 286 | 287 | public RImageView setCornerBottomLeft(float cornerBottomLeft) { 288 | this.mCorner = -1; 289 | this.mCornerBottomLeft = cornerBottomLeft; 290 | updateDrawableAttrs(); 291 | return this; 292 | } 293 | 294 | public float getCornerBottomRight() { 295 | return mCornerBottomRight; 296 | } 297 | 298 | public RImageView setCornerBottomRight(float cornerBottomRight) { 299 | this.mCorner = -1; 300 | this.mCornerBottomRight = cornerBottomRight; 301 | updateDrawableAttrs(); 302 | return this; 303 | } 304 | 305 | public RImageView setCorner(float topLeft, float topRight, float bottomRight, float bottomLeft) { 306 | this.mCorner = -1; 307 | this.mCornerTopLeft = topLeft; 308 | this.mCornerTopRight = topRight; 309 | this.mCornerBottomRight = bottomRight; 310 | this.mCornerBottomLeft = bottomLeft; 311 | updateDrawableAttrs(); 312 | return this; 313 | } 314 | 315 | /** 316 | * 转化TintMode 317 | * 318 | * @param tintMode 319 | * @return 320 | */ 321 | private PorterDuff.Mode wrapTintMode(int tintMode) { 322 | switch (tintMode) { 323 | case 3: 324 | return PorterDuff.Mode.SRC_OVER; 325 | case 5: 326 | return PorterDuff.Mode.SRC_IN; 327 | case 14: 328 | return PorterDuff.Mode.MULTIPLY; 329 | case 15: 330 | return PorterDuff.Mode.SCREEN; 331 | case 16: 332 | return PorterDuff.Mode.ADD; 333 | case 9: 334 | default: 335 | return PorterDuff.Mode.SRC_ATOP; 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RLinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.util.AttributeSet; 6 | import android.widget.LinearLayout; 7 | 8 | import com.ruffian.library.widget.helper.RBaseHelper; 9 | import com.ruffian.library.widget.iface.RHelper; 10 | 11 | /** 12 | * RLinearLayout 13 | * 14 | * @author ZhongDaFeng 15 | */ 16 | public class RLinearLayout extends LinearLayout implements RHelper { 17 | 18 | private RBaseHelper mHelper; 19 | 20 | public RLinearLayout(Context context) { 21 | this(context, null); 22 | } 23 | 24 | public RLinearLayout(Context context, AttributeSet attrs) { 25 | this(context, attrs, 0); 26 | } 27 | 28 | public RLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 29 | super(context, attrs, defStyleAttr); 30 | mHelper = new RBaseHelper(context, this, attrs); 31 | } 32 | 33 | @Override 34 | public RBaseHelper getHelper() { 35 | return mHelper; 36 | } 37 | 38 | @Override 39 | public void dispatchDraw(Canvas canvas) { 40 | super.dispatchDraw(canvas); 41 | mHelper.dispatchDraw(canvas); 42 | } 43 | 44 | @Override 45 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 46 | super.onLayout(changed, left, top, right, bottom); 47 | mHelper.onLayout(changed, left, top, right, bottom); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RRadioButton.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.support.v7.widget.AppCompatRadioButton; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | 9 | import com.ruffian.library.widget.helper.RCheckHelper; 10 | import com.ruffian.library.widget.iface.RHelper; 11 | 12 | /** 13 | * RRadioButton 14 | * 15 | * @author ZhongDaFeng 16 | */ 17 | public class RRadioButton extends AppCompatRadioButton implements RHelper { 18 | 19 | private RCheckHelper mHelper; 20 | 21 | public RRadioButton(Context context) { 22 | this(context, null); 23 | } 24 | 25 | public RRadioButton(Context context, AttributeSet attrs) { 26 | super(context, attrs); 27 | mHelper = new RCheckHelper(context, this, attrs); 28 | } 29 | 30 | @Override 31 | public RCheckHelper getHelper() { 32 | return mHelper; 33 | } 34 | 35 | @Override 36 | public void setEnabled(boolean enabled) { 37 | super.setEnabled(enabled); 38 | if (mHelper != null) mHelper.setEnabled(enabled); 39 | } 40 | 41 | @Override 42 | public boolean onTouchEvent(MotionEvent event) { 43 | if (mHelper != null) mHelper.onTouchEvent(event); 44 | return super.onTouchEvent(event); 45 | } 46 | 47 | @Override 48 | public void setSelected(boolean selected) { 49 | if (mHelper != null) mHelper.setSelected(selected); 50 | super.setSelected(selected); 51 | } 52 | 53 | @Override 54 | public void setChecked(boolean checked) { 55 | if (mHelper != null) mHelper.setChecked(checked); 56 | super.setChecked(checked); 57 | } 58 | 59 | @Override 60 | protected void onDraw(Canvas canvas) { 61 | super.onDraw(canvas); 62 | if (mHelper != null) mHelper.drawIconWithText(); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RRelativeLayout.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.util.AttributeSet; 6 | import android.widget.LinearLayout; 7 | import android.widget.RelativeLayout; 8 | 9 | import com.ruffian.library.widget.helper.RBaseHelper; 10 | import com.ruffian.library.widget.iface.RHelper; 11 | 12 | /** 13 | * RRelativeLayout 14 | * 15 | * @author ZhongDaFeng 16 | */ 17 | public class RRelativeLayout extends RelativeLayout implements RHelper { 18 | 19 | private RBaseHelper mHelper; 20 | 21 | public RRelativeLayout(Context context) { 22 | this(context, null); 23 | } 24 | 25 | public RRelativeLayout(Context context, AttributeSet attrs) { 26 | this(context, attrs, 0); 27 | } 28 | 29 | public RRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 30 | super(context, attrs, defStyleAttr); 31 | mHelper = new RBaseHelper(context, this, attrs); 32 | } 33 | 34 | @Override 35 | public RBaseHelper getHelper() { 36 | return mHelper; 37 | } 38 | 39 | @Override 40 | public void dispatchDraw(Canvas canvas) { 41 | super.dispatchDraw(canvas); 42 | mHelper.dispatchDraw(canvas); 43 | } 44 | 45 | @Override 46 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 47 | super.onLayout(changed, left, top, right, bottom); 48 | mHelper.onLayout(changed, left, top, right, bottom); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RTextView.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.support.v7.widget.AppCompatTextView; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | 9 | import com.ruffian.library.widget.helper.RTextViewHelper; 10 | import com.ruffian.library.widget.iface.RHelper; 11 | 12 | /** 13 | * RTextView 14 | * 15 | * @author ZhongDaFeng 16 | */ 17 | public class RTextView extends AppCompatTextView implements RHelper { 18 | 19 | private RTextViewHelper mHelper; 20 | 21 | public RTextView(Context context) { 22 | this(context, null); 23 | } 24 | 25 | public RTextView(Context context, AttributeSet attrs) { 26 | super(context, attrs); 27 | mHelper = new RTextViewHelper(context, this, attrs); 28 | } 29 | 30 | @Override 31 | public RTextViewHelper getHelper() { 32 | return mHelper; 33 | } 34 | 35 | @Override 36 | public void setEnabled(boolean enabled) { 37 | super.setEnabled(enabled); 38 | if (mHelper != null) mHelper.setEnabled(enabled); 39 | } 40 | 41 | @Override 42 | public boolean onTouchEvent(MotionEvent event) { 43 | if (mHelper != null) mHelper.onTouchEvent(event); 44 | return super.onTouchEvent(event); 45 | } 46 | 47 | @Override 48 | public void setSelected(boolean selected) { 49 | if (mHelper != null) mHelper.setSelected(selected); 50 | super.setSelected(selected); 51 | } 52 | 53 | @Override 54 | protected void onDraw(Canvas canvas) { 55 | super.onDraw(canvas); 56 | if (mHelper != null) mHelper.drawIconWithText(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/RView.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | 7 | import com.ruffian.library.widget.helper.RBaseHelper; 8 | import com.ruffian.library.widget.iface.RHelper; 9 | 10 | /** 11 | * RView 12 | * 13 | * @author ZhongDaFeng 14 | */ 15 | public class RView extends View implements RHelper { 16 | 17 | 18 | private RBaseHelper mHelper; 19 | 20 | public RView(Context context) { 21 | this(context, null); 22 | } 23 | 24 | public RView(Context context, AttributeSet attrs) { 25 | this(context, attrs, 0); 26 | } 27 | 28 | public RView(Context context, AttributeSet attrs, int defStyleAttr) { 29 | super(context, attrs, defStyleAttr); 30 | mHelper = new RBaseHelper(context, this, attrs); 31 | } 32 | 33 | @Override 34 | public RBaseHelper getHelper() { 35 | return mHelper; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/clip/ClipHelper.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.clip; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Color; 5 | import android.graphics.Paint; 6 | import android.graphics.Path; 7 | import android.graphics.PorterDuff; 8 | import android.graphics.PorterDuffXfermode; 9 | import android.os.Build; 10 | import android.support.v4.view.ViewCompat; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | /** 15 | * ClipHelper 16 | * 17 | * @author ZhongDaFeng 18 | */ 19 | public class ClipHelper implements IClip { 20 | 21 | private final Paint clipPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 22 | protected PorterDuffXfermode DST_OUT_MODE = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT); 23 | protected PorterDuffXfermode DST_IN_MODE = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); 24 | private final Path clipPath = new Path(); 25 | private final Path rectView = new Path(); 26 | 27 | private ClipPathManager clipPathManager = new ClipPathManager(); 28 | 29 | private boolean requestShapeUpdate = true; 30 | 31 | private View mView; 32 | private boolean mClipLayout; 33 | 34 | public ClipHelper() { 35 | clipPaint.setAntiAlias(true); 36 | clipPaint.setColor(Color.BLUE); 37 | clipPaint.setStyle(Paint.Style.FILL); 38 | clipPaint.setStrokeWidth(1); 39 | } 40 | 41 | 42 | /** 43 | * 初始化Clip 44 | * 45 | * @param view 46 | * @param clipLayout 47 | * @param clipPathCreator 48 | */ 49 | public void initClip(View view, boolean clipLayout, ClipPathManager.ClipPathCreator clipPathCreator) { 50 | this.mView = view; 51 | this.mClipLayout = clipLayout; 52 | if (!canClip()) return; 53 | 54 | getView().setDrawingCacheEnabled(true); 55 | getView().setWillNotDraw(false); 56 | 57 | if (Build.VERSION.SDK_INT <= 27) { 58 | clipPaint.setXfermode(DST_IN_MODE); 59 | getView().setLayerType(View.LAYER_TYPE_SOFTWARE, clipPaint); //Only works for software layers 60 | } else { 61 | clipPaint.setXfermode(DST_OUT_MODE); 62 | getView().setLayerType(View.LAYER_TYPE_SOFTWARE, null); //Only works for software layers 63 | } 64 | 65 | //设置clip 66 | clipPathManager.setClipPathCreator(clipPathCreator); 67 | 68 | requestShapeUpdate(); 69 | } 70 | 71 | 72 | @Override 73 | public void dispatchDraw(Canvas canvas) { 74 | if (!canClip()) return; 75 | 76 | if (requestShapeUpdate) { 77 | calculateLayout(canvas.getWidth(), canvas.getHeight()); 78 | requestShapeUpdate = false; 79 | } 80 | if (Build.VERSION.SDK_INT <= 27) { 81 | canvas.drawPath(clipPath, clipPaint); 82 | } else { 83 | canvas.drawPath(rectView, clipPaint); 84 | } 85 | 86 | if (Build.VERSION.SDK_INT <= 27) { 87 | getView().setLayerType(View.LAYER_TYPE_HARDWARE, null); 88 | } 89 | } 90 | 91 | @Override 92 | public void onLayout(boolean changed, int left, int top, int right, int bottom) { 93 | if (!canClip()) return; 94 | if (changed) requestShapeUpdate(); 95 | } 96 | 97 | 98 | private void calculateLayout(int width, int height) { 99 | rectView.reset(); 100 | rectView.addRect(0f, 0f, 1f * getView().getWidth(), 1f * getView().getHeight(), Path.Direction.CW); 101 | 102 | if (width > 0 && height > 0) { 103 | clipPathManager.setupClipLayout(width, height); 104 | clipPath.reset(); 105 | clipPath.set(clipPathManager.getClipPath()); 106 | 107 | //invert the path for android P 108 | if (Build.VERSION.SDK_INT > 27) { 109 | final boolean success = rectView.op(clipPath, Path.Op.DIFFERENCE); 110 | } 111 | 112 | //this needs to be fixed for 25.4.0 113 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && ViewCompat.getElevation(getView()) > 0f) { 114 | try { 115 | getView().setOutlineProvider(getView().getOutlineProvider()); 116 | } catch (Exception e) { 117 | e.printStackTrace(); 118 | } 119 | } 120 | } 121 | 122 | getView().postInvalidate(); 123 | } 124 | 125 | /** 126 | * 请求更新 127 | */ 128 | public void requestShapeUpdate() { 129 | this.requestShapeUpdate = true; 130 | getView().postInvalidate(); 131 | } 132 | 133 | public View getView() { 134 | return mView; 135 | } 136 | 137 | /** 138 | * 是否满足裁剪条件 139 | * 140 | * @return 141 | */ 142 | public boolean canClip() { 143 | return getView() != null && getView() instanceof ViewGroup && mClipLayout; 144 | } 145 | 146 | 147 | } 148 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/clip/ClipPathManager.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.clip; 2 | 3 | import android.graphics.Path; 4 | 5 | public class ClipPathManager { 6 | 7 | protected final Path path = new Path(); 8 | 9 | public ClipPathManager() { 10 | } 11 | 12 | /** 13 | * 设置裁剪布局 14 | * 15 | * @param width 16 | * @param height 17 | */ 18 | public void setupClipLayout(int width, int height) { 19 | path.reset(); 20 | Path clipPath = null; 21 | if (createClipPath != null) { 22 | clipPath = createClipPath.createClipPath(width, height); 23 | } 24 | if (clipPath != null) { 25 | path.set(clipPath); 26 | } 27 | } 28 | 29 | /** 30 | * 设置裁剪路径 31 | * 32 | * @return 33 | */ 34 | public Path getClipPath() { 35 | return path; 36 | } 37 | 38 | /** 39 | * 创建Path接口 40 | */ 41 | private ClipPathCreator createClipPath = null; 42 | 43 | public void setClipPathCreator(ClipPathCreator createClipPath) { 44 | this.createClipPath = createClipPath; 45 | } 46 | 47 | public interface ClipPathCreator { 48 | Path createClipPath(int width, int height); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/clip/IClip.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.clip; 2 | 3 | import android.graphics.Canvas; 4 | 5 | /** 6 | * IClip 7 | * 8 | * @author ZhongDaFeng 9 | */ 10 | public interface IClip { 11 | 12 | public void dispatchDraw(Canvas canvas); 13 | 14 | public void onLayout(boolean changed, int left, int top, int right, int bottom); 15 | } 16 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/helper/RCheckHelper.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.helper; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.CompoundButton; 6 | 7 | /** 8 | * Check-Helper 9 | * 10 | * @author ZhongDaFeng 11 | */ 12 | public class RCheckHelper extends RTextViewHelper { 13 | 14 | public RCheckHelper(Context context, CompoundButton view, AttributeSet attrs) { 15 | super(context, view, attrs); 16 | } 17 | 18 | @Override 19 | protected boolean isCompoundButtonChecked() { 20 | if (mView != null) return ((CompoundButton) mView).isChecked(); 21 | return false; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/iface/ITextViewFeature.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.iface; 2 | 3 | import android.view.MotionEvent; 4 | import android.view.View; 5 | 6 | /** 7 | * TextView特性功能接口 8 | * 9 | * @author ZhongDaFeng 10 | */ 11 | public interface ITextViewFeature { 12 | public void setEnabled(boolean enabled); 13 | 14 | public void onTouchEvent(MotionEvent event); 15 | 16 | public void setSelected(boolean selected); 17 | 18 | public void drawIconWithText(); 19 | } 20 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/iface/RHelper.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.iface; 2 | 3 | import com.ruffian.library.widget.helper.RBaseHelper; 4 | 5 | /** 6 | * Helper 7 | * 8 | * @author ZhongDaFeng 9 | */ 10 | public interface RHelper { 11 | T getHelper(); 12 | } 13 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/rounded/RoundDrawable.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.rounded; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapShader; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.ColorFilter; 8 | import android.graphics.Matrix; 9 | import android.graphics.Paint; 10 | import android.graphics.Path; 11 | import android.graphics.PixelFormat; 12 | import android.graphics.Rect; 13 | import android.graphics.RectF; 14 | import android.graphics.Shader; 15 | import android.graphics.drawable.BitmapDrawable; 16 | import android.graphics.drawable.Drawable; 17 | import android.graphics.drawable.LayerDrawable; 18 | import android.support.annotation.NonNull; 19 | import android.support.annotation.Nullable; 20 | import android.widget.ImageView; 21 | 22 | /** 23 | * Round Drawable 24 | * 25 | * @author ZhongDaFeng 26 | */ 27 | public class RoundDrawable extends Drawable { 28 | 29 | private Bitmap mBitmap; 30 | private final int mBitmapWidth; 31 | private final int mBitmapHeight; 32 | private final RectF mBitmapRect = new RectF(); 33 | 34 | private final Paint mBitmapPaint; 35 | private final Paint mBorderPaint; 36 | 37 | private final RectF mBorderRect = new RectF(); 38 | private final RectF mDrawableRect = new RectF(); 39 | 40 | private Path mPath = new Path(); 41 | private RectF mRectF = new RectF(); 42 | private final RectF mBounds = new RectF(); 43 | private final RectF mBoundsFinal = new RectF(); 44 | 45 | private boolean mRebuildShader = true; 46 | private final Matrix mShaderMatrix = new Matrix(); 47 | private ImageView.ScaleType mScaleType = ImageView.ScaleType.FIT_CENTER; 48 | 49 | //圆角 50 | private float mCorner = -1; 51 | private float mCornerTopLeft = 0; 52 | private float mCornerTopRight = 0; 53 | private float mCornerBottomLeft = 0; 54 | private float mCornerBottomRight = 0; 55 | private float mCornerRadii[] = new float[8]; 56 | //边框 57 | private float mBorderWidth = 0; 58 | private int mBorderColor = Color.BLACK; 59 | //是否圆形 60 | private boolean mCircle = true; 61 | 62 | public RoundDrawable(Bitmap bitmap) { 63 | mBitmap = bitmap; 64 | mBitmapWidth = bitmap.getWidth(); 65 | mBitmapHeight = bitmap.getHeight(); 66 | mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight); 67 | 68 | mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 69 | mBitmapPaint.setStyle(Paint.Style.FILL); 70 | mBitmapPaint.setAntiAlias(true); 71 | 72 | mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 73 | mBorderPaint.setStyle(Paint.Style.STROKE); 74 | mBorderPaint.setAntiAlias(true); 75 | updateBorder(); 76 | } 77 | 78 | @Override 79 | protected void onBoundsChange(Rect bounds) { 80 | super.onBoundsChange(bounds); 81 | mBoundsFinal.set(bounds);//更新Bounds 82 | updateShaderMatrix();//更新变化矩阵 83 | updateConner();//更新圆角 84 | } 85 | 86 | @Override 87 | public void setAlpha(int alpha) { 88 | mBitmapPaint.setAlpha(alpha); 89 | invalidateSelf(); 90 | } 91 | 92 | @Override 93 | public void setColorFilter(@Nullable ColorFilter colorFilter) { 94 | mBitmapPaint.setColorFilter(colorFilter); 95 | invalidateSelf(); 96 | } 97 | 98 | @Override 99 | public int getOpacity() { 100 | return PixelFormat.TRANSLUCENT; 101 | } 102 | 103 | @Override 104 | public void draw(@NonNull Canvas canvas) { 105 | 106 | if (mRebuildShader) { 107 | BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 108 | bitmapShader.setLocalMatrix(mShaderMatrix);// Shader.TileMode.CLAMP 109 | mBitmapPaint.setShader(bitmapShader); 110 | mRebuildShader = false; 111 | } 112 | 113 | if (mCircle) { 114 | // mDrawableRect 宽高是实际显示图片的宽高,类似于 marginTop 了 mDrawableRect.left 115 | float cx = mDrawableRect.width() / 2f + mDrawableRect.left; 116 | float cy = mDrawableRect.height() / 2f + mDrawableRect.top; 117 | float radiusX = mDrawableRect.width() / 2f; 118 | float radiusY = mDrawableRect.height() / 2f; 119 | float radiusDrawable = Math.min(radiusX, radiusY); 120 | float radiusBitmap = Math.min(mBitmapHeight, mBitmapWidth); 121 | float radius = Math.min(radiusBitmap, radiusDrawable); 122 | canvas.drawCircle(cx, cy, radius, mBitmapPaint); 123 | 124 | if (mBorderWidth > 0) { 125 | cx = mBorderRect.width() / 2f + mBorderRect.left; 126 | cy = mBorderRect.height() / 2f + mBorderRect.top; 127 | radiusX = mBorderRect.width() / 2f; 128 | radiusY = mBorderRect.height() / 2f; 129 | radiusDrawable = Math.min(radiusX, radiusY); 130 | radiusBitmap = Math.min(mBitmapHeight, mBitmapWidth); 131 | radius = Math.min(radiusBitmap, radiusDrawable); 132 | canvas.drawCircle(cx, cy, radius, mBorderPaint); 133 | } 134 | } else { 135 | updateDrawablePath(); 136 | canvas.drawPath(mPath, mBitmapPaint); 137 | if (mBorderWidth > 0) { 138 | updateBorderPath(); 139 | canvas.drawPath(mPath, mBorderPaint); 140 | } 141 | } 142 | } 143 | 144 | /** 145 | * 更新圆角 146 | */ 147 | private void updateConner() { 148 | if (mCorner >= 0) { 149 | for (int i = 0; i < mCornerRadii.length; i++) { 150 | mCornerRadii[i] = mCorner; 151 | } 152 | return; 153 | } 154 | 155 | if (mCorner < 0) { 156 | mCornerRadii[0] = mCornerTopLeft; 157 | mCornerRadii[1] = mCornerTopLeft; 158 | mCornerRadii[2] = mCornerTopRight; 159 | mCornerRadii[3] = mCornerTopRight; 160 | mCornerRadii[4] = mCornerBottomRight; 161 | mCornerRadii[5] = mCornerBottomRight; 162 | mCornerRadii[6] = mCornerBottomLeft; 163 | mCornerRadii[7] = mCornerBottomLeft; 164 | return; 165 | } 166 | } 167 | 168 | /** 169 | * 更新Drawable路径 170 | */ 171 | private void updateDrawablePath() { 172 | mPath.reset();//must重置 173 | mPath.addRoundRect(mDrawableRect, mCornerRadii, Path.Direction.CCW); 174 | } 175 | 176 | /** 177 | * 更新边框路径 178 | */ 179 | private void updateBorderPath() { 180 | mPath.reset();//must重置 181 | mPath.addRoundRect(mBorderRect, mCornerRadii, Path.Direction.CCW); 182 | } 183 | 184 | /** 185 | * 根据ScaleType更新ShaderMatrix 186 | * 此函数涉及更新的属性:mBorderWidth || mScaleType || mCircle 187 | */ 188 | private void updateShaderMatrix() { 189 | float scale; 190 | float dx = 0, dy = 0; 191 | float height, width; 192 | float half = mBorderWidth / 2f; 193 | mBounds.set(mBoundsFinal); 194 | switch (mScaleType) { 195 | case CENTER_INSIDE: 196 | if (mBitmapWidth <= mBounds.width() && mBitmapHeight <= mBounds.height()) { 197 | scale = 1.0f; 198 | height = mBitmapHeight; 199 | width = mBitmapWidth; 200 | } else {//bitmap > drawable 201 | scale = Math.min(mBounds.width() / (float) mBitmapWidth, mBounds.height() / (float) mBitmapHeight); 202 | if (mBounds.height() < mBounds.width()) {//高<宽 203 | height = mBounds.height(); 204 | width = mBitmapWidth * scale; 205 | } else if (mBounds.height() > mBounds.width()) {//宽<高 206 | height = mBitmapHeight * scale; 207 | width = mBounds.width(); 208 | } else {//宽=高 209 | height = mBitmapHeight * scale; 210 | width = mBitmapWidth * scale; 211 | } 212 | } 213 | //X,Y偏移 214 | dx = (int) ((mBounds.width() - mBitmapWidth * scale) * 0.5f + 0.5f); 215 | dy = (int) ((mBounds.height() - mBitmapHeight * scale) * 0.5f + 0.5f); 216 | 217 | mRectF = new RectF(dx, dy, width + dx, height + dy); 218 | mRectF.inset(mCircle ? mBorderWidth : half, mCircle ? mBorderWidth : half);//非圆 1/2兼容圆角 219 | mShaderMatrix.reset(); 220 | mShaderMatrix.setScale(scale, scale); 221 | mShaderMatrix.postTranslate(dx, dy); 222 | break; 223 | case CENTER: 224 | height = Math.min(mBounds.height(), mBitmapRect.height()); 225 | width = Math.min(mBounds.width(), mBitmapRect.width()); 226 | //裁剪或者Margin(如果View大,则 margin Bitmap,如果View小则裁剪Bitmap) 227 | float cutOrMarginH = mBounds.height() - mBitmapRect.height(); 228 | float cutOrMarginW = mBounds.width() - mBitmapRect.width(); 229 | float halfH = cutOrMarginH / 2f, halfW = cutOrMarginW / 2f; 230 | float top = halfH > 0 ? halfH : 0; 231 | float left = halfW > 0 ? halfW : 0; 232 | dx = halfW; 233 | dy = halfH; 234 | 235 | mRectF = new RectF(left, top, left + width, top + height); 236 | mRectF.inset(mCircle ? mBorderWidth : half, mCircle ? mBorderWidth : half);//非圆 1/2兼容圆角 237 | mShaderMatrix.reset(); 238 | mShaderMatrix.postTranslate((int) (dx + 0.5f) + half, (int) (dy + 0.5f) + half); 239 | break; 240 | case CENTER_CROP: 241 | mRectF.set(mBounds); 242 | mRectF.inset(mCircle ? mBorderWidth : half, mCircle ? mBorderWidth : half);//非圆 1/2兼容圆角 243 | if (mBitmapWidth * mRectF.height() > mRectF.width() * mBitmapHeight) { 244 | scale = mRectF.height() / (float) mBitmapHeight; 245 | dx = (mRectF.width() - mBitmapWidth * scale) * 0.5f; 246 | } else { 247 | scale = mRectF.width() / (float) mBitmapWidth; 248 | dy = (mRectF.height() - mBitmapHeight * scale) * 0.5f; 249 | } 250 | mShaderMatrix.reset(); 251 | mShaderMatrix.setScale(scale, scale); 252 | mShaderMatrix.postTranslate((int) (dx + 0.5f) + half, (int) (dy + 0.5f) + half); 253 | break; 254 | default: 255 | case FIT_CENTER: 256 | case FIT_END: 257 | case FIT_START: 258 | mBounds.inset(mCircle ? mBorderWidth : half, mCircle ? mBorderWidth : half);//非圆 1/2兼容圆角 259 | mRectF.set(mBitmapRect); 260 | mShaderMatrix.setRectToRect(mBitmapRect, mBounds, scaleTypeToScaleToFit(mScaleType)); 261 | mShaderMatrix.mapRect(mRectF); 262 | mShaderMatrix.setRectToRect(mBitmapRect, mRectF, Matrix.ScaleToFit.FILL); 263 | break; 264 | case FIT_XY: 265 | mBounds.inset(mCircle ? mBorderWidth : half, mCircle ? mBorderWidth : half);//非圆 1/2兼容圆角 266 | mRectF.set(mBounds); 267 | mShaderMatrix.reset(); 268 | mShaderMatrix.setRectToRect(mBitmapRect, mRectF, Matrix.ScaleToFit.FILL); 269 | break; 270 | } 271 | if (mCircle) { 272 | mBorderRect.set(mRectF.left - half, mRectF.top - half, mRectF.right + half, mRectF.bottom + half);//还原 273 | } else { 274 | mBorderRect.set(mBoundsFinal); 275 | mBorderRect.inset(half, half); 276 | } 277 | mDrawableRect.set(mRectF); 278 | mRebuildShader = true; 279 | } 280 | 281 | /** 282 | * 更新边框 283 | */ 284 | private void updateBorder() { 285 | mBorderPaint.setColor(mBorderColor); 286 | mBorderPaint.setStrokeWidth(mBorderWidth); 287 | } 288 | 289 | public Bitmap getBitmap() { 290 | return mBitmap; 291 | } 292 | 293 | /** 294 | * 统一设置参数 295 | */ 296 | public void setParams(ImageView.ScaleType scaleType, float borderWidth, int borderColor, boolean circle, float corner, float topLeft, float topRight, float bottomLeft, float bottomRight) { 297 | //scaleType 298 | if (scaleType == null) { 299 | scaleType = ImageView.ScaleType.FIT_CENTER; 300 | } 301 | if (mScaleType != scaleType) { 302 | mScaleType = scaleType; 303 | } 304 | 305 | //borderWidth 306 | mBorderWidth = borderWidth; 307 | //borderColor 308 | mBorderColor = borderColor; 309 | updateBorder(); 310 | 311 | //circle 312 | mCircle = circle; 313 | 314 | //corner 315 | mCorner = corner; 316 | mCornerTopLeft = topLeft; 317 | mCornerTopRight = topRight; 318 | mCornerBottomLeft = bottomLeft; 319 | mCornerBottomRight = bottomRight; 320 | updateConner(); 321 | 322 | //update 323 | updateShaderMatrix();//更新变化矩阵 324 | invalidateSelf();//更新 325 | } 326 | 327 | public RoundDrawable setScaleType(ImageView.ScaleType scaleType) { 328 | if (scaleType == null) { 329 | scaleType = ImageView.ScaleType.FIT_CENTER; 330 | } 331 | if (mScaleType != scaleType) { 332 | mScaleType = scaleType; 333 | updateShaderMatrix();//更新变化矩阵 334 | invalidateSelf(); 335 | } 336 | return this; 337 | } 338 | 339 | public RoundDrawable setCircle(boolean circle) { 340 | mCircle = circle; 341 | updateShaderMatrix();//更新变化矩阵 342 | invalidateSelf(); 343 | return this; 344 | } 345 | 346 | public RoundDrawable setBorderWidth(float borderWidth) { 347 | mBorderWidth = borderWidth; 348 | updateBorder(); 349 | updateShaderMatrix();//更新变化矩阵 350 | invalidateSelf(); 351 | return this; 352 | } 353 | 354 | public RoundDrawable setBorderColor(int borderColor) { 355 | mBorderColor = borderColor; 356 | updateBorder(); 357 | invalidateSelf(); 358 | return this; 359 | } 360 | 361 | public RoundDrawable setConner(float corner, float topLeft, float topRight, float bottomLeft, float bottomRight) { 362 | mCorner = corner; 363 | mCornerTopLeft = topLeft; 364 | mCornerTopRight = topRight; 365 | mCornerBottomLeft = bottomLeft; 366 | mCornerBottomRight = bottomRight; 367 | updateConner(); 368 | invalidateSelf(); 369 | return this; 370 | } 371 | 372 | public static RoundDrawable fromBitmap(Bitmap bitmap) { 373 | if (bitmap != null) { 374 | return new RoundDrawable(bitmap); 375 | } else { 376 | return null; 377 | } 378 | } 379 | 380 | public static Drawable fromDrawable(Drawable drawable) { 381 | if (drawable != null) { 382 | if (drawable instanceof RoundDrawable) { 383 | // just return if it's already a RoundedDrawable 384 | return drawable; 385 | } else if (drawable instanceof LayerDrawable) { 386 | ConstantState cs = drawable.mutate().getConstantState(); 387 | LayerDrawable ld = (LayerDrawable) (cs != null ? cs.newDrawable() : drawable); 388 | 389 | int num = ld.getNumberOfLayers(); 390 | 391 | // loop through layers to and change to RoundedDrawables if possible 392 | for (int i = 0; i < num; i++) { 393 | Drawable d = ld.getDrawable(i); 394 | ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d)); 395 | } 396 | return ld; 397 | } 398 | 399 | // try to get a bitmap from the drawable and 400 | Bitmap bm = drawableToBitmap(drawable); 401 | if (bm != null) { 402 | return new RoundDrawable(bm); 403 | } 404 | } 405 | return drawable; 406 | } 407 | 408 | public static Bitmap drawableToBitmap(Drawable drawable) { 409 | if (drawable instanceof BitmapDrawable) { 410 | return ((BitmapDrawable) drawable).getBitmap(); 411 | } 412 | 413 | Bitmap bitmap; 414 | int width = Math.max(drawable.getIntrinsicWidth(), 2); 415 | int height = Math.max(drawable.getIntrinsicHeight(), 2); 416 | try { 417 | bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 418 | Canvas canvas = new Canvas(bitmap); 419 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 420 | drawable.draw(canvas); 421 | } catch (Throwable e) { 422 | e.printStackTrace(); 423 | bitmap = null; 424 | } 425 | 426 | return bitmap; 427 | } 428 | 429 | private static Matrix.ScaleToFit scaleTypeToScaleToFit(ImageView.ScaleType st) { 430 | /** 431 | * 根据源码改造 sS2FArray[st.nativeInt - 1] 432 | */ 433 | switch (st) { 434 | case FIT_XY: 435 | return Matrix.ScaleToFit.FILL; 436 | case FIT_START: 437 | return Matrix.ScaleToFit.START; 438 | case FIT_END: 439 | return Matrix.ScaleToFit.END; 440 | case FIT_CENTER: 441 | return Matrix.ScaleToFit.CENTER; 442 | default: 443 | return Matrix.ScaleToFit.CENTER; 444 | } 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/shadow/ShadowBitmapDrawable.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.shadow; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.Path; 7 | import android.graphics.Rect; 8 | import android.graphics.RectF; 9 | import android.graphics.drawable.BitmapDrawable; 10 | 11 | import com.ruffian.library.widget.utils.ReflectUtils; 12 | 13 | import java.util.Arrays; 14 | 15 | /** 16 | * ShadowBitmapDrawable 17 | * 18 | * @author ZhongDaFeng 19 | */ 20 | public class ShadowBitmapDrawable extends BitmapDrawable { 21 | 22 | 23 | private int mShadowColor;//阴影颜色 24 | private float mShadowRadius;//阴影半径 25 | private float[] mRoundRadii;//矩形圆角半径 26 | private int mShadowDx;//阴影x偏移 27 | private int mShadowDy;//阴影y偏移 28 | 29 | private RectF mBoundsF; 30 | 31 | public ShadowBitmapDrawable() { 32 | mBoundsF = new RectF(); 33 | } 34 | 35 | /** 36 | * RBaseHelp中属性修改时调用 37 | * 38 | * @param shadowColor 39 | * @param shadowRadius 40 | * @param shadowDx 41 | * @param shadowDy 42 | * @param roundRadii 43 | */ 44 | public void updateParameter(int shadowColor, float shadowRadius, int shadowDx, int shadowDy, float[] roundRadii) { 45 | boolean needUpdate = false; 46 | if (mShadowColor != shadowColor || mShadowRadius != shadowRadius 47 | || mShadowDx != shadowDx || mShadowDy != shadowDy 48 | || !Arrays.equals(mRoundRadii, roundRadii)) { 49 | needUpdate = true; 50 | } 51 | this.mShadowColor = shadowColor; 52 | this.mRoundRadii = roundRadii; 53 | this.mShadowRadius = shadowRadius; 54 | this.mShadowDx = shadowDx; 55 | this.mShadowDy = shadowDy; 56 | 57 | if (needUpdate) { 58 | //重新创建bitmap 59 | Bitmap bitmap = makeShadowBitmap((int) mBoundsF.width(), (int) mBoundsF.height(), mShadowRadius, mShadowDx, mShadowDy, mShadowColor, mRoundRadii); 60 | 61 | //反射调用函数 setBitmap 更新阴影图片 62 | ReflectUtils.invokeMethod(this, "setBitmap", new Class[]{Bitmap.class}, new Object[]{bitmap}); 63 | } 64 | 65 | 66 | } 67 | 68 | @Override 69 | protected void onBoundsChange(Rect bounds) { 70 | super.onBoundsChange(bounds); 71 | if (bounds.right - bounds.left > 0 && bounds.bottom - bounds.top > 0) { 72 | updateBounds(bounds); 73 | } 74 | } 75 | 76 | /** 77 | * 更新Bounds 78 | * 79 | * @param bounds 80 | */ 81 | private void updateBounds(Rect bounds) { 82 | if (bounds == null) bounds = getBounds(); 83 | 84 | float left = bounds.left + mShadowRadius + Math.abs(mShadowDx); 85 | float right = bounds.right - mShadowRadius - Math.abs(mShadowDx); 86 | float top = bounds.top + mShadowRadius + Math.abs(mShadowDy); 87 | float bottom = bounds.bottom - mShadowRadius - Math.abs(mShadowDy); 88 | 89 | mBoundsF.set(left, top, right, bottom); 90 | 91 | //重新创建bitmap(不等高列表复用时可能会出现内存抖动,待优化) 92 | Bitmap bitmap = makeShadowBitmap((int) mBoundsF.width(), (int) mBoundsF.height(), mShadowRadius, mShadowDx, mShadowDy, mShadowColor, mRoundRadii); 93 | 94 | //反射调用函数 setBitmap 更新阴影图片 95 | ReflectUtils.invokeMethod(this, "setBitmap", new Class[]{Bitmap.class}, new Object[]{bitmap}); 96 | 97 | } 98 | 99 | 100 | /** 101 | * 创建阴影Bitmap 102 | * 103 | * @param shadowWidth 104 | * @param shadowHeight 105 | * @param shadowRadius 106 | * @param dx 107 | * @param dy 108 | * @param shadowColor 109 | * @param radii 110 | * @return 111 | */ 112 | public Bitmap makeShadowBitmap(int shadowWidth, int shadowHeight, float shadowRadius, 113 | float dx, float dy, int shadowColor, float[] radii) { 114 | 115 | //容错处理 116 | if (shadowWidth <= 0 || shadowHeight <= 0) 117 | return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_4444); 118 | 119 | 120 | //阴影bitmap 121 | Bitmap output = Bitmap.createBitmap(shadowWidth, shadowHeight, Bitmap.Config.ARGB_4444); 122 | Canvas canvas = new Canvas(output); 123 | 124 | RectF shadowRect = new RectF(shadowRadius, shadowRadius, shadowWidth - shadowRadius, shadowHeight - shadowRadius); 125 | 126 | 127 | //存在偏移时重新计算阴影矩形大小 128 | shadowRect.top += Math.abs(dy); 129 | shadowRect.bottom -= Math.abs(dy); 130 | shadowRect.left += Math.abs(dx); 131 | shadowRect.right -= Math.abs(dx); 132 | 133 | //阴影画笔 134 | Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); 135 | shadowPaint.setColor(shadowColor); 136 | shadowPaint.setShadowLayer(shadowRadius, dx, dy, shadowColor); 137 | 138 | //路径 139 | Path path = new Path(); 140 | path.addRoundRect(shadowRect, radii, Path.Direction.CW); 141 | canvas.drawPath(path, shadowPaint); 142 | 143 | return output; 144 | } 145 | 146 | 147 | } 148 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/shadow/ShadowDrawable.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.shadow; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.ColorFilter; 5 | import android.graphics.Paint; 6 | import android.graphics.Path; 7 | import android.graphics.PixelFormat; 8 | import android.graphics.Rect; 9 | import android.graphics.RectF; 10 | import android.graphics.drawable.Drawable; 11 | import android.support.annotation.Nullable; 12 | 13 | /** 14 | * Shadow Drawable 15 | * 16 | * @author ZhongDaFeng 17 | */ 18 | public class ShadowDrawable extends Drawable { 19 | 20 | private int mShadowColor;//阴影颜色 21 | private float mShadowRadius;//阴影半径 22 | private float[] mRoundRadii;//矩形圆角半径 23 | private int mShadowDx;//阴影x偏移 24 | private int mShadowDy;//阴影y偏移 25 | 26 | private Path mPath; 27 | private Paint mPaint; 28 | private RectF mBoundsF; 29 | 30 | public ShadowDrawable() { 31 | mPath = new Path(); 32 | mBoundsF = new RectF(); 33 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); 34 | } 35 | 36 | public void updateParameter(int shadowColor, float shadowRadius, int shadowDx, int shadowDy, float[] roundRadii) { 37 | this.mShadowColor = shadowColor; 38 | this.mRoundRadii = roundRadii; 39 | this.mShadowRadius = shadowRadius; 40 | this.mShadowDx = shadowDx; 41 | this.mShadowDy = shadowDy; 42 | /** 43 | * 设置阴影 44 | */ 45 | mPaint.setColor(mShadowColor); 46 | mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor); 47 | } 48 | 49 | @Override 50 | protected void onBoundsChange(Rect bounds) { 51 | super.onBoundsChange(bounds); 52 | if (bounds.right - bounds.left > 0 && bounds.bottom - bounds.top > 0) { 53 | updateBounds(bounds); 54 | } 55 | } 56 | 57 | /** 58 | * 更新Bounds 59 | * 60 | * @param bounds 61 | */ 62 | private void updateBounds(Rect bounds) { 63 | if (bounds == null) bounds = getBounds(); 64 | /** 65 | * 算法1 => 存在偏移的方向增加宽度 (效果不是很好) 66 | */ 67 | /* float left = bounds.left + getShadowOffsetHalf() + offset; 68 | left = mShadowDx < 0 ? left + Math.abs(mShadowDx) : left; 69 | 70 | float right = bounds.right - getShadowOffsetHalf() - offset; 71 | right = mShadowDx > 0 ? right - Math.abs(mShadowDx) : right; 72 | 73 | float top = bounds.top + getShadowOffsetHalf() + offset; 74 | top = mShadowDy < 0 ? top + Math.abs(mShadowDy) : top; 75 | 76 | float bottom = bounds.bottom - getShadowOffsetHalf() - offset; 77 | bottom = mShadowDy > 0 ? bottom - Math.abs(mShadowDy) : bottom;*/ 78 | 79 | /** 80 | * 算法2 => 水平/垂直存在偏移,对应方向增加间距 81 | */ 82 | float left = bounds.left + getShadowOffset() + Math.abs(mShadowDx); 83 | float right = bounds.right - getShadowOffset() - Math.abs(mShadowDx); 84 | float top = bounds.top + getShadowOffset() + Math.abs(mShadowDy); 85 | float bottom = bounds.bottom - getShadowOffset() - Math.abs(mShadowDy); 86 | 87 | mBoundsF.set(left, top, right, bottom); 88 | mPath.reset();//must重置 89 | mPath.addRoundRect(mBoundsF, mRoundRadii, Path.Direction.CW); 90 | } 91 | 92 | @Override 93 | public void draw(Canvas canvas) { 94 | canvas.drawPath(mPath, mPaint); 95 | } 96 | 97 | @Override 98 | public void setAlpha(int alpha) { 99 | mPaint.setAlpha(alpha); 100 | } 101 | 102 | @Override 103 | public void setColorFilter(@Nullable ColorFilter colorFilter) { 104 | mPaint.setColorFilter(colorFilter); 105 | } 106 | 107 | @Override 108 | public int getOpacity() { 109 | return PixelFormat.TRANSLUCENT; 110 | } 111 | 112 | public float getShadowOffset() { 113 | return mShadowRadius; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/utils/ReflectUtils.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.utils; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * 反射工具类 8 | * 9 | * @author ZhongDaFeng 10 | */ 11 | public class ReflectUtils { 12 | 13 | /** 14 | * 循环向上转型, 获取对象的 DeclaredMethod 15 | * 16 | * @param object 子类对象 17 | * @param methodName 父类中的方法名 18 | * @param parameterTypes 父类中的方法参数类型 19 | * @return 父类中的方法对象 20 | */ 21 | public static Method getDeclaredMethod(Object object, String methodName, Class... parameterTypes) { 22 | Method method = null; 23 | for (Class clazz = object.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) { 24 | try { 25 | method = clazz.getDeclaredMethod(methodName, parameterTypes); 26 | return method; 27 | } catch (Exception e) { 28 | //这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。 29 | //如果这里的异常打印或者往外抛,则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了 30 | } 31 | } 32 | return null; 33 | } 34 | 35 | /** 36 | * 直接调用对象方法, 而忽略修饰符(private, protected, default) 37 | * 38 | * @param object 子类对象 39 | * @param methodName 父类中的方法名 40 | * @param parameterTypes 父类中的方法参数类型 41 | * @param parameters 父类中的方法参数 42 | * @return 父类中方法的执行结果 43 | */ 44 | public static Object invokeMethod(Object object, String methodName, Class[] parameterTypes, Object[] parameters) { 45 | //根据 对象、方法名和对应的方法参数 通过反射 调用上面的方法获取 Method 对象 46 | Method method = getDeclaredMethod(object, methodName, parameterTypes); 47 | try { 48 | if (null != method) { 49 | //抑制Java对方法进行检查,主要是针对私有方法而言 50 | method.setAccessible(true); 51 | //调用object 的 method 所代表的方法,其方法的参数是 parameters 52 | return method.invoke(object, parameters); 53 | } 54 | } catch (IllegalArgumentException e) { 55 | e.printStackTrace(); 56 | } catch (IllegalAccessException e) { 57 | e.printStackTrace(); 58 | } catch (InvocationTargetException e) { 59 | e.printStackTrace(); 60 | } 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/utils/RippleDrawableUtils.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.utils; 2 | 3 | import android.content.res.ColorStateList; 4 | import android.graphics.drawable.Drawable; 5 | import android.graphics.drawable.RippleDrawable; 6 | import android.os.Build; 7 | import android.support.annotation.NonNull; 8 | import android.support.annotation.RequiresApi; 9 | 10 | 11 | /** 12 | * RippleDrawable包装类 13 | * 备注:此类没有实质意义,纯粹是绕一圈支持新版Android studio预览自定义View 14 | */ 15 | public class RippleDrawableUtils { 16 | 17 | private Drawable contentDrawable, maskDrawable; 18 | 19 | /** 20 | * 此处的参数必须为Object类型,否则导致 Android studio 无法预览 21 | * 22 | * @param content 23 | * @param mask 24 | */ 25 | public RippleDrawableUtils(Object content, Object mask) { 26 | this.contentDrawable = content == null ? null : (Drawable) content; 27 | this.maskDrawable = mask == null ? null : (Drawable) mask; 28 | } 29 | 30 | /** 31 | * 32 | * 新版Android studio 奇葩报错 33 | * 34 | * Exception Details java.lang.VerifyError: Bad type on operand stack Exception 35 | * Details: Location: com/ruffian/library/widget/helper/RBaseHelper.getRippleDrawableWithTag(ZI)[Ljava/lang/Object; @233: invokespecial 36 | * Reason: Type 'java/lang/Object' (current frame, stack[2]) is not assignable to 'android/graphics/drawable/Drawable' 37 | * Current Frame: 38 | * bci: @233 39 | * flags: { } 40 | * locals: { 'com/ruffian/library/widget/helper/RBaseHelper', integer, integer, 'java/lang/Object', 'java/lang/Object', '[[I', '[I', 'android/content/res/ColorStateList' } 41 | * stack: { uninitialized 226, uninitialized 226, 'java/lang/Object', 'java/lang/Object' } 42 | * 43 | */ 44 | 45 | /** 46 | * 获取 RippleDrawable 47 | * 新版 Android studio 有个BUG,目标接收 Drawable 的参数,如果开发者传递 null 会报错:'java/lang/Object' is not assignable to 'android/graphics/drawable/Drawable 48 | * 这里曲线救国,改为 Object 接收 content 和 mask 49 | */ 50 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 51 | public RippleDrawable getRippleDrawable(@NonNull ColorStateList color) { 52 | //color – The ripple color 53 | //content – The content drawable, may be null 54 | //mask – The mask drawable, may be null 55 | return new RippleDrawable(color, contentDrawable, maskDrawable); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /library/src/main/java/com/ruffian/library/widget/utils/TextViewUtils.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget.utils; 2 | 3 | import android.graphics.Paint; 4 | import android.widget.TextView; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | 9 | /** 10 | * TextView工具类 11 | * 12 | * @author ZhongDaFeng 13 | */ 14 | public class TextViewUtils { 15 | 16 | private static TextViewUtils instance = null; 17 | 18 | private TextViewUtils() { 19 | } 20 | 21 | public static TextViewUtils get() { 22 | if (instance == null) { 23 | synchronized (TextViewUtils.class) { 24 | if (instance == null) { 25 | instance = new TextViewUtils(); 26 | } 27 | } 28 | } 29 | return instance; 30 | } 31 | 32 | 33 | /** 34 | * 获取Text实际宽度 35 | * 备注:单行最大宽度 36 | * 37 | * @param view view 38 | * @param drawableWidth drawable宽度 39 | * @param paddingLeft 内边距-左 40 | * @param paddingRight 内边距-右 41 | * @param drawablePaddingHorizontal 水平方向上drawable间距 42 | * @return 43 | */ 44 | public float getTextWidth(TextView view, int drawableWidth, int paddingLeft, int paddingRight, int drawablePaddingHorizontal) { 45 | if (view == null) return 0; 46 | 47 | float textWidth; 48 | /** 49 | * 1.是否存在\n换行(获取宽度最大的值) 50 | * 2.不存在\n获取单行宽度值(判断是否自动换行临界值) 51 | */ 52 | String originalText = view.getText().toString(); 53 | if (originalText.contains("\n")) { 54 | String[] originalTextArray; 55 | ArrayList widthList; 56 | originalTextArray = originalText.split("\n"); 57 | int arrayLen = originalTextArray.length; 58 | widthList = new ArrayList<>(arrayLen); 59 | for (int i = 0; i < arrayLen; i++) { 60 | widthList.add(view.getPaint().measureText(originalTextArray[i])); 61 | } 62 | textWidth = Collections.max(widthList); 63 | } else { 64 | textWidth = view.getPaint().measureText(originalText); 65 | } 66 | 67 | //计算自动换行临界值,不允许超过换行临界值 68 | int maxWidth = view.getWidth() - drawableWidth - paddingLeft - paddingRight - drawablePaddingHorizontal; 69 | if (textWidth > maxWidth) { 70 | textWidth = maxWidth; 71 | } 72 | return textWidth; 73 | } 74 | 75 | /** 76 | * 获取Text实际高度 77 | * 备注:多行最大高度 78 | * 79 | * @param view view 80 | * @param drawableHeight drawable高度 81 | * @param paddingTop 内边距-左 82 | * @param paddingBottom 内边距-右 83 | * @param drawablePaddingVertical 垂直方向上drawable间距 84 | * @return 85 | */ 86 | public float getTextHeight(TextView view, int drawableHeight, int paddingTop, int paddingBottom, int drawablePaddingVertical) { 87 | if (view == null) return 0; 88 | 89 | /** 90 | * 1.单行高度*行数 91 | * 2.最大高度临界值 92 | */ 93 | Paint.FontMetrics fontMetrics = view.getPaint().getFontMetrics(); 94 | float singleLineHeight = Math.abs((fontMetrics.bottom - fontMetrics.top));//单行高度 95 | float textHeight = singleLineHeight * view.getLineCount(); 96 | 97 | //最大高度临界值,不允许超过最大高度临界值 98 | int maxHeight = view.getHeight() - drawableHeight - paddingTop - paddingBottom - drawablePaddingVertical;//最大允许的宽度 99 | if (textHeight > maxHeight) { 100 | textHeight = maxHeight; 101 | } 102 | return textHeight; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /library/src/test/java/com/ruffian/library/widget/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.ruffian.library.widget; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /picture/Gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/Gradient.png -------------------------------------------------------------------------------- /picture/REditText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/REditText.png -------------------------------------------------------------------------------- /picture/RImageView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/RImageView.png -------------------------------------------------------------------------------- /picture/RTextView_corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/RTextView_corner.png -------------------------------------------------------------------------------- /picture/RTextView_state.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/RTextView_state.gif -------------------------------------------------------------------------------- /picture/RTextView_typeface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/RTextView_typeface.png -------------------------------------------------------------------------------- /picture/RView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/RView.png -------------------------------------------------------------------------------- /picture/RViewGroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/RViewGroup.png -------------------------------------------------------------------------------- /picture/RWidgetHelper.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/RWidgetHelper.gif -------------------------------------------------------------------------------- /picture/all.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/all.gif -------------------------------------------------------------------------------- /picture/part1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/part1.png -------------------------------------------------------------------------------- /picture/part2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/part2.png -------------------------------------------------------------------------------- /picture/part3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/part3.png -------------------------------------------------------------------------------- /picture/part4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/part4.png -------------------------------------------------------------------------------- /picture/part5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/part5.png -------------------------------------------------------------------------------- /picture/ripple.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/ripple.gif -------------------------------------------------------------------------------- /picture/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RuffianZhong/RWidgetHelper/377b1598641a0a7d07a770a543da5e80d0cf59f6/picture/shadow.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library', ':library' 2 | --------------------------------------------------------------------------------