As many applications I have MainActivity with ViewPager.
When I select option in NavigationDrawer I want to change one fragment in ViewPager. So to achieve that I created something like this:
Option click listener:
#Override
public void onItemClick(View view, int pos) {
mainActivity.changeFragment(pos);
}
MainActivity method:
public void changeFragment(final int key) {
mDrawerLayout.closeDrawers();
new Handler(Looper.getMainLooper()).post(new Runnable(){
#Override
public void run() {
mainPagerAdapter.setKey(key);
}
});
}
And MainPagerAdapter:
public class MainPagerAdapter extends FragmentStatePagerAdapter {
public final static int NUM_OF_PAGES = 2;
private int mCurrentFragmentKey = 0;
public MainPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if (position == HOME) {
if (mCurrentFragmentKey == 0)
return new Fragment1();
else
return new Fragment2();
} else
return new SecondViewPagerFragment();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public int getCount() {
return NUM_OF_PAGES;
}
public void setKey(int key) {
mCurrentFragmentKey = key;
notifyDataSetChanged();
}
Question:
Unfortunately it's not working properly because when I select option whole view stutters giving bad performance. How can I fix that?
Edit
I changed notifying pager that data set changed into this:
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
mainPagerAdapter.setKey(key);
}
}, 500);
And now everything works fine, but I'm not sure that is best solution. Could someone help me with this?
What about if you have various adapter that extends FragmentStatePagerAdapter and when you need to change the contents in the viewpager, you simple call pager.setAdapter(anotherAdapter)?
If setAdapter supply the views to be shown, changing the adapter probably will make the content of your viewpager change.
Related
I using Fragment set cameraView
There is an error this onResume
#Override
public void onResume() {
super.onResume();
if (isCameraAccessGranted() && isRecordAudioGranted() && isWriteStorageGranted()) {
cvCamera.start();
} else {
ActivityCompat.requestPermissions(getActivity(), VIDEO_PERMISSIONS, 0);
}
}
I doing Stepper 5 Page Fragment this is PagerAdapter
public class PagerAdapter extends FragmentStatePagerAdapter {
public PagerAdapter(FragmentManager supportFragmentManager, int behavior) {
super(supportFragmentManager, behavior);
}
#Override
public int getCount() {
return 6;
}
#Override
public Fragment getItem(int position) {
return new BlankFragmentTestCamera();
}
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}
Please help me.
ViewPager by default keeps previous and next fragment in the resumed state. So it's like one camera opened on three screens at the same time.
Please, try to
Use only 1 fragment with camera view
Use flag FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT for the ViewPager
I've prepared a small working demo, please, take a look
https://github.com/3akat/Ziggeo_CameraView_PagerDemo
my problem is pretty simple but I don't see how to solve it... I didn't find any response to this problem.
I have 2 viewPager and when a page is selected I want to know which viewPager it comes from in onPageSelected().
There is my code :
public class MainActivity {
private ViewPager mCardsPager;
private ViewPager mIpsPager;
private MyCreditCardsPagerAdapter mCardsAdapter;
private MyInstantPaiementPagerAdapter mIpsAdapter;
private List<PayMethod> mCards;
private List<PayMethod> mIps;
//fill Pager/Adapter/List
#Override
public void onPageSelected(int position) {
if () {//If the page comes from mCardsPager
//do Something with mCards
} else { //else the page comes from mIpsPager
//do Something with mIps
}
}
}
You can set different listeners for each ViewPager rather than implement on Activity class.
Like this
mCardsPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i1) {
}
#Override
public void onPageSelected(int i) {
}
#Override
public void onPageScrollStateChanged(int i) {
}
});
AND
mIpsPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i1) {
}
#Override
public void onPageSelected(int i) {
}
#Override
public void onPageScrollStateChanged(int i) {
}
});
Setting same OnPageChangeListener for multiple ViewPager will not work. You need to create a class which implements OnPageChangeListener. In this class add a constructor which takes ViewPager as an input parameter and hold the value in a data member. Finally, use this class for adding OnPageChangeListener
Main goal is to update Fragment info mainly from its own class.
Main activity:
public class MainActivity extends AppCompatActivity {
final Handler GUIHandler = new Handler();
final Runnable r = new Runnable()
{
public void run()
{
updateFragments();
GUIHandler.postDelayed(this, 1000);
}
};
#Override
protected void onPause() {
super.onPause();
GUIHandler.removeCallbacks(r);
}
#Override
protected void onResume() {
super.onResume();
GUIHandler.postDelayed(r, 600);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
...
mViewPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new PagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
mViewPager.setAdapter(mPagerAdapter);
...
}
private void updateFragments() {
mPagerAdapter.updateFragments();
}
PagerAdapter:
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNumOfTabs;
private Observable mObservers = new FragmentObserver();
public PagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
#Override
public Fragment getItem(int position) {
mObservers.deleteObservers(); // Clear existing observers.
switch (position) {
case 0:
FragmentWeather weatherTab = new FragmentWeather();
weatherTab.setActivity(mActivity);
if(weatherTab instanceof Observer)
mObservers.addObserver((Observer) weatherTab);
return weatherTab;
case 1:
FragmentMemo tab2 = new FragmentMemo();
return tab2;
case 2:
FragmentHardware tab3 = new FragmentHardware();
return tab3;
default:
return null;
}
}
public void updateFragments() {
mObservers.notifyObservers();
}
}
FragmentObserver
public class FragmentObserver extends Observable {
#Override
public void notifyObservers() {
setChanged(); // Set the changed flag to true, otherwise observers won't be notified.
super.notifyObservers();
Log.d("Observer", "Sending notification");
}
}
FragmentWeather:
public class FragmentWeather extends Fragment implements Observer {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
return layout;
}
public void setTemperatures(){
Log.d("Android", "setTemperatures is called");
}
#Override
public void update(Observable observable, Object data) {
setTemperatures();
}
}
Problem now is, that PagerAdapter::getItem() doesnt get called when Fragments are created at the start of application. That means WeatherFragment dont get associated with mObservers. If I swipe to the 3rd view and then swipe back, everything is working properly. How to restructurize this to make it working?
this line:
mObservers.deleteObservers(); // Clear existing observers.
is removing all the observers, but the method getItem gets called several times, that means only the last time it calls anything stays there. REMOVE this line.
Also, the following code is a very bad pattern and it will go wrong on several occasions:
case 0:
FragmentWeather weatherTab = new FragmentWeather();
weatherTab.setActivity(mActivity);
if(weatherTab instanceof Observer)
mObservers.addObserver((Observer) weatherTab);
return weatherTab;
that's because fragments get re-created by the system when necessary, so setActivity is pointless, so as is addObserver. The moment the system needs to destroy/recreate the fragments, you'll have a memory leak of those old fragments, the old activity, and the new ones won't have the activity and won't be on the observers.
The best situation here is to rely on the natural callbacks from the fragments. An example follows (ps.: that was typed by heart, I'm sure there might be some mistakes, but you'll get the idea)
public interface ObservableGetter{
public Observable getObservable();
}
public void MyFragment extends Fragment implements Observer {
#Override onAttach(Activity activity){
super.onAtttach(activity);
if(activity instanceof ObservableGetter){
((ObservableGetter)activity).getObservable().
addObserver(this);
}
}
#Overrude onDetach(){
Activity activity = getActivity();
if(activity instanceof ObservableGetter){
((ObservableGetter)activity).getObservable().
removeObserver(this);
}
super.onDetach();
}
}
then you can just make the activity implements ObservableGetter and have the Observable on it.
Then your adapter code will be just:
case 0:
return new FragmentWeather();
all the rest of the logic uses the regular callbacks.
I hope it helps.
I'm trying to get SlidingMenu to work alongside ViewPager and I have followed the examples inside the library to the letter. The source for my Activity can be found bellow.
The problem is that despite the fact that the SlidingMenu and ViewPager work fine together there is this problem that after opening the SlidingMenu for the first time and closing it afterwards the ViewPager starts acting weird.
By weird I mean when you swipe to right and left the UI gets stuck on one page and doesn't change and I mean it's only the UI as I can see in the logger that the pages actually change.
I have searched the web extensively and although there has been a lot of similar problems with implementing SlidingMenu alongside ViewPager, my problem seems to be unique.
Any help is appreciated.
public class MainTabActivity extends SlidingFragmentActivity {
private static final String TAG = "MainTabActivity";
#InjectView(R.id.tabHostPager)
ViewPager tabHostPager;
#InjectView(R.id.tabs)
PagerSlidingTabStrip tabStrip;
FragmentPagerAdapter pagerAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_tab);
setBehindContentView(R.layout.sliding_menu_layout);
loadSlidingMenu();
ButterKnife.inject(this);
pagerAdapter = new MainTabAdapter(getSupportFragmentManager());
tabHostPager.setAdapter(pagerAdapter);
//Assign the TabStrip it's ViewPager
tabStrip.setViewPager(tabHostPager);
tabStrip.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
switch (position) {
case 0:
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
break;
default:
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
break;
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
tabHostPager.setCurrentItem(0);
}
private void loadSlidingMenu() {
// configure the SlidingMenu
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
getSlidingMenu().setMode(SlidingMenu.LEFT);
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
getSlidingMenu().setShadowWidth(100);
getSlidingMenu().setFadeDegree(0.35f);
getSlidingMenu().setBehindWidth(width / 100 * 90);
}
private class MainTabAdapter extends FragmentPagerAdapter {
public MainTabAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 4;
}
#Override
public Fragment getItem(int position) {
return Friends.newInstance();
}
#Override
public CharSequence getPageTitle(int position) {
return "Home";
}
}
}
Finally I managed to solve it. It had something to with manageLayers method from the SlidingMenu class.
Commenting out the method body was all it took:
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void manageLayers(float percentOpen) {
/*if (Build.VERSION.SDK_INT < 11) return;
boolean layer = percentOpen > 0.0f && percentOpen < 1.0f;
final int layerType = layer ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE;
if (layerType != getContent().getLayerType()) {
getHandler().post(new Runnable() {
public void run() {
Log.v(TAG, "changing layerType. hardware? " + (layerType == View.LAYER_TYPE_HARDWARE));
getContent().setLayerType(layerType, null);
getMenu().setLayerType(layerType, null);
if (getSecondaryMenu() != null) {
getSecondaryMenu().setLayerType(layerType, null);
}
}
});
}*/
}
I need to change a TextView's text that is placed in the layout of my FragmentActivity that has a ViewPager and a ViewPagerAdapter. Basically I have created an action bar in my application that presents the name of the current visible Fragment.
So I need this TextView to be changed on each change of the visible fragment in the ViewPager (either with the swipe motion or selected using the TabHost). From my search I found that this method should help me achieve my goal:
#Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
Log.d(TAG, "setUserVisibleHint invoked!");
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser)
{
if (odTab != null && getActivity() != null)
{
Log.d(TAG, "Currently visable tab: "+odTab.getTabTitle());
Toast.makeText(getActivity(), "Currently visable tab: "+odTab.getTabTitle(), Toast.LENGTH_LONG).show();
if (application.getCurrentDataSource() == DataSource.SSRS)
{
if (rsTab.getTabTitle().equals("") || rsTab.getTabTitle() == null)
{
tabName.setVisibility(View.GONE);
}
else
{
tabName.setText(rsTab.getTabTitle());
}
}
else if (application.getCurrentDataSource() == DataSource.SGRDL)
{
Log.d(TAG, "found Title textView from fragmentActivity");
if (odTab.getTabTitle().equals("") || odTab.getTabTitle() == null)
{
((TabsViewPagerFragmentActivity)getActivity()).findViewById(R.id.tvTabName).setVisibility(View.GONE);
}
else
{
Log.d(TAG, "set Title textView to: "+odTab.getTabTitle());
((TextView) ((TabsViewPagerFragmentActivity)getActivity()).findViewById(R.id.tvTabName)).setText(odTab.getTabTitle());
}
}
}
}
}
I tried to implement it in my fragment but for some reason this line:
((TextView) ((TabsViewPagerFragmentActivity)getActivity()).findViewById(R.id.tvTabName)).setText(odTab.getTabTitle());
doesn't do anything although this line do get presented in the log for each change of fragment in the viewPager:
Log.d(TAG, "set Title textView to: "+odTab.getTabTitle());
UPDATE:
My adapter:
public class ViewPagerAdapter extends FragmentPagerAdapter
{
private List<Fragment> fragments;
/**
* #param fm
* #param fragments
*/
public ViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentPagerAdapter#getItem(int)
*/
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
/* (non-Javadoc)
* #see android.support.v4.view.PagerAdapter#getCount()
*/
#Override
public int getCount() {
return this.fragments.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Am I doing something wrong? is there another way to do this operation?
In your activity you can do:
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int pos) {
// Change your textview
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
});
Although I can't pinpoint the problem, I would suggest to simplify your code:
avoid cast within cast. You use TabsViewPagerFragmentActivity both in if and else. assign this cast to a parameter and use this parameter. The processing overhead worth it.
instead of using findViewById, save R.id.tvTabName as a local parameter in TabsViewPagerFragmentActivity and create a method setTitle(String title) in TabsViewPagerFragmentActivity. It will also help you to debug this call at the activity itself.
I agree with #mromer - try to access the fragment from the activity. To achieve this, make your FragmentPagerAdapter inherit the following class:
public abstract class SmartFragmentPagerAdapter extends FragmentPagerAdapter {
private SparseArray<String> mFragmentTags;
private FragmentManager mFragmentManager;
public SmartFragmentPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
mFragmentManager = fragmentManager;
mFragmentTags = new SparseArray<String>();
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
// record the fragment tag here.
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
// use this method in onPageSelected
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
public FragmentManager getFragmentManager() {
return mFragmentManager;
}
}
The way I finally managed to deal with this issue is combined with a few things:
1. First I declared an interface in my fragment:
public interface OnFragmetVisibleListener{
public void fragmetVisible(boolean visible);
}
2. Next I added this function as well to the fragment to know when it's attached to the ViewPager:
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
onFragmetVisibleListener = (OnFragmetVisibleListener) activity;
isAttached = true; //flag for whether this framgnet is attache to pager
} catch (ClassCastException e){
throw new ClassCastException(activity.toString() + " must implement interface OnFragmetVisibleListener");
}
}
3. I added the following method as well to know when the fragment is visible and set it's data object in the Application class:
#Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
Log.d(TAG, "setUserVisibleHint invoked!");
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser)
{
if (odTab != null && getActivity() != null)
{
Log.d(TAG, "Currently visable tab: "+odTab.getTabTitle());
application.currentVisibleTab = odTab;
}
if (isAttached)
{
onFragmetVisibleListener.fragmetVisible(true);
}
}
}
4. In my ViewPager's activity I let it implement the defined interface in step 1:
public class TabsViewPagerFragmentActivity extends FragmentActivity implements GridFragment.OnFragmetVisibleListenerr
5. Next I added the unimplemented method:
#Override
public void fragmetVisible(boolean visible)
{
if(visible)
{
setCurrentTabTitle(application.currentVisibleTab.getTabTitle());
}
}
6. when setCurrentTabTitle is:
public void setCurrentTabTitle(String title)
{
tvTabTitle.setText(title); //my TextView that's needs to be changed.
Log.d(TAG, "set tab title from activity: "+title);
}
And that is it, this is how I changed a view of the activity depending on the visible fragment of the ViewPager.