I'm trying to build an Android application using the new ViewPager2. I've added two ViewPagers, separated by a view, and when you swipe, both viewpagers should move. Both view pagers are moving correctly but upon completion of the gesture, the swiped view flashes and the non swiped view reloads, as demonstrated by the attached gif. Here is my code for the Activity, ViewPagerAdapter, and Fragment. Any help is appreciated
public class MainActivity extends FragmentActivity {
ActivityMainBinding viewBinding;
MyPager adapter1, adapter2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
List<String> data = populateData();
adapter1 = new MyPager(this, data);
adapter2 = new MyPager(this, data);
viewBinding.viewPager.setAdapter(adapter1);
viewBinding.viewPager2.setAdapter(adapter2);
viewBinding.viewPager2.setOffscreenPageLimit(data.size());
viewBinding.viewPager.setOffscreenPageLimit(data.size());
viewBinding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
viewBinding.viewPager2.scrollTo(positionOffsetPixels, 0);
}
#Override
public void onPageScrollStateChanged(final int state) {
viewBinding.viewPager2.setCurrentItem(viewBinding.viewPager.getCurrentItem(), true);
}
});
viewBinding.viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
viewBinding.viewPager.scrollTo(positionOffsetPixels, 0);
}
#Override
public void onPageScrollStateChanged(final int state) {
viewBinding.viewPager.setCurrentItem(viewBinding.viewPager2.getCurrentItem(), true);
}
});
}
private List<String> populateData() {
List<String> data = new ArrayList<>();
for (int x = 0; x < 10; x++) {
String derril = "derril " + x;
data.add(derril);
}
return data;
}
}
public class MyPager extends FragmentStateAdapter {
List<String> data;
public MyPager(#NonNull FragmentActivity fragmentActivity, List<String> data) {
super(fragmentActivity);
this.data = data;
}
#NonNull
#Override
public Fragment createFragment(int position) {
return DerrilFragment.newInstance(data.get(position));
}
#Override
public int getItemCount() {
return data.size();
}
}
public class DerrilFragment extends Fragment {
PizzaBinding viewBinding;
String data;
private DerrilFragment(String data) {
this.data = data;
}
public static DerrilFragment newInstance(String data) {
return new DerrilFragment(data);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
viewBinding = DataBindingUtil.inflate(inflater, R.layout.pizza, container, false);
viewBinding.text.setText(data);
return viewBinding.getRoot();
}
}
You can implement by RecyclerView.
class MainActivity : AppCompatActivity() {
private var isFirstFocused = false
private val helper: SnapHelper = LinearSnapHelper()
private val helper2: SnapHelper = LinearSnapHelper()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recycler1.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (isFirstFocused) recycler2.scrollBy(dx, dy)
}
})
recycler2.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (!isFirstFocused) recycler1.scrollBy(dx, dy)
}
})
recycler2.setOnTouchListener { v, event ->
if (event.action != null && event.action == MotionEvent.ACTION_DOWN ) {
isFirstFocused = false
return#setOnTouchListener true
}
return#setOnTouchListener false
}
recycler1.setOnTouchListener { v, event ->
if (event.action != null && event.action == MotionEvent.ACTION_DOWN ) {
isFirstFocused = true
return#setOnTouchListener true
}
return#setOnTouchListener false
}
helper.attachToRecyclerView(recycler1)
helper2.attachToRecyclerView(recycler2)
val manager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
val manager2 = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
recycler1.layoutManager = manager
recycler2.layoutManager = manager2
recycler2.adapter = Adapter(items)
recycler1.adapter = Adapter(items)
}
private val items = listOf("One", "Two", "Three")
}
class Adapter(val list: List<String>) : RecyclerView.Adapter<Adapter.Holder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.activity_listview,
parent,
false
)
return Holder(view)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.itemView.text_view.text = list[position]
}
override fun getItemCount() = list.size
class Holder(view: View) : RecyclerView.ViewHolder(view)
}
You can try to use adapter.setOffscreenPageLimit = n. It sets the number of pages that should be retained to either side of the currently visible page(s). Pages beyond this limit will be recreated from the adapter when needed If you have a limited number of pages.
More details are here.
In your setCurrentItem() usages (under onPageScrollStateChanged()), set the second parameter to false
This will disable animation (not permanently) in the ViewPager when you set a page programatically, which is the reason you see the flash-like animation.
To know why:
Consider two ViewPagers A & B
You scroll from one page to another in A, which induces the same scroll in ViewPager B. Once you completely scroll from one page to another in A, the currentItem is internally set by A, but not for B.
Now, you inform B about the page change by calling setCurrentItem([index], true), which, irrespective of your scroll, will animate the page change introducing the flash (your induced scroll is reset to normal, and page animation for one page to another is started)
this looks a bit suspicious that you are calling setCurrentItem inside onPageScrollStateChanged for every passed state... try to add some if and setCurrentItem only when state==ViewPager2.SCROLL_STATE_IDLE
list of states:
ViewPager2.SCROLL_STATE_IDLE
ViewPager2.SCROLL_STATE_DRAGGING
ViewPager2.SCROLL_STATE_SETTLING
ViewPager2.SCROLL_STATE_DRAGGING state is handled in onPageScrolled method (sharing position offset aka. dragging by finger)
ViewPager2.SCROLL_STATE_SETTLING is auto-scroll (snap) of ViewPager2 to closest page - in here I'm not shure that ViewPager2 dragged "by code" (sharing position with scrollTo) will also enter in this state causing same snap behavior (so also onPageScrolled?) ... worth testing, put some logs in there
also: you are dragging one ViewPager2 and in onPageScrolled you are calling scrollTo on second ViewPager2. this may lead to call onPageScrolled call on this second listener, which is calling first ViewPager2 scrollTo method and so on... looped. consider dynamic unregistering listener when user will start drag one ViewPager2 and reregistering when movement ends (SCROLL_STATE_IDLE state)
I think the problem is that in 'onPageScrolled' method of both ViewPagers you are scrolling other pager. So, both pages might be scrolling each other at the same. For example, if you are scrolling ViewPager_1 then its onPageScrolled method will scroll ViewPager_2 and as ViewPager_2 is now scrolled then its onPageScrolled method will be called and that will scroll ViewPager_1 and vice versa.
So, You may need to consider applying the check that if touch is holded ny ViewPager_1 then code inside onPageScrolled method of ViewPager_2 should not be executed. Or if you want to avoid those checks. Then registerOnPageChangeCallback/unregisterOnPageChangeCallback the listener of other ViewPager when one holds the touch.
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
viewBinding.viewPager.scrollTo(positionOffsetPixels, 0);
}
Related
I'm making an app using TMDB API and have gotten stuck at a small issue.
TMDB API shows seasons and episodes which are empty, basically, those are yet to air but since those are empty, the app shows a blank item that I'm trying to get rid of.
Here's my adapter:
public class SeasonAdapter extends RecyclerView.Adapter<SeasonAdapter.ViewHolder> {
private final List<Season> seasons;
private final Context context;
private final RequestOptions requestOptions;
public SeasonAdapter(List<Season> seasons, Context context) {
this.seasons = seasons;
this.context = context;
requestOptions = new RequestOptions().centerCrop().placeholder(R.drawable.poster_placeholder).error(R.drawable.poster_placeholder);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_season_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
final Season season = seasons.get(position);
holder.tvTitle.setText(season.getSeasonTitle());
if (season.getSeasonDate() != null && !season.getSeasonDate().isEmpty()) {
holder.tvDate.setText(context.getResources().getString(R.string.aired_on) + season.getSeasonDate());
} else {
holder.tvDate.setVisibility(View.GONE);
}
if (season.getSeasonEpisodes() == 0) {
seasons.remove(position);
}
holder.tvEpisodes.setText(String.valueOf(season.getSeasonEpisodes()) + context.getResources().getString(R.string.total_episodes));
Glide.with(context).load(season.getSeasonImageURL()).apply(requestOptions).into(holder.ivPoster);
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(context, EpisodeActivity.class);
intent.putExtra("title", season.getShowTitle());
intent.putExtra("seasonTitle", season.getSeasonTitle());
intent.putExtra("seasonNo", season.getSeasonNo());
intent.putExtra("tvId", season.getTvId());
v.getContext().startActivity(intent);
});
}
#Override
public int getItemCount() {
return seasons.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView ivPoster;
public TextView tvTitle, tvDate, tvEpisodes;
public ViewHolder(#NonNull View itemView) {
super(itemView);
ivPoster = itemView.findViewById(R.id.ivSeasonPoster);
tvTitle = itemView.findViewById(R.id.tvSeasonTitle);
tvDate = itemView.findViewById(R.id.tvSeasonAired);
tvEpisodes = itemView.findViewById(R.id.tvSeasonEpisodes);
//Poster Corners
ivPoster.setClipToOutline(true);
}
}
}
I tried doing this:
if (season.getSeasonEpisodes() == 0) {
seasons.remove(position);
}
It does seem to hide the season which has no episodes but if a show has multiple seasons without episodes, my app crashes so I figured this isn't the right solution so any help is appreciated.
I suggest performing that removal logic in the constructor of the adapter rather than in onBind. onBind happens as the recycler view is finalising the details of each view holder immediately before it's shown to the user. You want to do as little as possible logic in here to keep the recycler view performant.
Inside the constructor (or even before the list is passed in) you should perform a loop and remove those items that don't meet the criteria before setting the instance variable.
It's been a long time since I wrote code in java and so I'd end up with unhelpful incorrect syntax if I tried to do it here.
I have a ListView with a with a Custom Adapter that extends ArrayAdapter, My ListView has 5 different types of Items that it can hold, That's why I am using an ArrayAdapter well the issue is fairy odd to me, I am not sure how to debug this. I will post the video.
as you can see I'm clicking my Checkbox and Modal Items without any issue at the start but as soon as I scroll down and back up they don't click anymore! This is a really weird bug I'm facing. Here is how I do my adapter.
This is my adapter class code. It's fairly compact.
public class SettingAdapter extends ArrayAdapter<Setting> {
private List<Setting> settings;
public SettingAdapter(Context context) {
super(context, 0);
setSettings(new ArrayList<>());
}
public SettingAdapter(Context context, List<Setting> settings) {
super(context, 0);
setSettings(settings);
}
public void setSettings(List<Setting> settings) {
this.settings = settings;
clear();
addAll(settings);
notifyDataSetChanged();
}
#Override
public int getViewTypeCount() {
return 4;
}
#Override
public Setting getItem(int i) {
return settings.get(i);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return Objects.requireNonNull(getItem(position)).getView(getContext());
}
}
I have an abstract class called Setting my ListView Items will extend that class and implement getView() and such.
Here is an example.
public class Summary extends Setting {
private View view;
public Summary(String name) {
super(name);
}
public int getType() {
return SettingType.SUMMARY;
}
#Override
public View getView(Context context) {
if(view == null) {
view = //Load the view here
}
return view;
}
}
I'm also saving my View reference for later, I'm not sure what I'm doing wrong here can anyone guide me towards how to debug this or fix this? Have spent over 10 hours still no success.
OK, try to not Caching the view, just recreate it on Setting.getView(Context context) and save the state of view and return it.
I Create 4 different items and work with me.
If you want to cache view i really recommended RecyclerView
I am working in a chat application for Android and I am using RecyclerView for listing the messages.
I have written the adapter, but I am having a problem with detecting when an element(TextView in this case) inside the layout is clicked.
This is my adapter:
public class ChatRoomThreadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private class ViewHolder extends RecyclerView.ViewHolder {
TextView message, timestamp;
private ViewHolder(View view) {
super(view);
message = (TextView) itemView.findViewById(R.id.message);
timestamp = (TextView) itemView.findViewById(R.id.timestamp);
}
}
public ChatRoomThreadAdapter(Context mContext, ArrayList<Message> messageArrayList, String userId) {
this.mContext = mContext;
this.messageArrayList = messageArrayList;
this.userId = userId;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
((ViewHolder) holder).message.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (((ViewHolder) holder).timestamp.getVisibility() == View.GONE) {
((ViewHolder) holder).timestamp.setVisibility(View.VISIBLE);
} else {
((ViewHolder) holder).timestamp.setVisibility(View.GONE);
}
}
});
}
#Override
public int getItemCount() {
return messageArrayList.size();
}
}
The current onClick works but I have to click twice on the message in order for the onClick to trigger. I have been searching and trying endless solutions for 3 days in order to fix this, but none of the solutions on the internet have worked so far.
Make sure you have both focusableInTouchMode & focusable disabled on the button. The first click will get the focus and the second click executes the onClickListener.
.
After scroll, recycler view items are not clickable and the issue is still open https://issuetracker.google.com/issues/66996774
Found a way to force click if the scroll state is still SCROLL_STATE_SETTLING
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.recyclerview.widget.RecyclerView
class WrapperRecyclerView(context: Context, attributeSet: AttributeSet?, defStyle: Int) :
RecyclerView(context, attributeSet, defStyle) {
constructor(context: Context) : this(context, null, 0)
constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet, 0)
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
val eventConsumed = super.onInterceptTouchEvent(event)
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> if (scrollState == SCROLL_STATE_SETTLING) {
parent.requestDisallowInterceptTouchEvent(false)
// only if it touched the top or the bottom.
if (!canScrollVertically(-1) || !canScrollVertically(1)) {
// stop scroll to enable child view to get the touch event
stopScroll()
// do not consume the event
return false
}
}
}
return eventConsumed
}
}
The following class will fix this issue :
class WrapperRecyclerView(context: Context, attributeSet: AttributeSet?, defStyle: Int) :
RecyclerView(context, attributeSet, defStyle) {
constructor(context: Context) : this(context, null, 0)
constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet, 0)
override fun onScrollStateChanged(state: Int) {
super.onScrollStateChanged(state)
if (state == RecyclerView.SCROLL_STATE_SETTLING) {
this.stopScroll();
}
}
}
For me focusableInTouchMode & focusable doesn't work.
Add this line to RecyclerView :
android:nestedScrollingEnabled="false"
Enjoy¡
A tip for those trying to debug this kind of scrolling state problem in recycler view. Create your own MyRecyclerView extending RecyclerView, and override fun onInterceptTouchEvent there just to add logging:
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
Log.d(TAG, "MyRecyclerView intercepted a touch event: ${event!!.action}")
Log.d(TAG, "MyRecyclerView is in state: ${scrollState}")
return super.onInterceptTouchEvent(event)
}
It helped me to see that my recycler view was stuck in SCROLL_STATE_DRAGGING.
I believe, for child views to receive clicks coming from recycler view, the recycler view has to be in SCROLL_STATE_IDLE state.
So my solution to the problem was based on what #Rocky suggested,
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
val eventConsumed = super.onInterceptTouchEvent(event)
/*
* Fix the following problem.
*
* When the recycler view received a click, it was still in the DRAGGING mode.
* It consumed the click, instead of passing it down to the child view.
* The user had to click a child view again, to be able to interact with it.
*/
when (event!!.actionMasked) {
MotionEvent.ACTION_DOWN -> {
if (scrollState == SCROLL_STATE_DRAGGING) {
stopScroll()
return false
}
}
}
return eventConsumed
}
So I have a ViewPager, the ViewPager contains two tabs, each tab contains a RecyclerView, cards can be added and removed from each RecyclerView. Each RecyclerView is contained within a Fragment. Each RecyclerViewuses an ItemTouchHelper to manage onSwiped() events. Cards within in the RecyclerView have several behaviors:
Swiping to the right opens an editor for the card
Swiping to the left deletes the Card
Holding down on the card and dragging vertically up or down re-positions the card.
My problem is that the ViewPager is intercepting the onSwiped() method for each RecyclerView, however, vertical touch events are still recieved properly by each RecyclerView. Obviously, this boils down to a touch conflict problem. The ViewPager wants the horizontal touch events for changing tabs, and the RecyclerView wants the horizontal touch events for swiping the cards. However, the ViewPager is stealing all of the touch events. I have disabled horizontal swiping on the ViewPager (the only way the user can change the tab is by tapping on the tab indicator), but the RecyclerView still won't receive any horizontal touch events.
Here's the ViewPager with disabled swiping:
public class NoSwipePager extends ViewPager {
public NoSwipePager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return false;
}
}
Here's a Fragment class:
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.pit_tab, container, false);
container.getParent().requestDisallowInterceptTouchEvent(true);
RecyclerView rv = (RecyclerView) view.findViewById(R.id.movie_recycler_view);
return view;
}
Here is the class that manages the RecyclerView:
public EditManager(RecyclerView rv, Context act, EditListener listener, ArrayList<Element> elements, boolean editing, boolean pit) {
//rv = (RecyclerView) act.findViewById(R.id.movie_recycler_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(act);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
rv.setLayoutManager(linearLayoutManager);
((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false);
if(elements != null) elementsAdapter = new ElementsAdapter(listener, act, new LinkedList<>(elements), editing);
else elementsAdapter = new ElementsAdapter(listener, act, null, editing);
rv.setAdapter(elementsAdapter);
ItemTouchHelper.Callback callback = new ElementTouchHelper(elementsAdapter);
ItemTouchHelper helper = new ItemTouchHelper(callback);
helper.attachToRecyclerView(rv);
}
And finally, here's the method that should be being called, but isn't:
#Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) {
if(direction == ItemTouchHelper.RIGHT) {
mElementsAdapter.postEdit(mElementsAdapter.getElement(viewHolder.getAdapterPosition()));
return;
}
}
Try replacing your NoSwipePager with this:
public class NoSwipePager extends ViewPager {
public NoSwipePager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected boolean canScroll(View v, boolean checkV, int dx, int x,
int y) {
if (v instanceof RecyclerView) {
return(true);
}
return(super.canScroll(v, checkV, dx, x, y));
}
}
I have used this approach successfully for a ViewPager with maps in the pages (substituting SurfaceView for the RecyclerView). My guess is that you will need to replace the v instanceof RecyclerView test with something more sophisticated (and custom to your app). But I think canScroll() is more likely to give you what you want than blocking onInterceptTouchEvent().
This is my situation:
I have several fragments added dynamicly to an FragmentStatePagerAdapter, this works fine. But now i want to be able to replace an fragment when I push on an button.
public class QuestionFragment extends UpperFragment {
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setRetainInstance(true);
CustomViewPager.enabled = true;
View rootView = inflater.inflate(R.layout.question, container, false);
Button btn = ((Button) rootView.findViewById(R.id.bQuestion));
if (how == true) {
btn.setVisibility(View.VISIBLE);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// lijst afgaan met alle how en kijken welke id nodig is
for (int i = 0; i < XmlParserSax.howFragments.size(); i++) {
Fragment how = XmlParserSax.howFragments.get(i);
if (howId.equals(((UpperFragment) how).getIdNum())) {
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction
.replace(R.id.flQuestion, how, "howFragment")
.setTransition(
FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null).commit();
break;
}
}
}
});
} else {
btn.setVisibility(View.GONE);
}
return rootView;
}
So when i press the button the current layout (R.id.flQuestion) is replaced with the new fragment. This works, but the tricky part comes here:
When I slide to the next fragment and the slide to the fragment with the button it keeps working but if i slide 2 times to the next fragment (of the same type QuestionFragment) it does the functionallity of the new fragment but it doesn't show the new fragment.. So it seems that it can't replace the R.id.flQuestion because it is stored in memory maybe?
I need to be sure that the fragment is always replaced even if the next 2 fragments are of the same type and same layout (R.id.flQuestion)..
This is the class layout of the new frag
public class HowFragment extends UpperFragment {
..
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setRetainInstance(true);
View rootView = inflater.inflate(R.layout.how, container, false);
//if back key pressed return to layout of Question
rootView.setFocusableInTouchMode(true); //this line is important
rootView.requestFocus();
rootView.setOnKeyListener( new View.OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
CustomViewPager.enabled = true;
return false;
}
return true;
}
} );
//don't allow pushing button again
rootView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return true;
}
});
return rootView;
}
Also important to tell: i'm using framelayouts for both the fragments (so no hard coded fragment tag in the xml)
To make it clear:
This happens when the next fragment is from different class, no problem
This happens when next fragments are from the same layout and class:
I'll have to test the code, but from what I see, easiest way to make this work is to grab new Fragment even if it is the same fragment class.
Fragment how = XmlParserSax.howFragments.get(i);
If this function is returning new instance of a fragment, it should work.
Hope that helps
Edit :
I'm pretty sure the activity can access the button after the fragments are created.
Otherwise you need a handler to pass the click to handle it in the adapter. I'm seeing the your list of fragments are static (Not recommended). Since you haven't added any codes for how you setup the pageradapter, I have no idea what list you are using, but you need to change out the item in that list. From the Activity where you initialized the pager, you can call the public function to replace the current pager item pager. (you can use ViewPager.getCurrentItem())
I haven't tested, so you might have to tweak and play around to perfect it.
Hope this helps.
Here is a sample :
public static class MyAdapter extends FragmentStatePagerAdapter {
ArrayList<Fragment> fragmentArray = new ArrayList<Fragment();
public MyAdapter(FragmentManager fm) {
super(fm);
}
//didn't put in the function to populate the list with fragments
public void replaceItem(Fragment newFrag, int pos){
fragmentArray.remove(pos);
fragmentArray.add(pos,newFrag);
notifyDataSetChanged();
}
#Override
public int getCount() {
return fragmentArray.size();
}
#Override
public Fragment getItem(int position) {
return fragmentArray.get(position);
}
}