├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── interstellar.ttf │ ├── marshmallows.ttf │ ├── nuaz.otf │ └── whaleitried.ttf │ ├── java │ └── com │ │ └── ramijemli │ │ └── sample │ │ ├── activity │ │ ├── AdaptiveColorsActivity.java │ │ ├── FillActivity.java │ │ ├── GradientColorsActivity.java │ │ ├── HomeActivity.java │ │ ├── PieActivity.java │ │ ├── RingActivity.java │ │ └── TextFormatterActivity.java │ │ ├── adapter │ │ └── HomeAdapter.java │ │ ├── fragment │ │ ├── BackgroundBarSubFragment.java │ │ ├── BackgroundSubFragment.java │ │ ├── BehaviorSubFragment.java │ │ ├── ProgressSubFragment.java │ │ └── TextSubFragment.java │ │ ├── util │ │ └── ScreenUtil.java │ │ └── viewmodel │ │ └── Showcase.java │ └── res │ ├── color │ └── all_tab_tint_s.xml │ ├── drawable-v21 │ ├── all_ico_collapse.xml │ ├── all_ico_decrement.xml │ ├── all_ico_expand.xml │ ├── all_ico_fill.xml │ ├── all_ico_github.xml │ ├── all_ico_increment.xml │ ├── all_ico_paint.xml │ ├── all_ico_pie.xml │ ├── all_ico_ring.xml │ └── all_ripple_white.xml │ ├── drawable │ ├── all_bg_btn.xml │ ├── all_bg_mode.xml │ ├── all_card_bg.xml │ ├── all_card_bg_top.xml │ ├── home_bg_anchor.xml │ └── home_bg_item.xml │ ├── layout │ ├── activity_adaptive_colors.xml │ ├── activity_fill.xml │ ├── activity_gradient_colors.xml │ ├── activity_home.xml │ ├── activity_pie.xml │ ├── activity_ring.xml │ ├── activity_text_formatter.xml │ ├── custom_tab.xml │ ├── item_dev.xml │ ├── item_home.xml │ ├── item_interpolator.xml │ ├── sub_fragment_background.xml │ ├── sub_fragment_background_bar.xml │ ├── sub_fragment_behavior.xml │ ├── sub_fragment_progress.xml │ └── sub_fragment_text.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── art ├── angle.png ├── cover.png ├── google-play-badge.png ├── showcase_1.gif ├── showcase_10.gif ├── showcase_11.gif ├── showcase_12.gif ├── showcase_2.gif ├── showcase_3.gif ├── showcase_4.gif ├── showcase_5.gif ├── showcase_6.gif ├── showcase_7.gif ├── showcase_8.gif └── showcase_9.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── percentagechartview ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── ramijemli │ │ └── percentagechartview │ │ ├── IPercentageChartView.java │ │ ├── PercentageChartView.java │ │ ├── annotation │ │ ├── ChartMode.java │ │ ├── GradientTypes.java │ │ ├── ProgressBarStyle.java │ │ ├── ProgressOrientation.java │ │ └── TextStyle.java │ │ ├── callback │ │ ├── AdaptiveColorProvider.java │ │ ├── OnProgressChangeListener.java │ │ └── ProgressTextFormatter.java │ │ └── renderer │ │ ├── BaseModeRenderer.java │ │ ├── FillModeRenderer.java │ │ ├── OffsetEnabledMode.java │ │ ├── OrientationBasedMode.java │ │ ├── PieModeRenderer.java │ │ └── RingModeRenderer.java │ └── res │ └── values │ └── attrs.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/caches 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | /.idea/navEditor.xml 8 | /.idea/assetWizardSettings.xml 9 | .DS_Store 10 | /build 11 | /captures 12 | .externalNativeBuild 13 | 14 | out 15 | lib 16 | 17 | ### IntelliJ ### 18 | *.iml 19 | *.ipr 20 | *.iws 21 | .idea/ 22 | 23 | ### Android ### 24 | # built application files 25 | *.apk 26 | *.ap_ 27 | 28 | # files for the dex VM 29 | *.dex 30 | 31 | # Java class files 32 | *.class 33 | 34 | # generated files 35 | bin/ 36 | gen/ 37 | 38 | # Proguard folder generated by Intellij 39 | proguard_logs/ 40 | 41 | ### Keystore file ### 42 | *.keystore 43 | 44 | local.properties -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.ramijemli.percentagechartview" 7 | minSdkVersion 21 8 | targetSdkVersion 28 9 | versionCode 5 10 | versionName "1.3" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | resConfigs "en" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled true 17 | shrinkResources true 18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | compileOptions { 22 | sourceCompatibility = '1.8' 23 | targetCompatibility = '1.8' 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | implementation project(':percentagechartview') 30 | 31 | implementation 'androidx.appcompat:appcompat:1.0.2' 32 | implementation 'com.google.android.material:material:1.0.0' 33 | implementation 'androidx.recyclerview:recyclerview:1.0.0' 34 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 35 | 36 | implementation 'com.github.QuadFlask:colorpicker:0.0.13' 37 | implementation 'com.ogaclejapan.smarttablayout:library:2.0.0@aar' 38 | implementation 'com.ogaclejapan.smarttablayout:utils-v4:2.0.0@aar' 39 | 40 | implementation 'com.jakewharton:butterknife:10.1.0' 41 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 33 | 34 | 38 | 39 | 43 | 44 | 48 | 49 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/assets/interstellar.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamiJ3mli/PercentageChartView/80df5df39b7d0d2ff658487dcf90d380892b270f/app/src/main/assets/interstellar.ttf -------------------------------------------------------------------------------- /app/src/main/assets/marshmallows.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamiJ3mli/PercentageChartView/80df5df39b7d0d2ff658487dcf90d380892b270f/app/src/main/assets/marshmallows.ttf -------------------------------------------------------------------------------- /app/src/main/assets/nuaz.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamiJ3mli/PercentageChartView/80df5df39b7d0d2ff658487dcf90d380892b270f/app/src/main/assets/nuaz.otf -------------------------------------------------------------------------------- /app/src/main/assets/whaleitried.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamiJ3mli/PercentageChartView/80df5df39b7d0d2ff658487dcf90d380892b270f/app/src/main/assets/whaleitried.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/activity/AdaptiveColorsActivity.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.activity; 2 | 3 | 4 | import android.content.res.ColorStateList; 5 | import android.graphics.Color; 6 | import android.os.Bundle; 7 | import android.transition.Explode; 8 | import android.transition.Fade; 9 | import android.transition.Transition; 10 | import android.transition.TransitionManager; 11 | import android.view.animation.OvershootInterpolator; 12 | import android.widget.Button; 13 | 14 | import com.flask.colorpicker.ColorPickerView; 15 | import com.flask.colorpicker.builder.ColorPickerDialogBuilder; 16 | import com.ramijemli.percentagechartview.PercentageChartView; 17 | import com.ramijemli.percentagechartview.callback.AdaptiveColorProvider; 18 | import com.ramijemli.sample.R; 19 | 20 | import java.util.Random; 21 | 22 | import androidx.appcompat.app.AppCompatActivity; 23 | import androidx.constraintlayout.widget.ConstraintLayout; 24 | import androidx.constraintlayout.widget.ConstraintSet; 25 | import androidx.core.graphics.ColorUtils; 26 | import butterknife.BindView; 27 | import butterknife.ButterKnife; 28 | import butterknife.OnClick; 29 | import butterknife.Unbinder; 30 | 31 | import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.MODE_FILL; 32 | import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.MODE_PIE; 33 | import static com.ramijemli.percentagechartview.renderer.BaseModeRenderer.MODE_RING; 34 | 35 | 36 | public class AdaptiveColorsActivity extends AppCompatActivity { 37 | 38 | @BindView(R.id.constraint_layout) 39 | ConstraintLayout mConstraintLayout; 40 | 41 | @BindView(R.id.pie_chart) 42 | PercentageChartView mPieChart; 43 | @BindView(R.id.ring_chart) 44 | PercentageChartView mRingChart; 45 | @BindView(R.id.fill_chart) 46 | PercentageChartView mFillChart; 47 | 48 | //PROVIDED COLORS 49 | @BindView(R.id.color_one) 50 | Button mColorOne; 51 | @BindView(R.id.color_two) 52 | Button mColorTwo; 53 | @BindView(R.id.color_three) 54 | Button mColorThree; 55 | @BindView(R.id.color_four) 56 | Button mColorFour; 57 | 58 | private Unbinder unbinder; 59 | private ConstraintSet mConstraintSet; 60 | private Transition fade; 61 | private AdaptiveColorProvider colorProvider; 62 | private int colorOne; 63 | private int colorTwo; 64 | private int colorThree; 65 | private int colorFour; 66 | private int displayedMode; 67 | 68 | @Override 69 | protected void onCreate(Bundle savedInstanceState) { 70 | super.onCreate(savedInstanceState); 71 | setContentView(R.layout.activity_adaptive_colors); 72 | unbinder = ButterKnife.bind(this); 73 | setupLayoutAnimation(); 74 | setupColorProvider(); 75 | } 76 | 77 | @Override 78 | protected void onDestroy() { 79 | colorProvider = null; 80 | unbinder.unbind(); 81 | unbinder = null; 82 | super.onDestroy(); 83 | } 84 | 85 | private void setupLayoutAnimation() { 86 | Explode transition = new Explode(); 87 | transition.setDuration(600); 88 | transition.setInterpolator(new OvershootInterpolator(1f)); 89 | getWindow().setEnterTransition(transition); 90 | 91 | displayedMode = MODE_PIE; 92 | 93 | mConstraintSet = new ConstraintSet(); 94 | mConstraintSet.clone(mConstraintLayout); 95 | 96 | fade = new Fade(); 97 | fade.setDuration(400); 98 | } 99 | 100 | private void setupColorProvider() { 101 | colorOne = Color.parseColor("#F44336"); 102 | colorTwo = Color.parseColor("#FFEA00"); 103 | colorThree = Color.parseColor("#03A9F4"); 104 | colorFour = Color.parseColor("#00E676"); 105 | 106 | //COLOR PROVIDER 107 | colorProvider = new AdaptiveColorProvider() { 108 | @Override 109 | public int provideProgressColor(float progress) { 110 | if (progress <= 25) 111 | return colorOne; 112 | else if (progress <= 50) 113 | return colorTwo; 114 | else if (progress <= 75) 115 | return colorThree; 116 | else 117 | return colorFour; 118 | } 119 | 120 | @Override 121 | public int provideBackgroundColor(float progress) { 122 | return ColorUtils.blendARGB(provideProgressColor(progress), 123 | Color.BLACK, 124 | .8f); 125 | } 126 | 127 | @Override 128 | public int provideTextColor(float progress) { 129 | if (displayedMode == MODE_RING) { 130 | return provideProgressColor(progress); 131 | } 132 | 133 | return ColorUtils.blendARGB(provideProgressColor(progress), 134 | Color.WHITE, 135 | .8f); 136 | } 137 | 138 | @Override 139 | public int provideBackgroundBarColor(float progress) { 140 | return ColorUtils.blendARGB(provideProgressColor(progress), 141 | Color.BLACK, 142 | .5f); 143 | } 144 | }; 145 | 146 | mPieChart.setAdaptiveColorProvider(colorProvider); 147 | mRingChart.setAdaptiveColorProvider(colorProvider); 148 | mFillChart.setAdaptiveColorProvider(colorProvider); 149 | } 150 | 151 | private void changeMode(int pendingMode) { 152 | switch (pendingMode) { 153 | case MODE_PIE: 154 | mConstraintSet.setVisibility(R.id.pie_chart, ConstraintSet.VISIBLE); 155 | mConstraintSet.setVisibility(R.id.fill_chart, ConstraintSet.GONE); 156 | mConstraintSet.setVisibility(R.id.ring_chart, ConstraintSet.GONE); 157 | break; 158 | case MODE_FILL: 159 | mConstraintSet.setVisibility(R.id.pie_chart, ConstraintSet.GONE); 160 | mConstraintSet.setVisibility(R.id.fill_chart, ConstraintSet.VISIBLE); 161 | mConstraintSet.setVisibility(R.id.ring_chart, ConstraintSet.GONE); 162 | break; 163 | case MODE_RING: 164 | mConstraintSet.setVisibility(R.id.pie_chart, ConstraintSet.GONE); 165 | mConstraintSet.setVisibility(R.id.fill_chart, ConstraintSet.GONE); 166 | mConstraintSet.setVisibility(R.id.ring_chart, ConstraintSet.VISIBLE); 167 | break; 168 | } 169 | 170 | displayedMode = pendingMode; 171 | applyTransition(); 172 | } 173 | 174 | private void applyTransition() { 175 | TransitionManager.beginDelayedTransition(mConstraintLayout, fade); 176 | mConstraintSet.applyTo(mConstraintLayout); 177 | } 178 | 179 | //############################################################################################## ACTIONS 180 | @OnClick(R.id.pie_chart) 181 | public void pieChartClickAction() { 182 | mPieChart.setProgress(new Random().nextInt(100), true); 183 | } 184 | 185 | @OnClick(R.id.ring_chart) 186 | public void ringChartClickAction() { 187 | mRingChart.setProgress(new Random().nextInt(100), true); 188 | } 189 | 190 | @OnClick(R.id.fill_chart) 191 | public void fillChartClickAction() { 192 | mFillChart.setProgress(new Random().nextInt(100), true); 193 | } 194 | 195 | @OnClick(R.id.pie_mode) 196 | public void pieModeAction() { 197 | if (displayedMode == MODE_PIE) return; 198 | changeMode(MODE_PIE); 199 | } 200 | 201 | @OnClick(R.id.ring_mode) 202 | public void ringModeAction() { 203 | if (displayedMode == MODE_RING) return; 204 | changeMode(MODE_RING); 205 | } 206 | 207 | @OnClick(R.id.fill_mode) 208 | public void fillModeAction() { 209 | if (displayedMode == MODE_FILL) return; 210 | changeMode(MODE_FILL); 211 | } 212 | 213 | 214 | @OnClick(R.id.color_one) 215 | public void colorOneAction() { 216 | ColorPickerDialogBuilder 217 | .with(this) 218 | .setTitle("Choose first color") 219 | .initialColor(Color.WHITE) 220 | .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER) 221 | .density(6) 222 | .setPositiveButton("SET", (dialog, selectedColor, allColors) -> { 223 | colorOne = selectedColor; 224 | mColorOne.setBackgroundTintList(ColorStateList.valueOf(colorOne)); 225 | }) 226 | .setNegativeButton("DISMISS", (dialog, which) -> { 227 | }) 228 | .build() 229 | .show(); 230 | } 231 | 232 | @OnClick(R.id.color_two) 233 | public void colorTwoAction() { 234 | ColorPickerDialogBuilder 235 | .with(this) 236 | .setTitle("Choose second color") 237 | .initialColor(Color.WHITE) 238 | .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER) 239 | .density(6) 240 | .setPositiveButton("SET", (dialog, selectedColor, allColors) -> { 241 | colorTwo = selectedColor; 242 | mColorTwo.setBackgroundTintList(ColorStateList.valueOf(colorTwo)); 243 | }) 244 | .setNegativeButton("DISMISS", (dialog, which) -> { 245 | }) 246 | .build() 247 | .show(); 248 | } 249 | 250 | @OnClick(R.id.color_three) 251 | public void colorThreeAction() { 252 | ColorPickerDialogBuilder 253 | .with(this) 254 | .setTitle("Choose three color") 255 | .initialColor(Color.WHITE) 256 | .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER) 257 | .density(6) 258 | .setPositiveButton("SET", (dialog, selectedColor, allColors) -> { 259 | colorThree = selectedColor; 260 | mColorThree.setBackgroundTintList(ColorStateList.valueOf(colorThree)); 261 | }) 262 | .setNegativeButton("DISMISS", (dialog, which) -> { 263 | }) 264 | .build() 265 | .show(); 266 | } 267 | 268 | @OnClick(R.id.color_four) 269 | public void colorFourAction() { 270 | ColorPickerDialogBuilder 271 | .with(this) 272 | .setTitle("Choose four color") 273 | .initialColor(Color.WHITE) 274 | .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER) 275 | .density(6) 276 | .setPositiveButton("SET", (dialog, selectedColor, allColors) -> { 277 | colorFour = selectedColor; 278 | mColorFour.setBackgroundTintList(ColorStateList.valueOf(colorFour)); 279 | }) 280 | .setNegativeButton("DISMISS", (dialog, which) -> { 281 | }) 282 | .build() 283 | .show(); 284 | } 285 | 286 | } 287 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/activity/FillActivity.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.activity; 2 | 3 | 4 | import android.animation.TimeInterpolator; 5 | import android.graphics.Color; 6 | import android.graphics.Typeface; 7 | import android.os.Bundle; 8 | import android.transition.Explode; 9 | import android.view.animation.OvershootInterpolator; 10 | 11 | import com.ogaclejapan.smarttablayout.SmartTabLayout; 12 | import com.ogaclejapan.smarttablayout.utils.v4.Bundler; 13 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter; 14 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems; 15 | import com.ramijemli.percentagechartview.PercentageChartView; 16 | import com.ramijemli.sample.R; 17 | import com.ramijemli.sample.fragment.BackgroundSubFragment; 18 | import com.ramijemli.sample.fragment.BehaviorSubFragment; 19 | import com.ramijemli.sample.fragment.ProgressSubFragment; 20 | import com.ramijemli.sample.fragment.TextSubFragment; 21 | 22 | import java.util.Random; 23 | 24 | import androidx.appcompat.app.AppCompatActivity; 25 | import androidx.viewpager.widget.ViewPager; 26 | import butterknife.BindView; 27 | import butterknife.ButterKnife; 28 | import butterknife.OnClick; 29 | import butterknife.Unbinder; 30 | 31 | import static com.ramijemli.sample.fragment.BackgroundSubFragment.OFFSET_STATE_ARG; 32 | import static com.ramijemli.sample.fragment.BehaviorSubFragment.ORIENTATION_STATE_ARG; 33 | 34 | 35 | public class FillActivity extends AppCompatActivity implements BehaviorSubFragment.OnBehaviorChangedListener, 36 | ProgressSubFragment.OnProgressChangedListener, 37 | BackgroundSubFragment.OnBackgroundChangedListener, 38 | TextSubFragment.OnTextChangedListener { 39 | 40 | @BindView(R.id.chart) 41 | PercentageChartView mChart; 42 | @BindView(R.id.view_pager) 43 | ViewPager mViewPager; 44 | @BindView(R.id.tabs) 45 | SmartTabLayout mTAbs; 46 | 47 | private Unbinder unbinder; 48 | private int shadowColor; 49 | private float blur, distX, distY; 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | setContentView(R.layout.activity_fill); 55 | unbinder = ButterKnife.bind(this); 56 | setupLayout(); 57 | } 58 | 59 | @Override 60 | protected void onDestroy() { 61 | unbinder.unbind(); 62 | unbinder = null; 63 | super.onDestroy(); 64 | } 65 | 66 | private void setupLayout() { 67 | Explode transition = new Explode(); 68 | transition.setDuration(600); 69 | transition.setInterpolator(new OvershootInterpolator(1f)); 70 | getWindow().setEnterTransition(transition); 71 | 72 | FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter( 73 | getSupportFragmentManager(), FragmentPagerItems.with(this) 74 | .add("behavior", BehaviorSubFragment.class, new Bundler().putBoolean(ORIENTATION_STATE_ARG, false).get()) 75 | .add("progress", ProgressSubFragment.class) 76 | .add("background", BackgroundSubFragment.class, new Bundler().putBoolean(OFFSET_STATE_ARG, true).get()) 77 | .add("text", TextSubFragment.class) 78 | .create()); 79 | 80 | mViewPager.setAdapter(adapter); 81 | mViewPager.setOffscreenPageLimit(3); 82 | mTAbs.setViewPager(mViewPager); 83 | 84 | shadowColor = Color.WHITE; 85 | blur = distX = distY = 2f; 86 | } 87 | 88 | //############################################################################################## CALLBACKS 89 | @Override 90 | public void onOrientationChanged(int orientation) { 91 | mChart.setOrientation(orientation); 92 | } 93 | 94 | @Override 95 | public void onStartAngleChanged(int angle) { 96 | mChart.setStartAngle(angle); 97 | } 98 | 99 | @Override 100 | public void onAnimDurationChanged(int duration) { 101 | mChart.setAnimationDuration(duration); 102 | } 103 | 104 | @Override 105 | public void onInterpolatorChanged(TimeInterpolator interpolator) { 106 | mChart.setAnimationInterpolator(interpolator); 107 | } 108 | 109 | @Override 110 | public void onProgressChanged(float progress, boolean animate) { 111 | mChart.setProgress(progress, animate); 112 | } 113 | 114 | @Override 115 | public void onProgressColorChanged(int color) { 116 | mChart.setProgressColor(color); 117 | } 118 | 119 | @Override 120 | public void onDrawBackgroundChanged(boolean draw) { 121 | mChart.setDrawBackgroundEnabled(draw); 122 | } 123 | 124 | @Override 125 | public void onBackgroundColorChanged(int color) { 126 | mChart.setBackgroundColor(color); 127 | } 128 | 129 | @Override 130 | public void onBackgroundOffsetChanged(int offset) { 131 | mChart.setBackgroundOffset(offset); 132 | } 133 | 134 | @Override 135 | public void onTextColorChanged(int color) { 136 | mChart.setTextColor(color); 137 | } 138 | 139 | @Override 140 | public void onTextSizeChanged(int textSize) { 141 | mChart.setTextSize(textSize); 142 | } 143 | 144 | @Override 145 | public void onTextFontChanged(Typeface typeface) { 146 | mChart.setTypeface(typeface); 147 | } 148 | 149 | @Override 150 | public void onTextStyleChanged(int textStyle) { 151 | mChart.setTextStyle(textStyle); 152 | } 153 | 154 | @Override 155 | public void onDrawShadowChanged(boolean draw) { 156 | if (draw && shadowColor != -1) { 157 | mChart.setTextShadow(shadowColor, blur, distX, distY); 158 | } else { 159 | mChart.setTextShadow(0, 0, 0, 0); 160 | } 161 | } 162 | 163 | @Override 164 | public void onShadowChanged(int color, float blur, float distX, float distY) { 165 | this.shadowColor = color; 166 | this.blur = blur; 167 | this.distX = distX; 168 | this.distY = distY; 169 | mChart.setTextShadow(color, blur, distX, distY); 170 | } 171 | 172 | //############################################################################################## ACTIONS 173 | @OnClick(R.id.chart) 174 | public void chartClickAction() { 175 | mChart.setProgress(new Random().nextInt(100), true); 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/activity/HomeActivity.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.activity; 2 | 3 | 4 | import android.app.ActivityOptions; 5 | import android.content.Intent; 6 | import android.graphics.Color; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.os.Handler; 10 | import android.view.animation.AnticipateOvershootInterpolator; 11 | 12 | import com.ramijemli.percentagechartview.PercentageChartView; 13 | import com.ramijemli.percentagechartview.callback.AdaptiveColorProvider; 14 | import com.ramijemli.sample.R; 15 | import com.ramijemli.sample.adapter.HomeAdapter; 16 | 17 | import java.util.Random; 18 | 19 | import androidx.appcompat.app.AppCompatActivity; 20 | import androidx.core.graphics.ColorUtils; 21 | import androidx.recyclerview.widget.LinearLayoutManager; 22 | import androidx.recyclerview.widget.RecyclerView; 23 | import butterknife.BindView; 24 | import butterknife.ButterKnife; 25 | import butterknife.Unbinder; 26 | 27 | 28 | public class HomeActivity extends AppCompatActivity { 29 | 30 | @BindView(R.id.recycler_view) 31 | RecyclerView mHomeRv; 32 | @BindView(R.id.chart) 33 | PercentageChartView mChart; 34 | 35 | private Unbinder unbinder; 36 | private HomeAdapter adapter; 37 | private Handler handler; 38 | private Runnable runnable; 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.activity_home); 44 | unbinder = ButterKnife.bind(this); 45 | setupLayout(); 46 | setupAnimation(); 47 | } 48 | 49 | @Override 50 | protected void onResume() { 51 | super.onResume(); 52 | animate(); 53 | } 54 | 55 | @Override 56 | protected void onStop() { 57 | handler.removeCallbacks(runnable); 58 | super.onStop(); 59 | } 60 | 61 | @Override 62 | protected void onDestroy() { 63 | handler.removeCallbacks(runnable); 64 | handler = null; 65 | runnable = null; 66 | adapter.setOnHomeClickedListener(null); 67 | adapter = null; 68 | unbinder.unbind(); 69 | unbinder = null; 70 | super.onDestroy(); 71 | } 72 | 73 | private void setupLayout() { 74 | final LinearLayoutManager llm = new LinearLayoutManager(this, RecyclerView.VERTICAL, false); 75 | llm.setItemPrefetchEnabled(true); 76 | mHomeRv.setLayoutManager(llm); 77 | mHomeRv.setHasFixedSize(true); 78 | adapter = new HomeAdapter(this); 79 | mHomeRv.setAdapter(adapter); 80 | 81 | adapter.setOnHomeClickedListener(position -> { 82 | Bundle transitionbundle = ActivityOptions.makeSceneTransitionAnimation(this).toBundle(); 83 | switch (position) { 84 | default: 85 | case 0: 86 | startActivity(new Intent(this, PieActivity.class), transitionbundle); 87 | break; 88 | 89 | case 1: 90 | startActivity(new Intent(this, RingActivity.class), transitionbundle); 91 | break; 92 | 93 | case 2: 94 | startActivity(new Intent(this, FillActivity.class), transitionbundle); 95 | break; 96 | 97 | case 3: 98 | startActivity(new Intent(this, AdaptiveColorsActivity.class), transitionbundle); 99 | break; 100 | 101 | case 4: 102 | startActivity(new Intent(this, GradientColorsActivity.class), transitionbundle); 103 | break; 104 | 105 | case 5: 106 | startActivity(new Intent(this, TextFormatterActivity.class), transitionbundle); 107 | break; 108 | 109 | case 6: 110 | Uri url = Uri.parse(getString(R.string.github_url)); 111 | Intent intent = new Intent(Intent.ACTION_VIEW, url); 112 | if (intent.resolveActivity(getPackageManager()) != null) { 113 | startActivity(intent); 114 | } 115 | break; 116 | } 117 | }); 118 | } 119 | 120 | private void setupAnimation() { 121 | mChart.setAnimationInterpolator(new AnticipateOvershootInterpolator()); 122 | mChart.setAnimationDuration(600); 123 | mChart.setAdaptiveColorProvider(new AdaptiveColorProvider() { 124 | @Override 125 | public int provideProgressColor(float progress) { 126 | String color; 127 | 128 | if (progress <= 25) 129 | color = "#F44336"; 130 | else if (progress <= 50) 131 | color = "#9C27B0"; 132 | else if (progress <= 75) 133 | color = "#03A9F4"; 134 | else color = "#FFC107"; 135 | 136 | return Color.parseColor(color); 137 | } 138 | 139 | @Override 140 | public int provideTextColor(float progress) { 141 | return ColorUtils.blendARGB(provideProgressColor(progress), Color.WHITE, .6f); 142 | } 143 | }); 144 | 145 | handler = new Handler(); 146 | runnable = () -> { 147 | mChart.setProgress(new Random().nextInt(100), true); 148 | animate(); 149 | }; 150 | animate(); 151 | } 152 | 153 | private void animate() { 154 | handler.postDelayed(runnable, 1500); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/activity/PieActivity.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.activity; 2 | 3 | 4 | import android.animation.TimeInterpolator; 5 | import android.graphics.Color; 6 | import android.graphics.Typeface; 7 | import android.os.Bundle; 8 | import android.transition.Explode; 9 | import android.transition.Slide; 10 | import android.view.Gravity; 11 | import android.view.animation.OvershootInterpolator; 12 | 13 | import com.ogaclejapan.smarttablayout.SmartTabLayout; 14 | import com.ogaclejapan.smarttablayout.utils.v4.Bundler; 15 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter; 16 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems; 17 | import com.ramijemli.percentagechartview.PercentageChartView; 18 | import com.ramijemli.sample.R; 19 | import com.ramijemli.sample.fragment.BackgroundSubFragment; 20 | import com.ramijemli.sample.fragment.BehaviorSubFragment; 21 | import com.ramijemli.sample.fragment.ProgressSubFragment; 22 | import com.ramijemli.sample.fragment.TextSubFragment; 23 | 24 | import java.util.Random; 25 | 26 | import androidx.appcompat.app.AppCompatActivity; 27 | import androidx.viewpager.widget.ViewPager; 28 | import butterknife.BindView; 29 | import butterknife.ButterKnife; 30 | import butterknife.OnClick; 31 | import butterknife.Unbinder; 32 | 33 | import static com.ramijemli.sample.fragment.BackgroundSubFragment.OFFSET_STATE_ARG; 34 | import static com.ramijemli.sample.fragment.BehaviorSubFragment.ORIENTATION_STATE_ARG; 35 | 36 | 37 | public class PieActivity extends AppCompatActivity implements BehaviorSubFragment.OnBehaviorChangedListener, 38 | ProgressSubFragment.OnProgressChangedListener, 39 | BackgroundSubFragment.OnBackgroundChangedListener, 40 | TextSubFragment.OnTextChangedListener { 41 | 42 | @BindView(R.id.chart) 43 | PercentageChartView mChart; 44 | @BindView(R.id.view_pager) 45 | ViewPager mViewPager; 46 | @BindView(R.id.tabs) 47 | SmartTabLayout mTAbs; 48 | 49 | private Unbinder unbinder; 50 | private int shadowColor; 51 | private float blur, distX, distY; 52 | 53 | @Override 54 | protected void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | setContentView(R.layout.activity_pie); 57 | unbinder = ButterKnife.bind(this); 58 | setupLayout(); 59 | } 60 | 61 | @Override 62 | protected void onDestroy() { 63 | unbinder.unbind(); 64 | unbinder = null; 65 | super.onDestroy(); 66 | } 67 | 68 | private void setupLayout() { 69 | Explode transition = new Explode(); 70 | transition.setDuration(600); 71 | transition.setInterpolator(new OvershootInterpolator(1f)); 72 | getWindow().setEnterTransition(transition); 73 | 74 | FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter( 75 | getSupportFragmentManager(), FragmentPagerItems.with(this) 76 | .add("behavior", BehaviorSubFragment.class, new Bundler().putBoolean(ORIENTATION_STATE_ARG, true).get()) 77 | .add("progress", ProgressSubFragment.class) 78 | .add("background", BackgroundSubFragment.class, new Bundler().putBoolean(OFFSET_STATE_ARG, true).get()) 79 | .add("text", TextSubFragment.class) 80 | .create()); 81 | 82 | mViewPager.setAdapter(adapter); 83 | mViewPager.setOffscreenPageLimit(3); 84 | mTAbs.setViewPager(mViewPager); 85 | 86 | shadowColor = Color.WHITE; 87 | blur = distX = distY = 2f; 88 | } 89 | 90 | //############################################################################################## CALLBACKS 91 | @Override 92 | public void onOrientationChanged(int orientation) { 93 | mChart.setOrientation(orientation); 94 | } 95 | 96 | @Override 97 | public void onStartAngleChanged(int angle) { 98 | mChart.setStartAngle(angle); 99 | } 100 | 101 | @Override 102 | public void onAnimDurationChanged(int duration) { 103 | mChart.setAnimationDuration(duration); 104 | } 105 | 106 | @Override 107 | public void onInterpolatorChanged(TimeInterpolator interpolator) { 108 | mChart.setAnimationInterpolator(interpolator); 109 | } 110 | 111 | @Override 112 | public void onProgressChanged(float progress, boolean animate) { 113 | mChart.setProgress(progress, animate); 114 | } 115 | 116 | @Override 117 | public void onProgressColorChanged(int color) { 118 | mChart.setProgressColor(color); 119 | } 120 | 121 | @Override 122 | public void onDrawBackgroundChanged(boolean draw) { 123 | mChart.setDrawBackgroundEnabled(draw); 124 | } 125 | 126 | @Override 127 | public void onBackgroundColorChanged(int color) { 128 | mChart.setBackgroundColor(color); 129 | } 130 | 131 | @Override 132 | public void onBackgroundOffsetChanged(int offset) { 133 | mChart.setBackgroundOffset(offset); 134 | } 135 | 136 | @Override 137 | public void onTextColorChanged(int color) { 138 | mChart.setTextColor(color); 139 | } 140 | 141 | @Override 142 | public void onTextSizeChanged(int textSize) { 143 | mChart.setTextSize(textSize); 144 | } 145 | 146 | @Override 147 | public void onTextFontChanged(Typeface typeface) { 148 | mChart.setTypeface(typeface); 149 | } 150 | 151 | @Override 152 | public void onTextStyleChanged(int textStyle) { 153 | mChart.setTextStyle(textStyle); 154 | } 155 | 156 | @Override 157 | public void onDrawShadowChanged(boolean draw) { 158 | if (draw && shadowColor != -1) { 159 | mChart.setTextShadow(shadowColor, blur, distX, distY); 160 | } else { 161 | mChart.setTextShadow(0, 0, 0, 0); 162 | } 163 | } 164 | 165 | @Override 166 | public void onShadowChanged(int color, float blur, float distX, float distY) { 167 | this.shadowColor = color; 168 | this.blur = blur; 169 | this.distX = distX; 170 | this.distY = distY; 171 | mChart.setTextShadow(color, blur, distX, distY); 172 | } 173 | 174 | //############################################################################################## ACTIONS 175 | @OnClick(R.id.chart) 176 | public void chartClickAction() { 177 | mChart.setProgress(new Random().nextInt(100), true); 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/activity/RingActivity.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.activity; 2 | 3 | 4 | import android.animation.TimeInterpolator; 5 | import android.graphics.Color; 6 | import android.graphics.Typeface; 7 | import android.os.Bundle; 8 | import android.transition.Explode; 9 | import android.view.animation.OvershootInterpolator; 10 | 11 | import com.ogaclejapan.smarttablayout.SmartTabLayout; 12 | import com.ogaclejapan.smarttablayout.utils.v4.Bundler; 13 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter; 14 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems; 15 | import com.ramijemli.percentagechartview.PercentageChartView; 16 | import com.ramijemli.sample.R; 17 | import com.ramijemli.sample.fragment.BackgroundBarSubFragment; 18 | import com.ramijemli.sample.fragment.BackgroundSubFragment; 19 | import com.ramijemli.sample.fragment.BehaviorSubFragment; 20 | import com.ramijemli.sample.fragment.ProgressSubFragment; 21 | import com.ramijemli.sample.fragment.TextSubFragment; 22 | 23 | import java.util.Random; 24 | 25 | import androidx.appcompat.app.AppCompatActivity; 26 | import androidx.viewpager.widget.ViewPager; 27 | import butterknife.BindView; 28 | import butterknife.ButterKnife; 29 | import butterknife.OnClick; 30 | import butterknife.Unbinder; 31 | 32 | import static com.ramijemli.sample.fragment.BehaviorSubFragment.ORIENTATION_STATE_ARG; 33 | import static com.ramijemli.sample.fragment.ProgressSubFragment.BAR_STATE_ARG; 34 | 35 | 36 | public class RingActivity extends AppCompatActivity implements BehaviorSubFragment.OnBehaviorChangedListener, 37 | ProgressSubFragment.OnProgressChangedListener, 38 | BackgroundSubFragment.OnBackgroundChangedListener, 39 | BackgroundBarSubFragment.OnBackgroundBarChangedListener, 40 | TextSubFragment.OnTextChangedListener { 41 | 42 | @BindView(R.id.chart) 43 | PercentageChartView mChart; 44 | @BindView(R.id.view_pager) 45 | ViewPager mViewPager; 46 | @BindView(R.id.tabs) 47 | SmartTabLayout mTAbs; 48 | 49 | private Unbinder unbinder; 50 | private int shadowColor; 51 | private float blur, distX, distY; 52 | 53 | @Override 54 | protected void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | setContentView(R.layout.activity_ring); 57 | unbinder = ButterKnife.bind(this); 58 | setupLayout(); 59 | } 60 | 61 | @Override 62 | protected void onDestroy() { 63 | unbinder.unbind(); 64 | unbinder = null; 65 | super.onDestroy(); 66 | } 67 | 68 | private void setupLayout() { 69 | Explode transition = new Explode(); 70 | transition.setDuration(600); 71 | transition.setInterpolator(new OvershootInterpolator(1f)); 72 | getWindow().setEnterTransition(transition); 73 | 74 | FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter( 75 | getSupportFragmentManager(), FragmentPagerItems.with(this) 76 | .add("behavior", BehaviorSubFragment.class, new Bundler().putBoolean(ORIENTATION_STATE_ARG, true).get()) 77 | .add("progress bar", ProgressSubFragment.class, new Bundler().putBoolean(BAR_STATE_ARG, true).get()) 78 | .add("background bar", BackgroundBarSubFragment.class) 79 | .add("background", BackgroundSubFragment.class, new Bundler().putBoolean(ORIENTATION_STATE_ARG, false).get()) 80 | .add("text", TextSubFragment.class) 81 | .create()); 82 | 83 | mViewPager.setAdapter(adapter); 84 | mViewPager.setOffscreenPageLimit(4); 85 | mTAbs.setViewPager(mViewPager); 86 | 87 | shadowColor = Color.WHITE; 88 | blur = distX = distY = 2f; 89 | } 90 | 91 | //############################################################################################## CALLBACKS 92 | @Override 93 | public void onOrientationChanged(int orientation) { 94 | mChart.setOrientation(orientation); 95 | } 96 | 97 | @Override 98 | public void onStartAngleChanged(int angle) { 99 | mChart.setStartAngle(angle); 100 | } 101 | 102 | @Override 103 | public void onAnimDurationChanged(int duration) { 104 | mChart.setAnimationDuration(duration); 105 | } 106 | 107 | @Override 108 | public void onInterpolatorChanged(TimeInterpolator interpolator) { 109 | mChart.setAnimationInterpolator(interpolator); 110 | } 111 | 112 | @Override 113 | public void onProgressChanged(float progress, boolean animate) { 114 | mChart.setProgress(progress, animate); 115 | } 116 | 117 | @Override 118 | public void onProgressColorChanged(int color) { 119 | mChart.setProgressColor(color); 120 | } 121 | 122 | @Override 123 | public void onDrawBackgroundChanged(boolean draw) { 124 | mChart.setDrawBackgroundEnabled(draw); 125 | } 126 | 127 | @Override 128 | public void onBackgroundColorChanged(int color) { 129 | mChart.setBackgroundColor(color); 130 | } 131 | 132 | @Override 133 | public void onBackgroundOffsetChanged(int offset) { 134 | mChart.setBackgroundOffset(offset); 135 | } 136 | 137 | @Override 138 | public void onTextColorChanged(int color) { 139 | mChart.setTextColor(color); 140 | } 141 | 142 | @Override 143 | public void onTextSizeChanged(int textSize) { 144 | mChart.setTextSize(textSize); 145 | } 146 | 147 | @Override 148 | public void onTextFontChanged(Typeface typeface) { 149 | mChart.setTypeface(typeface); 150 | } 151 | 152 | @Override 153 | public void onTextStyleChanged(int textStyle) { 154 | mChart.setTextStyle(textStyle); 155 | } 156 | 157 | @Override 158 | public void onDrawShadowChanged(boolean draw) { 159 | if (draw && shadowColor != -1) { 160 | mChart.setTextShadow(shadowColor, blur, distX, distY); 161 | } else { 162 | mChart.setTextShadow(0, 0, 0, 0); 163 | } 164 | } 165 | 166 | @Override 167 | public void onShadowChanged(int color, float blur, float distX, float distY) { 168 | this.shadowColor = color; 169 | this.blur = blur; 170 | this.distX = distX; 171 | this.distY = distY; 172 | mChart.setTextShadow(color, blur, distX, distY); 173 | } 174 | @Override 175 | public void onProgBarThicknessChanged(int thickness) { 176 | mChart.setProgressBarThickness(thickness); 177 | } 178 | 179 | @Override 180 | public void onProgBarStyleChanged(int progressStyle) { 181 | mChart.setProgressBarStyle(progressStyle); 182 | } 183 | 184 | 185 | @Override 186 | public void onDrawBgBarChanged(boolean draw) { 187 | mChart.setDrawBackgroundBarEnabled(draw); 188 | } 189 | 190 | @Override 191 | public void onBgBarColorChanged(int color) { 192 | mChart.setBackgroundBarColor(color); 193 | } 194 | 195 | @Override 196 | public void onBgBarThicknessChanged(int thickness) { 197 | mChart.setBackgroundBarThickness(thickness); 198 | } 199 | 200 | //############################################################################################## ACTIONS 201 | @OnClick(R.id.chart) 202 | public void chartClickAction() { 203 | mChart.setProgress(new Random().nextInt(100), true); 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/activity/TextFormatterActivity.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.activity; 2 | 3 | 4 | import android.graphics.Color; 5 | import android.os.Bundle; 6 | import android.text.Spannable; 7 | import android.text.SpannableString; 8 | import android.text.SpannableStringBuilder; 9 | import android.text.style.BulletSpan; 10 | import android.text.style.ForegroundColorSpan; 11 | import android.text.style.RelativeSizeSpan; 12 | import android.transition.Explode; 13 | import android.view.animation.OvershootInterpolator; 14 | import android.widget.TextView; 15 | 16 | import com.ramijemli.percentagechartview.PercentageChartView; 17 | import com.ramijemli.percentagechartview.callback.AdaptiveColorProvider; 18 | import com.ramijemli.sample.R; 19 | 20 | import java.util.Random; 21 | 22 | import androidx.appcompat.app.AppCompatActivity; 23 | import androidx.core.graphics.ColorUtils; 24 | 25 | import org.w3c.dom.Text; 26 | 27 | import butterknife.BindView; 28 | import butterknife.ButterKnife; 29 | import butterknife.OnClick; 30 | import butterknife.Unbinder; 31 | 32 | 33 | public class TextFormatterActivity extends AppCompatActivity { 34 | 35 | @BindView(R.id.pie_chart) 36 | PercentageChartView mPieChart; 37 | @BindView(R.id.ring_chart) 38 | PercentageChartView mRingChart; 39 | @BindView(R.id.fill_chart) 40 | PercentageChartView mFillChart; 41 | @BindView(R.id.text) 42 | TextView mText; 43 | 44 | private Unbinder unbinder; 45 | private AdaptiveColorProvider colorProvider; 46 | private int maxVoters = 10000; 47 | private int maxCalories = 3500; 48 | private int maxDays = 30; 49 | 50 | @Override 51 | protected void onCreate(Bundle savedInstanceState) { 52 | super.onCreate(savedInstanceState); 53 | setContentView(R.layout.activity_text_formatter); 54 | unbinder = ButterKnife.bind(this); 55 | setupLayout(); 56 | setupColorProvider(); 57 | } 58 | 59 | @Override 60 | protected void onDestroy() { 61 | colorProvider = null; 62 | unbinder.unbind(); 63 | unbinder = null; 64 | super.onDestroy(); 65 | } 66 | 67 | private void setupLayout() { 68 | Explode transition = new Explode(); 69 | transition.setDuration(600); 70 | transition.setInterpolator(new OvershootInterpolator(1f)); 71 | getWindow().setEnterTransition(transition); 72 | 73 | mPieChart.setTextFormatter(progress -> { 74 | int voters = (int) (progress * maxVoters / 100); 75 | if(voters > 1000) { 76 | voters /= 1000; 77 | return voters + " k voters"; 78 | } 79 | return voters + " Voters"; 80 | }); 81 | 82 | mFillChart.setTextFormatter(progress -> { 83 | int cals = (int) (progress * maxCalories / 100); 84 | return cals + " cal"; 85 | }); 86 | 87 | SpannableStringBuilder b = new SpannableStringBuilder(); 88 | mRingChart.setTextFormatter(progress -> { 89 | int days = (int) (progress * maxDays / 100); 90 | // b.clear(); 91 | // String text =; 92 | // b.append(text); 93 | // int start = String.valueOf(days).length(); 94 | // int end = b.length(); 95 | // b.setSpan(new RelativeSizeSpan(0.4f), 0, start, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 96 | // mText.setText(b); 97 | return days + " days"; 98 | }); 99 | } 100 | 101 | private void setupColorProvider() { 102 | //COLOR PROVIDER 103 | colorProvider = new AdaptiveColorProvider() { 104 | @Override 105 | public int provideProgressColor(float progress) { 106 | if (progress <= 25) 107 | return Color.parseColor("#F44336"); 108 | else if (progress <= 50) 109 | return Color.parseColor("#FFEA00"); 110 | else if (progress <= 75) 111 | return Color.parseColor("#03A9F4"); 112 | else 113 | return Color.parseColor("#00E676"); 114 | } 115 | 116 | @Override 117 | public int provideBackgroundColor(float progress) { 118 | return ColorUtils.blendARGB(provideProgressColor(progress), 119 | Color.BLACK, 120 | .8f); 121 | } 122 | 123 | @Override 124 | public int provideTextColor(float progress) { 125 | return ColorUtils.blendARGB(provideProgressColor(progress), 126 | Color.WHITE, 127 | .8f); 128 | } 129 | 130 | @Override 131 | public int provideBackgroundBarColor(float progress) { 132 | return ColorUtils.blendARGB(provideProgressColor(progress), 133 | Color.BLACK, 134 | .5f); 135 | } 136 | }; 137 | 138 | mPieChart.setAdaptiveColorProvider(colorProvider); 139 | mRingChart.setAdaptiveColorProvider(colorProvider); 140 | mFillChart.setAdaptiveColorProvider(colorProvider); 141 | } 142 | 143 | //############################################################################################## ACTIONS 144 | @OnClick(R.id.root_layout) 145 | public void pieChartClickAction() { 146 | int rand = new Random().nextInt(100); 147 | mPieChart.setProgress(rand, true); 148 | mRingChart.setProgress(rand, true); 149 | mFillChart.setProgress(rand, true); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/adapter/HomeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.res.ColorStateList; 5 | import android.graphics.Color; 6 | import android.util.SparseArray; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import com.ramijemli.sample.R; 13 | import com.ramijemli.sample.viewmodel.Showcase; 14 | 15 | import androidx.annotation.NonNull; 16 | import androidx.recyclerview.widget.RecyclerView; 17 | 18 | public class HomeAdapter extends RecyclerView.Adapter { 19 | 20 | private final int VIEW_TYPE_SHOWCASE = R.layout.item_home; 21 | private final int VIEW_TYPE_DEV = R.layout.item_dev; 22 | 23 | private Context mContext; 24 | private SparseArray mData; 25 | private OnHomeClickedListener mListener; 26 | 27 | public HomeAdapter(Context context) { 28 | this.mContext = context; 29 | mData = new SparseArray<>(); 30 | int[] colors = mContext.getResources().getIntArray(R.array.home_colors); 31 | String[] titles = mContext.getResources().getStringArray(R.array.home_titles); 32 | String[] subtitles = mContext.getResources().getStringArray(R.array.home_subtitles); 33 | 34 | for (int i = 0; i < colors.length; i++) { 35 | mData.append(i, new Showcase(colors[i], titles[i], subtitles[i])); 36 | } 37 | 38 | mData.append(colors.length, new Showcase(Color.parseColor("#37474F"), "", "")); 39 | } 40 | 41 | @Override 42 | public HomeAdapter.BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 43 | View view = LayoutInflater.from(mContext).inflate(viewType, parent, false); 44 | 45 | switch (viewType) { 46 | default: 47 | case VIEW_TYPE_SHOWCASE: 48 | return new ShowcaseViewHolder(view); 49 | case VIEW_TYPE_DEV: 50 | return new DevViewHolder(view); 51 | } 52 | } 53 | 54 | @Override 55 | public void onBindViewHolder(@NonNull HomeAdapter.BaseViewHolder viewHolder, int position) { 56 | Showcase data = mData.get(position); 57 | viewHolder.bind(data); 58 | } 59 | 60 | @Override 61 | public int getItemCount() { 62 | return mData == null ? 0 : mData.size(); 63 | } 64 | 65 | @Override 66 | public int getItemViewType(int position) { 67 | if (position == mData.size() - 1) return VIEW_TYPE_DEV; 68 | return VIEW_TYPE_SHOWCASE; 69 | } 70 | 71 | //############################################################################################## VIEW HOLDERS 72 | class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 73 | View card; 74 | 75 | BaseViewHolder(View itemView) { 76 | super(itemView); 77 | card = itemView.findViewById(R.id.card_bg); 78 | } 79 | 80 | void bind(Showcase data){ 81 | card.setBackgroundTintList(ColorStateList.valueOf(data.getColor())); 82 | card.setOnClickListener(this); 83 | } 84 | 85 | @Override 86 | public void onClick(View v) { 87 | if (mListener != null) { 88 | mListener.onItemClicked(getAdapterPosition()); 89 | } 90 | } 91 | } 92 | 93 | class ShowcaseViewHolder extends BaseViewHolder { 94 | TextView title; 95 | TextView subtitle; 96 | 97 | ShowcaseViewHolder(View itemView) { 98 | super(itemView); 99 | title = itemView.findViewById(R.id.title); 100 | subtitle = itemView.findViewById(R.id.subtitle); 101 | } 102 | 103 | void bind(Showcase data) { 104 | super.bind(data); 105 | title.setText(data.getTitle()); 106 | subtitle.setText(data.getSubtitle()); 107 | } 108 | } 109 | 110 | class DevViewHolder extends BaseViewHolder { 111 | 112 | DevViewHolder(View itemView) { 113 | super(itemView); 114 | } 115 | 116 | void bind(Showcase data) { 117 | super.bind(data); 118 | } 119 | } 120 | 121 | //############################################################################################## CLICK LISTENER 122 | public void setOnHomeClickedListener(OnHomeClickedListener mListener) { 123 | this.mListener = mListener; 124 | } 125 | 126 | public interface OnHomeClickedListener { 127 | void onItemClicked(int position); 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/fragment/BackgroundBarSubFragment.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.fragment; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.res.ColorStateList; 6 | import android.graphics.Color; 7 | import android.os.Bundle; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.Button; 12 | import android.widget.CheckBox; 13 | import android.widget.TextView; 14 | 15 | import com.flask.colorpicker.ColorPickerView; 16 | import com.flask.colorpicker.builder.ColorPickerDialogBuilder; 17 | import com.ramijemli.sample.R; 18 | import com.ramijemli.sample.util.ScreenUtil; 19 | 20 | import androidx.annotation.NonNull; 21 | import androidx.annotation.Nullable; 22 | import androidx.fragment.app.Fragment; 23 | import butterknife.BindView; 24 | import butterknife.ButterKnife; 25 | import butterknife.OnClick; 26 | import butterknife.Unbinder; 27 | 28 | 29 | public class BackgroundBarSubFragment extends Fragment { 30 | 31 | 32 | //DRAW BACKGROUND BAR STATE 33 | @BindView(R.id.draw_bg_bar) 34 | CheckBox mDrawBgBar; 35 | 36 | //BACKGROUND BAR COLOR 37 | @BindView(R.id.bg_bar_color) 38 | Button mBgBarColor; 39 | 40 | //BACKGROUND BAR THICKNESS 41 | @BindView(R.id.bg_bar_thickness_value) 42 | TextView mBgBarThicknessValue; 43 | 44 | 45 | private OnBackgroundBarChangedListener mListener; 46 | private Unbinder unbinder; 47 | 48 | public BackgroundBarSubFragment() { 49 | } 50 | 51 | public static BackgroundBarSubFragment newInstance() { 52 | return new BackgroundBarSubFragment(); 53 | } 54 | 55 | @Override 56 | public void onAttach(Context context) { 57 | super.onAttach(context); 58 | if (context instanceof OnBackgroundBarChangedListener) { 59 | mListener = (OnBackgroundBarChangedListener) context; 60 | } else { 61 | throw new RuntimeException(context.toString() 62 | + " must implement OnBackgroundBarChangedListener"); 63 | } 64 | } 65 | 66 | @Override 67 | public void onDetach() { 68 | super.onDetach(); 69 | mListener = null; 70 | } 71 | 72 | @Override 73 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 74 | View view = inflater.inflate(R.layout.sub_fragment_background_bar, container, false); 75 | unbinder = ButterKnife.bind(this, view); 76 | return view; 77 | } 78 | 79 | @Override 80 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 81 | super.onViewCreated(view, savedInstanceState); 82 | setupDrawBgBarState(); 83 | } 84 | 85 | @Override 86 | public void onDestroyView() { 87 | mDrawBgBar.setOnCheckedChangeListener(null); 88 | unbinder.unbind(); 89 | unbinder = null; 90 | super.onDestroyView(); 91 | } 92 | 93 | //############################################################################################## BEHAVIOR 94 | private void setupDrawBgBarState() { 95 | mDrawBgBar.setOnCheckedChangeListener((buttonView, isChecked) -> { 96 | String text = String.valueOf(isChecked); 97 | mDrawBgBar.setText(Character.toUpperCase(text.charAt(0)) + text.substring(1).toLowerCase()); 98 | if (mListener != null) { 99 | mListener.onDrawBgBarChanged(isChecked); 100 | } 101 | }); 102 | } 103 | 104 | private void tweakBgThickness(int amount) { 105 | int value = getTextViewValue(mBgBarThicknessValue); 106 | 107 | int maxValue = (int) ScreenUtil.convertPixelsToDIP(getActivity(), getView().getMeasuredWidth() / 2); 108 | if (value + amount > maxValue) { 109 | value = maxValue; 110 | } else if (value + amount <= 0) { 111 | value = 0; 112 | } else { 113 | value += amount; 114 | } 115 | 116 | mBgBarThicknessValue.setText(value + " dp"); 117 | if (mListener != null) { 118 | mListener.onBgBarThicknessChanged(ScreenUtil.convertDIPToPixels(getActivity(), value)); 119 | } 120 | } 121 | 122 | private int getTextViewValue(TextView view) { 123 | String value = view.getText().toString(); 124 | return Integer.parseInt(value.substring(0, value.length() - 3)); 125 | } 126 | 127 | //############################################################################################## ACTIONS 128 | @OnClick(R.id.bg_bar_color) 129 | void bgBarColorAction() { 130 | ColorPickerDialogBuilder 131 | .with(getActivity()) 132 | .setTitle("Choose Background bar color") 133 | .initialColor(Color.WHITE) 134 | .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER) 135 | .density(6) 136 | .setPositiveButton("SET", (dialog, selectedColor, allColors) -> { 137 | mBgBarColor.setBackgroundTintList(ColorStateList.valueOf(selectedColor)); 138 | if (mListener != null) { 139 | mListener.onBgBarColorChanged(selectedColor); 140 | } 141 | }) 142 | .setNegativeButton("DISMISS", (dialog, which) -> { 143 | }) 144 | .build() 145 | .show(); 146 | } 147 | 148 | @OnClick(R.id.increment_bg_bar_thickness) 149 | void incrementBgThicknessAction() { 150 | tweakBgThickness(1); 151 | } 152 | 153 | @OnClick(R.id.decrement_bg_bar_thickness) 154 | void decrementBgThicknessAction() { 155 | tweakBgThickness(-1); 156 | } 157 | 158 | //############################################################################################## LISTENER 159 | public interface OnBackgroundBarChangedListener { 160 | void onDrawBgBarChanged(boolean draw); 161 | 162 | void onBgBarColorChanged(int color); 163 | 164 | void onBgBarThicknessChanged(int thickness); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/fragment/BackgroundSubFragment.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.fragment; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.res.ColorStateList; 6 | import android.graphics.Color; 7 | import android.os.Bundle; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.Button; 12 | import android.widget.CheckBox; 13 | import android.widget.TextView; 14 | 15 | import com.flask.colorpicker.ColorPickerView; 16 | import com.flask.colorpicker.builder.ColorPickerDialogBuilder; 17 | import com.ramijemli.sample.R; 18 | import com.ramijemli.sample.util.ScreenUtil; 19 | 20 | import androidx.annotation.NonNull; 21 | import androidx.annotation.Nullable; 22 | import androidx.constraintlayout.widget.Group; 23 | import androidx.fragment.app.Fragment; 24 | import butterknife.BindView; 25 | import butterknife.ButterKnife; 26 | import butterknife.OnClick; 27 | import butterknife.Unbinder; 28 | 29 | 30 | public class BackgroundSubFragment extends Fragment { 31 | 32 | public static final String OFFSET_STATE_ARG = "BackgroundSubFragment.OFFSET_STATE_ARG"; 33 | 34 | //DRAW BACKGROUND STATE 35 | @BindView(R.id.draw_background) 36 | CheckBox mDrawBackground; 37 | 38 | //BACKGROUND COLOR 39 | @BindView(R.id.background_color) 40 | Button mBackgroundColor; 41 | 42 | //BACKGROUND OFFSET 43 | @BindView(R.id.offset_value) 44 | TextView mOffsetValue; 45 | @BindView(R.id.offset_visibility) 46 | Group mOffsetGroup; 47 | 48 | private OnBackgroundChangedListener mListener; 49 | private Unbinder unbinder; 50 | private boolean enableOffset; 51 | 52 | public BackgroundSubFragment() { 53 | } 54 | 55 | public static BackgroundSubFragment newInstance() { 56 | return new BackgroundSubFragment(); 57 | } 58 | 59 | @Override 60 | public void onAttach(Context context) { 61 | super.onAttach(context); 62 | if (context instanceof OnBackgroundChangedListener) { 63 | mListener = (OnBackgroundChangedListener) context; 64 | } else { 65 | throw new RuntimeException(context.toString() 66 | + " must implement OnBackgroundChangedListener"); 67 | } 68 | } 69 | 70 | @Override 71 | public void onDetach() { 72 | super.onDetach(); 73 | mListener = null; 74 | } 75 | 76 | @Override 77 | public void onCreate(Bundle savedInstanceState) { 78 | super.onCreate(savedInstanceState); 79 | if (getArguments() != null) { 80 | enableOffset = getArguments().getBoolean(OFFSET_STATE_ARG, false); 81 | } 82 | } 83 | 84 | @Override 85 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 86 | View view = inflater.inflate(R.layout.sub_fragment_background, container, false); 87 | unbinder = ButterKnife.bind(this, view); 88 | return view; 89 | } 90 | 91 | @Override 92 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 93 | super.onViewCreated(view, savedInstanceState); 94 | setupDrawBackgroundState(); 95 | if(enableOffset){ 96 | mOffsetGroup.setVisibility(View.VISIBLE); 97 | } else { 98 | mOffsetGroup.setVisibility(View.GONE); 99 | } 100 | } 101 | 102 | @Override 103 | public void onDestroyView() { 104 | mDrawBackground.setOnCheckedChangeListener(null); 105 | unbinder.unbind(); 106 | unbinder = null; 107 | super.onDestroyView(); 108 | } 109 | 110 | //############################################################################################## BEHAVIOR 111 | private void setupDrawBackgroundState() { 112 | mDrawBackground.setOnCheckedChangeListener((buttonView, isChecked) -> { 113 | String text = String.valueOf(isChecked); 114 | mDrawBackground.setText(Character.toUpperCase(text.charAt(0)) + text.substring(1).toLowerCase()); 115 | if (mListener != null) { 116 | mListener.onDrawBackgroundChanged(isChecked); 117 | } 118 | }); 119 | } 120 | 121 | private void tweakOffset(int amount) { 122 | int value = getTextViewValue(mOffsetValue); 123 | 124 | int maxValue = (int) ScreenUtil.convertPixelsToDIP(getActivity(), getView().getMeasuredWidth() / 2); 125 | if (value + amount > maxValue) { 126 | value = maxValue; 127 | } else if (value + amount <= 0) { 128 | value = 0; 129 | } else { 130 | value += amount; 131 | } 132 | 133 | mOffsetValue.setText(value + " dp"); 134 | 135 | if (mListener != null) { 136 | mListener.onBackgroundOffsetChanged(ScreenUtil.convertDIPToPixels(getActivity(), value)); 137 | } 138 | } 139 | 140 | private int getTextViewValue(TextView view) { 141 | String value = view.getText().toString(); 142 | return Integer.parseInt(value.substring(0, value.length() - 3)); 143 | } 144 | 145 | //############################################################################################## ACTIONS 146 | @OnClick(R.id.background_color) 147 | void backgroundAction() { 148 | ColorPickerDialogBuilder 149 | .with(getActivity()) 150 | .setTitle("Choose background color") 151 | .initialColor(Color.WHITE) 152 | .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER) 153 | .density(6) 154 | .setPositiveButton("SET", (dialog, selectedColor, allColors) -> { 155 | mBackgroundColor.setBackgroundTintList(ColorStateList.valueOf(selectedColor)); 156 | if (mListener != null) { 157 | mListener.onBackgroundColorChanged(selectedColor); 158 | } 159 | }) 160 | .setNegativeButton("DISMISS", (dialog, which) -> { 161 | }) 162 | .build() 163 | .show(); 164 | } 165 | 166 | @OnClick(R.id.increment_offset) 167 | void incrementOffsetAction() { 168 | tweakOffset(1); 169 | } 170 | 171 | @OnClick(R.id.decrement_offset) 172 | void decrementOffsetAction() { 173 | tweakOffset(-1); 174 | } 175 | 176 | //############################################################################################## LISTENER 177 | public interface OnBackgroundChangedListener { 178 | void onDrawBackgroundChanged(boolean draw); 179 | 180 | void onBackgroundColorChanged(int color); 181 | 182 | void onBackgroundOffsetChanged(int offset); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/fragment/ProgressSubFragment.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.fragment; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.res.ColorStateList; 6 | import android.graphics.Color; 7 | import android.os.Bundle; 8 | import android.util.TypedValue; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.Button; 13 | import android.widget.CheckBox; 14 | import android.widget.RadioGroup; 15 | import android.widget.SeekBar; 16 | import android.widget.TextView; 17 | import android.widget.Toast; 18 | 19 | import com.flask.colorpicker.ColorPickerView; 20 | import com.flask.colorpicker.builder.ColorPickerDialogBuilder; 21 | import com.ramijemli.percentagechartview.renderer.RingModeRenderer; 22 | import com.ramijemli.sample.R; 23 | import com.ramijemli.sample.util.ScreenUtil; 24 | 25 | import androidx.annotation.NonNull; 26 | import androidx.annotation.Nullable; 27 | import androidx.constraintlayout.widget.Group; 28 | import androidx.fragment.app.Fragment; 29 | import butterknife.BindView; 30 | import butterknife.ButterKnife; 31 | import butterknife.OnClick; 32 | import butterknife.Unbinder; 33 | 34 | 35 | public class ProgressSubFragment extends Fragment { 36 | 37 | public static final String BAR_STATE_ARG = "ProgressSubFragment.BAR_STATE_ARG"; 38 | 39 | //PROGRESS 40 | @BindView(R.id.progress_value) 41 | SeekBar mProgressValue; 42 | @BindView(R.id.progress_value_label) 43 | TextView mProgressLabel; 44 | @BindView(R.id.animate) 45 | CheckBox mAnimateProgress; 46 | 47 | //PROGRESS COLOR 48 | @BindView(R.id.progress_color) 49 | Button mProgressColor; 50 | 51 | //PROGRESS BAR THICKNESS 52 | @BindView(R.id.prog_thickness_value) 53 | TextView mPgBarThicknessValue; 54 | 55 | //PROGRESS BAR STYLE 56 | @BindView(R.id.prog_bar_style_value) 57 | RadioGroup mPgBarStyle; 58 | 59 | @BindView(R.id.bar_visibility) 60 | Group mBarGroup; 61 | 62 | private OnProgressChangedListener mListener; 63 | private Unbinder unbinder; 64 | private boolean enableBar; 65 | 66 | public ProgressSubFragment() { 67 | } 68 | 69 | public static ProgressSubFragment newInstance() { 70 | return new ProgressSubFragment(); 71 | } 72 | 73 | @Override 74 | public void onAttach(Context context) { 75 | super.onAttach(context); 76 | if (context instanceof OnProgressChangedListener) { 77 | mListener = (OnProgressChangedListener) context; 78 | } else { 79 | throw new RuntimeException(context.toString() 80 | + " must implement OnProgressChangedListener"); 81 | } 82 | } 83 | 84 | @Override 85 | public void onDetach() { 86 | super.onDetach(); 87 | mListener = null; 88 | } 89 | 90 | @Override 91 | public void onCreate(Bundle savedInstanceState) { 92 | super.onCreate(savedInstanceState); 93 | if (getArguments() != null) { 94 | enableBar = getArguments().getBoolean(BAR_STATE_ARG, false); 95 | } 96 | } 97 | 98 | @Override 99 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, 100 | Bundle savedInstanceState) { 101 | View view = inflater.inflate(R.layout.sub_fragment_progress, container, false); 102 | unbinder = ButterKnife.bind(this, view); 103 | return view; 104 | } 105 | 106 | @Override 107 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 108 | super.onViewCreated(view, savedInstanceState); 109 | setupProgress(); 110 | if(enableBar){ 111 | mBarGroup.setVisibility(View.VISIBLE); 112 | setupPgBarStyle(); 113 | } else { 114 | mBarGroup.setVisibility(View.GONE); 115 | } 116 | } 117 | 118 | @Override 119 | public void onDestroyView() { 120 | mProgressValue.setOnSeekBarChangeListener(null); 121 | mAnimateProgress.setOnCheckedChangeListener(null); 122 | mPgBarStyle.setOnCheckedChangeListener(null); 123 | unbinder.unbind(); 124 | unbinder = null; 125 | super.onDestroyView(); 126 | } 127 | 128 | //############################################################################################## BEHAVIOR 129 | private void setupProgress() { 130 | mProgressValue.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 131 | @Override 132 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 133 | mProgressLabel.setText(String.valueOf(progress)); 134 | 135 | if (mAnimateProgress.isChecked()) return; 136 | 137 | if (mListener != null) { 138 | mListener.onProgressChanged(progress, false); 139 | } 140 | } 141 | 142 | @Override 143 | public void onStartTrackingTouch(SeekBar seekBar) { 144 | 145 | } 146 | 147 | @Override 148 | public void onStopTrackingTouch(SeekBar seekBar) { 149 | if (mAnimateProgress.isChecked()) { 150 | if (mListener != null) { 151 | mListener.onProgressChanged(seekBar.getProgress(), true); 152 | } 153 | } 154 | mProgressLabel.setText(String.valueOf(seekBar.getProgress())); 155 | } 156 | }); 157 | } 158 | 159 | private void setupPgBarStyle() { 160 | mPgBarStyle.setOnCheckedChangeListener((group, checkedId) -> { 161 | if (mListener == null) return; 162 | switch (checkedId) { 163 | case R.id.round: 164 | mListener.onProgBarStyleChanged(RingModeRenderer.CAP_ROUND); 165 | break; 166 | case R.id.square: 167 | mListener.onProgBarStyleChanged(RingModeRenderer.CAP_SQUARE); 168 | break; 169 | } 170 | }); 171 | } 172 | 173 | private void tweakPgThickness(int amount) { 174 | int value = getTextViewValue(mPgBarThicknessValue); 175 | 176 | int maxValue = (int) ScreenUtil.convertPixelsToDIP(getActivity(), getView().getMeasuredWidth() / 2); 177 | if (value + amount > maxValue) { 178 | value =maxValue; 179 | } else if (value + amount <= 0) { 180 | value = 0; 181 | } else { 182 | value += amount; 183 | } 184 | 185 | mPgBarThicknessValue.setText(value + " dp"); 186 | if (mListener != null) { 187 | mListener.onProgBarThicknessChanged(ScreenUtil.convertDIPToPixels(getActivity(), value)); 188 | } 189 | } 190 | 191 | private int getTextViewValue(TextView view) { 192 | String value = view.getText().toString(); 193 | return Integer.parseInt(value.substring(0, value.length() - 3)); 194 | } 195 | 196 | //############################################################################################## ACTIONS 197 | @OnClick(R.id.progress_color) 198 | void progressAction() { 199 | ColorPickerDialogBuilder 200 | .with(getActivity()) 201 | .setTitle("Choose progress color") 202 | .initialColor(Color.WHITE) 203 | .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER) 204 | .density(6) 205 | .setPositiveButton("SET", (dialog, selectedColor, allColors) -> { 206 | mProgressColor.setBackgroundTintList(ColorStateList.valueOf(selectedColor)); 207 | if (mListener != null) { 208 | mListener.onProgressColorChanged(selectedColor); 209 | } 210 | }) 211 | .setNegativeButton("DISMISS", (dialog, which) -> { 212 | }) 213 | .build() 214 | .show(); 215 | } 216 | 217 | @OnClick(R.id.increment_prog_thickness) 218 | void incrementPgThicknessAction() { 219 | tweakPgThickness(1); 220 | } 221 | 222 | @OnClick(R.id.decrement_prog_thickness) 223 | void decrementPgThicknessAction() { 224 | tweakPgThickness(-1); 225 | } 226 | 227 | //############################################################################################## LISTENER 228 | public interface OnProgressChangedListener { 229 | void onProgressChanged(float progress, boolean animate); 230 | 231 | void onProgressColorChanged(int color); 232 | 233 | default void onProgBarThicknessChanged(int thickness){} 234 | 235 | default void onProgBarStyleChanged(int progressStyle){} 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/util/ScreenUtil.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.util; 2 | 3 | import android.content.Context; 4 | import android.content.res.Configuration; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Point; 7 | import android.util.DisplayMetrics; 8 | import android.util.TypedValue; 9 | import android.view.WindowManager; 10 | 11 | 12 | public class ScreenUtil { 13 | 14 | public static int getScreenWidthInDPs(Context context) { 15 | DisplayMetrics dm = new DisplayMetrics(); 16 | 17 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 18 | windowManager.getDefaultDisplay().getMetrics(dm); 19 | return Math.round(dm.widthPixels / dm.density); 20 | } 21 | 22 | public static int getScreenHeightInDPs(Context context) { 23 | DisplayMetrics dm = new DisplayMetrics(); 24 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 25 | windowManager.getDefaultDisplay().getMetrics(dm); 26 | return Math.round(dm.heightPixels / dm.density); 27 | } 28 | 29 | public static int getScreenWidthInPx(Context context) { 30 | DisplayMetrics dm = new DisplayMetrics(); 31 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 32 | windowManager.getDefaultDisplay().getMetrics(dm); 33 | return dm.widthPixels; 34 | } 35 | 36 | public static int getRealScreenWidthInPx(Context context) { 37 | Point size = new Point(); 38 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 39 | windowManager.getDefaultDisplay().getRealSize(size); 40 | return size.x; 41 | } 42 | 43 | public static int getScreenHeightInPx(Context context) { 44 | DisplayMetrics dm = new DisplayMetrics(); 45 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 46 | windowManager.getDefaultDisplay().getMetrics(dm); 47 | return dm.heightPixels; 48 | } 49 | 50 | public static int getRealScreenHeigthInPx(Context context) { 51 | Point size = new Point(); 52 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 53 | windowManager.getDefaultDisplay().getRealSize(size); 54 | return size.y; 55 | } 56 | 57 | /** 58 | * Converts the given device independent pixels (DIP) value into the corresponding pixels 59 | * value for the current screen. 60 | * 61 | * @param context Context instance 62 | * @param dip The DIP value to convert 63 | * @return The pixels value for the current screen of the given DIP value. 64 | */ 65 | public static int convertDIPToPixels(Context context, int dip) { 66 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 67 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); 68 | } 69 | 70 | /** 71 | * Converts the given device independent pixels (DIP) value into the corresponding pixels 72 | * value for the current screen. 73 | * 74 | * @param context Context instance 75 | * @param dip The DIP value to convert 76 | * @return The pixels value for the current screen of the given DIP value. 77 | */ 78 | public static int convertDIPToPixels(Context context, float dip) { 79 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 80 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); 81 | } 82 | 83 | /** 84 | * Converts the given pixels value into the corresponding device independent pixels (DIP) 85 | * value for the current screen. 86 | * 87 | * @param context Context instance 88 | * @param pixels The pixels value to convert 89 | * @return The DIP value for the current screen of the given pixels value. 90 | */ 91 | public static float convertPixelsToDIP(Context context, int pixels) { 92 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 93 | return pixels / (displayMetrics.densityDpi / 160f); 94 | } 95 | 96 | /** 97 | * Returns the current screen dimensions in device independent pixels (DIP) as a {@link Point} object where 98 | * {@link Point#x} is the screen width and {@link Point#y} is the screen height. 99 | * 100 | * @param context Context instance 101 | * @return The current screen dimensions in DIP. 102 | */ 103 | public static Point getScreenDimensionsInDIP(Context context) { 104 | Configuration configuration = context.getResources().getConfiguration(); 105 | return new Point(configuration.screenWidthDp, configuration.screenHeightDp); 106 | } 107 | 108 | /** 109 | * @param context Context instance 110 | * @return [true] if the device is in landscape orientation, [false] otherwise. 111 | */ 112 | public static boolean isInLandscapeOrientation(Context context) { 113 | Configuration configuration = context.getResources().getConfiguration(); 114 | return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE; 115 | } 116 | 117 | /** 118 | * @param context Context instance 119 | * @return [true] if the device has a small screen, [false] otherwise. 120 | */ 121 | public static boolean hasSmallScreen(Context context) { 122 | return getScreenSize(context) == Configuration.SCREENLAYOUT_SIZE_SMALL; 123 | } 124 | 125 | /** 126 | * @param context Context instance 127 | * @return [true] if the device has a normal screen, [false] otherwise. 128 | */ 129 | public static boolean hasNormalScreen(Context context) { 130 | return getScreenSize(context) == Configuration.SCREENLAYOUT_SIZE_NORMAL; 131 | } 132 | 133 | /** 134 | * @param context Context instance 135 | * @return [true] if the device has a large screen, [false] otherwise. 136 | */ 137 | public static boolean hasLargeScreen(Context context) { 138 | return getScreenSize(context) == Configuration.SCREENLAYOUT_SIZE_LARGE; 139 | } 140 | 141 | /** 142 | * @param context Context instance 143 | * @return [true] if the device has an extra large screen, [false] otherwise. 144 | */ 145 | public static boolean hasXLargeScreen(Context context) { 146 | return getScreenSize(context) == Configuration.SCREENLAYOUT_SIZE_XLARGE; 147 | } 148 | 149 | /** 150 | * The size of the screen, one of 4 possible values: 151 | *

152 | *

158 | *

159 | * See http://developer.android.com/reference/android/content/res/Configuration.html#screenLayout for more details. 160 | * 161 | * @param context Context instance 162 | * @return The size of the screen 163 | */ 164 | private static int getScreenSize(Context context) { 165 | Configuration configuration = context.getResources().getConfiguration(); 166 | return configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK; 167 | } 168 | 169 | 170 | public static int getStatusBarHeight(Context context) { 171 | int result = 0; 172 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); 173 | if (resourceId > 0) { 174 | result = context.getResources().getDimensionPixelSize(resourceId); 175 | } 176 | return result; 177 | } 178 | 179 | public static int getActionBarHeight(Context context) { 180 | final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes( 181 | new int[]{android.R.attr.actionBarSize}); 182 | int result = (int) styledAttributes.getDimension(0, 0); 183 | styledAttributes.recycle(); 184 | return result; 185 | } 186 | 187 | public static boolean hasNavBar(Context context) { 188 | int id = context.getResources().getIdentifier("config_showNavigationBar", "bool", "android"); 189 | return id > 0 && context.getResources().getBoolean(id); 190 | } 191 | 192 | 193 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ramijemli/sample/viewmodel/Showcase.java: -------------------------------------------------------------------------------- 1 | package com.ramijemli.sample.viewmodel; 2 | 3 | public class Showcase { 4 | private int color; 5 | private String title; 6 | private String subtitle; 7 | 8 | public Showcase(int color, String title, String subtitle) { 9 | this.color = color; 10 | this.title = title; 11 | this.subtitle = subtitle; 12 | } 13 | 14 | public int getColor() { 15 | return color; 16 | } 17 | 18 | public void setColor(int color) { 19 | this.color = color; 20 | } 21 | 22 | public String getTitle() { 23 | return title; 24 | } 25 | 26 | public void setTitle(String title) { 27 | this.title = title; 28 | } 29 | 30 | public String getSubtitle() { 31 | return subtitle; 32 | } 33 | 34 | public void setSubtitle(String subtitle) { 35 | this.subtitle = subtitle; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/res/color/all_tab_tint_s.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_collapse.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | 17 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_decrement.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_expand.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 15 | 19 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_fill.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_github.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 64 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_increment.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_paint.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_pie.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ico_ring.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/all_ripple_white.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/all_bg_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/all_bg_mode.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/all_card_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/all_card_bg_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/home_bg_anchor.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/home_bg_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_adaptive_colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 24 | 25 | 42 | 43 | 58 | 59 | 67 | 68 | 69 | 84 | 85 | 100 | 101 | 115 | 116 | 117 | 129 | 130 | 144 | 145 |