I have added a horizontal scroll view and in the layout I have added reference to two fragments such that both the fragments are visible in the scroll.
the problem is when I enter values in the left layout I want the value to be displayed in the right layout.
I am using an interface for the same, but its not working. as in I keep getting null pointer exception for the same.. How to get around this??
this is the main class fragment
public static SliderRightAdapter sliderRightAdapterFragment; <-- this calls the second layout
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.f_pp, container, false);
sliderRightAdapterFragment = (SliderRightAdapter) getActivity()
.getSupportFragmentManager().findFragmentById(
R.id.secondary_sliderProduct_description);
return view;
}
.
.
.
.
this is where i am getting null pointer exception on sliderRightAdapterfragment
interfaceObject.onButtonClick(CGS, sliderRightAdapterFragment);
and this is where it is initialized...
sliderRightAdapterFragment = PDFinterface.sliderRightAdapterFragment;
From what I'm seeing, you're letting the fragments communicate directly with each other.
I'd recommend against it, since this can quickly lead to maintaining unused references, as fragments can have quite diverse lifecycles. The idea behind fragments is to have small segments of functionality which can communicate with their parent some information.
I think a better way to approach would be to use the parent activity as a form of 'manager' between these two fragments.
Instead of maintaining a static reference to the other fragment, store a normal reference to the activity, preferably in the form of a listener interface the activity implements. Then you'd use the findFragmentById/Tag to notify the other fragment of the change. All in all, this would look something like this:
Fragment:
public interface ActivityListener {
void onText(String text);
}
private ActivityListener listener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if(activity instanceof ActivityListener) {
listener = (ActivityListener) activity;
}
// else some error
}
#Override
public void onDetach(Activity activity) {
// to make absolutely sure you don't maintain a reference to a killed activity
listener = null;
}
Activity:
#Override
public void onText(String text) {
Fragment frag = getSupportFragmentManager.findFragmentById(<someId>);
// notify with text information
}
Something like this should work fine.
Related
I have an Android Activity, from which I want to show a Dialog. It would probably be a custom DialogFragment. Now when the user clicks on specific buttons I want the layout's inside the dialog to change with the data from the previous DialogFragment and so that it would have an ability to also go back to previous Layout.
I dont think there is an easy way to change views inside of the same DialogFragment so what would be the best way to do this?
I have tried doing it in method onViewCreated and when a button is clicked, but nothing happens.
In my activity I call the fragment like this at the moment:
FragmentManager fm = getSupportFragmentManager();
NewDialog newDialog = NewDialog.newInstace(userId, loc, currentId);
newDialog.setNewClickListener(new NewDialog.OnNewClickListener() {
#Override
public void onCancelClicked() {
finishAdd();
}
#Override
public void onAcceptClicked() {
...
}
});
newDialog.show(fm, "new_frag");
And the fragment:
public class NewDeliveryPointDialog extends DialogFragment {
private LayoutInflater inflater;
private ViewGroup container;
public NewDialog(){
}
public static NewDialog newInstace(){
...
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
this.inflater = inflater;
this.container = container;
return inflater.inflate(R.layout.dialog_layout_1, container);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
saveButton.setOnClickListener(v -> {
View.inflate(getContext(), R.layout.dialog_layout_2, container);
view.invalidate();
view.refreshDrawableState();
}
});
}
}
A DialogFragment is not made to have navigation to other fragments within the same dialog.
You basically have these options:
On your button click you close the Dialog and open another Dialog. But this seems odd. If there is so much happening, probably dialogs are not the best shot.
Instead of DialogFragments have another fragment container overlaying the original one (basically what a Dialog fragment does for you). Within the second container you can easily navigate to other fragments and set it to gone when the user finished interaction.
If there are just a few Views in the Dialog, you could consider setting the old ones to gone and the new ones to visible
I think your code didn't work, because container is null. Method onCreateView gives you #Nullable ViewGroup container, which is null for DialogFragment (but non null for Fragment). So when you call View.inflate(getContext(), R.layout.dialog_layout_2, container), it just creates a view in memory and doesn't attach it to container, cause it is null. See LayoutInflater.inflate, cause View.inflate is just a convenience wrapper for this function.
I dont think there is an easy way to change views inside of the same DialogFragment so what would be the best way to do this?
Instead of changing dialog root you can just manipulate child views inside dialog root layout (add, remove them, or change visibility).
Also my advice is to use recommended way to create dialog with custom layout (onCreateDialog + setView), but if you don't want to do that, you can refer view you've created in onCreateView as dialog root.
You can try creating a dialog fragment with an empty shell layout in which you would replace your two different fragments with ChildFragmentManager and regular fragment transactions
passing data between them can be done using the activity's view model since they both live in the same activity.
So add the ShellDialogFragment using the activity's FragmentManager and in the shell fragment class change between NewDialog & NewDeliveryPointDialog on your button click listener with ChildFragmentManager
I am trying to write a test app which contains an activity. There are two fragments inside this activity, which are defined as LeftFragment and RightFragment. I used getFragmentManager().findFragmentById() to get connection from each other fragments. By using that methode I am able to get an LeftFragment object from RightFragment, but not RightFragment object from LeftFragment. It just works only oneway. I am doing this, because I want to call some operations from other fragment, that return some values. I was thinking about using EventBus but I failed too. How can I achive that?
Here is my LeftFragment
public class LeftFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.rightFragment);
if (rightFragment != null){
makeToast(rightFragment.getMessageFromRight());
}else {
makeToast("does not found rightFragment");
}
return inflater.inflate(R.layout.fragment_left, container, false);
}
public String getMessageFromLeft(){
return "Hi! Im left";
}
private void makeToast(String text){
Toast.makeText(getContext(),text,Toast.LENGTH_SHORT).show();
}
}
And here is my RightFragment
public class RightFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
LeftFragment leftFragment = (LeftFragment) getFragmentManager().findFragmentById(R.id.leftFragment);
if (leftFragment != null){
makeToast(leftFragment.getMessageFromLeft());
}else {
makeToast("does not found leftFragment");
}
return inflater.inflate(R.layout.fragment_right, container, false);
}
public String getMessageFromRight(){
return "Hi! Im right!";
}
private void makeToast(String text){
Toast.makeText(getContext(),text,Toast.LENGTH_SHORT).show();
}
}
There are many ways to communicate between 2 fragments . If 2 fragments loaded at the same time. I usaually use one of 2 ways below to do it.
You can use this link using obserable pattern to communication 2 fragments.
you can use EventBus lib for communication, it 's very simple
Your issue:
By using that methode I am able to get an LeftFragment object from
RightFragment, but not RightFragment object from LeftFragment
I think your problem is LeftFragment is intitialized previous, so you can find it from RightFragment. Your solution is ok, using EventBus. YOu need to review your codes to find the issue. You can test by creating other methods, after 2 fragment was initialized.
For ex: click button in LeftFragment, toast a message in RightFragment.
Probably what is happening is that the Left Fragment is getting the OnCreateView() call first, at which point the Right Fragment has not been inflated yet (therefore it can't be "found" by findFragmentbyId()).
I would suggest moving the code that gets the references to the other fragments into onStart(), and only inflate the fragments in onCreateView().
We all know that when using ViewPager with Fragment and FragmentPagerAdapter we get 3 Fragment loaded: the visible one, and both on each of its sides.
So, if I have 7 Fragments and I'm iterating through them to see which 3 of them are the ones that are loaded, and by that I mean onCreateView() has already been called, how can I determine this?
EDIT: The Fragment doesn't have to be the one that the ViewPager is showing, just that onCreateView() has already been called.
Well logically, this would be a reasonable test if onCreateView has been called:
myFragment.getView() != null;
Assuming you a have a reference to all of the fragments in the pager iterate, them and check if they have a view.
http://developer.android.com/reference/android/app/Fragment.html#getView()
Update
The above answer assumes that your fragments always create a view, and are not viewless fragments. If they are then I suggest sub classing the fragment like so:
public abstract class SubFragment extends Fragment
{
protected boolean onCreateViewCalled = false;
public boolean hasOnCreateViewBeenCalled()
{
return onCreateViewCalled;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup Container, Bundle state){
onCreateViewCalled = true;
return null;
}
}
Just bear in mind that further sub classes will have to call super or set the flag themselves should they override onCreateView as well.
I added an interface to Fragment. Looks like:
protected OnCreateViewCallback createViewCallback = null;
public void setCreateViewCallback(OnCreateViewCallback createViewCallback) {
this.createViewCallback = createViewCallback;
}
public interface OnCreateViewCallback {
void onCreateView();
}
In my onCreateView():
//initialize your view.
if (createViewCallback != null) {
createViewCallback.onCreateView();
createViewCallback = null;
}
return mainView;
From my activity:
if (ocrFragment.getView() == null) {
ocrFragment.setCreateViewCallback(new MainScreenFragment.OnCreateViewCallback() {
#Override
public void onCreateView() {
ocrFragment.ocrImage(picture, false);
}
});
} else {
ocrFragment.ocrImage(picture, false);
}
If you are trying to perform something after onCreateView is called, use onViewCreated:
Called immediately after onCreateView(LayoutInflater, ViewGroup,
Bundle) has returned, but before any saved state has been restored in
to the view. This gives subclasses a chance to initialize themselves
once they know their view hierarchy has been completely created. The
fragment's view hierarchy is not however attached to its parent at
this point.
public void onViewCreated(View view, Bundle savedInstanceState) {
MyActivity myActivity = (MyActivity) getActivity();
MyActivity.newAsyncTask(mPar);
}
You could also check for Fragment.isVisible() because a Fragment is in visible state when it's in the offscreen page limit of a ViewPager.
Edit: But it just really depends on what you really want to achieve with your question. Perhaps some kind of update to all UIs in your Fragments when their UI is ready?
EDIT:
Just another addition, you could listen to onViewCreated() and set a flag. Or notify your Activity and do further work (getActivity() will return your Activity at this point). But really, better state what you want to accomplish with your question.
We have a web service which serves up an XML file via a HTTP Post.
I am downloading and parsing this xml file into an object to populate some views inside a couple of fragments held in a FragmentPagerAdapter. I get this XML file via an AsyncTask and it tells my fragments the process has finished via a listener interface.
From there, I populate the view inside the fragment with data returned from the web service. This is all fine until the orientation changes. From what I understand, the ViewPager's adapter is supposed to retain the fragments it's created, which is fine, and which I want to happen, and I know the fragment's onCreateView method is still called to return the view. I've spent the last day or so hunting through posts here and the Google docs etc and I can't find a concrete method that lets me do what I want to do: retain the fragment, and it's already populated view so that I can simply restore it when the orientation changes and avoid unneccesary calls to the web service.
Some code snippets:
In the main activities onCreate:
mViewPager = (ViewPager) findViewById(R.id.viewpager);
if (mViewPager != null) {
mViewPager.setAdapter(new PagerAdapter(getSupportFragmentManager()));
}
if (savedInstanceState == null) {
if (CheckCredentials()) {
Refresh(0,0);
} else {
ShowCredentialsDialog(false);
}
}
Refresh method in main activity...
public void Refresh(Integer month, Integer year) {
if (mUpdater == null) {
mUpdater = new UsageUpdater(this);
// mUpdater.setDataListener(this);
}
if (isConnected()) {
mUpdater.Refresh(month, year);
usingCache = false;
mProgress.show();
} else {
mUpdater.RefreshFromCache();
usingCache = true;
}
}
This is the entire Fragment in question, minus some of the UI populating code as it's not important to show the setting of text in textviews etc...
public class SummaryFragment extends Fragment implements Listeners.GetDataListener {
private static final String KEY_UPDATER = "usageupdater";
private UsageUpdater mUpdater;
private Context ctx;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.ctx = activity;
}
private View findViewById(int id) {
return ((Activity)ctx).findViewById(id);
}
public void onGetData() {
// AsyncTask interface method, will be called from onPostExecute.
// Populate view from here
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_usagesummary, container, false);
mUpdater = (UsageUpdater) getArguments().getSerializable(KEY_UPDATER);
mUpdater.setDataListener(this);
return view;
}
}
If I understand any of this 'issue' it's that I'm returning an empty view in onCreateView but I don't know how to retain the fragment, return it's view prepopulated with data and manage all web service calling from the main activity.
In case you can't tell, Android is not a primary language for me and this probably looks a shambles. Any help is appreciated I'm getting rather frustrated.
If you're not using any alternative resources when the Activity is re-created, you could try handling the rotation event yourself by using configChange flags in your AndroidManifest:
<activity
...
android:configChanges="orientation|screenSize"
... />
There is no way to keep the same, pre-populated Views if your Activity is re-created since this would cause a Context leak:
http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/
Let's say I have this button:
<Button
android:id="#+id/idone"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="D2"
android:onClick="myMeth"/>
I have several times used this to call methods from a layout xml as it calls the method from the activity that inflated such view.
Recently with DialogFragments, well it does not work at all. I keep getting an error telling me that such method does not exist. Where is it then looking for such method? I have added it to the DialogFragment class:
public class myActivity extends DialogFragment {
public DiceDialog() {
// empty constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.myDialog, container);
getDialog().setTitle("Hello");
return view;
}
public void myMeth(View view) {
//...
}
As well as in the activity that instantiates the FragmentManager and calls the dialog:
public Class MainActiviry Extends FragmentActivity {
//...
public void onCreate(Bundle savedInstanceState) {
// ..
FragmentManager fm = getSupportFragmentManager();
MyActivity dialog = new AddDiceDialog();
dialog.show(fm, "tag");
}
public void myMeth(View view){
//...
}
And still the messag is that MyMeth is not found.
I have already read that using interfaces and listeners is the correct way to communicate between activity and dialog fragments, but what I am trying to figure out here is where that myMeth call is being made, because well,it is called.
You can implement public myMeth(View view) in your Activity, which will then check for the currently visible Fragment, and call its method.
If you want to use more then one callable method in your Fragment, you can utilize the id's of the calling views and implement a switch, calling a different fragment method according to the id of the View.