Bogdan Daniel
Bogdan Daniel

Reputation: 2749

How can I use a cursorLoader in a ViewPager?

I have a `cursorLoader` which is working fine.

The problem is I'm not using it like I'm supposed to, I load the data from the cursorLoader into arrayLists and then use the lists.

I found this tutorial which shows how to use a cursorLoader with a viewPager but I don't understand how to actually make it happen.

http://tumble.mlcastle.net/post/25875136857/bridging-cursorloaders-and-viewpagers-on-android

I have a fragment which looks like this:

public class FirstFragment extends Fragment {
    MapView mapView;
    GoogleMap map;
    // Store instance variables
    private String email,about,imagepath,latitude,longitude;
    Button getDirections;

    // newInstance constructor for creating fragment with arguments
    public static FirstFragment newInstance(String email,String about,String imagepath, String latitude, String longitude) {
        FirstFragment fragmentFirst = new FirstFragment();
        Bundle args = new Bundle();
        args.putString("email", email);
        args.putString("about", about);
        args.putString("imagepath", imagepath);
        args.putString("latitude", latitude);
        args.putString("longitude", longitude);
        fragmentFirst.setArguments(args);
        return fragmentFirst;
    }

    // Store instance variables based on arguments passed
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        email = getArguments().getString("email");
        about = getArguments().getString("about");
        imagepath = getArguments().getString("imagepath");
        latitude = getArguments().getString("latitude");
        longitude = getArguments().getString("longitude");
    }

    // Inflate the view for the fragment based on layout XML
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             final Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.zzfragment_pager_items, container, false);
        ImageView imageView = (ImageView) view.findViewById(R.id.listpager_imageView);
        TextView about = (TextView) view.findViewById(R.id.listpager_text);
        TextView emaill = (TextView) view.findViewById(R.id.listpager_title);
        about.setText(this.about);
        emaill.setText(this.email);

        ImageLoader imageLoader = ImageLoader.getInstance();
        DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true)
                .cacheOnDisc(true).resetViewBeforeLoading(true)
                .considerExifParams(true)
                .build();
        imageLoader.getInstance().displayImage(imagepath, imageView, options);

        getDirections = (Button) view.findViewById(R.id.getdirections);

        getDirections.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String strUri = "http://maps.google.com/maps?q=loc:" + latitude + "," + longitude + " (" + email + ")";
                Intent mapIntent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(strUri));

                mapIntent.setClassName("com.google.android.apps.maps", "com.google.android.maps.MapsActivity");

                getActivity().startActivity(mapIntent);

            }
        });

//        View v = inflater.inflate(R.layout.listviewtopager, container, false);
        // Gets the MapView from the XML layout and creates it
        mapView = (MapView) view.findViewById(R.id.mapview);
        mapView.onCreate(savedInstanceState);

        // Gets to GoogleMap from the MapView and does initialization stuff
        map = mapView.getMap();
        map.getUiSettings().setMyLocationButtonEnabled(false);
        map.setMyLocationEnabled(true);

        // Needs to call MapsInitializer before doing any CameraUpdateFactory calls
        MapsInitializer.initialize(this.getActivity());

        // Updates the location and zoom of the MapView
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude)), 10);
        map.animateCamera(cameraUpdate);

        return view;
    }

    @Override
    public void onResume() {
        mapView.onResume();
        super.onResume();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }
}

And I was calling it in this class:

public class ViewPagerFragment extends FragmentActivity implements
        LoaderManager.LoaderCallbacks<Cursor>{

    ArrayList<String> e

mail = new ArrayList<String>();
ArrayList<String> about = new ArrayList<String>();

ArrayList<String> imagepath = new ArrayList<String>();

ArrayList<String> latitude = new ArrayList<String>();

ArrayList<String> longitude = new ArrayList<String>();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_view_pager_fragment);
    getLoaderManager().initLoader(1, null, this);
}

private SmartFragmentStatePagerAdapter adapterViewPager;

// Extend from SmartFragmentStatePagerAdapter now instead for more dynamic ViewPager items
public static class MyPagerAdapter extends SmartFragmentStatePagerAdapter {
    private final ArrayList<String> email;
    private final ArrayList<String> about;
    private final ArrayList<String> imagepath;
    private final ArrayList<String> latitude;
    private final ArrayList<String> longitude;
    private int listPosition;

    public MyPagerAdapter(FragmentManager fragmentManager,ArrayList<String> email,ArrayList<String> about, ArrayList<String> imagepath,ArrayList<String> latitude,ArrayList<String> longitude,int lPosition) {
        super(fragmentManager);
        this.imagepath=imagepath;
        this.email=email;
        this.about = about;
        this.latitude= latitude;
        this.longitude = longitude;
        listPosition = lPosition;
    }

// Returns total number of pages
@Override
public int getCount() {
    return email.size();
}



 // Returns the fragment to display for that page
    @Override
        public Fragment getItem(int position) {
//            return FirstFragment.newInstance(listPosition+position, email.get(listPosition+position));
            return FirstFragment.newInstance(email.get(position), about.get(position),imagepath.get(position),latitude.get(position),longitude.get(position));
    }

    // Returns the page title for the top indicator
    @Override
    public CharSequence getPageTitle(int position) {
        return "Page " + position;
    }

}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    String[] projection =
            {
                    ActivitiesTable.KEY_EMAIL,
                    ActivitiesTable.KEY_ABOUT,
                    ActivitiesTable.KEY_IMAGEPATH,
                    ActivitiesTable.KEY_LATITUDE,
                    ActivitiesTable.KEY_LONGITUTDE

            };
    CursorLoader cursorLoader = new CursorLoader(this, NameContentProvider.NAME_ACTIVITIES_URI, projection,
            null, null, null);
    return cursorLoader;
}

@Override
public void onLoadFinished(android.content.Loader<Cursor> loader, Cursor cursor) {
    if (cursor != null && cursor.getCount() > 0) {
        cursor.moveToFirst();
        do {
            email.add(cursor.getString(cursor
                    .getColumnIndexOrThrow("email")));
            about.add(cursor.getString(cursor
                    .getColumnIndexOrThrow("about")));
            imagepath.add(cursor.getString(cursor
                    .getColumnIndexOrThrow("imagepath")));
            latitude.add(cursor.getString(cursor
                    .getColumnIndexOrThrow("serverLatitude")));
            longitude.add(cursor.getString(cursor
                    .getColumnIndexOrThrow("serverLongitude")));
        } while (cursor.moveToNext());
    }

    int listPosition = getIntent().getExtras().getInt("position");


    ViewPager vpPager = (ViewPager) findViewById(R.id.vpPager);
    adapterViewPager = new MyPagerAdapter(getSupportFragmentManager(),email,about,imagepath,latitude,longitude,listPosition);
    vpPager.setAdapter(adapterViewPager);
    vpPager.setCurrentItem(listPosition);

}

@Override
public void onLoaderReset(android.content.Loader<Cursor> loader) {



   }
}

How can I modify the code to use the viewPager with the loader directly by using that tutorial, instead of storing everything in lists and then using the lists ?

Upvotes: 8

Views: 1811

Answers (4)

BladeCoder
BladeCoder

Reputation: 12929

If you're looking for a good simple example of how to populate a ViewPager with Fragments from a Cursor, look at the code of this Activity I wrote for the FOSDEM Companion app.

Note: you should avoid putting a map inside a ViewPager because touch events will be intercepted by the MapView and the user will not be able to swipe to the next or previous page.

Upvotes: 0

Blackbelt
Blackbelt

Reputation: 157437

The first I would do, if I were in you is to create a model class, which will hold the information contained in your cursor.

public class Information implements Parcelable {

   public String imagepath;
   public String email;
   public String about;
   public String latitude;
   public String longitude;


protected Information(Parcel in) {
    imagepath = in.readString();
    email = in.readString();
    about = in.readString();
    latitude = in.readString();
    longitude = in.readString();
}

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

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(imagepath);
    dest.writeString(email);
    dest.writeString(about);
    dest.writeString(latitude);
    dest.writeString(longitude);
}

@SuppressWarnings("unused")
public static final Parcelable.Creator<Information> CREATOR = new Parcelable.Creator<Information>() {
    @Override
    public Information createFromParcel(Parcel in) {
        return new Information(in);
    }

    @Override
    public Information[] newArray(int size) {
        return new Information[size];
    }
};
} 

And that's already most of it. The second thing that I would do, is to create two helper methods, one to create an instance of this class, reading the data from the Cursor, and another to create a collection.

 public static Information createInfoFromCursor(Cursor c) {
         Information info = new Information();
         info.email = cursor.getString(cursor
                    .getColumnIndexOrThrow("email")));
         info.about = cursor.getString(cursor
                    .getColumnIndexOrThrow("about")));
         info.imagepath =(cursor.getString(cursor
                    .getColumnIndexOrThrow("imagepath")));
         info.latitude = (cursor.getString(cursor
                    .getColumnIndexOrThrow("serverLatitude")));
         info.longitude = (cursor.getString(cursor
                    .getColumnIndexOrThrow("serverLongitude")));
         return info;
  } 

and

 public static ArrayList<Information> createInfoListFromCursor(Cursor c) {
    ArrayList<Information> info = new ArrayList<>();
    while(c.moveToNext()) {
        info.add(createInfoFromCursor());
    }
    return info;
 }

now you can decide to provide the Cursor, or the ArrayList<Information>, to the adapter. If you decide to pass the Cursor, In your adapter you will have

Cursor cursor;

 public MyPagerAdapter(FragmentManager fragmentManager,Cursor c) {
    super(fragmentManager);
    cursor = c;
}


@Override
public Fragment getItem(int position) {
   cursor.moveAtPosition(position);
   Information info = createInfoFromCursor(cursor);
   // create a version of `newInstance` that takes an Information object
   return FirstFragment.newInstance(info);
}

@Override
public int getCount() {
  return cursor == null ? 0 : cursor.getCount();
}      

public Cursor swapCursor(Cursor newCursor) {
    if (newCursor == cursor) {
        return null;
    }
    cursor = newCursor;
    if (newCursor != null) {
        notifyDataSetChanged();
    } 
    return oldCursor;
}

in your Activity, create an instance of the Adapter, passing a null cursor, and keep it as member classe. When onLoadFinished is called, use that reference to call swapCursor, to refresh the Adapter's dataset

Upvotes: 1

ciprian
ciprian

Reputation: 151

Instead of keeping 5 arrays of data just keep the cursor in the view pager adapter and load the data from cursor when is needed. This way the UI will not block if you have a lot of items in your cursor.

Also you can add a method for swapping the cursor and notify that data has changed and viewpager will be refreshed instead of recreating and attaching the adapter each time (exactly as in the link that you mention)

The MyPagerAdapter should look like this:

Cursor cursor;

// Returns total number of pages 
@Override 
public int getCount() { 
    return cursor==null?0:cursor.getCount();
} 

// Returns the fragment to display for that page 
@Override 
public Fragment getItem(int position) {
    // position the cursor first
    cursor.moveToPosition(position);

    // read each field
    String email = cursor.getString(cursor
                    .getColumnIndexOrThrow("email"));
    ...
    return FirstFragment.newInstance(email, ...);
} 

public void swapCursor(Cursor cursor) {
    this.cursor = cursor;
    // notify that data has changed and viewpager needs to be refreshed
    notifyDataSetChanged();
}

Don't forget to clean once the loader is reset

@Override 
public void onLoaderReset(android.content.Loader<Cursor> loader) {
    adapterViewPager.swapCursor(null) 
}

Upvotes: 0

Gennadii Saprykin
Gennadii Saprykin

Reputation: 4573

I can't comment, so I'm writing an answer..

You have an activity that implements LoaderCallbacks<Cursor>. Your activity receives onLoadFinished callback when your data is loaded. Inside this method you have a Cursor that should be displayed in your ViewPager.

To display the data from Cursor, you call swapCursor method on the adapter. So, do not create adapter each time you load data. Create it once, and then just call swapCursor on it.

Also, do not find ViewPager each time - findViewById is a heavy operation, it should be performed once the view hierarchy is created.

So, your onLoadFinished will look like this:

@Override
public void onLoadFinished(android.content.Loader<Cursor> loader, Cursor cursor) {
    if (adapterViewPager == null) {
        adapterViewPager = new MyPagerAdapter(getSupportFragmentManager(), cursor);
        vpPager.setAdapter(adapterViewPager);
    } else {
        adapterViewPager.swapCursor(cursor);
    }
}

Upvotes: 1

Related Questions