diff --git a/README.md b/README.md index ec0dbe9..91b84b9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,14 @@ Rotating text is an Android library that can be used to make text switching painless and beautiful, with the use of interpolators, typefaces and more customisations. +## ✨ New Features + +- **Stop at Last Entry** - Rotation stops at the last word instead of cycling infinitely +- **XML Attributes** - Configure text size, stop behavior, and more directly in XML +- **Layout Preview** - See preview text in Android Studio's design view +- **Last Interpolator** - Use a different animation for the final word transition +- **Improved Text Sizing** - Text size now works correctly at any value + # Usage Just add the following dependency in your app's `build.gradle` ``` @@ -16,21 +24,23 @@ dependencies { } ``` -## Example Usage 1 (Simple) +## Example Usage 1 (Simple with XML Attributes) #### XML -``` +```xml + android:layout_height="wrap_content" + app:textSize="35sp" + app:stopAtLast="false" + app:previewText="This is Word" /> ``` #### Java -``` -RotatingTextWrapper rotatingTextWrapper = (RotatingTextWrapper) findViewById(R.id.custom_switcher); -rotatingTextWrapper.setSize(35); +```java +RotatingTextWrapper rotatingTextWrapper = findViewById(R.id.custom_switcher); Rotatable rotatable = new Rotatable(Color.parseColor("#FFA036"), 1000, "Word", "Word01", "Word02"); rotatable.setSize(35); @@ -42,57 +52,60 @@ rotatingTextWrapper.setContent("This is ?", rotatable); #### Result -## Example Usage 2 (Typeface + Interpolator) +## Example Usage 2 (Stop at Last with Custom Interpolator) #### XML -``` +```xml + android:layout_height="wrap_content" + app:textSize="35sp" + app:stopAtLast="true" /> ``` #### Java -``` -Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Raleway-Light.ttf"); -Typeface typeface2 = Typeface.createFromAsset(getAssets(), "fonts/Reckoner_Bold.ttf"); +```java +Typeface typeface = ResourcesCompat.getFont(this, R.font.your_font); -RotatingTextWrapper rotatingTextWrapper = (RotatingTextWrapper) findViewById(R.id.custom_switcher); -rotatingTextWrapper.setSize(35); -rotatingTextWrapper.setTypeface(typeface2); +RotatingTextWrapper rotatingTextWrapper = findViewById(R.id.custom_switcher); +rotatingTextWrapper.setTypeface(typeface); -Rotatable rotatable = new Rotatable(Color.parseColor("#FFA036"), 1000, "Word", "Word01", "Word02"); +Rotatable rotatable = new Rotatable(Color.parseColor("#FFA036"), 1000, "Loading", "Processing", "Done!"); rotatable.setSize(35); rotatable.setAnimationDuration(500); rotatable.setTypeface(typeface); rotatable.setInterpolator(new BounceInterpolator()); +// Different animation for last word! +rotatable.setLastInterpolator(new OvershootInterpolator(2.0f)); -rotatingTextWrapper.setContent("This is ?", rotatable); +rotatingTextWrapper.setContent("Status: ?", rotatable); ``` #### Result +The text will rotate through "Loading" → "Processing" → "Done!" and stop. The last word "Done!" will use a dramatic overshoot animation! + ## Example Usage 3 (Multiple Rotatables) #### XML -``` +```xml + android:layout_height="wrap_content" + app:textSize="35sp" /> ``` #### Java -``` -Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Raleway-Light.ttf"); -Typeface typeface2 = Typeface.createFromAsset(getAssets(), "fonts/Reckoner_Bold.ttf"); +```java +Typeface typeface = ResourcesCompat.getFont(this, R.font.your_font); -RotatingTextWrapper rotatingTextWrapper = (RotatingTextWrapper) findViewById(R.id.custom_switcher); -rotatingTextWrapper.setSize(35); -rotatingTextWrapper.setTypeface(typeface2); +RotatingTextWrapper rotatingTextWrapper = findViewById(R.id.custom_switcher); +rotatingTextWrapper.setTypeface(typeface); Rotatable rotatable = new Rotatable(Color.parseColor("#FFA036"), 1000, "Word", "Word01", "Word02"); rotatable.setSize(35); @@ -112,13 +125,72 @@ rotatingTextWrapper.setContent("This is ? and ?", rotatable, rotatable2); #### Result +## Example Usage 4 (Modern Countdown) +#### XML + +```xml + +``` + +#### Java + +```java +Typeface typeface = ResourcesCompat.getFont(this, R.font.barlow_condensed_semi_bold); + +RotatingTextWrapper wrapper = findViewById(R.id.countdown_text); +wrapper.setTypeface(typeface); + +// Create countdown: 10, 9, 8, ... 2, 1, 0 +String[] countdown = createCountdownArray(10); + +Rotatable rotatable = new Rotatable( + ContextCompat.getColor(this, R.color.white), + 200, // Fast transitions + countdown +); +rotatable.setSize(38); +rotatable.setStopAtLast(true); // Stop at 0 +rotatable.setAnimationDuration(200); +rotatable.setCenter(true); +rotatable.setTypeface(typeface); +rotatable.setInterpolator(new LinearInterpolator()); // Smooth countdown +rotatable.setLastInterpolator(new BounceInterpolator()); // Bounce on final number! + +wrapper.setContent("?", rotatable); +``` + # Documentation Rotating text is made of two parts : `RotatingTextWrapper` and `Rotatable`.
Each rotatable encapsulates the collection of words that are two be periodically switched and also defines various properties related to these words, like, size, color, animation interpolator etc.
Each Rotatable must be a part of a `RotatingTextWrapper`. This defines the actual layout of the text and the positions of the rotating text. -For eg : `rotatingTextWrapper.setContent("This is ?", rotatble);`. Here the `?` denotes the postion of the `rotatable`. +For eg : `rotatingTextWrapper.setContent("This is ?", rotatable);`. Here the `?` denotes the position of the `rotatable`. + +## XML Attributes + +Configure RotatingTextWrapper directly in XML: + +|Attribute |Type |Description | +|------------------|---------------|----------------------------------------| +|`app:textSize` | dimension | Text size (e.g., "24sp", "16dp") | +|`app:stopAtLast` | boolean | Stop at last word instead of cycling | +|`app:previewText` | string | Preview text in layout editor | +|`app:adaptable` | boolean | Adapt to parent width | + +**Example:** +```xml + +``` ## RotatingTextWrapper |Property |Function |Description | @@ -126,6 +198,9 @@ For eg : `rotatingTextWrapper.setContent("This is ?", rotatble);`. Here the `?` |Content | setContent(...) | Set the actual content. Composed of a String and array of Rotatables. | |Typeface | setTypeface(...) | Set the typeface of the non-rotating text | |Size | setSize(...) | Set the size of the non-rotating text | +|Stop At Last | setStopAtLast(...) | Stop rotation at last word instead of cycling | +|Preview Text | setPreviewText(...) | Set preview text for layout editor | +|Last Interpolator | setLastInterpolator(...) | Set special interpolator for last word animation | |Pause | pause(x) | Method to pause the 'x'th rotatable | |Resume | resume(x) | Method to resume the 'x'th rotatable | @@ -136,9 +211,48 @@ For eg : `rotatingTextWrapper.setContent("This is ?", rotatble);`. Here the `?` |Size | setSize(...) | Set the size of the rotating text associated with this rotatable | |Typeface | setTypeface(...) | Set the typeface of the rotating text associated with this rotatable | |Interpolator | setInterpolator(...) | Set the animation interpolator used while switching text | +|Last Interpolator | setLastInterpolator(...) | Set different interpolator for animating to last word | |Update Duration | setUpdateDuration(...) | Set the interval between switching the words | |Animation Duration | setAnimationDuration(...) | Set the duration of the switching animation | -|Center Align | setCenter(...) |Align the rotating text to center of the textview if set to **true** | +|Center Align | setCenter(...) | Align the rotating text to center of the textview if set to **true** | +|Stop At Last | setStopAtLast(...) | Stop at last word in the array instead of cycling | + +## New Features Guide + +### Stop at Last Entry +Stop rotation at the last word instead of cycling back: + +```java +rotatable.setStopAtLast(true); +// or +wrapper.setStopAtLast(true); // Applies to all rotatables +``` + +### Last Interpolator +Use a different animation for the final word: + +```java +rotatable.setInterpolator(new BounceInterpolator()); // For all words +rotatable.setLastInterpolator(new OvershootInterpolator(2.0f)); // Special finish! +``` + +**Perfect for:** +- Loading sequences with satisfying completions +- Countdowns with dramatic endings +- Status updates with emphasis on final state + +### Preview in Layout Editor +See your text in Android Studio's design view: + +```xml + +``` + +📚 **For more examples and detailed documentation, see:** +- [NEW_FEATURES.md](NEW_FEATURES.md) - Quick start guide +- [FEATURE_DOCUMENTATION.md](FEATURE_DOCUMENTATION.md) - Complete usage guide +- [LAST_INTERPOLATOR_FEATURE.md](LAST_INTERPOLATOR_FEATURE.md) - Last interpolator deep dive # License diff --git a/app/build.gradle b/app/build.gradle index 1a64fce..105b4df 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,16 +1,17 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdkVersion 36 + buildToolsVersion = '30.0.3' defaultConfig { applicationId "com.sdsmdg.harjot.rotatingtextlibrary" minSdkVersion 16 - targetSdkVersion 27 + targetSdkVersion 36 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } + namespace "com.sdsmdg.harjot.rotatingtextlibrary" buildTypes { release { minifyEnabled false @@ -20,6 +21,6 @@ android { } dependencies { - implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'androidx.appcompat:appcompat:1.6.1' implementation project(":rotatingtext") -} +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aa50730..35efb46 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ +> - + diff --git a/app/src/main/java/com/sdsmdg/harjot/rotatingtextlibrary/MainActivity.java b/app/src/main/java/com/sdsmdg/harjot/rotatingtextlibrary/MainActivity.java index 8f89744..a28f072 100644 --- a/app/src/main/java/com/sdsmdg/harjot/rotatingtextlibrary/MainActivity.java +++ b/app/src/main/java/com/sdsmdg/harjot/rotatingtextlibrary/MainActivity.java @@ -1,25 +1,37 @@ package com.sdsmdg.harjot.rotatingtextlibrary; +import static java.security.AccessController.getContext; + import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; + import android.text.TextUtils; +import android.util.Log; import android.view.View; import android.view.animation.AccelerateInterpolator; +import android.view.animation.BounceInterpolator; import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.OvershootInterpolator; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.core.content.res.ResourcesCompat; + import com.sdsmdg.harjot.rotatingtext.RotatingTextSwitcher; import com.sdsmdg.harjot.rotatingtext.RotatingTextWrapper; import com.sdsmdg.harjot.rotatingtext.models.Rotatable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.IntStream; public class MainActivity extends AppCompatActivity { @@ -41,15 +53,20 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); rotatingTextSwitcher = new RotatingTextSwitcher(this); - Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Raleway-Light.ttf"); - Typeface typeface2 = Typeface.createFromAsset(getAssets(), "fonts/Reckoner_Bold.ttf"); rotatingTextWrapper = findViewById(R.id.custom_switcher); + + // Option 1: Use the modern countdown implementation + // setRotating(rotatingTextWrapper, 10); + + // Option 2: Classic example with custom colors and typefaces + Typeface typeface = ResourcesCompat.getFont(this, R.font.love); + rotatingTextWrapper.setSize(30); - rotatingTextWrapper.setTypeface(typeface2); + rotatingTextWrapper.setTypeface(typeface); -// rotatable = new Rotatable(Color.parseColor("#FFA036"), 1000, "Word00", "Word01", "Word02"); - rotatable = new Rotatable(color , 1000, "rotating", "text", "library"); + // Example with color array + rotatable = new Rotatable(color, 1000, "rotating", "text", "library"); rotatable.setSize(25); rotatable.setTypeface(typeface); rotatable.setInterpolator(new AccelerateInterpolator()); @@ -61,16 +78,15 @@ protected void onCreate(Bundle savedInstanceState) { rotatable2.setInterpolator(new DecelerateInterpolator()); rotatable2.setAnimationDuration(500); - word = rotatable.getTextAt(0); + // Use multiple rotatables rotatingTextWrapper.setContent("?abc ? abc", rotatable, rotatable2); -// rotatingTextWrapper.setContent("? abc", rotatable); + // Or use single rotatable + // rotatingTextWrapper.setContent("? abc", rotatable); s1 = (Spinner) findViewById(R.id.spinner); - - List list = new ArrayList(); list.add(1); list.add(2); @@ -122,11 +138,60 @@ public void onClick(View view) { public void onClick(View view) { boolean apply = rotatable.getApplyHorizontal(); rotatable.setApplyHorizontal(!apply); - rotatable2.setApplyHorizontal(!apply); + if (rotatable2 != null) { + rotatable2.setApplyHorizontal(!apply); + } } }); + + // Add button to toggle stopAtLast feature + Button stopAtLastButton = findViewById(R.id.stop_at_last_button); + stopAtLastButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + boolean currentStopAtLast = rotatingTextWrapper.isStopAtLast(); + rotatingTextWrapper.setStopAtLast(!currentStopAtLast); + + // Restart the rotation to apply the change + if (rotatingTextWrapper.getSwitcherList().size() > 0) { + rotatingTextWrapper.resume(0); + } + } + }); + } + + private void setRotating(RotatingTextWrapper wrapper, int toCount) { + Typeface typeface = ResourcesCompat.getFont(this, R.font.love); + + wrapper.setTypeface(typeface); + + // Create array of numbers from 0 to toCount + rotatable = new Rotatable( + ContextCompat.getColor(this, android.R.color.black), + 200, // 200ms between transitions + createTextListForInteger(toCount) + ); + rotatable.setCycles(0); + rotatable.setSize(38); + rotatable.setStopAtLast(true); // Stop at last number + rotatable.setAnimationDuration(200); + rotatable.setCenter(true); + rotatable.setTypeface(typeface); + rotatable.setInterpolator(new LinearInterpolator()); // Smooth linear animation + rotatable.setLastInterpolator(new BounceInterpolator()); // Bounce on final number! + + // Store the first word for cycles feature + word = rotatable.getTextAt(0); + + wrapper.setContent("?", rotatable); } + private String[] createTextListForInteger(int n) { + List integerList = IntStream.rangeClosed(0, n) + .boxed() + .map(i -> i.toString()).toList(); + return integerList.toArray(new String[]{}); + } public void replaceWord(View view) { String newWord = e1.getText().toString(); if (TextUtils.isEmpty(newWord)) e1.setText("can't be left empty"); diff --git a/app/src/main/assets/fonts/LoveYaLikeASister.ttf b/app/src/main/res/font/love.ttf similarity index 100% rename from app/src/main/assets/fonts/LoveYaLikeASister.ttf rename to app/src/main/res/font/love.ttf diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 14f9618..8ccb8f0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,6 @@ + android:layout_marginLeft="10dp" + app:textSize="38sp" + app:stopAtLast="true" + app:previewText="0 1 2 3..." + app:adaptable="false" /> + +