mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 19:56:32 +00:00
add multiple steps to tutorialViews
This commit is contained in:
parent
e561e2db30
commit
b9e07e4573
14 changed files with 272 additions and 61 deletions
BIN
Habitica/res/drawable-hdpi/speechbubble_caret.png
Normal file
BIN
Habitica/res/drawable-hdpi/speechbubble_caret.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 618 B |
BIN
Habitica/res/drawable-mdpi/speechbubble_caret.png
Normal file
BIN
Habitica/res/drawable-mdpi/speechbubble_caret.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 478 B |
BIN
Habitica/res/drawable-xhdpi/speechbubble_caret.png
Normal file
BIN
Habitica/res/drawable-xhdpi/speechbubble_caret.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 738 B |
BIN
Habitica/res/drawable-xxhdpi/speechbubble_caret.png
Normal file
BIN
Habitica/res/drawable-xxhdpi/speechbubble_caret.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 951 B |
|
|
@ -1,26 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:clickable="true"
|
||||
android:fitsSystemWindows="true"
|
||||
android:id="@+id/background">
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:clickable="true"
|
||||
android:fitsSystemWindows="true"
|
||||
android:id="@+id/background">
|
||||
<View android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@android:color/black"
|
||||
android:alpha="0.6"/>
|
||||
android:layout_width="match_parent"
|
||||
android:background="@android:color/black"
|
||||
android:alpha="0.6"/>
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="24dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_marginBottom="?actionBarSize">
|
||||
<com.habitrpg.android.habitica.ui.SpeechBubbleView
|
||||
android:id="@+id/speech_bubble"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_marginBottom="?actionBarSize">
|
||||
<com.habitrpg.android.habitica.ui.SpeechBubbleView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:namePlate="Justin"
|
||||
app:npcDrawable="@drawable/justin_textbox"/>
|
||||
app:namePlate="Justin"
|
||||
app:npcDrawable="@drawable/justin_textbox"/>
|
||||
</FrameLayout>
|
||||
</RelativeLayout>
|
||||
|
|
|
|||
|
|
@ -19,16 +19,16 @@
|
|||
android:layout_marginTop="46dp"
|
||||
android:background="@drawable/white_rounded_bg"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="14dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingLeft="21dp"
|
||||
android:paddingRight="21dp"
|
||||
android:paddingTop="16dp">
|
||||
android:paddingTop="24dp"
|
||||
android:layout_marginBottom="10dp">
|
||||
|
||||
<TextView
|
||||
<com.habitrpg.android.habitica.ui.views.Typewriter
|
||||
android:id="@+id/textView"
|
||||
style="@style/Body1"
|
||||
style="@style/Subheader1"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/welcome_text" />
|
||||
|
||||
|
|
@ -37,7 +37,8 @@
|
|||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/dismissButton"
|
||||
|
|
@ -61,6 +62,15 @@
|
|||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/continue_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/speechbubble_caret"
|
||||
android:layout_gravity="bottom|center"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name_plate"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="tutorial_habits">Complete Positive Habits to earn gold and experience! Negative Habits will hurt your avatar if you tap them, so avoid them in real life!</string>
|
||||
<string name="tutorial_dailies">Defeat your repeating Daily tasks to gain gold and experience. Danger! Dailies will hurt your avatar if you don\'t complete them in time.</string>
|
||||
<string name="tutorial_todos">Complete your To-Dos in real life, then check them off for GOLD and EXPERIENCE so you can earn Rewards and unlock new features!</string>
|
||||
<string name="tutorial_rewards">These are your Rewards! Earn gold by completing real-world Habits, Dailies, and To-Dos. Then spend it on in-game Rewards or custom real-world Rewards!</string>
|
||||
<string name="tutorial_overview">Here we are! I’ve filled out some tasks for you based on your interests. Try adding a few of your own. You can edit any task by tapping the title.</string>
|
||||
<string name="tutorial_habits_1">First up is Habits. They can be positive Habits you want to improve or negative Habits you want to quit.</string>
|
||||
<string name="tutorial_habits_2">Every time you do a positive Habit, tap the + to get experience and gold!</string>
|
||||
<string name="tutorial_habits_3">If you slip up and do a negative Habit, tapping the - will reduce your avatar’s health to help you stay accountable.</string>
|
||||
<string name="tutorial_habits_4">Give it a shot! You can explore the other task types through the bottom navigation.</string>
|
||||
<string name="tutorial_dailies_1">Make Dailies for time sensitive tasks that need to be done on a regular schedule.</string>
|
||||
<string name="tutorial_dailies_2">Be careful — if you miss one, your avatar will take damage overnight. Checking them off consistently brings great rewards!</string>
|
||||
<string name="tutorial_todos_1">Use To-dos to keep track of tasks you need to do just once.</string>
|
||||
<string name="tutorial_todos_2">If your To-do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead!</string>
|
||||
<string name="tutorial_rewards_1">Buy gear for your avatar with the gold you earn!</string>
|
||||
<string name="tutorial_rewards_2">You can also make real-world Custom Rewards based on what motivates you.</string>
|
||||
|
||||
<string name="tutorial_equipment">When you buy equipment, it appears here. Your Battle Gear affects your stats, and your Costume (if enabled) affects what your avatar wears.</string>
|
||||
<string name="tutorial_items">Earn items by completing tasks and leveling up. Tap on an item to use it!</string>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.habitrpg.android.habitica.events;
|
|||
|
||||
import com.magicmicky.habitrpgwrapper.lib.models.TutorialStep;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by viirus on 26/01/16.
|
||||
*/
|
||||
|
|
@ -10,4 +12,5 @@ public class DisplayTutorialEvent {
|
|||
public TutorialStep step;
|
||||
public String tutorialText;
|
||||
|
||||
public List<String> tutorialTexts;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.ui;
|
|||
|
||||
|
||||
import com.habitrpg.android.habitica.R;
|
||||
import com.habitrpg.android.habitica.ui.views.Typewriter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
|
|
@ -9,6 +10,8 @@ import android.graphics.drawable.Drawable;
|
|||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
|
@ -16,13 +19,13 @@ import android.widget.TextView;
|
|||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class SpeechBubbleView extends FrameLayout {
|
||||
public class SpeechBubbleView extends FrameLayout implements View.OnClickListener {
|
||||
|
||||
@BindView(R.id.name_plate)
|
||||
TextView namePlate;
|
||||
|
||||
@BindView(R.id.textView)
|
||||
TextView textView;
|
||||
Typewriter textView;
|
||||
|
||||
@BindView(R.id.npc_image_view)
|
||||
ImageView npcImageView;
|
||||
|
|
@ -30,6 +33,10 @@ public class SpeechBubbleView extends FrameLayout {
|
|||
@BindView(R.id.confirmation_buttons)
|
||||
ViewGroup confirmationButtons;
|
||||
|
||||
@BindView(R.id.continue_indicator)
|
||||
View continueIndicator;
|
||||
private ShowNextListener showNextListener;
|
||||
|
||||
public SpeechBubbleView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
|
|
@ -51,7 +58,43 @@ public class SpeechBubbleView extends FrameLayout {
|
|||
}
|
||||
|
||||
confirmationButtons.setVisibility(View.GONE);
|
||||
|
||||
this.setOnClickListener(this);
|
||||
}
|
||||
|
||||
|
||||
public void setConfirmationButtonVisibility(int visibility) {
|
||||
confirmationButtons.setVisibility(visibility);
|
||||
}
|
||||
|
||||
public void animateText(String text) {
|
||||
this.textView.animateText(text);
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.textView.setText(text);
|
||||
}
|
||||
|
||||
public void setHasMoreContent(Boolean hasMoreContent) {
|
||||
continueIndicator.setVisibility(hasMoreContent ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void setShowNextListener(ShowNextListener listener) {
|
||||
showNextListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (textView.isAnimating()) {
|
||||
textView.stopTextAnimation();
|
||||
} else {
|
||||
if (showNextListener != null) {
|
||||
showNextListener.showNextStep();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ShowNextListener {
|
||||
void showNextStep();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,63 +1,103 @@
|
|||
package com.habitrpg.android.habitica.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.habitrpg.android.habitica.R;
|
||||
import com.magicmicky.habitrpgwrapper.lib.models.TutorialStep;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class TutorialView extends FrameLayout implements View.OnClickListener {
|
||||
public class TutorialView extends FrameLayout {
|
||||
|
||||
public TutorialStep step;
|
||||
@Nullable
|
||||
public OnTutorialReaction onReaction;
|
||||
@BindView(R.id.textView)
|
||||
TextView tutorialTextView;
|
||||
@BindView(R.id.speech_bubble)
|
||||
SpeechBubbleView speechBubbleView;
|
||||
@BindView(R.id.background)
|
||||
RelativeLayout background;
|
||||
@BindView(R.id.dismissButton)
|
||||
Button dismissButton;
|
||||
@BindView(R.id.completeButton)
|
||||
Button completeButton;
|
||||
@BindView(R.id.confirmation_buttons)
|
||||
ViewGroup confirmationButtons;
|
||||
|
||||
public TutorialView(Context context, TutorialStep step, OnTutorialReaction onReaction) {
|
||||
private List<String> tutorialTexts;
|
||||
private int currentTextIndex;
|
||||
|
||||
public TutorialView(Context context, TutorialStep step, @Nullable OnTutorialReaction onReaction) {
|
||||
super(context);
|
||||
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mInflater.inflate(R.layout.overlay_tutorial, this, true);
|
||||
ButterKnife.bind(this);
|
||||
background.setOnClickListener(this);
|
||||
dismissButton.setOnClickListener(this);
|
||||
completeButton.setOnClickListener(this);
|
||||
confirmationButtons.setVisibility(View.VISIBLE);
|
||||
speechBubbleView.setConfirmationButtonVisibility(View.GONE);
|
||||
speechBubbleView.setShowNextListener(this::displayNextTutorialText);
|
||||
this.step = step;
|
||||
this.onReaction = onReaction;
|
||||
}
|
||||
|
||||
public void setTutorialText(String text) {
|
||||
this.tutorialTextView.setText(text);
|
||||
this.speechBubbleView.animateText(text);
|
||||
speechBubbleView.setConfirmationButtonVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (Objects.equals(v, background) || Objects.equals(v, completeButton)) {
|
||||
public void setTutorialTexts(List<String> texts) {
|
||||
tutorialTexts = texts;
|
||||
currentTextIndex = -1;
|
||||
displayNextTutorialText();
|
||||
}
|
||||
|
||||
private void displayNextTutorialText() {
|
||||
currentTextIndex++;
|
||||
if (currentTextIndex < tutorialTexts.size()) {
|
||||
speechBubbleView.animateText(tutorialTexts.get(currentTextIndex));
|
||||
if (isDisplayingLastStep()) {
|
||||
speechBubbleView.setConfirmationButtonVisibility(View.VISIBLE);
|
||||
speechBubbleView.setHasMoreContent(false);
|
||||
} else {
|
||||
speechBubbleView.setHasMoreContent(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.completeButton)
|
||||
public void completeButtonClicked() {
|
||||
if (this.onReaction != null) {
|
||||
this.onReaction.onTutorialCompleted(this.step);
|
||||
} else if (Objects.equals(v, dismissButton)) {
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.dismissButton)
|
||||
public void dismissButtonClicked() {
|
||||
if (this.onReaction != null) {
|
||||
this.onReaction.onTutorialDeferred(this.step);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.background)
|
||||
public void backgroundClicked() {
|
||||
if (isDisplayingLastStep()) {
|
||||
if (this.onReaction != null) {
|
||||
this.onReaction.onTutorialCompleted(this.step);
|
||||
}
|
||||
} else {
|
||||
speechBubbleView.onClick(speechBubbleView);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDisplayingLastStep() {
|
||||
return currentTextIndex == (tutorialTexts.size()-1);
|
||||
}
|
||||
|
||||
public interface OnTutorialReaction {
|
||||
void onTutorialCompleted(TutorialStep step);
|
||||
|
||||
|
|
|
|||
|
|
@ -869,7 +869,11 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
|
|||
|
||||
@Subscribe
|
||||
public void onEvent(DisplayTutorialEvent tutorialEvent) {
|
||||
this.displayTutorialStep(tutorialEvent.step, tutorialEvent.tutorialText);
|
||||
if (tutorialEvent.tutorialText != null) {
|
||||
this.displayTutorialStep(tutorialEvent.step, tutorialEvent.tutorialText);
|
||||
} else {
|
||||
this.displayTutorialStep(tutorialEvent.step, tutorialEvent.tutorialTexts);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
|
|
@ -1091,6 +1095,20 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
|
|||
AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData);
|
||||
}
|
||||
|
||||
private void displayTutorialStep(TutorialStep step, List<String> texts) {
|
||||
TutorialView view = new TutorialView(this, step, this);
|
||||
view.setTutorialTexts(texts);
|
||||
view.onReaction = this;
|
||||
this.overlayLayout.addView(view);
|
||||
this.activeTutorialView = view;
|
||||
|
||||
Map<String, Object> additionalData = new HashMap<>();
|
||||
additionalData.put("eventLabel", step.getIdentifier() + "-android");
|
||||
additionalData.put("eventValue", step.getIdentifier());
|
||||
additionalData.put("complete", false);
|
||||
AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTutorialCompleted(TutorialStep step) {
|
||||
String path = "flags.tutorial." + step.getTutorialGroup() + "." + step.getIdentifier();
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import org.greenrobot.eventbus.EventBusException;
|
|||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
|
|
@ -41,7 +42,11 @@ public abstract class BaseFragment extends DialogFragment {
|
|||
if (step != null && !step.getWasCompleted() && (step.getDisplayedOn() == null || (new Date().getTime() - step.getDisplayedOn().getTime()) > 86400000)) {
|
||||
DisplayTutorialEvent event = new DisplayTutorialEvent();
|
||||
event.step = step;
|
||||
event.tutorialText = tutorialText;
|
||||
if (tutorialText != null) {
|
||||
event.tutorialText = tutorialText;
|
||||
} else {
|
||||
event.tutorialTexts = tutorialTexts;
|
||||
}
|
||||
EventBus.getDefault().post(event);
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +61,7 @@ public abstract class BaseFragment extends DialogFragment {
|
|||
return true;
|
||||
}
|
||||
};
|
||||
public List<String> tutorialTexts;
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
|
|||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
|
|
@ -100,22 +102,29 @@ public class TaskRecyclerViewFragment extends BaseFragment implements View.OnCli
|
|||
switch (fragment.classType) {
|
||||
case Task.TYPE_HABIT: {
|
||||
fragment.tutorialStepIdentifier = "habits";
|
||||
fragment.tutorialText = context.getString(R.string.tutorial_habits);
|
||||
fragment.tutorialTexts = Arrays.asList(context.getString(R.string.tutorial_overview),
|
||||
context.getString(R.string.tutorial_habits_1),
|
||||
context.getString(R.string.tutorial_habits_2),
|
||||
context.getString(R.string.tutorial_habits_3),
|
||||
context.getString(R.string.tutorial_habits_4));
|
||||
break;
|
||||
}
|
||||
case Task.FREQUENCY_DAILY: {
|
||||
fragment.tutorialStepIdentifier = "dailies";
|
||||
fragment.tutorialText = context.getString(R.string.tutorial_dailies);
|
||||
fragment.tutorialTexts = Arrays.asList(context.getString(R.string.tutorial_dailies_1),
|
||||
context.getString(R.string.tutorial_dailies_2));
|
||||
break;
|
||||
}
|
||||
case Task.TYPE_TODO: {
|
||||
fragment.tutorialStepIdentifier = "todos";
|
||||
fragment.tutorialText = context.getString(R.string.tutorial_todos);
|
||||
fragment.tutorialTexts = Arrays.asList(context.getString(R.string.tutorial_todos_1),
|
||||
context.getString(R.string.tutorial_todos_2));
|
||||
break;
|
||||
}
|
||||
case Task.TYPE_REWARD: {
|
||||
fragment.tutorialStepIdentifier = "rewards";
|
||||
fragment.tutorialText = context.getString(R.string.tutorial_rewards);
|
||||
fragment.tutorialTexts = Arrays.asList(context.getString(R.string.tutorial_rewards_1),
|
||||
context.getString(R.string.tutorial_rewards_2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package com.habitrpg.android.habitica.ui.views;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.habitrpg.android.habitica.R;
|
||||
|
||||
// http://stackoverflow.com/a/6700718/1315039
|
||||
public class Typewriter extends android.support.v7.widget.AppCompatTextView {
|
||||
|
||||
private SpannableStringBuilder stringBuilder;
|
||||
private Object visibleSpan;
|
||||
private Object hiddenSpan;
|
||||
private int index;
|
||||
private long delay = 30;
|
||||
|
||||
|
||||
public Typewriter(Context context) {
|
||||
super(context);
|
||||
setupTextColors(context);
|
||||
}
|
||||
|
||||
public Typewriter(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setupTextColors(context);
|
||||
}
|
||||
|
||||
private void setupTextColors(Context context) {
|
||||
visibleSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.textColorLight));
|
||||
hiddenSpan = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.transparent));
|
||||
}
|
||||
|
||||
private Handler handler = new Handler();
|
||||
private Runnable characterAdder = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stringBuilder.setSpan(visibleSpan, 0, index++, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
setText(stringBuilder);
|
||||
if(index <= stringBuilder.length()) {
|
||||
handler.postDelayed(characterAdder, delay);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void animateText(CharSequence text) {
|
||||
stringBuilder = new SpannableStringBuilder(text);
|
||||
stringBuilder.setSpan(hiddenSpan, 0, stringBuilder.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
index = 0;
|
||||
|
||||
setText(stringBuilder);
|
||||
handler.removeCallbacks(characterAdder);
|
||||
handler.postDelayed(characterAdder, delay);
|
||||
}
|
||||
|
||||
public void setCharacterDelay(long millis) {
|
||||
delay = millis;
|
||||
}
|
||||
|
||||
public boolean isAnimating() {
|
||||
return index < stringBuilder.length();
|
||||
}
|
||||
|
||||
public void stopTextAnimation() {
|
||||
index = stringBuilder.length();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue