How to initialize my ViewPager - android

I have an Activity A which contains a list and an Activity B which displays the detail of an item from activity A using a fragment. There is a ViewPager in Activity B, which allows me to swipe left and right to view different item details in the list. The problem is where and how to initialize Activity B to display the details of the item I clicked in Activity A?
Below is my FragmentStatePagerAdapter class, currently no matter which item i clicked in activity A, it always launches the first item (though the swiping function is working)
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
Cursor data;
ArticleDetailUpdateFragment detailUpdateFragment;
public ScreenSlidePagerAdapter(FragmentManager fm, Cursor data) {
super(fm);
this.data = data;
detailUpdateFragment = new ArticleDetailUpdateFragment();
long id = getIntent().getLongExtra(ArticleListActivity.EXTRA_ITEM_ID, -1);
Log.d(TAG, "the itemId received in detail activity: " + String.valueOf(id));
detailUpdateFragment.setId(id);
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.pager, detailUpdateFragment).commit();
}
#Override
public Fragment getItem(int position) {
detailUpdateFragment = new ArticleDetailUpdateFragment();
data.moveToPosition(position);
long id = data.getLong(data.getColumnIndex(ItemsContract.Items._ID));
detailUpdateFragment.setId(id);
return detailUpdateFragment;
}
#Override
public int getCount() {
return pageCount;
}
}

You will have to get the position of the item clicked, assuming it is a recycler view, you can get the position by using this method
viewholder.getAdapterPosition()
Next you will have to pass this position in activityB using intent and set the viewpager's current item as this position after you have attached an adapter to your viewpager
viewpager.setCurrentItem(position, false)
We pass in false because we dont want animation in the beginning

Related

PagerAdapter and ViewPager start a new fragment over the current visible view

I have a ViewPager with a PagerAdapter that is called from another fragment, a main fragment if you will.
My PagerAdapter is showing the user images and the user can swipe between them using ViewPager.
I've registered an onClick listener for every image and it seems to work as expected, at least it returns the correct position if I debug the position from instantiateItem.
The problem is that this onClick is supposed to start a new fragment, like an overlay over the image that the user clicked on but it doesn't start the fragment over the current image that the user clicked on for some reason.
Example of what I mean:
On page one onClick it starts the fragment at the correct position, (overlaying the current visible view).
On page two onClick it starts the fragment on page 1 (not in the visible view and not where the onClick fired).
On page three onClick it starts the fragment on page 2 (not in the visible view and not where the onClick fired).
Code:
MainFragment.java
public class MainFragment extends Fragment implements AdapterView.OnItemSelectedListener {
ViewPager viewPager;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
viewPager = (ViewPager) getView().findViewById(R.id.viewPager);
swipeAdapter = new SwipeAdapter(getActivity(), getContext(), stringFetchedName, stringFetchedUserID, stringFetchedUsername, stringFetchedRating, stringFetchedDescription, stringFetchedDate);
viewPager.setAdapter(swipeAdapter);
}
}
SwipeAdapter.java
public class SwipeAdapter extends PagerAdapter {
FragmentActivity activity;
Context context;
String names[], userid[], username[], rating[], description[], date[];
LayoutInflater layoutInflater;
ImageView imageView;
public View itemView;
public SwipeAdapter(FragmentActivity activity, Context context, String names[], String userid[], String username[], String rating[], String description[], String date[]) {
this.activity = activity;
this.context = context;
this.names = names;
this.userid = userid;
this.username = username;
this.rating = rating;
this.description = description;
this.date = date;
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return names.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((View) object);
}
#Override
public Object instantiateItem(final ViewGroup container, final int position) {
final View itemView = layoutInflater.inflate(R.layout.imageswap, container, false);
imageView = itemView.findViewById(R.id.imageView);
container.addView(itemView);
imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (itemView.findViewById(R.id.userText).isShown()) {
showImageOverlay(v);
} else {
hideImageOverlay(v);
}
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
public void showImageOverlay(View view) {
Fragment fragment = new ImageOverlayFragment().newInstance("0", "0");
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.imageSwap, fragment, IMAGE_OVERLAY);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
public void hideImageOverlay(View view) {
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(IMAGE_OVERLAY);
if(fragment != null) {
activity.getSupportFragmentManager().beginTransaction().remove(fragment).commit();
}
}
}
How can I make sure that the new fragment ImageOverlayFragment I start is started on top of the image that was clicked, like how could I take the position from instantiateItem into account etc? or what should I do?
If I debug Log.d("debug", "Clicked position:" + position); inside the imageView OnClickListener it gives me the correct position but when I call showImageOverlay(v), it seems like it always starts the fragment on the view to the left of my currently visual view?
Note:
I'm passing a FragmentActivity to the SwipeAdapter from the MainFragment which is why I can access getSupportFragmentManager().
Update:
I tried to use FragmentStatePagerAdapter instead of PagerAdapter as suggested by SO and the result is still the same. The onClick event is sending the correct position and I can toggle the visibility of visible elements within the onClick event etc but I still cannot start the new fragment over the view of where the user clicked. The new fragment still starts to the left, and not in the view of where the user clicked. Problem still remains.
fragmentTransaction.replace(R.id.imageSwap, fragment, IMAGE_OVERLAY);
You are using your activity's TransactionManager to start you overlay fragment. In the above code, what I am guessing is that you are giving the ID of the container viewgroup of , parent of ImageView in its layout file. In a ViewAdapter not the currently visible fragment is initialized or view is inflated, but adjacent fragments/views also. That means your view hierarchy from activity root contains more than one layout with the ID you provide for fragment transaction. When activity searches its view hierarchy, it will return the first view it reaches with given ID. In your case, when you are not on the first page of adapter, this first matching view will be inside the leftmost page inside ViewPager. ViewPager - by default - uses one view on left and one view on right of the visible page in created state. That is why you see your fragment is instantiated on the page before the one you want it to be.
As a solution, you can consider using fragments inside Pager instead of plain views. Every fragment has a 'childFragmentManager' that u can use to start your new fragment upon click.
I hope this helps.
I had a similar issue with FragmentStatePagerAdapter, and what worked had much to do with two of the adapter methods:
instantiateItem - "Create the page for the given position."
setPrimaryItem - "Called to inform the adapter of which item is currently considered to be the "primary", that is the one show to the user as the current page."
Because of the way the adapter works, where it instantiates the fragments before they are displayed, the instantiateItem method is called for fragments or slides that are not in view (apart from the first item). If you can override setPrimaryItem, and then leave the UI/view set up in instantiateItem, and then the rest of the logic (listener with the show/hide overlay) in setPrimaryItem, you can make sure the listener and the fragment are set to the currently active view/fragment.
Let me know if that makes sense to you

Sharing data between viewpagers

I created two ViewPagers in my activity and what I'm trying to do is to share the same data between them.
The ViewPager 1 shows a fragment for every string in a list named "A".
Every fragment has a simple layout with a TextView.
The ViewPager 2 shows a fragment for every string from the same list "A".
Every fragment has a simple layout with an EditText.
I'm trying to obtain this behavior:
1) VP 1 is visible and the user can swipe every fragment populated with the strings from the list "A".
2) VP 2 is invisible.
3) After the onClick event on the TextView, I'd like to hide VP 1 and show VP 2 with the same string content but in an EditText instead of TextView.
4) The user can modify the content of the EditText.
5) After 4) the user can press a button in the toolbar and come back to VP 1 with the content updated.
I'm using the FragmentStatePagerAdapter.
I was thinking to save data directly in a DB or in a file.
Is it possible to create something like that?
How can I implement the event that after tapping on fragment "n" in VP1 reaching the fragment "n" in VP2?
How can I update the content in VP 1 after the mod in VP 2?
The code at the moment is quite simple so I didn't post it here, by the way is very similar to this one.
Thanks
Why don't you just use one ViewPager, with the fragment having a TextView and an EditText. The EditText's visibility is set to "GONE". On click of the TextView set it's visibility to "GONE" and the EditText's to "VISIBLE". When the toolbar button is clicked do the opposite and also populate the new text to the appropriate TextView.
The Object-Oriented approach is using an interface. You can create an interface in your activity for the fragments to communicate with.
ViewPagerComm.class:
public interface ViewPagerComm {
String getData();
void setData(String data);
}
Activity:
public class FragmentStatePagerSupport extends Activity, implements ViewPagerComm {
String data;
...
#Override
public void setData(String data){
this.data = data;
}
#Override
public String getData(){
return data;
}
}
...
}
In the adapter:
public static class MyAdapter extends FragmentStatePagerAdapter {
ViewPagerComm callback;
public MyAdapter(FragmentManager fm, ViewPagerComm callback) {
this.callback = callback;
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position, callback);
}
}
In your Fragments:
public static class ArrayListFragment extends ListFragment {
ViewPagerComm callback;
static ArrayListFragment newInstance(int num, ViewPagerComm callback) {
ArrayListFragment f = new ArrayListFragment();
f.callback = callback;
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
}
Now, whenever a fragment calls setData(aString) the parent activity will hold the data. Whenever a fragment calls getData(), the data will be retrieved from the activity.
Just declare Variable in your Activity (your Viewpager Activity)
public class TestActivity extends AppCompatActivity {
public String test;
}
And access it in your Fragment (within code)
((TestActivity)getActivity()).test

Wrong position value in Android ViewPager

I'm trying to implement 3 slides composed of 3 fragments (or 3 layouts) with ViewPager and I want to know which slide I currently show in order to display the appropriate content. In simpler words, I want content 1 on slide 1, content 2 on slide 2 and so on.
Here is my actual code from my Activity (from android official doc) :
public class SliderActivity extends FragmentActivity {
private static final int NUM_PAGES = 3;
private ViewPager mPager;
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slider);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
// A simple pager adapter that represents 3 ScreenSlidePageFragment objects, in sequence.
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
System.out.println("POSITION = " + position); // Or mPager.getCurrentItem()
return new SlidesFragment();
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
No matter how hard I try, getCurrentItem() or position still prints wrong values. Only the second slide sends a number to the console. It's 2 when I swipe right and 0 when swipe to left.
What am I doing wrong ?
You definitely want to use the position value in getItem, do not call getCurrentItem because the pager will create items that are not the current item and not on-screen. The pager has an offscreen page limit of 2 by default. So, when it is first created, getItem will be called with a position of 0 and then again immediately with position of 1. The fragment at position 1 is not the current item, but it is being created offscreen so the user can start sliding over and see it. Then when you complete the swipe, getItem is called with a position of 2 to pre-load the next and final slide.
To accomplish "I want content 1 on slide 1, content 2 on slide 2", your SlidesFragment needs to take an argument (store in the arguments bundle) the position that tells it which content to display. Or, more likely, you have 3 different fragment types you would create based on the position.

How do I position to a particular fragment when using a FragmentStatePagerAdapter?

I am creating detail fragments from an ArrayList of objects, the ArrayList is a representation of a ListFragment. I am using a FragmentStatePagerAdapter to manage each detail page (fragment). Here's the code for my FragmentActivity's FragmentStatePagerAdapter inner class
public static class DetailPagerAdapter extends FragmentStatePagerAdapter {
public DetailPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
if (BuildConfig.DEBUG) {
Log.e(Constants.TAG, "DetailPagerAdapter creating DetailFragment - item " + i );
}
Fragment fragment = new DetailFragment();
// get object from outer class ArrayList
Object fobject = detailslist.get(i);
final Detail fDetail = (Detail)fobject;
Bundle args = new Bundle();
args.putInt(DetailFragment.ARG_POSITION, i + 1);
args.putString("detailTitle",fDetail.getDetailName());
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
// object collection same size as list
return detailslist.size();
}
#Override
public CharSequence getPageTitle(int position) {
return "Position #" + (position + 1);
}
}
This creates all the detail fragments but it always shows the user the first ListFragment entry. I can swipe forwards and backwards OK. If the user selects an an item from the ListFragment and if that item is in the middle of the list I want to position to that detail object instance. I also want the user to be able to swipe back and forward to the other detail pages. I pretty sure I need to use the FragmentManager transaction methods but I not sure how to use it when using a FragmentStatePagerAdapter. Anybody got any pointers?
You can use ViewPager.setCurrentItem().

ViewPager doesnt show images in more than 1 Fragment

My app is a news app, with news listing fragment, NewsDetailsFragment (which contains menu, search, etc). I am using the single activity model and each screen is represented by a Fragment. NewsDetailsFragment includes a ViewPager (gallery).
When I navigate to NewsDetailsFragment initially, the gallery is loaded, now if I load a new instance of NewsDetailsFragment fragment, to choose another news from the menu, doing a search, etc. The ViewPager in the 2nd NewsDetailsFragment won't display the images, although the Log shows that everything works fine, data is there, adapter calls getView()...
So basically if there is 1 loaded gallery ViewPager, any more NewsDetailsFragment won't show their galleries, only if I press Back till I have no fragment (hence no ViewPager running), only then if I open a new instance of NewsDetailsFragment it will load the gallery.
I hope I explained clearly. I tried my best and I'm clueless now.
This is how I pass the gallery data to the ViewPager after it is loaded (code excerpt from NewsDetailsFragment):
ViewPager pager = (ViewPager) getView().findViewById(R.id.vpAlbum);
ArticleAlbumAdapter adapter = new ArticleAlbumAdapter(getActivity().getSupportFragmentManager(), article.album);
pager.setAdapter(adapter);
ArticleAlbumAdapter:
public class ArticleAlbumAdapter extends FragmentStatePagerAdapter
{
private List<String> urlList;
public ArticleAlbumAdapter(FragmentManager fm, List<String> urlList)
{
super(fm);
this.urlList = urlList;
Log.d("ArticleAlbumAdapter()", "urlList=" + urlList);
}
#Override
public Fragment getItem(int position)
{
Log.d("ArticleAlbumAdapter.getItem()", "position=" + position + ", url=" + urlList.get(position));
Fragment image = new AlbumImageFragment();
Bundle bundle = new Bundle();
bundle.putString("url", urlList.get(position));
bundle.putInt("pos", position);
image.setArguments(bundle);
return image;
}
#Override
public int getCount()
{
return urlList.size();
}
}
Pass the ViewPager your current Fragment's getChildFragmentManager() rather than the FragmentManager of the activity. Based on your description, I'm assuming your main screen which creates the first ViewPager is also a Fragment. Since the ViewPager is a child to your Fragment and it in turn is creating child Fragments, it needs the child FragmentManager rather than the main one.

Categories

Resources