I'm writing my first fragment-based app and running into some heavy problems which i couldn't solve with the API or Stackoverflow.
I am using a viewPager to swipe between two lists. Each list has a header button to create a new list element (similar to the native android alarm app). The button returns currently an error message for debugging.
The problem is:
FragmentList A returns the debug message for FragmentList B
FragmentList B returns no debug message
... // The main class
public class DemoApp extends FragmentActivity implements ActionBar.TabListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Set up the action bar.
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
PageAdapter mPageAdapter = new PageAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mPageAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
for (int i = 0; i < mPageAdapter.getCount(); i++) {
actionBar.addTab(actionBar.newTab().setText(mPageAdapter.getPageTitle(i)).setTabListener(this));
}
}
...
My custom PageAdapter will create two list framgent objects:
public class PageAdapter extends FragmentStatePagerAdapter {
private final int NUMBER_PAGES = 2;
public PageAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new FoodListFragment();
} else if (position == 1){
return new LocationListFragment();
} else {
throw new IllegalArgumentException("Invalid page position: " + position);
}
}
#Override
public int getCount() {
return NUMBER_PAGES;
}
...
}
FoodListFragment snipped (The other list looks similar except the debug output) :
public class FoodListFragment extends ListFragment implements LoaderCallbacks<Cursor> {
/*
* (non-Javadoc)
*
* #see android.support.v4.app.Fragment#onActivityCreated(android.os.Bundle)
*/
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// add a list header with the button to create a new list item
View v = getActivity().getLayoutInflater().inflate(R.layout.list_header, null);
getListView().addHeaderView(v);
...
setListAdapter(getSomeAdapter());
// get the list header button
ImageButton createItem = (ImageButton) getActivity().findViewById(R.id.create_new_entry);
createItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e(LOG_TAG, "onclick FoodListFragment");
}
});
}
main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/wrapper"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".DemoApp" />
</LinearLayout>
I've found the bug. In each list i was calling the same header view and same header button. The onClick method could not be clearly assigned
Related
I have an activity that uses a ViewPager for multiple Fragments, the fragments controlled by a JSONArray object. When I add a new array and update the adapter, everything updates to the correct information except the TabLayout does not reflect the change until a scroll action takes place.
If I add a Fragment when I am on the current last fragment, a new frag is created and the model updates, but the TabLayout shows the current selected as the last tab. If you do a scroll motion, you can then see the new tab, but the view update is not automatic.
Has anyone run into this and do you have any ideas how to make the TabLayout view update?
Here is the Activity and Adapter:
#EActivity(R.layout.activity_displays)
public class DisplaysActivity extends FitActivity {
#ViewById(R.id.button_right)
FitButton rightButton;
#ViewById(R.id.displays_tabview)
TabLayout tabView;
ViewPager viewPager;
JSONArray huds;
DisplaysPagerAdapter mAdapter;
#AfterViews
public void AfterViews() {
huds = StandardHuds.getStandardHudPagerData();
mAdapter = new DisplaysPagerAdapter(getSupportFragmentManager());
viewPager = (ViewPager) findViewById(R.id.displays_pager);
viewPager.setAdapter(mAdapter);
viewPager.setOffscreenPageLimit(10);
tabView.setTabMode(TabLayout.MODE_SCROLLABLE);
tabView.setupWithViewPager(viewPager);
}
#Click(R.id.button_right)
void newHud() {
addHud();
}
public class DisplaysPagerAdapter extends FragmentPagerAdapter {
public DisplaysPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return LegendFragment_.builder().build();
} else {
JSONArray a = new JSONArray();
DisplayFragment frag = DisplayFragment_.builder().build();
try {
a = huds.getJSONArray(position - 1);
} catch (JSONException e) {
e.printStackTrace();
}
frag.setHudArray(a);
return frag;
}
}
#Override
public int getCount() {
return huds.length() + 1;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) {
return "Legend";
} else {
return "Display " + position;
}
}
}
public void addHud() {
JSONArray newArray = new JSONArray();
JSONArray rowArray = new JSONArray();
newArray.put(rowArray);
newArray.put(rowArray);
newArray.put(rowArray);
huds.put(newArray);
mAdapter.notifyDataSetChanged();
viewPager.refreshDrawableState();
tabView.refreshDrawableState();
}
}
xml of the ViewPager/TabLayout within the activity
<android.support.v4.view.ViewPager
android:id="#+id/displays_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/footer">
<android.support.design.widget.TabLayout
android:id="#+id/displays_tabview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="?attr/colorFitBg1"
app:tabIndicatorColor="?attr/colorFitPrimary"
app:tabSelectedTextColor="?attr/colorFitPrimary" />
</android.support.v4.view.ViewPager>
I have tried to update the tabView a few different ways, but the tab is still off of the screen. Changing the max offscreen count does not affect this problem.
Let me know if you have any ideas. Thank you in advance.
I am developing an Android app. In my app, I am using TabLayout with ViewPager. I need to update the Tabs of TabLayout and its fragments programmatically when an item at the bottom navigation bar is selected. I can update the tabs of TabLayout. But the fragments of pager are not updated.
This is the issue:
As you can see in the above, tabs are changed but its fragments are not changed. List Fragment is always displayed. All the fragments are just the same with the first tab selected. I mean fragments are not changing at all.
This is my XML file for the tabs and view pager:
<android.support.design.widget.AppBarLayout android:layout_height="wrap_content"
android:layout_width="match_parent" android:theme="#style/AppTheme.AppBarOverlay">
<android.support.design.widget.TabLayout
android:id="#+id/ma_tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="fixed" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:id="#+id/ma_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
This is my whole activity:
public class MainActivity extends AppCompatActivity {
private BottomBar bottomBar;
private TabLayout topTabLayout;
private ViewPager mainViewPager;
private MainPagerAdapter pagerAdapter;
private ArrayList<Fragment> pagerFragments;
private ArrayList<String> pagerTitleList;
protected TfrApplication app;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (TfrApplication)getApplication();
setContentView(R.layout.activity_main);
initialize();
initViews();
setUpViews();
}
private void initialize()
{
pagerFragments = new ArrayList<Fragment>();
pagerTitleList = new ArrayList<String>();
}
private void initViews()
{
bottomBar = (BottomBar)findViewById(R.id.ma_bottom_action_bar);
mainViewPager = (ViewPager)findViewById(R.id.ma_viewpager);
topTabLayout = (TabLayout)findViewById(R.id.ma_tab_layout);
}
private void setUpViews()
{
pagerAdapter = new MainPagerAdapter(getSupportFragmentManager(),pagerFragments,pagerTitleList);
mainViewPager.setAdapter(pagerAdapter);
topTabLayout.setupWithViewPager(mainViewPager);
setUpBottomBar();
}
private void clearPagerFragments()
{
if(pagerAdapter!=null && pagerFragments!=null && pagerFragments.size()>0)
{
pagerFragments.removeAll(pagerFragments);
pagerTitleList.removeAll(pagerTitleList);
pagerAdapter.notifyDataSetChanged();
}
}
private void setUpNewsPagerFragments()
{
NewsFragment latestNewsFragment = new NewsFragment();
Bundle latestNewsBundle = new Bundle();
latestNewsBundle.putInt(NewsFragment.FIELD_CATEGORY_ID,0);
latestNewsBundle.putInt(NewsFragment.FIELD_LEAGUE_ID,0);
latestNewsBundle.putInt(NewsFragment.FIELD_COUNTRY_ID,0);
latestNewsBundle.putInt(NewsFragment.FIELD_TEAM_ID,0);
latestNewsFragment.setArguments(latestNewsBundle);
pagerTitleList.add("LATEST");
pagerFragments.add(latestNewsFragment);
PrioritizedTeamsFragment teamsFragment = new PrioritizedTeamsFragment();
pagerTitleList.add("TEAMS");
pagerFragments.add(teamsFragment);
NewsFragment articlesFragment = new NewsFragment();
Bundle articlesBundle = new Bundle();
articlesBundle.putInt(NewsFragment.FIELD_CATEGORY_ID, 0);
articlesBundle.putInt(NewsFragment.FIELD_LEAGUE_ID, 0);
articlesBundle.putInt(NewsFragment.FIELD_COUNTRY_ID, 0);
articlesBundle.putInt(NewsFragment.FIELD_TEAM_ID, 0);
articlesFragment.setArguments(articlesBundle);
pagerTitleList.add("ARTICLES");
pagerFragments.add(articlesFragment);
TopFragment topFragment = new TopFragment();
pagerTitleList.add("TOP 10");
pagerFragments.add(topFragment);
}
private void setUpMatchesPagerFragments()
{
MatchesFragment matchesFragment = new MatchesFragment();
pagerTitleList.add("MATCHES");
pagerFragments.add(matchesFragment);
StatisticsFragment statisticsFragment = new StatisticsFragment();
pagerTitleList.add("STATISTICS");
pagerFragments.add(statisticsFragment);
}
private void setUpBottomBar()
{
bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
#Override
public void onTabSelected(int tabId) {
switch (tabId){
case R.id.bottom_tab_news:
clearPagerFragments();
setUpNewsPagerFragments();
pagerAdapter.notifyDataSetChanged();
break;
case R.id.bottom_tab_matches:
clearPagerFragments();
setUpMatchesPagerFragments();
pagerAdapter.notifyDataSetChanged();
topTabLayout.getTabAt(0).select();
break;
case R.id.bottom_tab_meme:
Toast.makeText(getBaseContext(),"MEME",Toast.LENGTH_SHORT).show();
break;
case R.id.bottom_tab_settings:
Toast.makeText(getBaseContext(),"Settings",Toast.LENGTH_SHORT).show();
break;
}
}
});
}
}
I showed the whole activity because code the whole activity dealing with that problem. Besides, the whole activity only contains code for creating tabs, view pager and bottom bar. All are connected.
This is my view pager adapter:
public class MainPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> fragmentList;
private ArrayList<String> titleList;
public MainPagerAdapter(FragmentManager fragmentManager,ArrayList<Fragment> fragmentsParam,ArrayList<String> titlesParam)
{
super(fragmentManager);
this.fragmentList = fragmentsParam;
this.titleList = titlesParam;
}
#Override
public int getCount() {
return fragmentList.size();
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public CharSequence getPageTitle(int position) {
return titleList.get(position);
}
}
Why is the content or fragments of view pager are not changed when tabs are changed? What is wrong with my code?
As you can see on bottom item selected listener I tried to set the selected fragment like this:
topTabLayout.getTabAt(0).select();
I tried this as well:
mainViewPager.setCurrentItem(0);
Both are not working.
try to add a method to swap the fragment-list and call notifyDataSetChanged() in your adapter like this:
public void notifyDataSetChanged(List<Fragment> list) {
fragmentList.removeAll();
fragmentList.addAll(list);
notifyDataSetChanged();
}
changing data in activity and call adapter.notifyDataSetChanged() may fail to update your view.You can log in getItem() to check if the adapter knows the data set has changed.
Just Check which layout you are inflating in MatchesFragment class.
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
v = inflater.inflate(R.layout.matches_fragment, container, false);
}
If your tab is set correctly, under the code where the tab is set, run this again:
mainViewPager.setAdapter(pagerAdapter);
Here is my code:
selectedTab = 0;
tabLayout.getTabAt(selectedTab).select();
viewPager.setAdapter(tabsAdapter);
I make a simple example of tab view using fragment and pager .I want to send to send data from one fragment to another fragment when user use tab button. I will give you more detail I have one list view in one tab . In that i take one array list to display the data in list view.I want to send that list view in another fragment . So I make one interface and implement it on main activity .But I don’t know how to send on which event to send fragment .when user select tab I want to send array list to second fragment
here is my code
mainActivity
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
ViewPager viewPager;
FragmentpagerAdapter fragmentpagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ActionBar actionBar =getActionBar();
viewPager = (ViewPager) findViewById(R.id.pager);
fragmentpagerAdapter =new FragmentpagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(fragmentpagerAdapter);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.addTab(actionBar.newTab().setText("Stations").setTabListener(this));
actionBar.addTab(actionBar.newTab().setText("fav Station").setTabListener(this));
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i1) {
}
#Override
public void onPageSelected(int i) {
actionBar.setSelectedNavigationItem(i);
}
#Override
public void onPageScrollStateChanged(int i) {
}
});
}
#Override
public void onTabSelected(ActionBar.Tab tab, android.app.FragmentTransaction ft) {
Fragmentone fragmentOne = (Fragmentone) getSupportFragmentManager().findFragmentByTag(makeFragmentName(R.id.pager,0));
// get your arraylist using method of FragmentOne
ArrayList<DataModel> yourArrayList = fragmentOne.getData();
// refer your second fragment and set the above arraylist in that
FragmentTwo fragmentTwo = (FragmentTwo) getSupportFragmentManager().findFragmentByTag(makeFragmentName(R.id.pager, 1));
fragmentTwo.setData(yourArrayList);
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, android.app.FragmentTransaction ft) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, android.app.FragmentTransaction ft) {
}
private static String makeFragmentName(int viewPagerId, int index) {
return "android:switcher:" + viewPagerId + ":" + index;
}
}
fragmentone
public class Fragmentone extends Fragment {
ArrayList<DataModel> name;
boolean isPressed=false;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_one, container, false);
name=new ArrayList<DataModel>();
name.add(new DataModel("First Station",false));
name.add(new DataModel("Second Station",false));
ListView listView = (ListView) view.findViewById(R.id.list_view);
CustomAdapter customAdapter =new CustomAdapter(getActivity(),name);
listView.setAdapter(customAdapter);
return view;
}
public ArrayList getData() {
return name;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
} catch (Exception e) {
e.printStackTrace();
}
}
}
FragmentTwo
public class FragmentTwo extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_two, container, false);
}
public void methodInFragmentB(ArrayList arrayList){
for (int i=0;i<arrayList.size();i++){
}
}
public void setData(ArrayList<DataModel> yourArrayList){
Toast.makeText(getActivity(), "ArrayList Size: " + yourArrayList.size(), Toast.LENGTH_SHORT).show();
}
}
PagerAdaper
public class FragmentpagerAdapter extends FragmentPagerAdapter {
public FragmentpagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
switch (i) {
case 0:
return new Fragmentone();
case 1:
return new FragmentTwo();
default:
break;
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
customAdapater
public class CustomAdapter extends BaseAdapter implements View.OnClickListener {
private Activity activity;
private ArrayList data;
private static LayoutInflater inflater = null;
boolean isPressed=false;
public CustomAdapter(Activity a, ArrayList d) {
/********** Take passed values **********/
activity = a;
data = d;
inflater = (LayoutInflater) activity.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
if (data.size() <= 0)
return 1;
return data.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public void onClick(View v) {
}
/*********
* Create a holder Class to contain inflated xml file elements
*********/
public static class ViewHolder {
public TextView text;
public ImageButton imageButton;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View vi = convertView;
final ViewHolder holder;
if (convertView == null) {
/****** Inflate tabitem.xml file for each row ( Defined below ) *******/
vi = inflater.inflate(R.layout.row_layout, null);
/****** View Holder Object to contain tabitem.xml file elements ******/
holder = new ViewHolder();
holder.text = (TextView) vi.findViewById(R.id.station_name);
holder.imageButton = (ImageButton) vi.findViewById(R.id.favorite);
holder.imageButton.setBackgroundResource(R.drawable.off);
/************ Set holder with LayoutInflater ************/
vi.setTag(holder);
} else
holder = (ViewHolder) vi.getTag();
if (data.size() <= 0) {
holder.text.setText("No Data");
} else {
DataModel dataModel = (DataModel) data.get(position);
/************ Set Model values in Holder elements ***********/
holder.text.setText(dataModel.getText());
// this is for overall row click
vi.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("row is click","row click"+position);
}
});
// this is for image button onclick
holder.imageButton.setOnClickListener(new View.OnClickListener() {
DataModel dataModel = (DataModel) data.get(position);
#Override
public void onClick(View v) {
if(dataModel.isselected()){
holder.imageButton.setBackgroundResource(R.drawable.off);
dataModel.setIsselected(false);
}else{
holder.imageButton.setBackgroundResource(R.drawable.on);
dataModel.setIsselected(true);
}
isPressed = !isPressed; // reverse
}
});
;
}
return vi;
}
}
MainActivity.xml
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/pager"
>
</android.support.v4.view.ViewPager>
fragmentone.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#325633"
>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/list_view">
</ListView>
</LinearLayout>
Fragmenttwo.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#ee2333">
</LinearLayout>
rowlayout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:descendantFocusability="blocksDescendants">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/station_name"
android:padding="10dp"
android:textColor="#eee345"
android:textAppearance="?android:textAppearanceLarge"
/>
<ImageButton android:id="#+id/favorite"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="#00ffffff"
/>
</LinearLayout>
I need to to send ArrayList name; in second fragment ..I used interface but I don't know on event how to get value of that array list when user swipe or tab on tab view in second fragment?
Here i am posting some code that might help, see the scenario is like this,
Your tabs are in your Activity so the click and swipe events would be handled there so declaring Interface will not help as you can not fire that Callback method in Tab swipe or click, so what you can do is create a method in FragmentOne which will return you ArrayList like below
public ArrayList<String> getData(){
return yourArrayList();
}
now in FragmentTwo create a method that will receive the ArrayList from FragmentOne like below
public void setData(ArrayList<String> yourArrayList){
Toast.makeText(YourActivity.this,"ArrayList Size: "+yourArrayList.size(),Toast.LENGTH_SHORT).show();
}
Now about how to reference the Fragments in your Activity you can do something like this:
private static String makeFragmentName(int viewPagerId, int index) {
return "android:switcher:" + viewPagerId + ":" + index;
}
Now in Tab Swipe or click call above method to Refer your fragments like below
ArrayList<String> yourArrayList = new ArrayList<>();
FragmentOne fragmentOne = getSupportFragmentManger().findFragmentByTag(makeFragmentName(viewPagerId,0))
if(fragmentOne != null){
// get your arraylist using method of FragmentOne
yourArrayList = fragmentOne.getData();
}
// refer your second fragment and set the above arraylist in that
FragmentTwo fragmentTwo = getSupportFragmentManger().findFragmentByTag(makeFragmentName(viewPagerId,1))
if(fragmentTwo != null){
fragmentTwo.setData(yourArrayList);
}
and you are done
see above 0 and 1 are index of fragments in adapter you will need to manage that
let me know if you need further help
In your first fragment named Fragmentone, do something like this:
if(callBack!=null){
callBack.sendData(yourDataArrayList);
}
After doing this see what are you getting in log.
Can it be possible to slide the viewpager half of the screen?
My ultimate goal is to display two list view at a time, after first page slide, left list would be the previous list.
So Like as below..
list1,list2
list2,list3
list3,list4
Any solutions?
Thanks
Okay, I am going to take a stab at this. I accomplished what (I think) you are trying to do. My application has 3 ListViews, and each list contains different content fetched from an online source and populates a ViewPager using custom adapters and ListViews. The custom adapter is then assigned to a fragment on a PagerAdapter. I copied a lot of my code from a Google resource, and will try to outline what I did.
First, I added a ViewPager to my layout for my MainActivity
activity_main.xml:
<android.support.v4.view.ViewPager
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- add a PagerTitleStrip -->
<android.support.v4.view.PagerTitleStrip
android:id="#+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"/>
</android.support.v4.view.ViewPager>
Then, I created a separate ListView layout I could use for my custom adapters:
listview.xml
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#E6E6E6"
android:background="#E6E6E6"
tools:context=".MainActivity" />
After I had these set, I dug into my activity. The rest takes place within MainActivity.java:
First, lay out some variables:
public class MainActivity extends FragmentActivity implements OnNavigationListener {
// your pager adapter
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
// your custom adapters (look this up on your own if you do not understand)
ArrayList<ListEntry> listOneArrayList = null;
ArrayList<ListEntry> listTwoArrayList = null;
CustomAdapterListOne customAdapterListOne = null;
CustomAdapterListTwo customAdapterListTwo = null;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// more on that in the next block...
}
}
Now, let's get into onCreate() and start creating!
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// set up your pager adapter
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mViewPager.setAdapter(mSectionsPagerAdapter);
// if you want to set a default view:
mViewPager.setCurrentItem(0);
// now, run some AsyncTasks to load up our lists
// I use AsyncTasks because I fetch my data from a server
new generateListOne().execute();
new generateListTwo().execute();
}
/*
* Get the entries and create a list adapter
*/
private class generateListOne extends AsyncTask<String, Void, Object> {
#Override
protected Object doInBackground(String... args) {
listOneArrayList = new ArrayList<ListEntry>();
// this is where I would do all of my networking stuff
// and populate my arraylist
return null;
}
#Override
protected void onPostExecute(Object result) {
// you have to create a new xml layout for 'listview_row' to use here v
customAdapterListOne = new CustomAdapterListOne(self, R.layout.listview_row, listOneArrayList);
/** Very important! This is where you specify where the list goes: **/
// * Note: Fragment pages start at 0!
ListSectionFragment fragment = (ListSectionFragment) getSupportFragmentManager().findFragmentByTag(
"android:switcher:"+R.id.viewpager+":0"); // <- this is where you specify where the list goes
if (fragment != null) { // <- Could be null if not instantiated yet
if(fragment.getView() != null) {
customAdapterListOne.notifyDataSetChanged();
fragment.updateListOneDisplay(customAdapterListOne);
}
}
}
}
I'm not going to write out generateListTwo(), but hopefully you understand the concept from generateListOne(). Pay very close attention to what is happening in onPostExecute(). Now, we have to write out the FragmentPagerAdapter and our ListSection Fragment. Also, we have to include our custom list Adapter. All of that stuff follows:
/*
* Your Custom Adapter Class
*/
private class CustomAdapterListOne extends ArrayAdapter<ListEntry> {
/*
* Read up on the rest of this for custom adapter if you
* are unfamilar. There are plenty of resources..
*
* I am not going to type it all out.
*/
}
/*
* SectionsPagerAdapter class for FragmentPagerAdapter title
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new ListSectionFragment();
Bundle args = new Bundle();
args.putInt(ListSectionFragment.ARG_SECTION_NUMBER, i + 1);
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
// make sure this is correct
int yourNumberOfLists = 5;
return yourNumberOfLists;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0: return "First List";
case 1: return "Second List";
//case 2: etc..
}
return null;
}
public boolean onInterceptTouchEvent(MotionEvent event) {
return false;
}
}
/*
* ListSectionFragment class for ListFragment(s)
*/
public static class ListSectionFragment extends ListFragment {
public static final String ARG_SECTION_NUMBER = "section_number";
public static int CURRENT_SECTION = 0;
static ListSectionFragment newInstance(int num) {
ListSectionFragment fragment = new ListSectionFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
public void updateListOneDisplay(ArrayAdapter<ListEntry> listOneAdapter) {
setListAdapter(listOneAdapter);
}
public void updateListTwoDisplay(ArrayAdapter<ListEntry> listTwoAdapter) {
setListAdapter(listTwoAdapter);
}
// etc..
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle args = getArguments();
CURRENT_SECTION = args.getInt(ARG_SECTION_NUMBER);
// note, we are using your listview here v
View view = inflater.inflate(R.layout.listview, container, false);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// and just for kicks:
Log.i(TAG, "Item clicked: " + position);
}
}
Don't forget your last } to close out the MainActivity.java class. Hopefully this helps someone, I know it took me forever to figure out. The effect that this code provides is similar to that of the Android Place application.
Edit: I forgot to mention when the list loads. When a list gains focus, it also loads the previous and next list. This makes it possible to transition to it and have it already be there ready to go. For example:
You go to list 2 and list 1 and list 3 are loaded. You then go to list 3 (and it transitions smoothly because it is loaded already), and list 4 and list 2 are loaded. This ensures that when you transition to a new list, it is already loaded or in the process of being generated.
For ActionBarSherlock I would like to have (Action Bar) Tabs + Pager. I use Fragments inside that pager container. I already got the examples of http://actionbarsherlock.com/ working, but I can't manage to get a details fragment inside that pager container when I would click on let's say a listitem in the first fragment.
Is it impossible to have something like this:
Activity with Tabs and pager container
Fragment A inside pager container under Tab1
Click on something in Fragment A and show Fragment B in same pager container under Tab1.
Fragment A is then not visible, only Fragment B is visible, but also all the Tabs.
At the moment I think only a new activity (which would hold Fragment B inside it) can be started after clicking something in Fragment A.
Here is my solution for the (Tabs + Fragment + ViewPager) it is works for me as i wanted,
hope that works for you as well
here is the xml file
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="5" />
<FrameLayout
android:id="#+id/fragment_details"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="4.3" />
</LinearLayout>
here is the code for MainActivity.java I'll post relevant code only so you'll have to manage it
public class MainActivity extends FragmentActivity implements
DialogInterface.OnDismissListener, TabDataResponder {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
artistTab = getSupportActionBar().newTab().setText(
R.string.tab_name_artist);
albumTab = getSupportActionBar().newTab().setText(
R.string.tab_name_album);
songTab = getSupportActionBar().newTab().setText(
R.string.tab_name_songs);
map = new HashMap<String, Integer>();
mViewPager = (ViewPager) findViewById(R.id.pager);
FrameLayout deatil = (FrameLayout) findViewById(R.id.fragment_details);
mDualPane = (deatil != null) && (deatil.getVisibility() == View.VISIBLE);
mTabsAdapter = new TabsAdapter(this, getSupportActionBar(), mViewPager);
if (savedInstanceState != null) {
flag = true;
index = savedInstanceState.getInt("index");
}
setUpTabView();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("index", getSupportActionBar()
.getSelectedNavigationIndex());
}
private void setUpTabView() {
mTabsAdapter.addTab(artistTab, ArtistFragment.class, null);
mTabsAdapter.addTab(albumTab, AlbumFragment.class, null);
mTabsAdapter.addTab(songTab, SongFragment.class, null);
getSupportActionBar().setSelectedNavigationItem(index);
}
public static class TabsAdapter extends FragmentPagerAdapter implements
ViewPager.OnPageChangeListener, ActionBar.TabListener {
private FragmentActivity mContext;
private ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<String> mTabs = new ArrayList<String>();
private TabDataResponder responder;
public TabsAdapter(FragmentActivity activity, ActionBar actionBar,
ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = actionBar;
mViewPager = pager;
// TabDataResponder is an interface which is implemented in MainActivity
// You can find implementation # the last
responder = (TabDataResponder) activity;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
//I have used map to save state of the fragment
map.put(SongFragment.TYPE_FRAGMENT.trim(), 0);
map.put(AlbumFragment.TYPE_FRAGMENT.trim(), 0);
map.put(ArtistFragment.TYPE_FRAGMENT.trim(), 0);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
mTabs.add(clss.getName());
// mArgs.add(args);
mActionBar.addTab(tab.setTabListener(this));
notifyDataSetChanged();
}
#Override
public int getCount() {
return mTabs.size();
}
#Override
public Fragment getItem(int position) {
return Fragment
.instantiate(mContext, mTabs.get(position), /*
* mArgs.get(
* position)
*/null);
}
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
Log.i(TAG, "PageSelected....");
mActionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
Log.i(TAG, "ScrollSateChanged....");
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
String a = null;
if (mDualPane) {
a = mTabs.get(tab.getPosition());
responder.loadData(a, map.get(a));
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
Log.i(TAG, "Tab is released now....");
}
}
#Override
public void onDismiss(DialogInterface dialog) {
setUpTabView();
}
//This interface must be call from fragment class
//# the time of event you want to show detail
// pass the class name in the type argument using class.getName() method
#Override
public void loadData(String type, int index) {
DetailFragment viewer = (DetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.fragment_details);
if (mDualPane) {
if (viewer == null || viewer.getShownIndex() != index
|| viewer.getTypeFragment() != type) {
DetailFragment df = DetailFragment.newInstance(index, type);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_details, df)
.setTransition(
FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
map.put(type.trim(), index);
}
} else {
Intent intent = new Intent();
intent.setClass(MainActivity.this, DetailActivity.class);
intent.putExtra("index", index);
intent.putExtra("type", type);
startActivity(intent);
}
}
}
and here is how i deal with detail fragment not very efficient but kind of working
public class DetailFragment extends Fragment{
public static DetailFragment newInstance(int index, String TYPE_FRAGMENT) {
DetailFragment f = new DetailFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
args.putString("type", TYPE_FRAGMENT);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
public String getTypeFragment(){
String a = getArguments().getString("type");
return a;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//template is blank layout
View view = inflater.inflate(R.layout.template, container, false);
if(getTypeFragment().equals(ArtistFragment.TYPE_FRAGMENT)){
view = null;
view = inflater.inflate(R.layout.artist_details, container, false);
//....
}
else if(getTypeFragment().equals(AlbumFragment.TYPE_FRAGMENT)){
//do's for album fragment
}
else if(getTypeFragment().equals(SongFragment.TYPE_FRAGMENT)){
//do's for song fragment
}
return view;
}
}
do not save the state of tab in their individual fragment it will conflict, we are already doing it here
EDIT:
Cheered too soon. Now the details_container is not a viewpager and I cannot use it to 'swipe tabs'.
Found it! Just had to define two FrameLayouts, with in the first one the ViewPager and in the second the details fragments can be 'loaded'. This is done by adding fragments dynamically and replace them.
First the two FrameLayouts:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fadingEdge="none" >
<FrameLayout
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
<FrameLayout
android:id="#+id/details_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Then replace a fragment dynamically:
// Create new fragment and transaction
Fragment detailsFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment container view with this fragment
// and add the transaction to the back stack
transaction.replace(R.id.details_container, detailsFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
Very simple and I don't understand why it took me hours to figure this out..
I still did not find a possibility to have a Pager container where fragments should be loaded in and also keep the (ActionBar) Tabs. I have however found a really dirty solution to acomplish this, with starting intens (Main Activity with the Tabs) and finishing the previous ones when the backbutton doesn't need it anymore.
I adapted the code from ABS: Support Demos - Tabs and Pager. But again it's really dirty:
LoaderCursorSupport.CursorLoaderListFragment under Tab2
#Override public void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = new Intent();
intent.setClass(getActivity(), ActionBarTabsPager.class);
intent.putExtra("index", position);
intent.putExtra("fragment", "details");
intent.putExtra("tab", 1);
ActionBarTabsPager.mPreviousActivity = getActivity();
startActivity(intent);
ActionBarTabsPager (Main Activity with Tabs)
public class ActionBarTabsPager extends FragmentActivity {
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
static Activity mPreviousActivity;
static Activity mActivity;
static int mTabPosition = -1;
static Boolean mTabRefreshed = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.actionbar_tabs_pager);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab1 = getSupportActionBar().newTab().setText("Tab 1");
ActionBar.Tab tab2 = getSupportActionBar().newTab().setText("Tab 2");
ActionBar.Tab tab3 = getSupportActionBar().newTab().setText("Tab 3");
ActionBar.Tab tab4 = getSupportActionBar().newTab().setText("Tab 4");
String fragment = "";
try {
Bundle bundle = this.getIntent().getExtras();
fragment = bundle.getString("fragment");
mTabPosition = bundle.getInt("tab");
} catch (Exception ex) {
}
mViewPager = (ViewPager) findViewById(R.id.pager);
mTabsAdapter = new TabsAdapter(this, getSupportActionBar(), mViewPager);
mTabsAdapter.addTab(tab1, FragmentStackSupport.CountingFragment.class);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) {
mTabsAdapter.addTab(tab2, FragmentStackSupport.CountingFragment.class);
mTabsAdapter.addTab(tab3, FragmentStackSupport.CountingFragment.class);
mTabsAdapter.addTab(tab4, FragmentStackSupport.CountingFragment.class);
} else {
if (!fragment.contains("details")) {
mTabsAdapter.addTab(tab2, LoaderCursorSupport.CursorLoaderListFragment.class);
} else {
mTabsAdapter.addTab(tab2, ExampleFragment.class);
}
mTabsAdapter.addTab(tab3, LoaderCustomSupport.AppListFragment.class);
mTabsAdapter.addTab(tab4, LoaderThrottleSupport.ThrottledLoaderListFragment.class);
}
if (savedInstanceState != null) {
getSupportActionBar().setSelectedNavigationItem(savedInstanceState.getInt("index"));
}
if (mTabPosition > -1) {
mTabsAdapter.setPrimaryItem(mTabPosition);
mActivity = this;
}
}
Inside this Class there's a TabsAdapter
public static class TabsAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener, ActionBar.TabListener {
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<String> mTabs = new ArrayList<String>();
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mTabPosition > -1 && mTabRefreshed) {
int tabPosition = tab.getPosition();
if (mTabPosition != tabPosition) {
if (mPreviousActivity != null) {
mPreviousActivity.finish();
mTabRefreshed = false;
mPreviousActivity = null;
mTabPosition = -1;
Intent intent = new Intent();
intent.setClass(mContext, ActionBarTabsPager.class);
intent.putExtra("fragment", "home");
intent.putExtra("tab", tabPosition);
mActivity.startActivity(intent);
mActivity.finish();
}
}
}
mViewPager.setCurrentItem(tab.getPosition());
}
Can this be done simpler? Or should I just give up on having Tabs together with fragment history? This was done before Android 3.0 with ActivityGroups and Activities, but it seems this can't be done with fragments.
I found the other good example of the same implementation in hear... https://github.com/UweTrottmann/SeriesGuide
In this example under package com.battlelancer.seriesguide.ui
you can find UpcomingRecentActivity.java, and UpcomingFragment.java
and layout upcoming_multipan.xml
this example works for me...
I got one problem while adding different content for detail-fragment the different tabs, it gives me class-cast-exception
so i implemented a common detalFragment class and created separate layout in onCreateView method
but the only one problem i found is layout is not changing on tab switched, may be need to do it by implementing some listener
I'll tell you when i found the answer.