Reputation: 2749
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
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
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
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
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