2

I'm developing an app that has some tabs,something like this app using vviewPager

Each tab is a Fragment, and each fragment displays a listview of articles, categories and some other information. What I'm trying to do is to when I tap in an item from the listview in a fragment, open a new Fragment with the full article or more info. I've read that this has to do with nested Fragments, and that I have to use the method getChildFragmentManager(). I am using a ViewPager to display my fragments in the tabs, this is my main Activity:

public class MainActivity extends FragmentActivity{
    private String tabs[]={"Tab1, Tab2,.<other Fragments>..,VideosFragment"};
    ViewPager viewPager=null;
    FragmentManager fragmentManager=getSupportFragmentManager();
    android.support.v4.app.FragmentTransaction ft=fragmentManager.beginTransaction();
    AdapterView adapterView;
    public static final int TIME_ENTRY_REQUEST_CODE=1;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager=(ViewPager)findViewById(R.id.pager);
        viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener(){
        public void onPageSelected(int position){
            getActionBar().setSelectedNavigationItem(position);
        }
    });
    viewPager.setAdapter(new AdapterView(fragmentManager));
    viewPager.setOffscreenPageLimit(tabs.length);
    adapterView=new AdapterView(getSupportFragmentManager());
    /*initialization of the action bar: color, icon & title.*/
    final android.app.ActionBar actionbar=getActionBar();
    ColorDrawable color=new ColorDrawable(Color.parseColor("#cd853f"));
    actionbar.setBackgroundDrawable(color);
    actionbar.setTitle("Title");
    ActionBar.TabListener tabListener=new ActionBar.TabListener() {
        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {

        }   


        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            viewPager.setCurrentItem(tab.getPosition());
            ft=getFragmentManager().beginTransaction();
            //ft.replace(layouts[tab.getPosition()], (adapterView.getItem(tab.getPosition()));
            ft.commit();

        }

        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {  

        }
    };
    /*Displaying Tabs for the app */
    for(int i=0;i<tabs.length;i++)
        actionbar.addTab(actionbar.newTab().setText(tabs[i]).setTabListener(tabListener));
    actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            actionbar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }
    });
}

} EDIT: and when I tap in an item of a listview, I want to open a new Fragment, this is my adapter for the listView:

public class ItemAdapter extends BaseAdapter implements OnItemClickListener{
Item item=new Item();
private ArrayList<Item> news=new ArrayList<Item>();
private static final int NO_PICTURE_VIEW=0;
private static final int PICTURE_VIEW=1;

public ItemAdapter(Context context,ArrayList<Item> items) {
    super();
    news.addAll(items);
}

@Override
public int getCount() {
    // TODO Auto-generated method stub
    return news.size();

}

@Override
public Item getItem(int index) {
    // TODO Auto-generated method stub
    return getItem(index);
}

@Override
public long getItemId(int index) {
    // TODO Auto-generated method stub
    return index;
}

public int getViewTypeCount() {
    return 2;
}

public int getItemViewType(int position) {
    Item article=news.get(position);
    //  if(article.getImageUrl()!=null&&!article.getImageUrl().equals("")&&article.getImageUrl().indexOf("no_pic.png")==-1)//if there's no pic    
    if(article.getImage()!=null)      
        return PICTURE_VIEW;
      else
          return NO_PICTURE_VIEW;
}
@Override
public View getView(int index, View view, ViewGroup parent) {
    item=news.get(index);
    int type=getItemViewType(index);
        if(view==null){
            LayoutInflater inflater=LayoutInflater.from(parent.getContext());
            if(type==NO_PICTURE_VIEW){
                view=inflater.inflate(R.layout.item_no_picture_list,parent,false);
                TextView titleView=(TextView)view.findViewById(R.id.titleNoPicture);
                titleView.setText(item.getTitle());
            }
            else{
                view=inflater.inflate(R.layout.item_picture_list,parent,false);
                TextView titleView=(TextView)view.findViewById(R.id.titleArticle);
                ImageView image=(ImageView)view.findViewById(R.id.imageItem);
                titleView.setText(item.getTitle());
                image.setImageBitmap(item.getImage());
            }
    }

    return view;
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) {
    Log.d("ItemAdapter","clicked on "+position);
}

}

This is one of the fragments in the ViewPager:

public class VideosFragment extends Fragment{

static ListView listvideo;
ItemAdapter itemAdapter;
Context context;
Activity activity;
FragmentManager fm=getChildFragmentManager();
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState){
       super.onCreateView(inflater, container, savedInstanceState);
       View view=inflater.inflate(R.layout.videos_activity, container,false);
       listvideo=(ListView)view.findViewById(R.id.videoslist);
       context=getActivity();
       setRetainInstance(true); 
       return view;
    }

public void onViewCreated(View view, Bundle savedInstanceState){
    super.onViewCreated(view, savedInstanceState);
    ViewPager vp=(ViewPager)view.findViewById(R.id.pager);
    vp.setAdapter(new AdapterView(fm));
}

public void displayVideos(final List<Item> videos){
    try {        
         if(videos==null || videos.size()==0){
            Log.d("videos activity","the list is null!!");          
         }  
     } catch (Exception e) {
         e.printStackTrace();
     }
    for(int i=0;i<videos.size();i++){

    }
    itemAdapter=new ItemAdapter(context,(ArrayList<Item>) videos);
    listvideo.setAdapter(itemAdapter);
    listvideo.setOnItemClickListener(new OnItemClickListener(){
        @Override
        public void onItemClick(android.widget.AdapterView<?> parent,
                View view, int position, long id) {
            Log.d("HomeActivity","Tapped on "+position);
            //FragmentTransaction ft=fm.beginTransaction();
            //Fragment vp=new VideoPlayer();
            //ft.add(R.id.videoview, vp);
            ///ft.addToBackStack("VideosActivity");
            //ft.commit();
        }
    });
}   

public static class MyAdapter extends FragmentPagerAdapter {

    public MyAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return 4;
    }

    @Override
    public Fragment getItem(int position) {

        return new VideoPlayer();//ChildFragment
    }
}
}

The commented lines for fragmenttransaction and the lines after that gave me a NPE, so I guess I'm missing something. VideoPlayer is the ChildFragment, and at the moment I only want to be able to display the fragment without info, just to know how it works. This is my adapter for the fragments:

class AdapterView extends FragmentPagerAdapter{
public static int NUM_ITEMS=3;
Tab1 tab1=new Tab1();
    Tab2 tab2=new Tab2();
VideoFragment va=new VideoFragment();

public AdapterView(FragmentManager fm) {
    super(fm);
}

@Override
public Fragment getItem(int i) {
    Log.d("FRAGMENT","EN: "+i);
    switch(i){
    case 0:
        return tab1;
    case 1:
        return tab2;
    case 2:
            return va;
    default:
        return null;
    }
}

@Override
public int getCount() {
    return  NUM_ITEMS;
}

}

I haven't found a tutorial for what I'm looking for, so I hope some of you can help me understand how it works. I know it's possible, since the Android API 4.2 allows it. I just want to know how the nested fragments work, and how to use them the way my apps needs it. Any information is appreciated, thanks.

Valery Viktorovsky
  • 5,832
  • 3
  • 32
  • 42
Rosie
  • 94
  • 2
  • 11
  • Why is your fragment named `VideosActivity`? – Kevin Coppock Jun 26 '14 at 17:06
  • Check my answer here http://stackoverflow.com/questions/13379194/how-to-add-a-fragment-inside-a-viewpager-using-nested-fragment-android-4-2/13381792#13381792 for a small example, I think this is what you want(?!) – user Jun 26 '14 at 17:07
  • I'm following some conventions, some documents someone provided me, but the structure is for a fragment, all are fragments... I don't think the name really matters – Rosie Jun 26 '14 at 17:08
  • It does matter, for clarity. If anyone else ever looks at your code, it's going to be very confusing to see a class called `VideosActivity` only to realize it's not an `Activity`. `VideosFragment` would be much better. – Kevin Coppock Jun 26 '14 at 17:24
  • @Luksprog thanks, but I'm still missing some things: WrapperFragment is the one attached at the beginning to the ViewPager (mainFragment), and why would you do a new instance of it. Also, I can see that you have two inner fragments, wich one is the one that will be used? And how would I call the new fragment when I tap in an article from the listView?? – Rosie Jun 26 '14 at 17:24
  • You need the `WrapperFragment` because you can't just do transactions with the fragments used by the `ViewPager`(so you need a "container" for the nested fragments). The pager adapter will only use the `WrapperFragment`. In that `WrapperFragment` you'll initially add as a nested fragment the normal fragment that you currently show(the list of stuff). When the user taps an item you'll propagate that event to the `WrapperFragment`(which holds the list of items) which will use the `getChildFragmentManager()` to replace the initial list fragment with the fragment containing the details. – user Jun 26 '14 at 17:36
  • 2
    Also, your list adapter's code is incorrect(ItemAdapter), you should never set the data in the `if` clause where you check the `convertView` for being null. I'm surprise that you didn't see any row repetition yet. And I second what kcoppock said, proper naming it's quite important to help others that try to help you(like naming the adapter of the `ViewPager` as `AdapterView`, a well know class in the android sdk which is connected to lists and grids, and not fragments). – user Jun 26 '14 at 17:40
  • Please specify where the NPE gets thrown. Is it the FragmentManager? You should set it in `onCreate()` – einschnaehkeee Jun 26 '14 at 19:07
  • @einschnaehkeee I got the NPE in the line `ft.commit()` and it said `illegalstateexception: activity has been destroyed`, and I commented it so I could run the app – Rosie Jun 26 '14 at 19:46
  • You've somehow mixed two different things. First you've implemented ActionBar tabs with ViewPager/Fragments and INSIDE those Fragments you also seem to have a ViewPager, which holds the same Fragments again. That's some kind if a loop you have there, very very strange. You should really start to understand what a ViewPager is by doing a Tutorial on the Android Developers page or create a new Android Project with the ADT wizard and start from there with a Fragment tab template. – einschnaehkeee Jun 26 '14 at 20:32

1 Answers1

8

If what you want is replace a fragment inside the viewpager with another fragment, take a look at this. However I would suggest you to create another Fragment (a container), and move your viewpager and fragments inside of this new fragment:

enter image description here

This way when the user clicks an item of a list, you can replace the MainFragment with a Details Fragment.

About the ChildFragmentManager(), it is used when you have nested fragments. If you change your implementation as I suggested, you would need to pass a ChildFragmentManager to your PagerAdapter:

viewPager.setAdapter(new AdapterView(getChildFragmentManager()));

Another thing about your commented lines of code inside VideosFragment. Make your activity commit the fragment transaction instead of doing it inside the Fragment. For a better explanation, read this.

Community
  • 1
  • 1
ILovemyPoncho
  • 2,622
  • 2
  • 19
  • 33