Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.stepstone.stepper.internal.TabsContainer;
import com.stepstone.stepper.type.AbstractStepperType;
import com.stepstone.stepper.type.StepperTypeFactory;
import com.stepstone.stepper.type.TabsStepperType;
import com.stepstone.stepper.util.AnimationUtil;
import com.stepstone.stepper.util.TintUtil;
import com.stepstone.stepper.viewmodel.StepViewModel;
Expand Down Expand Up @@ -175,6 +176,9 @@ public final void goToPrevStep() {
@ColorInt
private int mSelectedColor;

@ColorInt
private int mErrorColor;

@DrawableRes
private int mBottomNavigationBackground;

Expand Down Expand Up @@ -205,6 +209,10 @@ public final void goToPrevStep() {

private int mCurrentStepPosition;

private boolean mShowErrorState;

private boolean mShowErrorStateOnBack;

@NonNull
private StepperListener mListener = StepperListener.NULL;

Expand Down Expand Up @@ -295,6 +303,10 @@ public int getUnselectedColor() {
return mUnselectedColor;
}

public int getErrorColor() {
return mErrorColor;
}

public int getTabStepDividerWidth() {
return mTabStepDividerWidth;
}
Expand Down Expand Up @@ -326,6 +338,23 @@ public void setCompleteButtonVerificationFailed(boolean verificationFailed) {
mCompleteNavigationButton.setVerificationFailed(verificationFailed);
}

/**

* Set whether when going backwards should clear the error state from the Tab. Default is false.
* @param mShowErrorStateOnBack
*/
public void setShowErrorStateOnBack(boolean mShowErrorStateOnBack) {
this.mShowErrorStateOnBack = mShowErrorStateOnBack;
}

/**
* Set whether the tab should display error or not. Default false.
* @param mShowErrorState
*/
public void setShowErrorState(boolean mShowErrorState) {
this.mShowErrorState = mShowErrorState;
}

/**
* Set the number of steps that should be retained to either side of the
* current step in the view hierarchy in an idle state. Steps beyond this
Expand Down Expand Up @@ -427,13 +456,15 @@ private void extractValuesFromAttributes(AttributeSet attrs, @AttrRes int defSty
if (a.hasValue(R.styleable.StepperLayout_ms_completeButtonColor)) {
mCompleteButtonColor = a.getColorStateList(R.styleable.StepperLayout_ms_completeButtonColor);
}

if (a.hasValue(R.styleable.StepperLayout_ms_activeStepColor)) {
mSelectedColor = a.getColor(R.styleable.StepperLayout_ms_activeStepColor, mSelectedColor);
}
if (a.hasValue(R.styleable.StepperLayout_ms_inactiveStepColor)) {
mUnselectedColor = a.getColor(R.styleable.StepperLayout_ms_inactiveStepColor, mUnselectedColor);
}
if (a.hasValue(R.styleable.StepperLayout_ms_errorColor)) {
mErrorColor = a.getColor(R.styleable.StepperLayout_ms_errorColor, mErrorColor);
}
if (a.hasValue(R.styleable.StepperLayout_ms_bottomNavigationBackground)) {
mBottomNavigationBackground = a.getResourceId(R.styleable.StepperLayout_ms_bottomNavigationBackground, mBottomNavigationBackground);
}
Expand Down Expand Up @@ -464,10 +495,14 @@ private void extractValuesFromAttributes(AttributeSet attrs, @AttrRes int defSty

mShowBackButtonOnFirstStep = a.getBoolean(R.styleable.StepperLayout_ms_showBackButtonOnFirstStep, false);

mShowErrorState = a.getBoolean(R.styleable.StepperLayout_ms_showErrorState, false);

if (a.hasValue(R.styleable.StepperLayout_ms_stepperType)) {
mTypeIdentifier = a.getInt(R.styleable.StepperLayout_ms_stepperType, DEFAULT_TAB_DIVIDER_WIDTH);
}

mShowErrorStateOnBack = a.getBoolean(R.styleable.StepperLayout_ms_showErrorStateOnBack, false);

a.recycle();
}
}
Expand All @@ -477,6 +512,7 @@ private void initDefaultValues() {
ContextCompat.getColorStateList(getContext(), R.color.ms_bottomNavigationButtonTextColor);
mSelectedColor = ContextCompat.getColor(getContext(), R.color.ms_selectedColor);
mUnselectedColor = ContextCompat.getColor(getContext(), R.color.ms_unselectedColor);
mErrorColor = ContextCompat.getColor(getContext(), R.color.ms_errorColor);
mBottomNavigationBackground = R.color.ms_bottomNavigationBackgroundColor;
mBackButtonText = getContext().getString(R.string.ms_back);
mNextButtonText = getContext().getString(R.string.ms_next);
Expand Down Expand Up @@ -509,6 +545,11 @@ private void onNext() {
if (verifyCurrentStep(step)) {
return;
}

//if moving forward and got no errors, set hasError to false, so we can have the tab with the check mark.
if(mShowErrorState)
mStepperType.setErrorStep(mCurrentStepPosition, false);

OnNextClickedCallback onNextClickedCallback = new OnNextClickedCallback();
if (step instanceof BlockingStep) {
((BlockingStep) step).onNextClicked(onNextClickedCallback);
Expand All @@ -530,6 +571,11 @@ private void onError(@NonNull VerificationError verificationError) {
Step step = findCurrentStep();
if (step != null) {
step.onError(verificationError);

//if moving forward and got errors, set hasError to true, showing the error drawable.
if(mShowErrorState)
mStepperType.setErrorStep(mCurrentStepPosition, true);

}
mListener.onError(verificationError);
}
Expand All @@ -539,6 +585,7 @@ private void onComplete(View completeButton) {
if (verifyCurrentStep(step)) {
return;
}
mStepperType.setErrorStep(mCurrentStepPosition, false);
mListener.onCompleted(completeButton);
}

Expand All @@ -558,6 +605,8 @@ private void onUpdate(int newStepPosition, boolean animate) {
updateNextButtonText(viewModel);
}

//needs to be here in case user for any reason decide to change whether or not to show errors when going back.
mStepperType.showErrorStateOnBack(mShowErrorStateOnBack);
mStepperType.onStepSelected(newStepPosition);
mListener.onStepSelected(newStepPosition);
Step step = mStepAdapter.findStep(mPager, newStepPosition);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
package com.stepstone.stepper.internal;

import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
Expand All @@ -47,6 +50,9 @@ public class StepTab extends RelativeLayout {
@ColorInt
private int mSelectedColor;

@ColorInt
private int mErrorColor;

private final TextView mStepNumber;

private final View mStepDivider;
Expand All @@ -55,8 +61,12 @@ public class StepTab extends RelativeLayout {

private final ImageView mStepDoneIndicator;

private final AppCompatImageView mStepErrorIndicator;

private int mDividerWidth = StepperLayout.DEFAULT_TAB_DIVIDER_WIDTH;

private boolean hasError;

public StepTab(Context context) {
this(context, null);
}
Expand All @@ -71,9 +81,11 @@ public StepTab(Context context, AttributeSet attrs, int defStyleAttr) {

mSelectedColor = ContextCompat.getColor(context, R.color.ms_selectedColor);
mUnselectedColor = ContextCompat.getColor(context, R.color.ms_unselectedColor);
mErrorColor = ContextCompat.getColor(context, R.color.ms_errorColor);

mStepNumber = (TextView) findViewById(R.id.ms_stepNumber);
mStepDoneIndicator = (ImageView) findViewById(R.id.ms_stepDoneIndicator);
mStepErrorIndicator = (AppCompatImageView) findViewById(R.id.ms_stepErrorIndicator);
mStepDivider = findViewById(R.id.ms_stepDivider);
mStepTitle = ((TextView) findViewById(R.id.ms_stepTitle));
}
Expand All @@ -91,15 +103,45 @@ public void toggleDividerVisibility(boolean show) {
* @param done true if the step is done and the step's number should be replaced with a <i>done</i> icon, false otherwise
* @param current true if the step is the current step, false otherwise
*/
public void updateState(final boolean done, final boolean current) {
public void updateState(final boolean done, final boolean showErrorOnBack, final boolean current) {
//if this tab has errors and the user decide not to clear when going backwards, simply ignore the update
if(this.hasError && showErrorOnBack)
return;

mStepDoneIndicator.setVisibility(done ? View.VISIBLE : View.GONE);
mStepNumber.setVisibility(!done ? View.VISIBLE : View.GONE);
mStepErrorIndicator.setVisibility(GONE);
colorViewBackground(done ? mStepDoneIndicator : mStepNumber, done || current);

this.hasError = false;

mStepTitle.setTextColor(ContextCompat.getColor(getContext(), R.color.ms_black));
mStepTitle.setTypeface(current ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
mStepTitle.setAlpha(done || current ? OPAQUE_ALPHA : INACTIVE_STEP_TITLE_ALPHA);
}

/**
* Update the error state of this tab. If it has error, show the error drawable.
* @param hasError whether the tab has errors or not.
*/
public void updateErrorState(boolean done, boolean hasError) {
if(hasError) {
mStepDoneIndicator.setVisibility(View.GONE);
mStepNumber.setVisibility(View.GONE);
mStepErrorIndicator.setVisibility(VISIBLE);
mStepErrorIndicator.setColorFilter(mErrorColor);
mStepTitle.setTextColor(mErrorColor);
} else if(done) {
mStepDoneIndicator.setVisibility(View.VISIBLE);
mStepErrorIndicator.setVisibility(GONE);
colorViewBackground(mStepDoneIndicator, true);

mStepTitle.setTextColor(ContextCompat.getColor(getContext(), R.color.ms_black));
}

this.hasError = hasError;
}

/**
* Sets the name of the step
* @param title step name
Expand All @@ -124,6 +166,10 @@ public void setSelectedColor(int selectedColor) {
this.mSelectedColor = selectedColor;
}

public void setErrorColor(int errorColor) {
this.mErrorColor = errorColor;
}

private void colorViewBackground(View view, boolean selected) {
Drawable d = view.getBackground();
TintUtil.tintDrawable(d, selected ? mSelectedColor : mUnselectedColor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public void onTabClicked(int position) {
@ColorInt
private int mSelectedColor;

@ColorInt
private int mErrorColor;

private int mDividerWidth = StepperLayout.DEFAULT_TAB_DIVIDER_WIDTH;

private final int mContainerLateralPadding;
Expand All @@ -79,6 +82,8 @@ public void onTabClicked(int position) {

private List<CharSequence> mStepTitles;

private boolean mShowErrorStateOnBack;

public TabsContainer(Context context) {
this(context, null);
}
Expand All @@ -93,6 +98,7 @@ public TabsContainer(Context context, AttributeSet attrs, int defStyleAttr) {

mSelectedColor = ContextCompat.getColor(context, R.color.ms_selectedColor);
mUnselectedColor = ContextCompat.getColor(context, R.color.ms_unselectedColor);
mErrorColor = ContextCompat.getColor(context, R.color.ms_errorColor);
if (attrs != null) {
final TypedArray a = getContext().obtainStyledAttributes(
attrs, R.styleable.TabsContainer, defStyleAttr, 0);
Expand All @@ -104,6 +110,10 @@ public TabsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
mUnselectedColor = a.getColor(R.styleable.TabsContainer_ms_inactiveTabColor, mUnselectedColor);
}

if (a.hasValue(R.styleable.StepperLayout_ms_errorColor)) {
mErrorColor = a.getColor(R.styleable.StepperLayout_ms_errorColor, mErrorColor);
}

a.recycle();
}
mContainerLateralPadding = context.getResources().getDimensionPixelOffset(R.dimen.ms_tabs_container_lateral_padding);
Expand All @@ -120,6 +130,10 @@ public void setSelectedColor(@ColorInt int selectedColor) {
this.mSelectedColor = selectedColor;
}

public void setErrorColor(@ColorInt int mErrorColor) {
this.mErrorColor = mErrorColor;
}

public void setDividerWidth(int dividerWidth) {
this.mDividerWidth = dividerWidth;
}
Expand Down Expand Up @@ -152,20 +166,37 @@ public void setCurrentStep(int newStepPosition) {
StepTab childTab = (StepTab) mTabsInnerContainer.getChildAt(i);
boolean done = i < newStepPosition;
final boolean current = i == newStepPosition;
childTab.updateState(done, current);
childTab.updateState(done, mShowErrorStateOnBack, current);
if (current) {
mTabsScrollView.smoothScrollTo(childTab.getLeft() - mContainerLateralPadding, 0);
}
}
}

/**
* Set whether when going backwards should clear the error state from the Tab. Default is false
* @param mShowErrorStateOnBack
*/
public void setShowErrorStateOnBack(boolean mShowErrorStateOnBack) {
this.mShowErrorStateOnBack = mShowErrorStateOnBack;
}

public void setErrorStep(int stepPosition, boolean hasError){
if(mStepTitles.size() < stepPosition)
return;

StepTab childTab = (StepTab) mTabsInnerContainer.getChildAt(stepPosition);
childTab.updateErrorState(mStepTitles.size() - 1 == stepPosition ,hasError);
}

private View createStepTab(final int position, @Nullable CharSequence title) {
StepTab view = (StepTab) LayoutInflater.from(getContext()).inflate(R.layout.ms_step_tab_container, mTabsInnerContainer, false);
view.setStepNumber(String.valueOf(position + 1));
view.toggleDividerVisibility(!isLastPosition(position));
view.setStepTitle(title);
view.setSelectedColor(mSelectedColor);
view.setUnselectedColor(mUnselectedColor);
view.setErrorColor(mErrorColor);
view.setDividerWidth(mDividerWidth);

view.setOnClickListener(new View.OnClickListener() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ public AbstractStepperType(StepperLayout stepperLayout) {
*/
public abstract void onStepSelected(int newStepPosition);

/**
* Called to set whether the stepPosition has an error or not, changing it's appearance.
* @param stepPosition the step to set the error
* @param hasError whether it has error or not
*/
public void setErrorStep(int stepPosition, boolean hasError){ }

/**
* Called to set whether navigating backwards should keep the error state.
* @param mShowErrorStateOnBack
*/
public void showErrorStateOnBack(boolean mShowErrorStateOnBack){ }

/**
* Called when {@link StepperLayout}'s adapter gets changed
* @param stepAdapter new stepper adapter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public TabsStepperType(StepperLayout stepperLayout) {
mTabsContainer.setVisibility(View.VISIBLE);
mTabsContainer.setSelectedColor(stepperLayout.getSelectedColor());
mTabsContainer.setUnselectedColor(stepperLayout.getUnselectedColor());
mTabsContainer.setErrorColor(stepperLayout.getErrorColor());
mTabsContainer.setDividerWidth(stepperLayout.getTabStepDividerWidth());
mTabsContainer.setListener(stepperLayout);
}
Expand All @@ -53,6 +54,22 @@ public void onStepSelected(int newStepPosition) {
mTabsContainer.setCurrentStep(newStepPosition);
}

/**
* {@inheritDoc}
*/
@Override
public void setErrorStep(int stepPosition, boolean hasError) {
mTabsContainer.setErrorStep(stepPosition, hasError);
}

/**
* {@inheritDoc}
*/
@Override
public void showErrorStateOnBack(boolean mShowErrorStateOnBack) {
mTabsContainer.setShowErrorStateOnBack(mShowErrorStateOnBack);
}

/**
* {@inheritDoc}
*/
Expand Down
Loading