I would like to create an activity which shows 3 tabs. The first tab should show a user's uploaded photos the 2nd all the images a user liked, and 3rd the user's favorite images. All these data comes from a webservice.I would like to use the same fragment with it's layout file and call different URLs. The fragment implements a StringReader and populates a listview inside.
My problem is that I managed to load the Fragment in the first tab, but the second and third tabs remains empty. If I navigate to the last tab and started to navigate back, then the first tab remains empty and the second gets populated. I watched so many tutorials and red what I could reached but It seems that nothing has helped.
The Fragment instantiation in my Activity looks like this :
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
switch (position) {
case 0:
return UsersSpheresFragment.newInstance();
case 1:
return UsersSpheresFragment.newInstance();
case 2:
return UsersSpheresFragment.newInstance();
default:
return null;
}
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "SECTION 1";
case 1:
return "SECTION 2";
case 2:
return "SECTION 3";
}
return null;
}
}
my fragment is :
public class UsersSpheresFragment extends Fragment {
CircleImageView userImageFragment;
TextView userNameTextFragment;
TextView nrOfSpheresFragment;
LinearLayout loadingLayoutFragment;
ListView listview_spheresFragment;
public PhotosphereListAdapter adapter;
TextView nrOfLikes;
TextView nrOfSeens;
String userId="";
String userName = "";
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_spheres_fragment,container,false);
return view;
}
// newInstance constructor for creating fragment with arguments
public static UsersSpheresFragment newInstance() {
UsersSpheresFragment fragmentFirst = new UsersSpheresFragment();
return fragmentFirst;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
userImageFragment = (CircleImageView) getActivity().findViewById(R.id.userImageFragment);
userNameTextFragment = (TextView) getActivity().findViewById(R.id.userNameTextFragment);
nrOfSpheresFragment = (TextView) getActivity().findViewById(R.id.nrOfSpheresFragment);
loadingLayoutFragment = (LinearLayout) getActivity().findViewById(R.id.loadingLayoutFragment);
listview_spheresFragment = (ListView) getActivity().findViewById(R.id.listview_spheresFragment);
nrOfLikes = (TextView)getActivity().findViewById(R.id.nrOfLikes);
nrOfSeens = (TextView)getActivity().findViewById(R.id.nrOfSeens);
GlobalUserData userData = (GlobalUserData)getActivity().getApplication();
Bundle bundle = this.getArguments();
if (bundle != null) {
userId = bundle.getString("userId","");
userName = bundle.getString("userName","");
userNameTextFragment.setText(userName);
}
else{
if (userData.getUserImageLink().length() > 0) {
LoadUserImage(userData.getName(), userData.getUserImageLink());
}
userNameTextFragment.setText(userData.getName());
}
//Log.i("TAG",userData.GetAllData());
if (userId.length() > 0)
loadImagesFromAPI(MainActivity.URL_DATA+"typ=mysphere&id="+userId);
else
loadImagesFromAPI(MainActivity.URL_DATA+"typ=mysphere&id="+userData.getUserId());
listview_spheresFragment.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//send the photosphereId to the Panoviewer fragment
Bundle bundle = new Bundle();
bundle.putString("Id", view.getTag().toString());
bundle.putString("Title", ((Photosphere) adapter.getItem(position)).getTitle());
bundle.putString("Description", ((Photosphere) adapter.getItem(position)).getDescription());
bundle.putString("ThumbUrl", ((Photosphere) adapter.getItem(position)).getImageUrl());
bundle.putBoolean("ShowUserLink", userId.length() > 0 ? false : true);
//create the fragment
SimpleFragment fragment = new SimpleFragment();
fragment.setArguments(bundle);
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.content_main, fragment);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.addToBackStack(null);
transaction.commit();
}
});
}
public void LoadUserImage(String userName, String imageUrl){
ImageLoader imageLoader = CustomVolleyRequest.getInstance(getActivity()).getImageLoader();
Bitmap bm = imageLoader.get(imageUrl, ImageLoader.getImageListener(userImageFragment, android.R.color.white, android.R.color.white)).getBitmap();
userImageFragment.setImageBitmap(bm);
userNameTextFragment.setText(userName);
}
public void loadImagesFromAPI(final String Url) {
loadingLayoutFragment.setVisibility(View.VISIBLE);
StringRequest stringRequest = new StringRequest(com.android.volley.Request.Method.GET, Url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
ArrayList<Photosphere> photospheres = new ArrayList<>();
JSONObject jsonObject = new JSONObject(response);
if(jsonObject.isNull("userModel") || jsonObject.getJSONObject("userModel").isNull("OtherImages")){
loadingLayoutFragment.setVisibility(View.GONE);
nrOfSpheresFragment.setText("No images");
}
else {
if (userId.length() >0){
LoadUserImage((jsonObject.getJSONObject("userModel")).getString("UserName"),(jsonObject.getJSONObject("userModel")).getString("ImgPath"));
}
photospheres = new WebServiceReader().processSpheresModelJsonData(jsonObject.getJSONObject("userModel"),"OtherImages");
loadingLayoutFragment.setVisibility(View.GONE);
//init adapter
adapter = new PhotosphereListAdapter(getActivity(), photospheres, (userId.length() > 0 ? false : true),true);
listview_spheresFragment.setAdapter(adapter);
nrOfSpheresFragment.setText(nrOfSpheresFragment.getText()+"("+listview_spheresFragment.getCount()+")");
nrOfLikes.setText((jsonObject.getJSONObject("userModel")).getString("TotalLikes"));
nrOfSeens.setText((jsonObject.getJSONObject("userModel")).getString("TotalViews"));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
nrOfSpheresFragment.setText("An error occurred. Please try again!");
}
}
);
RequestQueue requestQueue = Volley.newRequestQueue(getActivity());
requestQueue.add(stringRequest);
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
Update!
It seems that all of the fragments are loaded and overwritten in the first tab.
nrOfSpheresFragment.setText(nrOfSpheresFragment.getText()+"("+listview_spheresFragment.getCount()+")");
this line of code updates a textview and I can see that the textview is updated 3 times (25)(10)(5) which means the number of results. Only the last result's listview is visible
I figured out what was the problem.
When I used the fragment, (the fragment was created before, and used in other way) I didn't notice that when I initialize the controls in the page, I use getactivity() instead of rootview. So in order to make it work I changed those two as shown
userImageFragment = (CircleImageView) view.findViewById(R.id.userImageFragment);
userNameTextFragment = (TextView) view.findViewById(R.id.userNameTextFragment);
nrOfSpheresFragment = (TextView) view.findViewById(R.id.nrOfSpheresFragment);
loadingLayoutFragment = (LinearLayout) view.findViewById(R.id.loadingLayoutFragment);
listview_spheresFragment = (ListView) view.findViewById(R.id.listview_spheresFragment);
nrOfLikes = (TextView)view.findViewById(R.id.nrOfLikes);
nrOfSeens = (TextView)view.findViewById(R.id.nrOfSeens);
You can set variable to fragment, witch helps you to choose content you need
private String mUrl; //url of content
//set url of content via newInstanse() method
public static UsersSpheresFragment newInstance(String dataUrl) {
UsersSpheresFragment fragment = new UsersSpheresFragment();
Bundle arguments = new Bundle();
arguments.putInt(EXTRA_KEY_ULR, dataUrl);
fragment.setArguments(arguments);
return fragment;
}
//get url in onCreate
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getArguments();
if (extras == null || !extras.containsKey(EXTRA_KEY_URL))
throw new IllegalArgumentException("missing authorId argument");
mUrl = extras.getInt(EXTRA_KEY_URL);
//load your data
webService.loadData(mUrl);
}
another variant is set type of content via newIntanse(int type) method and choose data match it type in fragment#onCreate()
Related
I have written code that fetches a JSON object from a remote endpoint. For this question, I am passing in a mock object which is an array of objects.
My goal is to deconstruct the object and for each element of the array, create a fragment within a ViewPager. Each fragment should display the id of the object it is representing.
The object looks like this:
{'data':[{'id':1},{'id':2}]}
If I uncomment the code in FragmentClass, each fragment will display "test." However, if I try to set the text based on the value I pass via arguments, I run into this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.os.Bundle.get(java.lang.String)' on a null object reference
The code for FragmentClass is:
public class FragmentClass extends Fragment {
private TextView txtId;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = (ViewGroup) inflater.inflate(
R.layout.fragment_screen_slide, container, false);
txtId = (TextView) rootView.findViewById(R.id.txtId);
txtId.setText(getArguments().get("id").toString());
// txtId.setText("test");
return rootView;
}
}
The code for the ViewPager class, ScreenSliderPagerActivity is:
public class ScreenSliderPagerActivity extends FragmentActivity {
private ViewPager mPager;
private PagerAdapter pagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
String data = intent.getStringExtra("data");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_slide);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(), data);
mPager.setAdapter(pagerAdapter);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public int NUM_PAGES; //making this a public variable within the inner class allows me to dynamically change the number of fragments created. In this instance, 2 will be created, one for each element of the array.
public ScreenSlidePagerAdapter(FragmentManager fm, String data) {
super(fm);
try {
JSONObject json = new JSONObject(data);
JSONArray jsonArray = json.getJSONArray("data");
NUM_PAGES = jsonArray.length();
Log.d("NUM_PAGES", String.valueOf(NUM_PAGES)); //2
FragmentTransaction ft = fm.beginTransaction();
Bundle args = new Bundle();
// I think the problem is something in the loop, but I cannot seem to figure another way to do this.
for (int i = 0; i < NUM_PAGES; i++){
JSONObject obj = (JSONObject) jsonArray.get(i);
FragmentClass fragment = new FragmentClass();
ft.add(R.id.pager, fragment);
args.putString("id", obj.get("id").toString());
fragment.setArguments(args);
}
ft.commit();
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public Fragment getItem(int position) {
return new FragmentClass();
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
Finally, my MainActivity containing the mock object is:
public class MainActivity extends AppCompatActivity {
private Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.btn);
}
public void handleClick(View view){
String mockData = "{'data':[{'id':1},{'id':2}]}";
Intent intent = new Intent(MainActivity.this, ScreenSliderPagerActivity.class);
intent.putExtra("data",mockData);
MainActivity.this.startActivity(intent);
}
}
FragmentStatePagerAdapters don't work that way. You can't add your fragments in constructor of your adapter to the fragment manager and expect it to be added in the right place. You have to return the constructed fragment with the arguments in getItem(int) method:
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public int NUM_PAGES; //making this a public variable within the inner class allows me to dynamically change the number of fragments created. In this instance, 2 will be created, one for each element of the array.
private ArrayList<FragmentClass> mFragments = new ArrayList<>();
public ScreenSlidePagerAdapter(FragmentManager fm, String data) {
super(fm);
try {
JSONObject json = new JSONObject(data);
JSONArray jsonArray = json.getJSONArray("data");
NUM_PAGES = jsonArray.length();
Log.d("NUM_PAGES", String.valueOf(NUM_PAGES)); //2
for (int i = 0; i < NUM_PAGES; i++){
JSONObject obj = (JSONObject) jsonArray.get(i);
Bundle args = new Bundle();
FragmentClass fragment = new FragmentClass();
args.putString("id", obj.get("id").toString());
fragment.setArguments(args);
mFragments.add(fragment);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
So, as the title states, I wish to produce some amount of layouts within a Fragment which is in turn within a fragment. The number of layouts depends on the number of columns returned from a database, and the number of columns returned depends on which Fragment the user is currently on.
The first fragments are a BottomNavigation View, then the second set of Fragments are a range of swipe panels within each part of the BottomNavigation. It is in these fragments that I want to produce the variable number of layouts.
Here is a screenshot of what I'm looking to achieve
I was trying to create the Layouts within the OnCreateView() method for the inner Fragment but that is causing errors. I know that OnCreateView() returns a view so that must be wrong to try to create these layouts here.
I essentially have my main class which is divided into the 4 bottomNav fragments, each of which are divided into between 2-6 Fragments themselves.
Where should I be creating these layouts? Is there some other way to achieve the objective? Why is it not possible to create a layout within OnCreateView()? I'm very confused by the whole process, though the issue is probably a simple enough fix. Any help would be much appreciated.
If necessary I can provide code, though hopefully this is a simple enough issue that it is not needed.
EDIT: Below I have added the code I have so far:
MainActivity (which produces the BottomNavigation):
public class MainActivity extends AppCompatActivity {
private static final String SELECTED_ITEM = "arg_selected_item";
private BottomNavigationView mBottomNav;
private int mSelectedItem;
public static int numTabs;
public static String fragType;
public static String[] headings;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBottomNav = (BottomNavigationView) findViewById(R.id.navigation);
mBottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
selectFragment(item);
return true;
}
});
MenuItem selectedItem;
if (savedInstanceState != null) {
mSelectedItem = savedInstanceState.getInt(SELECTED_ITEM, 0);
selectedItem = mBottomNav.getMenu().findItem(mSelectedItem);
} else {
selectedItem = mBottomNav.getMenu().getItem(0);
}
selectFragment(selectedItem);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SELECTED_ITEM, mSelectedItem);
super.onSaveInstanceState(outState);
}
#Override
public void onBackPressed() {
MenuItem homeItem = mBottomNav.getMenu().getItem(0);
if (mSelectedItem != homeItem.getItemId()) {
// select home item
selectFragment(homeItem);
} else {
super.onBackPressed();
}
}
private void selectFragment(MenuItem item) {
Fragment frag = null;
// init corresponding fragment
switch (item.getItemId()) {
//USER PROFILE
case R.id.menu_home:
numTabs = 3;
fragType = getString(R.string.text_home);
headings = new String[numTabs];
headings[0] = "PROFILE HEADING 1";
headings[1] = "PROFILE HEADING 2";
headings[2] = "PROFILE HEADING 3";
frag = MenuFragment.newInstance(fragType);
break;
//DISCOVER
case R.id.menu_search:
fragType = getString(R.string.text_search);
numTabs = 6;
headings = new String[numTabs];
headings[0] = "DISCOVER HEADING 1";
headings[1] = "DISCOVER HEADING 2";
headings[2] = "DISCOVER HEADING 3";
headings[3] = "DISCOVER HEADING 4";
headings[4] = "DISCOVER HEADING 5";
headings[5] = "DISCOVER HEADING 6";
frag = MenuFragment.newInstance(fragType);
break;
//SCHEDULE
case R.id.menu_notifications:
fragType = getString(R.string.text_notifications);
numTabs = 2;
headings = new String[numTabs];
headings[0] = "SCHEDULE HEADING 1";
headings[1] = "SCHEDULE HEADING 2";
frag = MenuFragment.newInstance(fragType);
break;
//FOLLOWED
case R.id.menu_followed:
fragType = getString(R.string.text_follow);
numTabs = 3;
headings = new String[numTabs];
headings[0] = "FOLLOWED HEADING 1";
headings[1] = "FOLLOWED HEADING 2";
headings[2] = "FOLLOWED HEADING 3";
frag = MenuFragment.newInstance(fragType);
}
// update selected item
mSelectedItem = item.getItemId();
// uncheck the other items.
for (int i = 0; i< mBottomNav.getMenu().size(); i++) {
MenuItem menuItem = mBottomNav.getMenu().getItem(i);
menuItem.setChecked(menuItem.getItemId() == item.getItemId());
}
if (frag != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.container, frag, frag.getTag());
ft.commit();
}
}
}
Here's the MenuFragment class:
public class MenuFragment extends Fragment {
private static final String ARG_TEXT = "arg_text";
private String mText;
private TextView mTextView;
private Button contactButton;
private Button logoutButton;
public static Fragment newInstance(String text) {
Fragment frag = new MenuFragment();
Bundle args = new Bundle();
args.putString(ARG_TEXT, text);
frag.setArguments(args);
return frag;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View result = inflater.inflate(R.layout.fragment_menu, container, false);
ViewPager view = (ViewPager)result.findViewById(R.id.pager);
view.setAdapter(buildAdapter());
return(result);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
Bundle args = getArguments();
mText = args.getString(ARG_TEXT);
} else {
mText = savedInstanceState.getString(ARG_TEXT);
}
// initialize views
mTextView = (TextView) view.findViewById(R.id.text);
contactButton = (Button) view.findViewById(R.id.contactButton);
logoutButton = (Button) view.findViewById(R.id.logoutbutton);
// set text
mTextView.setText(mText);
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(ARG_TEXT, mText);
super.onSaveInstanceState(outState);
}
private PagerAdapter buildAdapter() {
return(new MidSectionAdapter(getActivity(), getChildFragmentManager()));
}
}
And here is the MidSectionAdapter:
public class MidSectionAdapter extends FragmentPagerAdapter {
Context ctxt=null;
String title;
public MidSectionAdapter(Context ctxt, FragmentManager mgr) {
super(mgr);
this.ctxt=ctxt;
}
#Override
public int getCount() {
return(MainActivity.numTabs);
}
#Override
public Fragment getItem(int position) {
if(MainActivity.fragType.equals("MY PROFILE")) {
return(ProfileFragment.newInstance(position));
} else if(MainActivity.fragType.equals("DISCOVER")) {
return(DiscoverFragment.newInstance(position));
} else if(MainActivity.fragType.equals("SCHEDULE")) {
return(ScheduleFragment.newInstance(position));
} else {
return(UpComingFragment.newInstance(position));
}
}
#Override
public String getPageTitle(int position) {
if(MainActivity.fragType.equals("MY PROFILE")) {
title = ProfileFragment.getTitle(ctxt, position);
} else if(MainActivity.fragType.equals("DISCOVER")) {
title = DiscoverFragment.getTitle(ctxt, position);
} else if(MainActivity.fragType.equals("SCHEDULE")) {
title = ScheduleFragment.getTitle(ctxt, position);
} else {
title = UpComingFragment.getTitle(ctxt, position);
}
return(title);
}
}
And here is an example of the inner Fragment (I have reverted it back to the code I had before I was having the issue, so it doesn't do much other than display "in panel x"):
public class ScheduleFragment extends Fragment {
private static final String KEY_POSITION="position";
private TextView panelView;
private static String head;
private static int panelPosition;
String panelCheck;
static ScheduleFragment newInstance(int position) {
ScheduleFragment frag=new ScheduleFragment();
Bundle args=new Bundle();
args.putInt(KEY_POSITION, position);
frag.setArguments(args);
return(frag);
}
static String getTitle(Context ctxt, int position) {
head = MainActivity.headings[position];
panelPosition = position;
return(head);
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View result=inflater.inflate(R.layout.schedule_content, container, false);
panelView = (TextView) result.findViewById(R.id.testFrag);
int position=getArguments().getInt(KEY_POSITION, -1);
if(getTitle(getActivity(), position).equals(MainActivity.headings[0])) {
//MAY NOT NEED THIS NITIAL IF STATEMENT
panelCheck = "in first panel";
} else {
//tableType = "Venue";
panelCheck = "in second panel";
}
panelView.setText(panelCheck);
return(result);
}
}
By the way, you can create/add/replace n of fragments in Android Activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activity)
//or
LinearLayout layout = getLayoutInflator.inflate(R.layout.your_activity);
setContentView(layout)
}
Similarly you can create n of Views within Activity as well as Fragment.
private LayoutInflater fragmentInflater,
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.fragmentInflater = inflater;
View view = inflater.inflate(R.layout.alto_dialog, container, false);
return view;
}
Basically you can create Views/ViewGroups add/remove views at any time you want. What you want is the fragmentInflater object.
According to your screenshot req, you may need a ListView or RecyclerView with different view types. If your list is small you can simply go head with ScrollView + LinearLayout
Hope this helps.
My objective is to replace the Fragment inside viewpager. I got a matching solution for the same Replace one fragment with other
Below is the code i am using almost same from the above link
public interface SecondPageFragmentListener {
void onSwitchToNextFragment();
}
I have implemented the above interface in my ViewPager Adapter.I am just pasting my full Code for the Adapter so it easy to understand it for everyone
public class TabPagerAdapter extends FragmentPagerAdapter{
private final Resources resources;
private Context mContext;
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
Fragment result = null;
FirstPageListener listener = new FirstPageListener();
public TabPagerAdapter(final Resources resources, FragmentManager fm,Context mContext) {
super(fm);
this.mContext = mContext;
this.resources = resources;
}
public void updateData(){
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
// First Fragment of First Tab
result = new Position();
break;
case 1:
// First Fragment of Second Tab
result = new Alerts();
break;
case 2:
// First Fragment of Third Tab
result = new Reports();
break;
default:
result = null;
break;
}
return result;
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(final int position) {
switch (position) {
case 0:
return resources.getString(R.string.tab_position_title);
case 1:
return resources.getString(R.string.tab_alert_title);
case 2:
return resources.getString(R.string.tab_reports_title);
default:
return null;
}
}
#Override
public int getItemPosition(Object object)
{
if (object instanceof Alerts &&
result instanceof CaptureIncident) {
return POSITION_NONE;
}
if (object instanceof CaptureIncident &&
result instanceof Alerts) {
return POSITION_NONE;
}
return POSITION_UNCHANGED;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
private final class FirstPageListener implements
SecondPageFragmentListener {
public void onSwitchToNextFragment() {
((AppCompatActivity)mContext).getSupportFragmentManager().beginTransaction().remove(result)
.commit();
if (result instanceof Alerts){
result = CaptureIncident.newInstance("","",listener);
}else{ // Instance of NextFragment
result = Alerts.newInstance("","",listener);
}
notifyDataSetChanged();
}
}
}
I have just created a Inner class and implemented my SecondPageFragmentListener interface as mentioned in the link.
Now I have init the interface in my Alert Fragment
static SecondPageFragmentListener secondPageListener;
public static Alerts newInstance(String param1, String param2,SecondPageFragmentListener listener) {
Alerts fragment = new Alerts();
secondPageListener = listener;
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
Now I am calling the interface method on button click
secondPageListener.onSwitchToNextFragment();
Below is My Fragment Code
public class Alerts extends RootFragment{
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
private RecyclerView gridview;
private GridAdapter gridAdapter;
private GridLayoutManager gridLayoutManager;
private OnFragmentInteractionListener mListener;
public static SecondPageFragmentListener secondPageListener;
public Alerts() {
// Required empty public constructor
}
#SuppressLint("ValidFragment")
public Alerts(SecondPageFragmentListener listener){
secondPageListener = listener;
}
public static Alerts newInstance(String param1, String param2,SecondPageFragmentListener listener) {
Alerts fragment = new Alerts();
secondPageListener = listener;
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_alerts, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initUiView();
RecyclerListener();
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
private void initUiView() {
gridview = (RecyclerView)getActivity().findViewById(R.id.gridview);
gridview.setHasFixedSize(true);
gridview.setLayoutManager(new GridLayoutManager(getActivity(), 3));
//list Population
List<ItemObject> rowListItem = getAllItemList();
gridAdapter = new GridAdapter(getActivity(),rowListItem);
gridview.setAdapter(gridAdapter);
gridAdapter.notifyDataSetChanged();
}
private List<ItemObject> getAllItemList(){
List<ItemObject> allItems = new ArrayList<ItemObject>();
allItems.add(new ItemObject("altercation","0,50 miles - 10 min ago", R.drawable.altercation));
allItems.add(new ItemObject("offence","0,25 miles - 45 min ago", R.drawable.offence));
allItems.add(new ItemObject("harassment","0,60 miles - 1h ago", R.drawable.harassment));
allItems.add(new ItemObject("doubt","0,5 miles - 48 min ago", R.drawable.doubt));
allItems.add(new ItemObject("incident","0,50 miles - 30 min ago", R.drawable.incident));
return allItems;
}
private void RecyclerListener(){
gridview.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), gridview, new ItemClickListener(){
#Override
public void onClick(View view, int position) {
Alerts.secondPageListener.onSwitchToNextFragment();
}
#Override
public void onLongClick(View view, int position) {
}
}));
}
}
But it showing me the error
java.lang.NullPointerException
at
com.oxilo.mrsafer.fragement.Alerts$1.onClick(Alerts.java:173)
The error indicates that secondpagelitener is null.
Please help me to resolve the issue.
Also on the below link
Replace one fragment with other
he implement the same interface in Fragment too as he already implemented
the interface in view pager adapter. so why he is implementing in the Fragment too.From my understanding it wrong but please correct me if i am wrong.
you can try this. for tab2 fragment create xml file like this:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/main"> <!-- this line is the most important part-->
<!-- your other layout s or views go here-->
</FrameLayout>
or just set id to your topest layout in you frag xml and and use it as container id in java code.
then in your fragment code in where you try to call interface to switch fragment just do that:
getChildFragmentManager().beginTransaction().replace(R.id.main, destfragment).addToBackStack("youtag").commit();
You are calling this code
public void onSwitchToNextFragment() {
((AppCompatActivity)mContext).getSupportFragmentManager().beginTransaction().remove(result).commit();
if (result instanceof Alerts){
result = CaptureIncident.newInstance("","",listener);
} else{ // Instance of NextFragment
result = Alerts.newInstance("","",listener);
}
notifyDataSetChanged();
}
From object created here:
case 1:
// First Fragment of Second Tab
result = new Alerts();
break;
so secondPageListener is not initialized yet. You can try to create change your code to this:
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
// First Fragment of First Tab
result = new Position();
break;
case 1:
// First Fragment of Second Tab
result = Alerts.newInstance("","",listener); // This line is important
break;
case 2:
// First Fragment of Third Tab
result = new Reports();
break;
default:
result = null;
break;
}
return result;
}
EDIT
If it's going to be static then you can try this:
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
// First Fragment of First Tab
result = new Position();
break;
case 1:
// First Fragment of Second Tab
result = Alerts.newInstance("","",listener); // This line is important
Alerts.secondPageListener = listener;
break;
case 2:
// First Fragment of Third Tab
result = new Reports();
break;
default:
result = null;
break;
}
return result;
}
Also make sure to call it like this in your onClick
Alerts.secondPageListener.onSwitchToNextFragment();
I'm writing a simple app using fragment and viewpager to display data calling from webservice. Problem is viewpager creates 3 fragments at the first time, but data only view on the last fragment.
Here's my activity:
public class LineDetailTabs extends FragmentActivity {
//declare some variables here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_line_detail_tabs);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
PlaceholderFragment hf = new PlaceholderFragment();
fragmentTransaction.replace(R.id.container, hf);
fragmentTransaction.commit();
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setCurrentItem(index);
}
This is my adapter,
list_nodep is a String array contains id of many deparments.
count is number of departments, also number of tabs will be created.
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
noDep = list_nodep[position];
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
//put some agurments
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
return count;
}
}
And my fragment here, same layout xml file for all fragments.
public static class PlaceholderFragment extends Fragment {
public static View rootView;
public static TextView tvHeader;
public static TextView tvPO;
public static TextView tvLot;
// many textviews
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
activity = (LineDetailTabs) getActivity();
loadData(noDep, date, username, dbIP, dbName, dbUsername, dbPassword);
rootView = inflater.inflate(R.layout.fragment_line_detail_tabs, container, false);
tvHeader = (TextView) rootView.findViewById(R.id.txtHeader);
tvPO = (TextView) rootView.findViewById(R.id.txtPO);
tvLot = (TextView) rootView.findViewById(R.id.txtLOT);
// many textviews remaining...
return rootView;
}
public static void loadData(String aNoDep, String aDate, String aUsername, String aDbIP, String aDbName, String aDbUser, String aDbPass) {
RequestParams params = new RequestParams();
//put params
invokeWS(params);
}
public static void invokeWS(final RequestParams params) {
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://...", params, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, String response) {
try {
// JSON Object
JSONObject obj = new JSONObject(response);
// When the JSON response has status boolean value assigned with true
JSONArray jArr = obj.getJSONArray("list");
// Display info detail on screen
obj = jArr.getJSONObject(0);
tvHeader.setText = obj.getString("NAME_DEP");
tvPO.setText = obj.getString("NO_PO");
tvLot.setText = obj.getString("NO_LOT");
//set text for all textviews
}
// When the response returned by REST has Http response code other than '200'
#Override
public void onFailure(int statusCode, Throwable error,
String content) {
// When Http response code is '404'
if (statusCode == 404) {
Toast.makeText(activity.getApplicationContext(), Constants.ERR_CODE_404, Toast.LENGTH_SHORT).show();
}
// When Http response code is '500'
else if (statusCode == 500) {
Toast.makeText(activity.getApplicationContext(), Constants.ERR_CODE_500, Toast.LENGTH_SHORT).show();
}
// When Http response code other than 404, 500
else {
Toast.makeText(activity.getApplicationContext(), Constants.ERR_CODE_, Toast.LENGTH_SHORT).show();
}
}
});
}
}
When debugging my app, I saw fragment onCreateView run 3 times continuously, and after that cursor jumps to onSuccess, so just last fragment displayed data. I think I put loadData() method or setText at the wrong place. Please help.
OK, finally I fixed it. That's my mistake, I didn't pass params from activity to fragment.
Just put params here
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
noDep = list_nodep[position];
Bundle bundle = new Bundle();
bundle.putString("NO_DEP", noDep);
bundle.putString("DATE", date);
bundle.putString("USERNAME", username);
bundle.putString("DB_IP", dbIP);
bundle.putString("DB_NAME", dbName);
bundle.putString("DB_USER", dbUsername);
bundle.putString("DB_PASS", dbPassword);
bundle.putString("SERVER_IP", serverIP);
PlaceholderFragment fragment = new PlaceholderFragment();
fragment.setArguments(bundle);
return fragment;
}
Then get these arguments in onCreateView(), and call loadData() method.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_line_detail_tabs, container, false);
activity = (LineDetailTabs) getActivity();
noDep = getArguments().getString("NO_DEP");
date = getArguments().getString("DATE");
username = getArguments().getString("USERNAME");
dbIP = getArguments().getString("DB_IP");
dbName = getArguments().getString("DB_NAME");
dbUsername = getArguments().getString("DB_USER");
dbPassword = getArguments().getString("DB_PASS");
serverIP = getArguments().getString("SERVER_IP");
loadData(noDep, date, username, dbIP, dbName, dbUsername, dbPassword);
tvHeader = (TextView) rootView.findViewById(R.id.txtHeader);
tvPO = (TextView) rootView.findViewById(R.id.txtPO);
//...
return rootView;
}
I'm building an Android application using a FragmentStatePagerAdapter for tabbed navigation and dynamic content in each tab. Each tab has Fragment with content which is to be replaced upon user input (for example, the first tab has a Fragment containing a list of books, and upon clicking, you can access detailed information of the book, which is displayed using another Fragment
Problem: I haven't find a way of correctly handling the onBack events nor the BackStack, so when I'm reviewing any book's details, I can easily go back pressing the back button - I mean, popping the last state from the BackStack.
What I suspect: The way I'm switching Fragment objects may not the the best one, but except for the back button issue, it is working just as I want. I suspect some problem between the FragmentStatePagerAdapter's adapter, and the FragmentManager's own collection of Fragments; probably this is something with an easy solution I didn't see.
Unaswered question (not very detailed though): Adding Fragment to BackStack using FragmentStatePagerAdapter
The code:
// MAIN ACTIVITY, Just this simple.
public class MainActivity extends FragmentActivity {
public static final String TAG = "MainActivity";
// Whether the Log Fragment is currently shown
private boolean mLogShown;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
MainTabSliderFragment fragment = new MainTabSliderFragment();
transaction.replace(R.id.sample_content_fragment, fragment);
transaction.commit();
}
}
}
.
// THE SLIDE TAB FRAGMENT, which becomes the parent view of the tabs.
public class MainTabSliderFragment extends Fragment {
static final String LOG_TAG = MainTabSliderFragment.class.getSimpleName();
private SlidingTabLayout mSlidingTabLayout;
private ViewPager mViewPager;
private CustomFragmentStatePageAdapter cfspAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_sample, container, false);
return root;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
cfspAdapter = new CustomFragmentStatePageAdapter(getFragmentManager());
List<String> pageTitles = new ArrayList<>();
pageTitles.add(getString(R.string.page_one));
pageTitles.add(getString(R.string.page_two));
pageTitles.add(getString(R.string.page_three));
List<Fragment> pageFragments = new ArrayList<>();
final BookListPageFragment pageOne = BookListPageFragment.newInstance(new CustomFragmentStatePageAdapter.SwitchFragmentListener() {
#Override
public void onSwitchFragments(Class<? extends Fragment> clazz, Map<String, String> ... args) {
cfspAdapter.switchFragment(CustomFragmentStatePageAdapter.PagePosition.POSITION_PAGE_ONE, clazz, this, args);
}
});
CustomerPageFragment pageTwo = CustomerPageFragment.newInstance(...);
ForumPageFragment pageThree = ForumPageFragment.newInstance(...);
pageFragments.add(pageOne);
pageFragments.add(pageTwo);
pageFragments.add(pageThree);
cfspAdapter.addFragments(pageFragments, pageTitles);
mViewPager.setAdapter(cfspAdapter);
mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
mSlidingTabLayout.setViewPager(mViewPager);
}
}
.
// THE FIRST TAB, In its initial state (the initial fragment).
public class BookListPageFragment extends Fragment {
private static final String TAG = BookListPageFragment.class.getSimpleName();
private BookListAdapter bAdapter;
private static CustomFragmentStatePageAdapter.SwitchFragmentListener switchFragmentListener;
public static BookListPageFragment newInstance(CustomFragmentStatePageAdapter.SwitchFragmentListener _switchFragmentListener) {
switchFragmentListener = _switchFragmentListener;
BookListPageFragment f = new BookListPageFragment();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.page_one_booklist, container, false);
final ListView lv = (ListView) v.findViewById(R.id.list);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
BookRowData bRow = (BookRowData) lv.getItemAtPosition(position);
Log.i(TAG, "Clicked on book " + bRow.getBookId());
Map<String, String> param = new HashMap<>();
param.put("book_id", Long.toString(bRow.getBookId()));
switchFragmentListener.onSwitchFragments(ReviewBookPageFragment.class, new Map[]{param});
}
});
initializeTestList(v, lv); // Just add some books to the list.
return v;
}
.
// THE PAGE ADAPTER, used for handling tab's Fragment switching.
public class CustomFragmentStatePageAdapter extends FragmentStatePagerAdapter {
private final static String TAG = FragmentStatePagerAdapter.class.getSimpleName();
private FragmentManager fragmentManager;
private List<Fragment> fragmentList = new ArrayList<>();
private List<String> tabTitleList = new ArrayList<>();
public CustomFragmentStatePageAdapter(FragmentManager fm) {
super(fm);
fragmentManager = fm;
}
public void addFragments(List<Fragment> fragments, List<String> titles) {
fragmentList.clear();
tabTitleList.clear();
fragmentList.addAll(fragments);
tabTitleList.addAll(titles);
notifyDataSetChanged();
}
#Override
public int getItemPosition(Object object) {
if (fragmentList.contains(object)) {
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
#Override
public Fragment getItem(int item) {
if (item >= fragmentList.size()) {
return null;
}
return fragmentList.get(item);
}
#Override
public int getCount() {
return fragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return tabTitleList.get(position);
}
/**
* Switching pages
*
* #param newFragment
*/
public void switchFragment(final PagePosition position, Class<? extends Fragment> newFragment, SwitchFragmentListener sfListener, Map<String, String> ... args) {
final Fragment old = fragmentList.get(position.getPagePosition());
fragmentManager.beginTransaction().remove(old).commit(); //FIRST VERSION: IF HITTING BACK, IT EXITS APP AT ONCE.
//fragmentManager.beginTransaction().addToBackStack("page_one").remove(old).commit(); //SECOND VERSION: NOW I NEED TO HIT BACK TWICE TO EXIT, BUT THE VIEW DOESN'T CHANGE AFTER HITTING THE FIRST TIME.
try {
Fragment f = (Fragment) newFragment.asSubclass(Fragment.class).getMethod("newInstance", SwitchFragmentListener.class, Map[].class).invoke(newFragment, new Object[]{sfListener, args});
fragmentList.set(position.getPagePosition(), f);
} catch (IllegalAccessException iae) {
Log.e(TAG, "Fragment class access exception");
} catch (NoSuchMethodException e) {
Log.e(TAG, "Fragment instantiation exception (reflection)");
} catch (InvocationTargetException e) {
Log.e(TAG, "Fragment instantiation exception (reflection: no public constructor)");
}
notifyDataSetChanged();
}
public interface SwitchFragmentListener {
void onSwitchFragments(Class<? extends Fragment> clazz, Map<String, String> ... args);
}
public enum PagePosition {
POSITION_PAGE_ONE (0),
POSITION_PAGE_TWO (1),
POSITION_PAGE_THREE (2);
private final int position;
PagePosition(int position) {
this.position = position;
}
public int getPagePosition() {
return this.position;
}
}
}
.
// AND FINALLY THE FRAGMENT I WANT TO GO BACK FROM; this is the book review Fragment, which is displayed also in the first tab when clicking on a book from the list. Second and third tabs are ommitted.
public class ReviewBookPageFragment extends Fragment {
private static final String TAG = ReviewBookPageFragment.class.getSimpleName();
private CommentsListAdapter cAdapter;
private Long bookId;
private static CustomFragmentStatePageAdapter.SwitchFragmentListener switchFragmentListener;
public static ReviewBookPageFragment newInstance() {
ReviewBookPageFragment f = new ReviewBookPageFragment();
return f;
}
public static ReviewBookPageFragment newInstance(CustomFragmentStatePageAdapter.SwitchFragmentListener _sfListener, Map<String, String> ... args) {
switchFragmentListener = _sfListener;
Bundle b = BundlePacker.packMaps(args); // Custom util class for packing the params into a bundle.
ReviewBookPageFragment f = new ReviewBookPageFragment();
f.setArguments(b);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.page_review_book, container, false);
Bundle bookIdBundle = this.getArguments();
Long bId = Long.parseLong(bookIdBundle.getString("book_id"));
Log.i(TAG, "Book ID: " + bId);
initializeTestList(v); // Just fill the book's reviews with test data.
return v;
}
}
So, that's the bunch of code. The idea, as a summary, is to switch from the books list view (shown on tab one), to the book's reviews when tapping on any book from the list; the reviews are also shown on the first tab, and I want to go back to the books list when pressing back. Currently, it closes the application hitting back ONCE, and if I add the transaction to the backstack (see my CustomFragmentStatePageAdapter), TWICE (but the view doesn't change after hitting back the first time.
Any help with the issue will be greatly appreciated.
For fixing the popback issue you can use this code in your activity class,
#Override
public void onBackPressed() {
// if there is a fragment and the back stack of this fragment is not empty,
// then emulate 'onBackPressed' behaviour, because in default, it is not working
FragmentManager fm = getSupportFragmentManager();
for (Fragment frag : fm.getFragments()) {
if (frag.isVisible()) {
FragmentManager childFm = frag.getChildFragmentManager();
if (childFm.getBackStackEntryCount() > 0) {
childFm.popBackStack();
return;
}
}
}
super.onBackPressed();
}
I did somethink like this:
private View _view;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(_view==null){
_view = inflater.inflate(R.layout.page_review_book, container, false);
// your_code
}
return _view;
}