Vamos a crear un ejemplo para este tutorial en el cual nuestro activity tendrá tabs haciendo uso de fragment y este fragment utilizará un RecyclerView.
Vayamos paso por paso:
Crearemos el Activity que definirá los tab pasandole la lista de nuestro modelo a mostrar en cada tab. Después el Fragment contendrá el RecyclerView; entre ambos trabajrán para mostrar cada ítem adecuadamente y devolverán al Activity la acción sobre un item, enviando el modelo sobre el que se hizo click.
Las dependencias que necesitas en tu archivo build
Estas son las dependencias que incluyes
dependencies {
compile 'com.android.support:appcompat-v7:25.1.1'
compile 'com.android.support:design:25.1.1'
compile 'com.android.support:support-v4:25.1.1'
compile 'com.android.support:recyclerview-v7:25.1.1'
}
El modelo POJO
Este el un modelo bastante tonto pero que nos servirá bien para que crees tu app de ejemplo con tabs usando fragment. Extiende de Parcelable a fin de enviar este modelo al fragment como parámetro.
public class DummyModel implements Parcelable {
private final String id;
private final String title;
private final String detail;
....
}
El activity principal con el TabLayout
Tu Activity principal con dos elementos, el widget TabLayout que provee un layout horizontal para mostrar tabs y el ViewPager que te permitirá desplazarte entre los tabs deslizando con un gesto horizontalmente.
Tu xml debe lucir de este modo para definir el TabLayout y el ViewPager a fin de mostrar tabs.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.gustavopeiretti.tabexample.TabExampleMainActivity">
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:elevation="6dp"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_below="@id/tab_layout"/>
</RelativeLayout>
Para el TabLayout agregarás en este ejemplo tres tabs con sus respectivos títulos.
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setText(R.string.title_tab0));
//... otros tabs
Luego para el ViewPager debes agregarle un adapter que se encargará entre otras cosas de devolver el Fragment que corresponda a la posición del tab elegido.
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final PagerAdapter adapter = new PagerAdapter(...)
viewPager.setAdapter(adapter);
El Activity también implementará un Listener ItemFragment.OnListFragmentInteractionListener. Verás mas adelante que recibirá la info desde el RecyclerView cuando se haga click sobre un elemento y enviará el Modelo que se ha clickeado al Activity TabExampleMainActivity.
public void onListFragmentInteraction(DummyModel model) {
Toast.makeText(TabExampleMainActivity.this, DummyModel.class.getSimpleName() + ":" + model.getId() + " - " +model.getTitle(), Toast.LENGTH_LONG).show();
}
Puedes definir otro listener para cuando se pasa de un tab a otro
private TabLayout.OnTabSelectedListener getOnTabSelectedListener(final ViewPager viewPager) {
return new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
Toast.makeText(TabExampleMainActivity.this, "Tab selected " + tab.getPosition(), Toast.LENGTH_SHORT).show();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// nothing now
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// nothing now
}
};
}
El código completo para mostar tab en un Activity te queda así.
public class TabExampleMainActivity extends AppCompatActivity implements ItemFragment.OnListFragmentInteractionListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tab_example_activity_main);
// define TabLayout
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setText(R.string.title_tab0));
tabLayout.addTab(tabLayout.newTab().setText(R.string.title_tab1));
tabLayout.addTab(tabLayout.newTab().setText(R.string.title_tab2));
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
// define ViewPager
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
// fake list model... for test
DummyModel[] listModel0 = createDummyListModel("tab 0");
DummyModel[] listModel1 = createDummyListModel("tab 1");
DummyModel[] listModel2 = createDummyListModel("tab 2");
// ViewPager need a PagerAdapter
final PagerAdapter adapter = new PagerAdapter
(getSupportFragmentManager(), tabLayout.getTabCount(), listModel0, listModel1, listModel2 );
viewPager.setAdapter(adapter);
// Listeners
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(getOnTabSelectedListener(viewPager));
}
/**
* Listener for tab selected
* @param viewPager
* @return
*/
@NonNull
private TabLayout.OnTabSelectedListener getOnTabSelectedListener(final ViewPager viewPager) {
return new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
Toast.makeText(TabExampleMainActivity.this, "Tab selected " + tab.getPosition(), Toast.LENGTH_SHORT).show();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// nothing now
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// nothing now
}
};
}
/**
* Listener that comunicate fragment / recycler with this activity
* @param model
*/
@Override
public void onListFragmentInteraction(DummyModel model) {
// the user clicked on this item over the list
Toast.makeText(TabExampleMainActivity.this, DummyModel.class.getSimpleName() + ":" + model.getId() + " - " +model.getTitle(), Toast.LENGTH_LONG).show();
}
// model for test purpose
private DummyModel[] createDummyListModel(String msj) {
List<DummyModel> l = new ArrayList<>();
for(int i = 0; i < 20; i++ ) {
l.add(new DummyModel(String.valueOf(i), "Title " + i , "Descrip. " + i + " -" + msj));
}
return l.toArray(new DummyModel[l.size()]);
}
}
Creas el PagerAdapter
El PagerAdapter debe extender de FragmentStatePagerAdapter. Observa como recibe la info de nuestro modelo para cada tab y como devuelve un fragment para cada posición del tab según su selección.
public class PagerAdapter extends FragmentStatePagerAdapter {
private int numTabs;
private DummyModel[] dummyModels0;
private DummyModel[] dummyModels1;
private DummyModel[] dummyModels2;
public PagerAdapter(FragmentManager fm, int numTabs, DummyModel[] dummyModels0,
DummyModel[] dummyModels1, DummyModel[] dummyModels2) {
super(fm);
this.numTabs = numTabs;
this.dummyModels0 = dummyModels0;
this.dummyModels1 = dummyModels1;
this.dummyModels2 = dummyModels2;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
ItemFragment tab1 = ItemFragment.newInstance(dummyModels0);
return tab1;
case 1:
ItemFragment tab2 = ItemFragment.newInstance(dummyModels1);
return tab2;
case 2:
ItemFragment tab3 = ItemFragment.newInstance(dummyModels2);
return tab3;
default:
throw new RuntimeException("Tab position invalid " + position);
}
}
@Override
public int getCount() {
return numTabs;
}
}
El fragment que mostrará la información de nuestro modelo
El fragment lo creas con los datos del model que deseas mostrar y harás uso de un RecyclerView a fin de displayarlos luego.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem" />
<TextView
android:id="@+id/detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem" />
</LinearLayout>
Como crear RecyclerView
El fragment utilizará un RecyclerView
<android.support.v7.widget.RecyclerView 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:id="@+id/dummyModels"
android:name="com.gustavopeiretti.tabexample.ItemFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context="com.gustavopeiretti.tabexample.ItemFragment"
tools:listitem="@layout/fragment_item" />
Recuerda que antes creaste el PagerAdapter que recibe este array y luego crea las instancias de ItemFragment para cada tab pasandole el respectivo array del modelo a mostrar.
Tu clase ItemFragment recibirá como argumento el array del modelo que deseas mostrar en este fragment y lo dejará como parámetro para cuando se cree el Fragment efectivamente en onCreate().
public static ItemFragment newInstance(DummyModel[] dummyModels) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
args.putParcelableArray(KEY_MODEL, dummyModels);
fragment.setArguments(args);
return fragment;
}
onCreate() se ejecuta cuando efectivamente se crea el fragment y allí obtendremos la lista que dejamos previamente en el Bundle.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() == null) {
throw new RuntimeException("You must to send a dummyModels ");
}
dummyModels = (DummyModel[]) getArguments().getParcelableArray(KEY_MODEL);
}
onCreateView() se ejecuta cuando se “pinta” la interface, allí debes crear el RecyclerView que usarás definiendo el Adapter y le pasaremos el listener que al final recibirá la acción click sobre el item y la comunicará al Activitity TabExampleMainActivity
Te recomiendo leer luego este otro post a fin de comprender mejor RecyclerView
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item_list, container, false);
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(new ItemRecyclerViewAdapter(dummyModels, interactionListener));
return view;
}
Observa que las Activities (en este ejemplo ‘TabExampleMainActivity’) que usen este Fragment están obligadas a implementar OnListFragmentInteractionListener . Esto es a fin de poder enviarle luego a la Activity el ítem que fue elegido al hacer click. Por lo que TabExampleMainActivity debe implementar este Listener.
Así queda tu clase ItemFragment completa
public class ItemFragment extends Fragment {
private static final String KEY_MODEL = "KEY_MODEL";
private DummyModel[] dummyModels;
private OnListFragmentInteractionListener interactionListener;
public ItemFragment() {
}
/**
* Receive the model list
*/
public static ItemFragment newInstance(DummyModel[] dummyModels) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
args.putParcelableArray(KEY_MODEL, dummyModels);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() == null) {
throw new RuntimeException("You must to send a dummyModels ");
}
dummyModels = (DummyModel[]) getArguments().getParcelableArray(KEY_MODEL);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item_list, container, false);
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(new ItemRecyclerViewAdapter(dummyModels, interactionListener));
return view;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
// activity must implement OnListFragmentInteractionListener
if (context instanceof OnListFragmentInteractionListener) {
interactionListener = (OnListFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnListFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
interactionListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* <p/>
*/
public interface OnListFragmentInteractionListener {
void onListFragmentInteraction(DummyModel item);
}
}
El RecyclerView.Adapter
Crea esta clase ItemRecyclerViewAdapter que extienda de RecyclerView.Adapter la que será al final la encargada de setear los valores que recibe y de informar haciendo uso del listener ItemFragment.OnListFragmentInteractionListener cuando se haga click sobre un item.
public class ItemRecyclerViewAdapter extends RecyclerView.Adapter<ItemRecyclerViewAdapter.ViewHolder> {
private final DummyModel[] dummyModels;
private final ItemFragment.OnListFragmentInteractionListener interactionListener;
public ItemRecyclerViewAdapter(DummyModel[] items, ItemFragment.OnListFragmentInteractionListener listener) {
dummyModels = items;
interactionListener = listener;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
DummyModel dm = dummyModels[position];
holder.dummyModelItem = dm;
holder.idView.setText(dm.getId());
holder.titleView.setText(dm.getTitle());
holder.detailView.setText(dm.getDetail());
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != interactionListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
interactionListener.onListFragmentInteraction(holder.dummyModelItem);
}
}
});
}
@Override
public int getItemCount() {
return dummyModels.length;
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final TextView idView;
public final TextView titleView;
public final TextView detailView;
public DummyModel dummyModelItem;
public ViewHolder(View view) {
super(view);
mView = view;
idView = (TextView) view.findViewById(R.id.id);
titleView = (TextView) view.findViewById(R.id.title);
detailView = (TextView) view.findViewById(R.id.detail);
}
}
}