Fragment View is Null When Called From Parent Activity - android

I have an activity and its child Fragment with a LinearLayout that I generate buttons inside of. When the fragment is created, everything runs fine. However, when the parent activity downloads a new item, I call the method in the fragment that is used to generate the buttons and add them to the view, but the LinearLayout returns null and I can't figure out why. I either need to fix it or find a way to "re-display" my fragment. Here is the related code:
SongFragment:
LinearLayout linearLayout;
DatabaseHelper databaseHelper;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_song, container, false);
linearLayout = rootView.findViewById(R.id.songFragmentMainLayout);
databaseHelper = new DatabaseHelper(getActivity());
return rootView;
}
#Override
public void onResume() {
super.onResume();
RefreshButtons();
}
public void RefreshButtons(){
linearLayout.removeAllViews(); //this line is where the NullPointerException is called
...
}
MainActivity:
//refresh fragment view
SongFragment fragment = (SongFragment) sectionsPagerAdapter.getItem(0);
if(downloadQueue.size() == 0){
fragment.RefreshButtons();
Toast.makeText(context, "New songs downloaded", Toast.LENGTH_SHORT).show();
}
...
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new SongFragment();
break;
case 1:
fragment = new PlaceholderFragment();
break;
}
return fragment;
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Songs";
case 1:
return "Playlists";
}
return null;
}
}
Thanks for your help.

The method fragment.RefreshButtons(); returns an NPE because if you implemented SectionsPagerAdapter like you should, getItem() returns a new Instance of that fragment which is not yet attached to the fragment manager, therefore causing a Nullpointer exception.
So what you should do is get a currently active fragment instance like this:
Fragment frag = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, 0);
0 is the position of your fragment, so for example if you have 3 fragments, 0 will return the first fragment instance etc...

Related

Textview not updating on first Tab unless scrolled to 3rd Tab

I have a tablayout (with 3 tabs) with viewpager and fragments.
I m trying to send the parsed Json data from MainActivity( When searchview data submitted ) to show in the textview of tabs fragments
See this Image link
The data is succesfully parsing but textview with data(in first tab) is not showing unless scrolled to 3rd tab
//Passing data from MainActivity
public String getMyData() {
return meaning;
}
//Setting value to textview from Fragment
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v=inflater.inflate(R.layout.fragment_meaning, container, false);
MainActivity mainActivity= (MainActivity) getActivity();
assert mainActivity != null;
String data= mainActivity.getMyData();
TextView textView=v.findViewById(R.id.textVIew);
textView.setText(data);
return v;
}
Want to able to show data changes instantly as it is parsed, instead of scrolling to 3rd tab to see changes
Here are some steps that might help you.
On the ViewPager adaptor you have created, make the fragment objects. like below
FragmentOne fragOne; // this should be global
On the viewPager adaptor, do some thing like this,
fragOne = new FragmentOne() // whatever your implementation is.
Then after fetching the data from the server,
if ( fragOne != null ) {
fragOne.setValueOnView( " your data to be passed" );
}
and on the FragmentOne, create a function called setValueOnView
void setValueOnView(String yourString) {
v.findViewById(R.id.textVIew).setText(yourString);
}
And one more thing, while initializing the fragment onCreateView, create an object of View
View v; // global variable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragment_meaning, container, false);
Use this approach for other fragments as well
Inside getItem() method in ViewPager class use Fragment constructors with String parameter
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
FragmentOne tab1 = new FragmentOne("string parameter");
return tab1;
case 1:
FragmentTwo tab2 = new FragmentTwo("string parameter");
return tab2;
case 2:
FragmentThree tab3 = new FragmentThree("string parameter");
return tab3;
default:
return null;
}
}
Inside your Fragment:
public FragmentOne(String stringParameter) {
yourLocalVariable = stringParameter; // yourLocalVariable is declared inside Fragment class;
//now you can setText() for your TextView inside onViewCreated()
}
Of course you pass your String from MainActivity to ViewPager like you did earlier.
Use Observer
public class FragmentObserver extends Observable {
#Override
public void notifyObservers() {
setChanged(); // Set the changed flag to true, otherwise observers won't be notified.
super.notifyObservers();
}
}
Activity:
public class MyActivity extends Activity {
private MyAdapter mPagerAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.my_activity);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new MyAdapter();
pager.setAdapter(mPagerAdapter);
}
private void updateFragments() {
mPagerAdapter.updateFragments();
}
}
Viewpager adapter
public class MyAdapter extends FragmentPagerAdapter {
private Observable mObservers = new FragmentObserver();
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
mObservers.deleteObservers(); // Clear existing observers.
Fragment fragment = new MyFragment();
if(fragment instanceof Observer)
mObservers.addObserver((Observer) fragment);
return fragment;
}
public void updateFragments() {
mObservers.notifyObservers();
}
}
Your Fragment
public class MyFragment extends Fragment implements Observer {
/* Fragment related stuff... */
#Override
public void update(Observable observable, Object data) {
View root = getView();
// Update your views here.
}
}
You will get data to update method even your fragment already loaded

App freezes forever when Fragment get's displayed

Since I implemented a BottomAppBar my App isn't responding when a certain Fragment should get displayed even though I haven't changed anything. There isn't an error message either and all other Fragments which are getting displayed when another item in the BottomNavigationBar is clicked work fine.
Fragment:
public class StatisticsFragment extends Fragment {
private TabLayout mTabLayout;
private ViewPager mViewPager;
private BankAccountsStatisticsFragment bankAccountsStatisticsFragment = new BankAccountsStatisticsFragment();
private BillsStatisticsFragment billsStatisticsFragment = new BillsStatisticsFragment();
private CategoriesStatisticsFragment categoriesStatisticsFragment = new CategoriesStatisticsFragment();
private GoalsStatisticsFragment goalsStatisticsFragment = new GoalsStatisticsFragment();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View inflatedView = inflater.inflate(R.layout.fragment_statistics, container, false);
mTabLayout = (TabLayout) inflatedView.findViewById(R.id.tl_statistics);
mViewPager = (ViewPager) inflatedView.findViewById(R.id.vp_statistics);
mViewPager.setAdapter(new Adapter(getChildFragmentManager()));
mTabLayout.setupWithViewPager(mViewPager);
return inflatedView;
}
private class Adapter extends FragmentPagerAdapter {
private static final int TABS = 4;
public Adapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position){
case 0: return bankAccountsStatisticsFragment;
case 1: return billsStatisticsFragment;
case 2: return categoriesStatisticsFragment;
case 3: return goalsStatisticsFragment;
default: throw new IllegalStateException("Couldn't find a fragment for position " + position);
}
}
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0: return getString(R.string.tab_bank_accounts);
case 1: return getString(R.string.tab_bills);
case 2: return getString(R.string.tab_categories);
default: return getString(R.string.tab_goals);
}
}
#Override
public int getCount() {
return TABS;
}
}
}
LOGCAT
LOGCAT
Inflating a Layout and creating a Fragment transition (like the "FragmentManager....replace().commit()") are done in the MainThread where the UserInterface is rendered, so the UserInterface will freeze in these moments.
I think you have to pre-load the "bad" Fragment and just HIDE it instead of Destroy it...in this way the next time you want to create it is already ready.

Access fragment variable outside of Fragment in view pager

enter image description here
Before reading the question, please refer to image.
I am using viewpager to show the fragment.
Problem
In the fragment, I have used two edittext lets say editText1, editText2 now the problem is how I will get the editText data. I can only get the editText values when user click on next button but the next button is outside of fragment. How do I access the editText outside the fragment.
Before downvoting the question, let me know the reason so that I can improve my question.
Fragment java class
// newInstance constructor for creating fragment with arguments
public static BpDetails newInstance(int page) {
BpDetails fragmentFirst = new BpDetails();
Bundle args = new Bundle();
args.putInt("someInt", page);
fragmentFirst.setArguments(args);
return fragmentFirst;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
page = getArguments().getInt("someInt", 0);
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.bp_details, container, false);
Log.i("View ",view.toString());
Log.i("DOB is ",Long.toString(Constants.dob));
systolic =(EditText) view.findViewById(R.id.systolic);
diastolic =(EditText) view.findViewById(R.id.diastolic);
return view;
}
ViewPager Activity
vpPager = (ViewPager) findViewById(R.id.view_pager);
adapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
vpPager.setAdapter(adapterViewPager);
Fragment fragment=adapterViewPager.getItem(prevPage);
if (fragment.getClass().equals(BpDetails.class)){
Log.i("Call ","Yes");
}
findViewById(R.id.btn_prev).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// checking for last page
// if last page home screen will be launched
int current = getItem(-1);
if (current!=0)
prevPage=current-1;
if (current < 4) {
// move to next screen
vpPager.setCurrentItem(current);
} else {
//final reached.
}
}
});
findViewById(R.id.btn_next).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// checking for last page
// if last page home screen will be launched
int current = getItem(+1);
if (current!=0)
prevPage=current-1;
System.out.println("Prev page "+prevPage);
if (current < 4) {
// move to next screen
Fragment prevFragment=adapterViewPager.getItem(prevPage);
} else {
//final reached.
}
}
});
}
private int getItem(int i) {
return vpPager.getCurrentItem() + i;
}
public static class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 4;
private static int mSelectedPosition;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
//mSelectedPosition=selectedPosition;
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return BasicDetails.newInstance(0);
case 1:
return BpDetails.newInstance(1);
case 2:
return BslDetails.newInstance(2);
case 3:
return Summary.newInstance(3);
default:
return null;
}
}
}
Create two getters inside your fragment like this.
public String getSystolic(){
return this.systolic.getText().toString();
}
public String getDiastolic(){
return this.diastolic.getText().toString();
}
BpDetails fr = (BpDetails)myAdapter.getItem(myViewPager.getCurrentItem());
String systolicString = fr.getSystolic();
I had a similar issue. .getItem() instantiates a new Fragment, so upon calling myAdapter.getItem(...) you would be getting null for all elements in the Fragment, but not null for the Fragment.
When I fixed this, what I had to do was create another method inside of MyPagerAdapter called getInstantiatedFragment:
public Fragment getInstantiatedFragment(int position)
{
return fragments.get(position);
}
fragments is a new field for the class:
private ArrayList<Fragment> fragments = new ArrayList<>();
I would override getItem() (as you have done already) and change it to:
#Override
public Fragment getItem(int position)
{
switch (position) {
case 0:
BasicDetails basicDetails = BasicDetails.newInstance(0);
fragments.add(basicDetails);
return basicDetails;
...
}
where you're adding the fragment to fragments before returning, then you would call:
BpDetails fr = (BpDetails)myAdapter.getInstantiatedItem(myViewPager.getCurrentItem());
to get the instance of the created fragment and then call
String systolicString = fr.getSystolic();
if you're using the previous answer's method.
This is so that you can keep track of the instantiated fragments in fragments. I'm sure there are better ways.

When creating two instances of the same fragment and registering them only the second one created gets event

I have created two fragments in a ViewPager , when I click on first one , Second fragment is taking the click.
This issue puts me in another position, when I create two instance from same fragment but with different data.
{
#Override
public Fragment getItem(int index) {
switch (index) {
case 1:
return FragmentBrandList.getInstance(tabs.getBrandList2(), 19,
title);
case 0:
return FragmentBrandList.getInstance(tabs.getBrandList1(), 19,
title);
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
After creating ViewPager , both the fragments get created correctly , but when I click on any thing in the first fragment , the click event gets fired in second fragment not in the first fragment.
EDIT
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 1:
return FragmentBrandList.getInstance(tabs.getBrandList2(), 19,
title);
case 0:
return FragmentBrandList.getInstance(tabs.getBrandList1(), 19,
title);
}
return null;
}
#Override
public int getCount() {
return 2;
}
in FragmentBrandList
public class FragmentBrandList extends Fragment {
ArrayList<Brand> brandList = new ArrayList<Brand>();
int discoverID;
RecyclerView listView;
LinearLayoutManager mLayoutManager;
public static FragmentBrandList getInstance(ArrayList<Brand> brandList,
int discoverID, String title) {
FragmentBrandList frag = new FragmentBrandList();
Bundle b = new Bundle();
b.putSerializable("brandList", brandList);
b.putInt("discoverID", discoverID);
b.putString("title", title);
frag.setArguments(b);
return frag;
}
public FragmentBrandList() {
}
String title = "";
View v;
boolean isInflated = false;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (v == null) {
v = inflater.inflate(R.layout.fragment_list_view_brownbg,
container, false);
isInflated = true;
} else {
isInflated = false;
((ViewGroup) v.getParent()).removeView(v);
}
return v;
}
MainActivity activity;
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (isInflated) {
activity = (MainActivity) getActivity();
initView();
}
}
public void initView(){
title = getArguments().getString("title");
discoverID = getArguments().getInt("discoverID");
listView = (RecyclerView) v.findViewById(R.id.listView);
mLayoutManager = new LinearLayoutManager(getActivity());
listView.setItemAnimator(new DefaultItemAnimator());
listView.setHasFixedSize(true);
listView.setLayoutManager(mLayoutManager);
listView.setAdapter(new BrandListRecAdapter(getActivity(),
R.layout.single_item_listview, brandList));
}
#Override
public void onResume() {
// handle on click
((BrandListRecAdapter) listView.getAdapter())
.setOnItemClickListener(new ItemClickListener() {
#Override
public void onItemClickListener(final int pos, View v) {
activity.replaceCurrentFragment(
FragmentBrandDetails.getInstance(
brandList.get(pos), "bank"), true,
true);
}}
EDIT
i think problem cause
when create second fragment , listview.onclick is overwrite first one !!
how can solve this peb?
EDIT
thank you to every one try to help me
problem is already because i use same adapter and same fragment
when second fragment created it is overwrite on item click
so when click in item is called second one !!!
Just put this android:clickable="true" in every fragment layout, and this will not happen again.
This is just an educated guess, but because a ViewPager will always create at least one extra Fragment on either side of the currently visible fragment, you may be creating two virtually identical Fragments in parallel, assigning them both onItemClickListeners in onResume and as such they are both responding to item clicks when an item is pressed on either Fragment.
You could try moving the onItemClickListener to the ViewHolder in your Adapter, rather assigning it in onResume. In addition, I wonder what a Brand object looks like in your RecyclerView, and whether it wouldn't be simpler to pass the current ViewPager page as a parameter in getInstance, and use this to access an Array containing the information necessary to fill your RecyclerView rows.
Here is a very brief example of how your ViewHolder may look:
class MyRecyclerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
public MyRecyclerViewHolder(View itemView) {
itemView.setOnClickListener(this);
//etc.

ViewPager creates duplicate views

This is the PagingAdapter I am using:
public class PagingAdapter extends FragmentStatePagerAdapter {
Context context;
public PagingAdapter(FragmentManager fm, Context con) {
super(fm);
context = con;
}
#Override
public Fragment getItem(int index) {
try {
switch (index) {
case 0:
return Fragment.instantiate(context, ActivityFragment.class.getName());
case 1:
return Fragment.instantiate(context, GroupFragment.class.getName());
case 2:
return Fragment.instantiate(context, MessageFragment.class.getName());
case 3:
return Fragment.instantiate(context, NotificationFragment.class.getName());
}
return null;
}
catch (Exception ex)
{
ex.printStackTrace();
return null;
}
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 4;
}
}
This creates same view for first two tabs. I want separate content for all tabs.
I am placing WebView in all 4 tabs.
In Activity, I am doing this :
mViewPager.setOffscreenPageLimit(3);
Use SparseArray<Fragment> in your adapter, and in your getItem use new instantiate your fragments like
return new ActivityFragment();
If you want to add bundle info than first create your fragment than add bundle in your
fragment.setArgument(bundle);
And use these line in your deateyItem before calling super
if(0<=sparseArray.indexOfKey(position))
sparseArray.remove(position);
First of all your method instantiate is returning a Fragment right?
Does it add the param implicitly in that method using setArguements if not you may want to change to it. On the other hand FragmentManager will call default constructor of each fragment when needed. So you have to keep them (do not overload constructor) and in destroyItem try removing the fragment for real.
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
getFragmentManager().beginTransaction().remove((Fragment)(object)).commit();
}
What do you mean by separate view? are you sure that you are not inflating the wrong layout in.
GroupClassFragment.java class
Do check int the method.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myCorrectView = inflater.inflate(R.layout.my_correct_view,container,false);
return myCorrectView;
}

Categories

Resources