Changing tabs in TabLayout through code, with one obstacle - android

The idea is that I have a listener FAQFragment.OnQuestionSelectedListener that is attached to the main activity which holds a TabLayout. Now, I want every time I receive the event, to pass the event arguments to one of the fragments in the TabLayout. This is my code so far:
public class MainActivity extends AppCompatActivity implements FAQFragment.OnQuestionSelectedListener{
private SectionsPagerAdapter sectionsPagerAdapter;
private TabLayout tabLayout;
private ViewPager viewPager;
#Override
public void onQuestionSelected(String question) {
sectionsPagerAdapter.setFaq(question);
sectionsPagerAdapter.notifyDataSetChanged();
selectPage(1);
}
private void selectPage(int pageIndex) {
tabLayout.setScrollPosition(pageIndex,0f,true);
viewPager.setCurrentItem(pageIndex);
}
public class SectionsPagerAdapter extends FragmentPagerAdapter{
private String faq;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
faq = null;
}
public void setFaq(String faq) {
this.faq = faq;
}
#Override
public Fragment getItem(int position) {
if (position == 0) return FAQFragment.newInstance();
else if (position == 1) {
ChatFragment chatFragment = ChatFragment.newInstance(faq);
faq = null;
return chatFragment;
}
else if (position == 2) return SettingsFragment.newInstance();
return null;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0: return "FAQ";
case 1: return "Morty";
case 2: return "Settings";
default: return null;
}
}
#Override
public int getCount() {
return 3;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupTabLayout();
}
private void setupTabLayout() {
// adapter
sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// viewpager
viewPager = findViewById(R.id.container);
viewPager.setAdapter(sectionsPagerAdapter);
// tablayout
tabLayout = findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
// load chat initially
viewPager.setCurrentItem(1);
// listeners
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager));
}
}
The problem with this code is that it doesn't go through the FragmentPagerAdapter's getItemMethods() when tabs are swapped through code. Now, I know that I could do this using fragmentManager() to swap the Fragments when I receive the event, but when I did that, the transition was not smooth (there is a small flickering that grinds my gears). So is there any way to implement a similar logic that would pass a new Fragment Instance every time a tab is selected through viewPager.setCurrentItem or tabLayout.getItem(1).select ?

You can get FAQFragment, ChatFragment or SettingsFragment from ViewPager as described here. Once you got the needed Fragment you can manipulate whatever you want.

Related

Implement Listener with ViewPager

I have a Main Activity with a Tablayout, which contains 3 Fragments. There's a FAB in the Main Activity that sits on tops of all the Fragments as a I swipe. I need to create an interface, so that when the FAB is clicked, my ViewPager is alerted and performs a certain action. Does anyone know how I could create this listener?
The Listener should just return a boolean indicating that the Fab was clicked.
I added an Interface in my Main Activity but am not sure how to utilize it and connect it to my ViewPagerAdapter Class?
Here is my Main Activity:
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private ArrayList<Fragment> fragments = new ArrayList<>();
private ArrayList<String> titles = new ArrayList<>();
//MY LISTENER....
private static SwitchFragmentsListener switchFragsListener;
public static interface SwitchFragmentsListener{
public void doISwitch(boolean change);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout)findViewById(R.id.collapsing_toolbar);
setSupportActionBar(toolbar);
fragments.addAll(Arrays.asList(new Tasks(),new Calendar(),new Contacts()));
titles.addAll(Arrays.asList(getResources().getString(R.string.fragment_task_title),
getResources().getString(R.string.fragment_cal_title),getResources().getString(R.string.fragment_contacts_title)));
TabLayout tabLayout = (TabLayout)findViewById(R.id.tabs);
final FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab);
fab.hide();
viewPager = (ViewPager)findViewById(R.id.container);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(),fragments,titles);
viewPager.setAdapter(viewPagerAdapter);
viewPager.setCurrentItem(1);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
if (position == 1) {
fab.hide();
}else {
fab.show();
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
tabLayout.setupWithViewPager(viewPager);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fabAction();
}
});
if (savedInstanceState==null){
if (getSupportActionBar()!=null){
getSupportActionBar().setDisplayShowTitleEnabled(true);
}
}
}
public interface FragChangeListener{
public void doIChange(boolean change);
}
public void fabAction(){
int current = viewPager.getCurrentItem();
switch (current){
case 0:{
//NEED TO IMPLEMENT LISTENER HERE
break;
}
case 2:{
Toast.makeText(this, "IN TAB 3", Toast.LENGTH_LONG).show();
break;
}
}
}}
And My ViewPagerAdapter
public class ViewPagerAdapter extends FragmentPagerAdapter {
ArrayList<Fragment> fragments;
ArrayList<String> titles;
private Fragment mFragmentAtPos0;
private FragmentManager fragManager;
//NEED TO IMPLEMENT THE LISTENER IN THIS CLASS TELLING ME THAT THE BUTTON WAS CLICKED IN MAIN ACTIVITY
public ViewPagerAdapter(FragmentManager fm, ArrayList<Fragment> fragments, ArrayList<String>titles) {
super(fm);
this.fragments = fragments;
this.titles = titles;
fragManager = fm;
}
#Override
public Fragment getItem(int position) {
if (position == 0)
{
mFragmentAtPos0 = new AddContact();
fragManager.beginTransaction().replace(R.id.container,mFragmentAtPos0).commit();
notifyDataSetChanged();
return mFragmentAtPos0;
}else{
return fragments.get(position);
}
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
#Override
public int getItemPosition(Object object)
{
if (object instanceof Tasks && mFragmentAtPos0 instanceof AddContact)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
}
when the FAB is clicked, my ViewPager is alerted and performs a
certain action
I think you don't need to create listener in this case. You can detect the click FAB and pass it to the adapter directly. In your adapter, just create a method to get the action click of FAB.
In your ViewPagerAdapter, create method
public void clickFAB() {
// your code here
}
and in SwitchFragmentsListener, just call it:
ViewPagerAdapter viewPagerAdapter;
public void fabAction(){
int current = viewPager.getCurrentItem();
switch (current){
case 0:{
//NEED TO IMPLEMENT LISTENER HERE
viewPagerAdapter.clickFAB();
break;
}
case 2:{
Toast.makeText(this, "IN TAB 3", Toast.LENGTH_LONG).show();
break;
}
}
}}

Fragment is blank when the tab is reselected

Initially the fragment returns first and second tab's corresponding listview. But returns empty fragment for third tab. When i select the first tab once again empty fragment is getting displayed. No problem with listadapter
. When i set the adapter in second tab its working fine. Problem is with Mainactivity and in Fragment
Mainactivity
public class SwipemainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener
{
private TextView displayTextView;
public static SwipemainActivity instance;
TabLayout tabLayout;
ViewPager viewPager;
Context context;
FragmentAdapterClass fragmentAdapter;
public static Context getInstance() {
return instance;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swipemain);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
instance = this;
tabLayout = (TabLayout) findViewById(R.id.tabs);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setSupportActionBar(toolbar);
tabLayout.addTab(tabLayout.newTab().setText("NEW"));
tabLayout.addTab(tabLayout.newTab().setText("CONFIRMED"));
tabLayout.addTab(tabLayout.newTab().setText("FINISHED"));
fragmentAdapter = new FragmentAdapterClass(getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(fragmentAdapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab LayoutTab) {
viewPager.setCurrentItem(LayoutTab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab LayoutTab) {
}
#Override
public void onTabReselected(TabLayout.Tab LayoutTab) {
}
});
}
}
Fragment
public class FragmentAdapterClass extends FragmentStatePagerAdapter {
int TabCount;
public FragmentAdapterClass(FragmentManager fragmentManager, int CountTabs) {
super(fragmentManager);
this.TabCount = CountTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
Tab1 tab1 = new Tab1();
return tab1;
case 1:
Tab2 tab2 = new Tab2();
return tab2;
case 2:
Tab3 tab3 = new Tab3();
return tab3;
default:
return null;
}
}
#Override
public int getCount() {
return TabCount;
}
}
I never use this listener for changing the viewpager fragment. And use the setOffScreenPageLimit to tell the viewpager how many fragments needs to be saved in memory. This is how I always setup my fragments with tabbedlayout, and viewpager.
viewPager.setOffscreenPageLimit(AMOUNT_OF_FRAGMENTS);

How to prevent Fragment from loading data every time from server when swiping Tabs in TabLayout in android?

I am a newbie with Fragments. I have 5 different Fragmentsin my TabLayout. In each fragment I am loading data from the server.
My problem is as follows:
When the first fragment loads it calls the function and loads data from the server and displays on the RecyclerView.
Then When I swipe to the second tab the server data loads and so on. But when I swipe again to say fragment one the data is loading again from the server and the RecyclerView needs to be set again.
Is there anyway where I can load the server data only once (without saving it to a local database) and set the recyclerview only once so that no matter how many times I swipe the tab the data does not load again? I have referred here but i really didnt understand how to solve my problem.I am posting a snippet of my code.
TabActivity
public class TabActivity extends AppCompatActivity {
ViewPager viewpager;
Toolbar toolbar;
TabLayout tablayout;
ViewPagerAdapter viewPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tab_activity);
initialize();
}
private void initialize() {
viewpager = (ViewPager) findViewById(R.id.viewpager);
toolbar = (Toolbar) findViewById(R.id.toolbar);
tablayout = (TabLayout) findViewById(R.id.tablayout);
setSupportActionBar(toolbar);
viewPagerAdapter=new ViewPagerAdapter(getSupportFragmentManager());
viewpager.setAdapter(viewPagerAdapter);
tablayout.setTabsFromPagerAdapter(viewPagerAdapter);
tablayout.setupWithViewPager(viewpager);
}
}
class ViewPagerAdapter extends FragmentStatePagerAdapter implements ViewPager.OnPageChangeListener {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:return LocatedEvents.newInstance();
case 1:return QrCodeScanner.newInstance();
case 2:return NewsFeed.newInstance();
case 3:return CreatePoll.newInstance();
case 4:return MyProfile.newInstance();
}
return NewsFeed.newInstance();
}
#Override
public int getCount() {
return 5;
}
#Override
public CharSequence getPageTitle(int position) {
return "Tab " + (position + 1);
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
}
NewsFeed Fragment
public class NewsFeed extends Fragment {
#Nullable
public static NewsFeed newInstance() {
return new NewsFeed();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.newsfeed_fragment, container, false);
load_data();
return view;
}
}
Avoid creating new Fragments every time in
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return LocatedEvents.newInstance();
case 1:
return QrCodeScanner.newInstance();
case 2:
return NewsFeed.newInstance();
case 3:
return CreatePoll.newInstance();
case 4:
return MyProfile.newInstance();
}
return NewsFeed.newInstance();
}
Make an array of fragments and pass it to your Adapter.Something like this
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
initFragments();
}
private void initFragments() {
ArrayList < Fragment > fragmentArrayList = new ArrayList < > ();
fragmentArrayList.add(LocatedEvents.newInstance());
fragmentArrayList.add(QrCodeScanner.newInstance());
fragmentArrayList.add(NewsFeed.newInstance());
fragmentArrayList.add(MyProfile.newInstance();
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(), fragmentArrayList); viewpager.setAdapter(viewPagerAdapter); tablayout.setTabsFromPagerAdapter(viewPagerAdapter); tablayout.setupWithViewPager(viewpager);
}
And in your adapter change it to
private ArrayList < Fragment > fragments;
public ViewPagerAdapter(FragmentManager fm, ArrayList < Fragment > fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}

call method from activity in all fragment instance

I have PengaduanHomeActivity.class with 3 tabs using same Fragment PengaduanFragment.class like here :
public class PengaduanHomeActivity extends AppCompatActivity implements AppBarLayout.OnOffsetChangedListener {
....
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detail_profile_user);
appBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
actionbar.setHomeButtonEnabled(true);
tabLayout = (TabLayout) findViewById(R.id.tabs);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setOffscreenPageLimit(7);
mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mPagerAdapter);
tabLayout.setupWithViewPager(mViewPager);
appBarLayout.addOnOffsetChangedListener(this);
}
int index = 0;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
index = i;
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (index == 0) {
PengaduanFragment.enableRefreshLayout(true);
} else {
PengaduanFragment.enableRefreshLayout(false);
}
break;
}
return super.dispatchTouchEvent(ev);
}
public class PagerAdapter extends FragmentPagerAdapter {
private final String[] TITLES =
{"News Task", "Task On Progress", "Completed Task"};
private Bundle bundle;
public PagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public int getCount() {
return TITLES.length;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return PengaduanFragment.newInstance("0");
case 1:
return PengaduanFragment.newInstance("1");
case 2:
return PengaduanFragment.newInstance("2");
default:
return null;
}
}
}
in code above, i call method in PengaduanFragment
public static void enableRefreshLayout(boolean b) {
mSwipeRefreshLayout.setEnabled(b);
}
but method just called in last PengaduanFragment with position 2. Other PengaduanFragment with position 0 or 1, not called. So how to Called method enableRefreshLayout in all Fragment ? thaks
You should create an interface that declares the method you want to use:
public interface IMyInterface {
public void myMethod();
}
Then, each fragment you have should implement this method:
public class MyFragment extends Fragment implements IMyInterface {
//Fragment code
public void myMethod() {
//Method code
}
}
Finally, make your adapter have a collection of this interface and loop over it when invoked by the activity
public class MyAdapter extends FragmentPagerAdapter {
//Collection of fragments
Fragment[] fragments;
//Adapter code
public void invokeMe(){
//Loop through each fragment
if (fragment instanceof IMyInterface) {
fragment.myMethod();
}
}
}
You should then be able to call invokeMe and have the adapter delegate to the right fragments the right behaviour. Just be careful of if the fragment instance is still active as per your settings on your ViewPager's current offscreen limit (adjustable by calling setOffscreenPageLimit())

Navigating between Fragments while using a ViewPager

I've looked at so many questions here that I don't even know exactly what I'm looking for.
I have a simple app that uses a ViewPager. It has 3 tabs and there is a fragment for each tab. The first fragment contains a ListView. I want to be able to click on an element in the ListView and have that bring me to a different fragment.
So basically I want to remove the fragment that contained the ListView once an element is clicked and add in a new fragment. I've tried to do this in a few different ways with none working.
The last thing I tried was to edit the TabsPageAdapter once an element was clicked which pretty much works except when I press the back button it exits the app. Also it doesn't seem like the cleanest way of doing this.
TabsPagerAdapter
public class TabsPagerAdapter extends FragmentStatePagerAdapter {
SherlockFragment mf;
TalkingPointsFragment tpf;
ContactFragment gf;
int mode = 0;
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
mf = new CanvassFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
public TabsPagerAdapter(FragmentManager fm, int mode)
{
super(fm);
if(mode == 0)
{
mf = new CanvassFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
else if(mode == 1)
{
mf = new ContactFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
}
#Override
public SherlockFragment getItem(int index) {
switch (index) {
case 0:
return mf;
case 1:
return tpf;
case 2:
return gf;
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object)
{
}
}
The onclick code:
ViewPager viewp = (ViewPager) getActivity().findViewById(R.id.pager);
TabsPagerAdapter mAdapter = new TabsPagerAdapter(getActivity().getSupportFragmentManager(),1);
viewp.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
layout_main.xml
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:actionBarTabStyle="#drawable/actionbar_tab_indicator">
I FragmentStatePagerAdapter too and when a user selects map from the ActionBar, I add the GoogleMapsFragment on top of the FragmentStatePagerAdapter:
// create a new map
mapsFragment = GoogleMapFragment.newInstance();
// Then we add it using a FragmentTransaction.
FragmentTransaction fragmentTransaction = getSupportFragmentManager()
.beginTransaction();
fragmentTransaction.add(android.R.id.content, mapsFragment,
FRAGMENT_MAP_TAG);
fragmentTransaction.commit();
For your case you would probably need to add it to the backstack too, which I have not because in my app the user has to navigate back using the ActionBar.
Thought this approach could work for you too when a user selects an item from your list.
Of course this has the disadvantage of not being able to use the FragmentStatePagerAdapter until the user navigates back. So I am not sure wether that would be acceptable for your app.
Ok, so this took a bit more code that I imagined. Hope you get the idea:
public class MainClass extends FragmentActivity implements CanvassCallback {
// save a single reference to ViewPager and TabsPagerAdapter
private ViewPager mViewPager;
private TabsPagerAdapter mAdapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mViewPager = (ViewPager) findViewById(R.id.pager);
this.mAdapter = new TabsPagerAdapter(getSupportFragmentManager(), this);
mViewPager.setAdapter(mAdapter);
...
}
// from the CanvassCallback interface
public void itemSelected() {
mAdapter.canvassSelected();
mAdapter.notifyDataSetChanged();
}
#Override
public void onBackPressed() {
if (mViewPager.getCurrentItem() == 0 && mAdapter.isCanvassSelected() {
mAdapter.canvassSelected();
mAdapter.notifyDataSetChanged();
}
else {
super.onBackPressed();
}
}
}
Mockup of your CanvassFragment showing the callback
public class CanvassFragment extends SherlockFragment {
public interface CanvassCallback {
public void itemSelected();
}
private CanvassCallback canvassCallback;
public void setCanvassCallback(CanvassCallback canvassCallback) {
this.canvassCallback = canvassCallback;
}
...
// The onClick of your item
private void onClick() {
// notify your activity that an item was selected
canvassCallback.itemSelected();
}
}
The registeredFragments are not strictly needed but I think it provides some value if your need to call methoods on your Fragment from activity.
public class TabsPagerAdapter extends FragmentStatePagerAdapter {
// see upvoted answer from http://stackoverflow.com/questions/8785221/retrieve-a-fragment-from-a-viewpager
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
private boolean canvassSelected = false;
private CanvassCallback canvassCallback;
public TabsPagerAdapter(FragmentManager fm, CanvassCallback canvassCallback) {
super(fm);
this.canvassCallback = canvassCallback;
}
public void canvassSelected() {
canvassSelected = !canvassSelected;
}
public boolean isCanvassSelected() {
return canvassSelected;
}
#Override
public SherlockFragment getItem(int index) {
switch (index) {
case 0:
if (canvassSelected)
return new ContactFragment();
CanvassFragment canvassFragment = new CanvassFragment();
// this ensures that your Activity gets notified when an item is clicked
canvassFragment.setCanvassCallback(canvassCallback);
return canvassFragment;
case 1:
return new TalkingPointsFragment();
case 2:
return new ContactFragment();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Log.d(TAG, "instantiateItem " + position);
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem " + position);
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
}

Categories

Resources