I'm trying to have a button appear on the last page of a viewPager (which contains images in fragments). In these fragments, I'm creating a button that is invisible until the last page is shown. So far, I've achieved this behavior, but I'm having a bug where in page 2 the button appears, even though I'm "showing" (with setVisibility(View.VISIBLE)) the button only when the current page is greater or equal than 3 (there are 5 pages total, and also, greater or equal than 3 because the viewPager.getCurrentItem() behaves oddly and won't count the last or first page as an index).
My custom fragment's onCreateView() looks like this:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.imageview, container, false);
ivImage = (ImageView) root.findViewById(R.id.ivImageView);
btn_register = (Button) root.findViewById(R.id.button_register);
if( ((WelcomeActivity) this.getActivity()).test()>=3){
btn_register.setVisibility(View.VISIBLE);
}
setImageInViewPager();
return root;
}
In my main activity, the test() method simply returns the current page the viewPager is in:
public int test(){
return(viewPage.getCurrentItem());
}
How could I better detect the last page of a viewpager and use it so that I only display the button when the user is in the last page of the viewpager? thanks!
EDIT:
Here is a video of the problem.
HEre are the contents of my src folder: WelcomeActivity.java, FragmentPagerAdapter.java, Images.java, FragmentImageView.java.
In onPageSelected(), check the position : if(position==5) then show the button. Here I used
5 because you mention that there are 5 pages.
#Override
public void onPageSelected(int position) {
if(position == 5)
btn_register.setVisibility(View.VISIBLE);
}
Try this hope it will help you :)
You need to somehow tie the count of fragments to the individual fragments. So that each fragment knows its index. Can you add your code on how and when you are creating/adding the fragments to the view-pager?
Basing on that you can add a param to the arguments bundle while initialising the fragment there by fragment knows it's index in the view-pager.
Or else try this,
private int mCurrentPageSelected;
// In your activity
#Override
public void onPageSelected(int position) {
mCurrentPageSelected = position;
}
public int getSelectedPagePosition(){
return mCurrentPageSelected;
}
// In your fragment
public void onAttach(){
Log.d(TAG, "Fragment attached and current selected position is" + etActivity().getSelectedPagePosition());
if(getActivity().getSelectedPagePosition() >= 3){
// show the btn
btn_register.setVisibility(View.VISIBLE);
}else{
//hide the button
btn_register.setVisibility(View.GONE);
}
}
Related
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
I am declaring my fragments like so in my activity:
public void setupViewPager(ViewPager viewP) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new ProjectPro1(), "proj1");
adapter.addFragment(projectPro2 = new ProjectPro2(), "proj2");
viewP.setAdapter(adapter);
}
Now in my activity at the very end of everything being done I call a setOnClickListener on a button which works fine if the fragments fully load - since when clicked the button tells a fragment not in view to update part of its view. However, if I click on the button right away then it will crash the app since the fragment is not fully loaded yet and gives a null error. How can I have the button enabled once the fragment is fully loaded or setTheOnClicklistener when a fragment is fully loaded?
EDIT:
To make things clearer - I have a follow button that displays on both fragments. One on fragment it shows just pictures on the other fragment it shows the person's details. If a user clicks the follow button on either fragment the count on the details fragment has to update (followers count + 1).
UPDATE:
This is the attempt for your answer adelphus - This is my fragment class:
private boolean profileDetailsCreated = false;
private boolean trackChanged = false;
private int trackValue = 0;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.project_profile_details, container, false);
profileDetailsCreated = true;
if(trackChanged) {
Log.d(Constants.DEBUG, "THE TRACK CHANGED HAS OCCURED BEFORE PROFILE DETAILS CREATED");
setTrackCount(trackValue);
}
return rootView;
}
public void setTrackCount(int value) {
Log.d(Constants.DEBUG, "THE value for set track count is " + value);
if(profileDetailsCreated) {
Log.d(Constants.DEBUG, "THE PROFILE DETAIL IS CREATED CHANGE SHOULD HAPPEN");
dbProject.setTrackedCount(trackC + value);
db.projectUpdateTrack(dbProject);
trackC = dbProject.getTrackedCount();
showTrackBackgroundText();
} else {
Log.d(Constants.DEBUG, "THE PROFILE DETAIL IS NOT CREATED STORE THE DATA " + value);
trackValue = value;
trackChanged = true;
}
}
Based upon your comments, I'll try and make my explanation a bit clearer. It's difficult to visualise what problem you're facing since you don't describe what the update does.
Here is my suggestion (in psuedo-code):
class MainActivity extends Activity {
public void onCreate(..) {
...
// assuming the button is part of the activity view
btn.setOnClickListener((View) => {
Info info = getInfoNeededForUpdate();
fragment1.update(info);
fragment2.update(info);
});
}
}
// Frag2 is the same logic as Frag1
class Frag1 extends Fragment {
boolean mCreated;
Info mPendingInfo;
public void onCreateView(...) {
// inflate view, blah
...
// mark us as created
mCreated = true
// apply any pending updates
if (mPendingInfo != null)
this.update(info);
}
public void update(Info info) {
// first check if we've been created
if (this.mCreated) {
// yep - go ahead and apply the updates to the views
...
} else {
// nope - save it for later (update will be applied in onCreateView)
this.mPendingInfo = info;
}
}
}
Hopefully you can see from this that it doesn't matter when the update is applied to the fragment - the view will either get updated immediately (if the fragment's onCreateView() has already been called) or it will wait until onCreateView() is called before applying the update.
In this case you should do the setting of the OnClickListener in the onActivityCreated lifecycle method:
private Button yourButton;
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
View rooView = getView();
yourButton = (Button) rooView.findViewById(R.id.your_button);
//then you can set the
yourButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//do stuff here!
}
});
}
I have an activity with 6 fragments(Swipe disabled). For each fragment I have 2 button( next and previous) which move to 1 fragment next and previous perfectly. I have 2 checkboxes which I need to check if they are checked or not before migrating to next activity. But I get null pointer because checkboxes are initialized in onCreateView Method but the fragment is loaded already due to viewpager. How can I check if checkboxes are checked or not?
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
v = inflater.inflate(R.layout.fragment_step6, container, false);
cb1 = (CheckBox)v.findViewById(R.id.cb1);
next = (Button)v.findViewById(R.id.next);
next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(cb1.isChecked())
return true;
}
});
}
ViewPager will preload fragments for you and it's not going to remove old fragments from memory immediately. I do not think the problem is that the view is gone when the user clicks on the next button.
Check your layout file and make sure you have a checkbox in res/layout/fragment_step6.xml with the ID cb1.
pager.setOffscreenPageLimit(6);//6 = to the number you included in your question.
this will tell viewpager to retain your fragments while scrolling pages so you dont lost your state.
I want to show a same fragment for example fragment having activities of a day of week in a viewpager for all days with different data. I will be giving the dayNumber parameter to each fragment being instantiated and showing related activities. The problem is I see same fragment in each tab no matter what parameter I passed. I think the last fragment added or instantiated by a pager overrides all the other tab fragments instance. Because when I open a list item in expendableList View it is opened in all fragments of the pager.
This is how I am using the pager and fragment.
Pager
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// When swiping between pages, select the
// corresponding tab.
if (bar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS)
if (bar.getSelectedNavigationIndex() != position)
bar.setSelectedNavigationItem(position);
// should be changed when some solution comes.
if (tab == 0) {
Fragment ev;
if ((ev = (Fragment) mPagerAdapter.instantiateItem(mPager,
0)) instanceof frTimetable)
((frTimetable) ev).refresh(day.Monday);
} else if (tab == 1) {
Fragment ac;
if ((ac = (Fragment) mPagerAdapter.instantiateItem(mPager,
1)) instanceof frTimetable)
((frTimetable) ac).refresh(day.Tuesday);
}
tab = position;
}
Fragement
{
//class other methods
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fr_timetable, container, false);
ExpandList = (ExpandableListView) view.findViewById(R.id.expActivityView);
//I will change the list items in refresh method of the fragment for a day type
ExpListItems = new ArrayList<Items>(Timetable_Provider.getAllActivites());
ExpAdapter = new ExpandListAdapter(getActivity(), ExpListItems);
ExpandList.setAdapter(ExpAdapter);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
instantiateItem() is certainly not what you want. That is called by ViewPager, not by a consumer of a ViewPager.
Normally, you would provide the data to the ViewPager as part of setting up the pages, inside of your PagerAdapter. For example, this sample app uses the arguments Bundle to pass the page number of the page to the Fragment that is the implementation of the page.
If the data inside a page needs to be updated, ideally the page itself determines on its own that this is needed and handles it. Or, use an event bus (e.g., LocalBroadcastManager, greenrobot's EventBus, Square's Otto) to publish information that relevant pages can pick up. There is no great way to get at an existing page from outside of the page itself using FragmentPagerAdapter or FragmentStatePagerAdapter (e.g., to have an activity push data into a page), which is one of the reasons I wrote ArrayPagerAdapter.
I'm creating an application with a CustomPagerAdapter that can be controlled by ActionBar tabs or horizontal swipe. When you select a tab, a fragment corresponding to that tab is displayed on the screen. When the app is created and when any tab is selected, the adjacent tabs, fragments are loaded into memory. I do not want this to happen. I would like it so that when a tab is selected only that selected tab's fragment is loaded into memory. Is there a way to do this?
Edit: The code I'm currently having trouble with is as follows:
public class fragA extende Fragment
{
private VideoView videoViewA;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_A, container, false);
videoViewA = (VideoView) rootView.findViewById(R.id.videoViewA);
return rootView;
}
#Override
public void setUserVisibleHint(final boolean isVisibleToUser)
{
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser)
{
videoViewA.setVideoURI(LINK);
videoViewA.start();
}
else
{
videoViewA.stopPlayback();
}
}
}
The error I'm receiving is at the videoViewA.setVideoURI(LINK); line. Mind you, the link is actually there, but for privacy reasons I cannot post it.
Edit 2: It's ajava.lang.NullPointerException.
Edit 3: Sorry, but I'm doing this all the hard way. The code now reflects what I have actually written.
Try loading your videos within setUserVisbleHint(), which gets fired by the FragmentPageAdapter upon showing the fragment.
http://developer.android.com/reference/android/support/v4/app/Fragment.html#setUserVisibleHint(boolean)
If that doesn't work for you, you can also try to do check onHiddenChanged(boolean hidden).
http://developer.android.com/reference/android/app/Fragment.html#onHiddenChanged(boolean)