如何在android中创建附加设计

如何在android中创建附加设计,android,android-layout,Android,Android Layout,我想用xml android创建这个设计。请告诉我我能做什么 使用框架布局和线性布局支持 <FrameLayout height="match_parent" width="match_parent" > <ImageView height="match_parent" width="wrap_content" android:src="vertical line"/> <LinearLayout android:orientation="vert

我想用xml android创建这个设计。请告诉我我能做什么


使用框架布局和线性布局支持

    <FrameLayout
height="match_parent"
width="match_parent"
    >
<ImageView
height="match_parent"
width="wrap_content"
android:src="vertical line"/>

<LinearLayout
android:orientation="vertical"
android:weightSum="3">

<LinearLayout
android:orientation="horizontal">

Make circle and text here
</LinearLayout>

</LinearLayout>

</FrameLayout>

在这里画圆圈和文字

希望它能帮助您。

使用framelayout和linearlayout支持

    <FrameLayout
height="match_parent"
width="match_parent"
    >
<ImageView
height="match_parent"
width="wrap_content"
android:src="vertical line"/>

<LinearLayout
android:orientation="vertical"
android:weightSum="3">

<LinearLayout
android:orientation="horizontal">

Make circle and text here
</LinearLayout>

</LinearLayout>

</FrameLayout>

在这里画圆圈和文字
希望它能对您有所帮助。

试试这个例子

您的
MainActivity.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
framentt
BlankFragment.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
Xml文件
activity\u main.Xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:fitsSystemWindows="true"
    tools:context="me.drozdzynski.library.sample.steppers.MainActivity">

    <android.support.design.widget.AppBarLayout android:layout_width="match_parent"
        android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
            android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    tools:context="me.drozdzynski.library.sample.steppers.BlankFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_blank_fragment" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button" />

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="me.drozdzynski.library.sample.steppers.MainActivity"
    tools:showIn="@layout/activity_main">

    <me.drozdzynski.library.steppers.SteppersView
        android:id="@+id/steppersView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>
Xml
fragment\u blank.Xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:fitsSystemWindows="true"
    tools:context="me.drozdzynski.library.sample.steppers.MainActivity">

    <android.support.design.widget.AppBarLayout android:layout_width="match_parent"
        android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
            android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    tools:context="me.drozdzynski.library.sample.steppers.BlankFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_blank_fragment" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button" />

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="me.drozdzynski.library.sample.steppers.MainActivity"
    tools:showIn="@layout/activity_main">

    <me.drozdzynski.library.steppers.SteppersView
        android:id="@+id/steppersView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>
已添加或使用库文件

stepperViewHolder.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
SteppersView.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
SteppersAdapter.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
OnFinishAction.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
Class
OnCancelAction.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
Class
AnimationUtils.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
试试这个例子

您的
MainActivity.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
framentt
BlankFragment.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
Xml文件
activity\u main.Xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:fitsSystemWindows="true"
    tools:context="me.drozdzynski.library.sample.steppers.MainActivity">

    <android.support.design.widget.AppBarLayout android:layout_width="match_parent"
        android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
            android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    tools:context="me.drozdzynski.library.sample.steppers.BlankFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_blank_fragment" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button" />

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="me.drozdzynski.library.sample.steppers.MainActivity"
    tools:showIn="@layout/activity_main">

    <me.drozdzynski.library.steppers.SteppersView
        android:id="@+id/steppersView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>
Xml
fragment\u blank.Xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:fitsSystemWindows="true"
    tools:context="me.drozdzynski.library.sample.steppers.MainActivity">

    <android.support.design.widget.AppBarLayout android:layout_width="match_parent"
        android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
            android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    tools:context="me.drozdzynski.library.sample.steppers.BlankFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_blank_fragment" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button" />

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="me.drozdzynski.library.sample.steppers.MainActivity"
    tools:showIn="@layout/activity_main">

    <me.drozdzynski.library.steppers.SteppersView
        android:id="@+id/steppersView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>
已添加或使用库文件

stepperViewHolder.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
SteppersView.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
SteppersAdapter.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
OnFinishAction.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
Class
OnCancelAction.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}
Class
AnimationUtils.java

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.ArrayList;

import me.drozdzynski.library.steppers.OnCancelAction;
import me.drozdzynski.library.steppers.OnFinishAction;
import me.drozdzynski.library.steppers.SteppersItem;
import me.drozdzynski.library.steppers.SteppersView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        SteppersView.Config steppersViewConfig = new SteppersView.Config();
        steppersViewConfig.setOnFinishAction(new OnFinishAction() {
            @Override
            public void onFinish() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setOnCancelAction(new OnCancelAction() {
            @Override
            public void onCancel() {
                MainActivity.this.startActivity(new Intent(MainActivity.this, MainActivity.class));
                MainActivity.this.finish();
            }
        });

        steppersViewConfig.setFragmentManager(getSupportFragmentManager());
        ArrayList<SteppersItem> steps = new ArrayList<>();

        int i = 0;
        while (i <= 10) {

            final SteppersItem item = new SteppersItem();
            item.setLabel("Step nr " + i);
            item.setPositiveButtonEnable(i % 2 != 0);

            if(i % 2 == 0) {
                BlankFragment blankFragment = new BlankFragment();
                blankFragment.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        item.setPositiveButtonEnable(true);
                    }
                });
                item.setSubLabel("Fragment: " + blankFragment.getClass().getSimpleName());
                item.setFragment(blankFragment);
            } else {
                BlankSecondFragment blankSecondFragment = new BlankSecondFragment();
                item.setSubLabel("Fragment: " + blankSecondFragment.getClass().getSimpleName());
                item.setFragment(blankSecondFragment);
            }

            steps.add(item);
            i++;
        }

        SteppersView steppersView = (SteppersView) findViewById(R.id.steppersView);
        steppersView.setConfig(steppersViewConfig);
        steppersView.setItems(steps);
        steppersView.build();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankSecondFragment extends Fragment {


    public BlankSecondFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank_second, container, false);
    }
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class BlankFragment extends Fragment {

    private Button.OnClickListener onClickListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
        return view;
    }

    public void setOnClickListener(Button.OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}


}
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SteppersViewHolder extends RecyclerView.ViewHolder {

    private boolean isChecked;

    protected View itemView;
    protected RoundedView roundedView;
    protected TextView textViewLabel;
    protected TextView textViewSubLabel;
    protected LinearLayout linearLayoutContent;
    protected FrameLayout frameLayout;
    protected LinearLayout frameLayoutsContainer;
    protected Button buttonContinue;
    protected Button buttonCancel;
    protected Fragment fragment;

    public SteppersViewHolder(View itemView) {
        super(itemView);
        this.itemView = itemView;
        this.roundedView = (RoundedView) itemView.findViewById(R.id.roundedView);
        this.textViewLabel = (TextView) itemView.findViewById(R.id.textViewLabel);
        this.textViewSubLabel = (TextView) itemView.findViewById(R.id.textViewSubLabel);
        this.linearLayoutContent = (LinearLayout) itemView.findViewById(R.id.linearLayoutContent);
        this.frameLayout = (FrameLayout) itemView.findViewById(R.id.frameLayout);
        //this.frameLayoutsContainer = (LinearLayout) itemView.findViewById(R.id.frameLayoutsContainer);
        this.buttonContinue = (Button) itemView.findViewById(R.id.buttonContinue);
        this.buttonCancel = (Button) itemView.findViewById(R.id.buttonCancel);
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public Fragment getFragment() {
        return fragment;
    }


    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }
}   
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import java.util.List;

public class SteppersView extends LinearLayout {

    private RecyclerView recyclerView;
    private SteppersAdapter steppersAdapter;

    private Config config;
    private List<SteppersItem> items;

    public SteppersView(Context context) {
        super(context);
    }

    public SteppersView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SteppersView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public SteppersView setConfig(Config config) {
        this.config = config;
        return this;
    }

    public SteppersView setItems(List<SteppersItem> items) {
        this.items = items;
        return this;
    }

    /*public void setPositiveButtonEnable(int position, boolean enable) {
        this.items.get(position).setPositiveButtonEnable(enable);

    }*/

    public void build() {
        if(config != null) {
            setOrientation(LinearLayout.HORIZONTAL);

            //LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //inflater.inflate(R.layout.steppers_recycle, this, true);
            //recyclerView = (RecyclerView) getChildAt(0);

            recyclerView = new RecyclerView(getContext());
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            recyclerView.setLayoutParams(layoutParams);

            addView(recyclerView);

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

            steppersAdapter = new SteppersAdapter(this, config, items);
            //steppersAdapter.setPossitiveButtonEnable(possitiveButtonEnable);

            recyclerView.setAdapter(steppersAdapter);

        } else {
            throw new RuntimeException("SteppersView need config, read documentation to get more info");
        }
    }

    private boolean possitiveButtonEnable = true;

    public static class Config {

        private OnFinishAction onFinishAction;
        private OnCancelAction onCancelAction;
        private FragmentManager fragmentManager;

        public Config() {

        }

        public Config setOnFinishAction(OnFinishAction onFinishAction) {
            this.onFinishAction = onFinishAction;
            return this;
        }

        public OnFinishAction getOnFinishAction() {
            return onFinishAction;
        }

        public Config setOnCancelAction(OnCancelAction onCancelAction) {
            this.onCancelAction = onCancelAction;
            return this;
        }

        public OnCancelAction getOnCancelAction() {
            return onCancelAction;
        }

        public void setFragmentManager(FragmentManager fragmentManager) {
            this.fragmentManager = fragmentManager;
        }

        public FragmentManager getFragmentManager() {
            return fragmentManager;
        }
    }

    static int fID = 190980;
    protected static int findUnusedId(View view) {
        while( view.getRootView().findViewById(++fID) != null );
        return fID;
    }

}
import android.support.v4.app.Fragment;

import java.util.Observable;

public class SteppersItem extends Observable {

    private String label;
    private String subLabel;
    private boolean buttonEnable = true;
    private Fragment fragment;

    private boolean displayed = false;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String getSubLabel() {
        return subLabel;
    }

    public void setSubLabel(String subLabel) {
        this.subLabel = subLabel;
    }

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    public boolean isPositiveButtonEnable() {
        return buttonEnable;
    }

    public void setPositiveButtonEnable(boolean buttonEnable) {
        synchronized (this) {
            this.buttonEnable = buttonEnable;
        }
        setChanged();
        notifyObservers();
    }

    protected synchronized boolean isDisplayed() {
        return displayed;
    }

    protected void setDisplayed(boolean displayed) {
        this.displayed = displayed;
    }
}
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class SteppersAdapter extends RecyclerView.Adapter<SteppersViewHolder> {

    private static final String TAG = "SteppersAdapter";
    private SteppersView steppersView;
    private Context context;
    private SteppersView.Config config;
    private List<SteppersItem> items;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;

    private Map<Integer, Integer> frameLayoutIds = new HashMap<>();

    private int VIEW_COLLAPSED = 0;
    private int VIEW_EXPANDED = 1;

    private int removeStep = -1;
    private int beforeStep = -1;
    private int currentStep = 0;

    public SteppersAdapter(SteppersView steppersView, SteppersView.Config config, List<SteppersItem> items) {
        this.steppersView = steppersView;
        this.context = steppersView.getContext();
        this.config = config;
        this.items = items;
        this.fragmentManager = config.getFragmentManager();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == currentStep ? VIEW_EXPANDED : VIEW_COLLAPSED);
    }

    @Override
    public SteppersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = null;
        if (viewType == VIEW_COLLAPSED) {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item, parent, false);
        } else {
            v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.steppers_item_expanded, parent, false);
        }

        SteppersViewHolder vh = new SteppersViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(final SteppersViewHolder holder, final int position) {
        final SteppersItem steppersItem = items.get(position);

        holder.setChecked(position < currentStep);
        if(holder.isChecked()) {
            holder.roundedView.setChecked(true);
        } else {
            holder.roundedView.setChecked(false);
            holder.roundedView.setText(position + 1 + "");
        }

        if(position == currentStep || holder.isChecked()) holder.roundedView.setCircleAccentColor();
        else holder.roundedView.setCircleGrayColor();

        holder.textViewLabel.setText(steppersItem.getLabel());
        holder.textViewSubLabel.setText(steppersItem.getSubLabel());

        holder.linearLayoutContent.setVisibility(position == currentStep || position == beforeStep ? View.VISIBLE : View.GONE);

        holder.buttonContinue.setEnabled(steppersItem.isPositiveButtonEnable());
        steppersItem.addObserver(new Observer() {
            @Override
            public void update(Observable observable, Object data) {
                if(observable != null) {
                    SteppersItem item = (SteppersItem) observable;
                    holder.buttonContinue.setEnabled(item.isPositiveButtonEnable());
                }
            }
        });

        if (position == getItemCount() - 1) holder.buttonContinue.setText(context.getResources().getString(R.string.step_finish));
        else holder.buttonContinue.setText(context.getResources().getString(R.string.step_continue));

        holder.buttonContinue.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position == getItemCount() - 1) config.getOnFinishAction().onFinish();
                else nextStep();
            }
        });

        if(config.getOnCancelAction() != null)
            holder.buttonCancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    config.getOnCancelAction().onCancel();
                }
            });


        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //FrameLayout frameLayout = (FrameLayout) inflater.inflate(R.layout.frame_layout, holder.frameLayout, true);

        if(frameLayoutIds.get(position) == null) frameLayoutIds.put(position, findUnusedId(holder.itemView));

        //frameLayout.setId(frameLayoutIds.get(position));

        if(config.getFragmentManager() != null && steppersItem.getFragment() != null) {
            holder.frameLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
            holder.frameLayout.setTag(frameLayoutName());

            if (fragmentTransaction == null) {
                fragmentTransaction = fragmentManager.beginTransaction();
            }

            String name = makeFragmentName(steppersView.getId(), position);
            Fragment fragment = fragmentManager.findFragmentByTag(name);

            if(position < beforeStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + fragment);
                    fragmentTransaction.detach(fragment);
                }
            } else if(position == beforeStep || position == currentStep) {
                if (fragment != null) {
                    if(BuildConfig.DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment + " d=" + fragment.isDetached());
                    fragmentTransaction.attach(fragment);
                } else {
                    fragment = steppersItem.getFragment();
                    if(BuildConfig.DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " n=" + name);
                    fragmentTransaction.add(steppersView.getId(), fragment,
                            name);

                }
            }

            if (fragmentTransaction != null) {
                fragmentTransaction.commitAllowingStateLoss();
                fragmentTransaction = null;
                fragmentManager.executePendingTransactions();
            }

            if(fragmentManager.findFragmentByTag(name) != null &&
                    fragmentManager.findFragmentByTag(name).getView() != null) {

                View fragmentView = fragmentManager.findFragmentByTag(name).getView();

                if(fragmentView.getParent() != null && frameLayoutName() != ((View) fragmentView.getParent()).getTag()) {
                    steppersView.removeViewInLayout(fragmentView);

                    holder.frameLayout.removeAllViews();
                    holder.frameLayout.addView(fragmentView);
                }
            }
        }

        if(beforeStep == position) {
            AnimationUtils.hide(holder.linearLayoutContent);
        }
        if(currentStep == position && !steppersItem.isDisplayed()) {
            steppersItem.setDisplayed(true);
        }
    }

    private void nextStep() {
        this.removeStep = currentStep - 1 > -1 ? currentStep - 1 : currentStep;
        this.beforeStep = currentStep;
        this.currentStep = this.currentStep + 1;
        notifyItemRangeChanged(removeStep, currentStep);
    }

    protected void setItems(List<SteppersItem> items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    private int fID = 87352142;

    public int findUnusedId(View view) {
        while( view.findViewById(++fID) != null );
        return fID;
    }

    private static String frameLayoutName() {
        return "android:steppers:framelayout";
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:steppers:" + viewId + ":" + id;
    }
}
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.lang.reflect.Field;

public class RoundedView extends View {

    public RoundedView(Context context) {
        super(context);
    }

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    Paint paint = new Paint(Paint.DITHER_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        //if (getBackgroundColor(this) != 0) color = getBackgroundColor(this);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);

        paint.setColor(color);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2,
                getWidth() / 2, paint);

        if(text != null && !checked) drawText(canvas);
        if(checked && text == null) drawChecked(canvas);
    }

    private int color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
    private String text = null;
    private boolean checked = false;

    public void setCircleColor(int color) {
        this.color = color;
        invalidate();
    }

    public void setCircleAccentColor(){
        final TypedValue value = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.colorAccent, value, true);
        if(value != null) color = value.data;
        else ContextCompat.getColor(getContext(), R.color.circle_color_default_blue);
        invalidate();
    }

    public void setCircleGrayColor(){
        color = ContextCompat.getColor(getContext(), R.color.circle_color_default_gray);
        invalidate();
    }

    public void setText(String text) {
        this.text = text;
        this.checked = false;
        invalidate();
    }

    public void setChecked(boolean checked){
        this.checked = checked;
        text = null;
        invalidate();
    }

    private int getBackgroundColor(View view) {
        ColorDrawable drawable = (ColorDrawable) view.getBackground();
        if(drawable != null) {
            if (Build.VERSION.SDK_INT >= 11) {
                return drawable.getColor();
            }
            try {
                Field field = drawable.getClass().getDeclaredField("mState");
                field.setAccessible(true);
                Object object = field.get(drawable);
                field = object.getClass().getDeclaredField("mUseColor");
                field.setAccessible(true);
                return field.getInt(object);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return 0;
    }

    private void drawText(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(getResources().getDimension(R.dimen.item_circle_text_size));

        Rect areaRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());

        RectF bounds = new RectF(areaRect);

        bounds.right = paint.measureText(text, 0, text.length());

        bounds.bottom = paint.descent() - paint.ascent();

        bounds.left += (areaRect.width() - bounds.right) / 2.0f;
        bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

        paint.setColor(Color.WHITE);
        canvas.drawText(text, bounds.left, bounds.top - paint.ascent(), paint);
    }

    private void drawChecked(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_check);

        float posX = (canvas.getWidth()  - bitmap.getWidth()) / 2;

        float posY = (canvas.getHeight() - bitmap.getHeight()) / 2;

        canvas.drawBitmap(bitmap, posX, posY, paint);
    }

}
package me.drozdzynski.library.steppers;

public interface OnFinishAction {

    public void onFinish();

}
package me.drozdzynski.library.steppers;

public interface OnCancelAction {

    public void onCancel();

}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;

public class AnimationUtils {

    protected static void hide(final View view){
        Animation fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setDuration(500);

        Animation collapse = new ExpandCollapse(view, view.getHeight(), 0);
        collapse.setStartOffset(500);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(fadeOut);
        animation.addAnimation(collapse);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static void show(final View view){
        Animation fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setStartOffset(500);
        fadeIn.setDuration(500);

        Animation collapse = new ScaleAnimation(1, 1, 0, 1);
        collapse.setDuration(500);

        AnimationSet animation = new AnimationSet(false);
        animation.setInterpolator(new AccelerateInterpolator());
        animation.addAnimation(collapse);
        animation.addAnimation(fadeIn);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(animation);
    }

    protected static class ExpandCollapse extends Animation {
        private View mView;
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandCollapse(View view, int startHeight, int endHeight) {
            mView = view;
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
            Log.d("Expand", startHeight + ", " + endHeight + " : " + mDeltaHeight);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mView.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mView.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    }

}

我会在我的活动中使用此图像。如果它不是动态的。它是动态的,1,2,3也是动态的和文本的。我会在我的活动中使用这个图像。如果它不是动态的。它是动态的,1,2,3也是动态的,并且是文本。但是圆之间的线呢?线是框架布局的背景…或者你可以添加一个高度匹配的imageview,并将线指定给它。你可以编辑答案并用线给出适当的xml吗。您的圆圈将与线条重叠。Thanx我将尝试此操作,但圆圈之间的线条如何?线条是框架布局的背景…或者您可以添加一个具有高度匹配父对象的图像视图,并将线条指定给该视图。您可以编辑答案并使用线条提供适当的xml吗?只需将线条高度与父对象匹配即可。你的圆圈将与直线重叠。Thanx我会试试这个