Android – Implement list view in combination with swipe view

In this article I shall present how I have implemented swipe view in combination with list that resembles  Gmail application on Android, you can check screenshots  of Gmail application here. I am about to explain details of implementing small application having as main activity one list view populated with number of items as titles and on user action as pressing one of the items in a list view, application should show details of pressed item by opening them in new view – fragment. This new view user will be able to swipe to the left and right and in that way viewing details of other items in starting list view. On pressing back button, application returns to the starting list view.

Details how to implement  swipe view you can find on developer.android.com , there is a nice article here, but we shall also go trough details of implementing swipe view maybe in more details and in addition we shall see how we can combine it with list view.

Swipe view elements we can implement using ViewPager widget that is a part of Support Library. Also to insert child pages into this ViewPager we need two use adapter, in this case Android provides two implementations of adapter for ViewPager:

FragmentPageAdapter – Used in cases when number of pages is small and/or fixed since every page is created and kept in memory

FragmentStatePageAdapter – Has a capability to destroy page when user navigates from one to another page, having this in mind adapter should be used when there is a bigger number of pages or if pages are dynamically created.

In our example we shall use FragmentStatePageAdapter.

Now lets start with developing sample application, first we define our layout of main activity (activity_main.xml) that will represent simple list view wrapped in FrameLayout


<?xml version="1.0" encoding="utf-8"?>
   <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

      <ListView android:scrollbarAlwaysDrawVerticalTrack="true"
       android:id="@+id/list_view"
       android:scrollbars="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>
   </FrameLayout>

And layout for the item in list (item_list.xml) having only text view:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical" android:layout_width="match_parent"
   android:layout_height="match_parent">
   <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/item_label"
   />
</LinearLayout>

Before we go to more details of our sample application, I shall stop here and provide details of one simple class representing our data in list and swipe view later. This class will have two variables one containing text that will be presented in the list view and another int variable that will represent id of the picture. Pictures and text will be displayed on the swipe views once user clicks an item in the list view. For this example we shall have six jpg images in drawable resource folder to be used in these swipe views.

public class PageData implements Parcelable{
   private int picture;
   private String title;

   public PageData(){
   }

   public PageData(Parcel in){
      picture = in.readInt();
      title = in.readString();
   }

   @Override
   public int describeContents() {
      return 0;
   }

   @Override
   public void writeToParcel(Parcel dest, int flags) {
      dest.writeInt(picture);
      dest.writeString(title);
   }

   public PageData(int picture, String title){
      this.picture = picture;
      this.title = title;
   }

   public int getPicture() {
      return picture;
   }

   public String getTitle() {
      return title;
   }
}

You can see this class implements android.os.Parcelable interface, this is due to the fact that we need this data to be passed to between views (fragments) using android.os.Bundle. Details about Parcelable is out of the scope of this article and will not be discussed here in details.

Having layouts defined, we will go into details of our main activity class . It is worth mentioning that main activity extends FragmentAcivity since we are using fragments in this application.

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
public class MainActivity extends FragmentActivity{

   PagerFragment pagerFragment;

   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      final ArrayList<PageData> data = new ArrayList<PageData>();
      data.add(new PageData(R.drawable.d1, "Picture 1"));
      data.add(new PageData(R.drawable.d2, "Picture 2"));
      data.add(new PageData(R.drawable.d3, "Picture 3"));
      data.add(new PageData(R.drawable.d4, "Picture 4"));
      data.add(new PageData(R.drawable.d5, "Picture 5"));
      data.add(new PageData(R.drawable.d6, "Picture 6"));

      ListView lv = (ListView) findViewById(R.id.list_view);
      ListViewAdapter lva = new ListViewAdapter(this, R.layout.list_item, data);
      lv.setAdapter(lva);
      lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            //peace of code that create launch new fragment with swipe view inside
            PagerFragment pagerFragment = new PagerFragment();
            Bundle bundle = new Bundle();
            bundle.putInt("CURRENT_POSITION", position);
            bundle.putParcelableArrayList("DATA_LIST", data);
            pagerFragment.setArguments(bundle);
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction ft = fragmentManager.beginTransaction();
            ft.replace(R.id.container, pagerFragment, "swipe_view_fragment").commit();
         }
      });
   }
}

In the onCreate method there are couple of steps that are done, first one is to set layout and create ArrayList of PageData objects that we explained earlier. After this we fetch ListView and set adapter for it (ListViewAdapter) and finally setting OnItemClickListener instance on the list view. In this listener we shall override method onItemClick that simply creates  instance of custom fragment that will contain swipe view and passing to fragment list of our PageData objects and position of clicked list item, this position will be used to initially open up correct swipe view with proper data.

Before we get to the implementation of fragment with swipe view inside it, I shall just post here ListViewAdapter code used in the onCreate method of the main activity:

public class ListViewAdapter extends ArrayAdapter<PageData> {
   protected List<PageData> data;
   protected LayoutInflater inflater;

   public ListViewAdapter(Activity activity, int textViewResourceId, List<PageData> objects){
      super(activity, textViewResourceId, objects);
      data= objects;
   }

   public View getView(final int position, View convertView, ViewGroup parent) {
      LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      View row = inflater.inflate(R.layout.list_item, parent, false);
      PageData pd = data.get(position);
      TextView tv = (TextView)row.findViewById(R.id.item_label);
      tv.setText(pd.getTitle());
      return row;
   }
}

Now it is time to define fragment that will be created once user clicks on the element of list view. Layout of this fragment is simple:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:background="#ffffff">
    <android.support.v4.view.ViewPager
     android:id="@+id/pager_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent" />
</FrameLayout>

In this layout we can see that ViewPager from support library is used and it represents our swipe view holder. Below you can see implementation of the fragment class that is going to use this layout.

public class PagerFragment extends Fragment{

   private List<PageData> data;
   private int currentPosition;

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      View v = inflater.inflate(R.layout.fragment_pager, container, false);
      ViewPager mViewPager = (ViewPager) v.findViewById(R.id.pager_view);
      currentPosition = getArguments().getInt("CURRENT_POSITION");
      data = getArguments().getParcelableArrayList("DATA_LIST");

      FragmentItemPagerAdapter fragmentItemPagerAdapter = new FragmentItemPagerAdapter(getFragmentManager(), data);
      mViewPager.setAdapter(fragmentItemPagerAdapter);
      mViewPager.setCurrentItem(currentPosition);
      return v;
   }
}

In this implementation of a fragment, inside method onCreateView we set layout for the fragment, get the ViewPager instance by id and retrieve passed data as a bundle from fragment arguments. Second part of this method represents creating pager adapter that will display out data in swipe view, here it is important to mention that we need to pass two things, one is instance of fragment manager since swiping is actually replacing fragments and data that will be represented in those fragments. Then we need to adapter for ViewPager instance and finally we set position of element in list (currenPosition – representing the element that was clicked in list view)  so that ViewPager can open up fragment with details of item that was clicked. Code of mentioned FragmentItemPagerAdapter can be seen next:

 


public class FragmentItemPagerAdapter extends FragmentStatePagerAdapter {
   private List<PageData> data;

   public FragmentItemPagerAdapter(FragmentManager fm, List<PageData> data){
      super(fm);
      this.data = data;
   }

   @Override
   public Fragment getItem(int position) {
      Fragment fragment = new PageFragment();
      Bundle args = new Bundle();
      args.putString(PageFragment.TITLE, data.get(position).getTitle());
      args.putInt(PageFragment.PICTURE, data.get(position).getPicture());
      fragment.setArguments(args);
      return fragment;
   }

   @Override
   public int getCount() {
      return data.size();
   }

   public static class PageFragment extends Fragment{
      public static final String TITLE = "title";
      public static final String PICTURE = "picture";

      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
         Bundle savedInstanceState) {
         View rootView = inflater.inflate(R.layout.fragment_item, container, false);
         ((TextView) rootView.findViewById(R.id.item_label)).setText(
             getArguments().getString(TITLE));
         ((ImageView) rootView.findViewById(R.id.image_view)).setImageResource(
             getArguments().getInt(PICTURE));

         return rootView;
      }
   }
}

In our implementation we can see that we have overriden several methods from the FragmentStatePagerAdapter class:
getItem(int position) is a method that is supposed to return fragment instance of current record, in our case we have defined PageFragment class that rep resents our fragment, layout for this fragment is given below.
getCount() – returns number of pages (fragments) that swipe view is supposed to have – size of list that we have passed to the adapter

Layout for PageFragment is simple, TextView and ImageView wrapped in vertical LinearLayout:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:padding="8dp"
   android:orientation="vertical">

   <TextView
      android:id="@+id/item_label"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerHorizontal="true"
      android:layout_marginTop="10sp"
      android:text="Image Name"
      android:textColor="@android:color/black"
      android:textSize="18sp"
      android:layout_gravity="center_horizontal" />
   <ImageView
      android:id="@+id/image_view"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/d1"
      android:layout_marginTop="50sp"
      android:layout_alignParentLeft="true"
      android:layout_alignParentStart="true"
      android:layout_gravity="center_horizontal"/>

</LinearLayout>

In order to conclude this article, I shall add one more piece of code as a part of main activity class that is handling back button (in case back button is pressed it checks if out swipe view fragment is shown on application, in case it is it just removes it so that list view is displayed, in case it is not is just calls super.onBackPressed() method):


@Override
public void onBackPressed() {
   FragmentManager fm = getSupportFragmentManager();
   Fragment f = fm.findFragmentByTag("swipe_view_fragment");
   if(f!=null){
      fm.beginTransaction().remove(f).commit();
   }else{
      super.onBackPressed();
   }
}

I hoe you have found this article interesting and helpful. Have a nice day …

Posted in Android Development Tagged with: , , ,
8 comments on “Android – Implement list view in combination with swipe view
  1. thomas says:

    very nice article

  2. Mansa says:

    Thanks for the article.This is what I am seeking for past 3 days.But it would be better that if you include headerfiles for java class and name for each layout.It works perfectly for me after some changes in code.
    .Again thank you so much for the good one.

    • Dorde Popovic says:

      Thanks, I am having in plan to put all source code on github but just need to find some time for it. If you liked the article, please share it.

  3. Milad says:

    Hi, Thanks for the article. I used this and I have a question.
    In PageFragment (extends Fragment) i have some fields that load from DB(sqlite)
    and this fragment have a button that after click,update table in db.
    I want to reload fragment to show updated data(without swiping).
    I konw that android keep states in memory, but I need to show updated fields whitohut swiping.
    I try to use setOffscreenPageLimit, notifyDataSetChanged, detach, attach and etc..but none of them work. Please help me.

    Thankyou.

  4. Karthik Saakre says:

    May I please know where is the fragment_pager.xml file here which is to be inflated in PagerFragment?

    I am getting error for fragment_pager.

    View v = inflater.inflate(R.layout.fragment_pager, container, false);

  5. Kumar says:

    Same example How to get data from db and print images place means images replace with db data and print in listview and viewpager screens please help how to solve?
    Thanks in advance..

  6. SKumar says:

    Hi, thanks for this example. how to call db images in same example please help me.
    thanks in advance. i am waiting for ur reply.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe for Newsletter

Categories