Is it possible to launch a dialogfragment from a dialogfragment in android - java

I have a scenario where I'd like to open a dialogfragment from a button click in an already open dialog fragment. I'd like the original to either stay open and the new one open on top of it or hide the original and then reopen it when the second dialogfragment is dismissed. Just wondering if this is feasible before I go about trying to do it and possibly waste my time. Can anyone advise?

Following on from the comments above I have found that the following appears to work (Compiles and runs on a test handset although no logic is yet implemented). I replaced the onAttach method above with the following code.
#Override
public void onAttach(Context context) {
super.onAttach(context);
TextPropertiesDialogFragment prev = (TextPropertiesDialogFragment) getFragmentManager().findFragmentByTag("TextPropertiesDialogFragment");
if (prev != null) {
if (prev instanceof OnColourPickerFragmentInteractionListener) {
mListener = (OnColourPickerFragmentInteractionListener) prev;
} else {
throw new RuntimeException(prev.toString()
+ " must implement OnFragmentInteractionListener");
}
}
}
For reference this onAttach method is in the second dialog fragment called OnColourPickerDialogFragment. The TextPropertiesDialogFragment is the first dialogfragment that is called from the activity. For completeness I've included the definition of the listener and interface below.
public class TextPropertiesDialogFragment extends DialogFragment implements View.OnClickListener, ColourPickerDialogFragment.OnColourPickerFragmentInteractionListener{
public void colourPickerCallBackMethod(Bundle bundle){
//Do some work here
}
//........
}
public class ColourPickerDialogFragment extends DialogFragment implements View.OnClickListener {
private View topLevelFragmentView;
private OnColourPickerFragmentInteractionListener mListener;
private Bundle callBackBundle;
public interface OnColourPickerFragmentInteractionListener{
void colourPickerCallBackMethod(Bundle callBackBundle);
}
//......
}

Related

Create generic/base form class regardless if activity or fragment

I'm trying to create a generic/base form regardless if it is an activity or fragment. To make it simple, a Form can submit so:
class BaseFormActivity extends AppCompatActivity {
public abstract void submitForm();
#Override
public void setContentView(int layoutResID) {
ConstraintLayout activityBaseForm = (ConstraintLayout) getLayoutInflater().inflate(R.layout.activity_base_form, null);
FrameLayout frameBaseForm = activityBaseForm.findViewById(R.id.frame_base_form);
getLayoutInflater().inflate(layoutResID, frameBaseForm, true);
findViewById(R.id.btn_submit).setOnClickListener(v -> submitForm()) // for the sake of simplicity, there's a button that will trigger submitForm() method
super.setContentView(activityBaseForm);
}
}
Here, I just include some default layout for a form, and a button for submit that triggers the abstract method submitForm(). But, this is only for android activities. How can I make this also available for fragments without writing a BaseFormFragment? I don't want to repeat default behaviors from activity to the fragment and vice versa.
Consider it as a sample Presenter class which handle your button click and get all form fields and send to server
public class MyPresenter {
private MyPresenterIView iViewInstance;
public MyPresenter(MyPresenterIView iView){
this.iViewInstance=iView;
}
public void onSubmitClick(){
//write your logic here
String fieldOneText=iViewInstance.getFieldOneText();
sendToServer(fieldOneText);
}
private void sendToServer(String stringInfo){
//send info to server
}
}
MyPresenterIView Interface
public interface MyPresenterIView{
String getFieldOneText();
}
And use Presenter in your Activity or Fragment
//implement MyPresenterIView to your Activity or Fragment
public class MyActivity extent SomeActivity implements MyPresenterIView{
private MyPresenter myPresenter;
//in onCreate or onCreateView(if its a fragment) initialize myPresenter
protected void onCreate(..){
myPresenter=new MyPresenter(this);//this will enforce Activity/Fragment to implement IView
}
#Override //comes from MyPresenterIView
public String getFieldOneText(){
return ((EditText)findViewById(R.id.edttext_field_one)).getText().toString().trim();
}
}

How do I communicate between a class and a fragment which uses it?

I'm using Android Studio. I haven't been able to find an answer online, so even a link to a solution would be helpful.
I have an Activity, which includes a number of Fragments. One of these Fragments is called BookGridFragment, which uses a class called BookGrid.
BookGridFragment looks like this (I've left out irrelevant bits):
public class BookGridFragment extends Fragment {
BookGrid myBookGrid;
public BookGridFragment() {}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
// Inflate layout
View rootView = inflater.inflate(
R.layout.fragment_book_grid, container, false);
myBookGrid = rootView.findViewById(book_grid);
return rootView;
}
public void setBook(Book thisBook) {
myBookGrid.setBook(thisBook);
}
}
The BookGrid class is:
public class BookGrid extends View {
private Book mBook;
public BookGrid(Context thisContext, AttributeSet attrs) {
super(thisContext, attrs);
}
public void setBook(Book newBook) {
mBook = newBook;
}
protected void onDraw(Canvas canvas) {
if (mBook == null) return;
canvas.save();
draw_book_details();
// draw_book_details() is a function which just takes
// the book info and displays it in a grid
canvas.restore();
}
public boolean onTouchEvent(MotionEvent event) {
// This function responds to the user tapping a piece of
// book info within the grid
// THIS IS WHERE I'M HAVING PROBLEMS
}
}
So, that all works fine. The issue is, that I need the BookGridFragment to know when the user touches the BookGrid and to pass that information to another Fragment (via the Activity). So, I assume that when the onTouchEvent is reached, that should somehow notify the BookGridFragment that the BookGrid was touched, but I can't figure out how to do that.
Everything I've found online is about passing information between Fragments, but that approach doesn't work here as the BookGrid class doesn't "know" that it's within a BookGridFragment.
You could use the same idea that is used to communicate a Fragment and an Activity. Create an interface:
public interface OnBookGridTouched{
void onTouchGrid();
}
Add a variable to your BookGrid:
private OnBookGridTouched mCallback;
Add a setter to this variable:
public void setCallback(OnBookGridTouched callback){
mCallback = callback;
}
Then make your fragment implement the interface:
public class BookGridFragment extends Fragment implements OnBookGridTouched {
You'll be forced to implement the method onTouchGrid
In your fragment onCreateView pass the fragment to your custom view:
myBookGrid.setCallback(this);
Finally, in your custom view you can call the callback to reference the fragment:
public boolean onTouchEvent(MotionEvent event) {
// This function responds to the user tapping a piece of
// book info within the grid
// THIS IS WHERE I'M HAVING PROBLEMS
mCallback.onTouchGrid();
}
A solution could be to set the onTouch/onClick listener in the fragment instead of in the BookGrid itself. From there you can use the fragment method getActivity() to call an activity method, parsing on the correct data to the correct fragment.
I think this situation is very similar to a Fragment containing a Button.
The Button has a method which accepts something implementing a certain interface (for the Button: View.OnClickListener). The Fragment calls that method (for the Button: setOnClickListener()) to pass in the desired Object implementing all the required methods, either an anonymous class or maybe a field or the Fragment itself. There are pros and cons for all three approaches, it depends on your situation which one is best.
They have in common that BookGrid should have an interface as well as a method so other classes can set the current Object implementing that interface.
I am not quite sure about the exact scenario that you are having there. However, if the problem is the communication between the fragment and an activity which hosts the fragment, then you might think of the following implementation.
Let me point out some of your concerns first.
Everything I've found online is about passing information between
Fragments, but that approach doesn't work here as the BookGrid class
doesn't "know" that it's within a BookGridFragment.
BookGrid class will know the context of its existence when you will pass the Context towards it while calling a function of it. So I would like to suggest passing the context of the Activity or Fragment when you are calling a function from your BookGrid class.
public class BookGrid extends View {
private Book mBook;
private Context context;
public BookGrid(Context thisContext, AttributeSet attrs) {
super(thisContext, attrs);
this.context = thisContext;
}
public void setBook(Book newBook) {
mBook = newBook;
}
protected void onDraw(Canvas canvas) {
if (mBook == null) return;
canvas.save();
draw_book_details();
// draw_book_details() is a function which just takes
// the book info and displays it in a grid
canvas.restore();
}
public boolean onTouchEvent(MotionEvent event) {
// Call the function of your host activity
((YourActivity)(thisContext)).onBookGridTouched();
}
}
Now write a public method in your activity class which hosts the fragment named onBookGridTouched.
public void onBookGridTouched() {
// Communicate with other fragments here
}
However, a noble approach of solving this problem in a more generic way is to use an interface and then implement the interface wherever necessary like #LeviAlbuquerque suggested.
I am just putting another workaround which is a bit static.
Assuming that you have ONE Activity responsible of all fragments:
1.Create an interface in your BookGrid:
public interface ActionHappened {
void onActionHappened();
}
2.Create an instance of your interface within your BookGrid class and trigger the method onActionHappened where you want it to be triggered. For instance, if you would like it to happen in your onDraw(), then do the following:
ActionHappened actionHappened;
protected void onDraw(Canvas canvas) {
if (mBook == null) return;
canvas.save();
draw_book_details();
// draw_book_details() is a function which just takes
// the book info and displays it in a grid
canvas.restore();
actionHappened.onActionHappened();
}
3.Implement your interface within your activity
public class ActivityA extends AppCompatActivity implements BookGrid.ActionHappened {}
4.Within your implemented method, trigger the method:
#Override
public void onActionHappened() {
Fragment fragmentA = getSupportFragmentManager().findFragmentByTag(R.id.fragmentA);
Fragment fragmentB = getSupportFragmentManager().findFragmentByTag(R.id.fragmentB);
//Trigger that method from your activity to fragmentA or fragmentB
fragmentA.doWork();
fragmentB.doWork();
}
Wether you would like to pass data to fragmentA or fragmentB, doWork() method will do that for you. Make you create such a method in the corresponding fragment.

Android - How to properly get a variable from a parent activity from a fragment

I read the official Android documentation on creating an interface to be able to communicate between a parent activity and a fragment. So I did but my app crashes when I call one of the methods to get a value from the parent activity.
if have an interface like this in my fragment
public interface InteractWithFragmentA {
String getStringText();
}
In my calling activity I tested it out with a dummy text
#Override
public String getStringText(){ return "Some dummy text";}
I have a variable in FragmentA.java that's a reference to the host activity and casted to InteractWithFragmentA, but when I call the method using
_hostActivity.getStringText()
the app crashes. Is there something that I'm missing? I've seen some suggested methods for getting the host activity's variables by making them public and static or some other method but I'm trying not to couple the fragment to that activity. Thanks in advance.
Yo should do this:
Activity
public class YourActivity implements YourActivityInterface{
#Override public String getStringText(){ return "Some dummy text";}
}
Interface
public interface YourActivityInterface {
String getStringText();
}
Fragment
public class YourFragment extends Fragment {
YourActivityInterface mListener;
//your method...
mListener.getStringText()
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof YourActivityInterface) {
mListener = (YourActivityInterface) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement YourActivityInterface");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
}
Try this from fragment
((YourActivity) getActivity()).getStringText();

Make UI changes in MainActivity from helper class

I am trying to put in app billing in my android app. I am currently stuck on how to call back to my MainActivity to perform a UI change from the helper billing class once it is found the user has previously bought something.
I have searched and searched but I cannot find what I'm looking for, I'm sure I need to implement a callback or a listener or both?
My code is the following:
MainActivity
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
MyBilling bill;
private Menu nav_Menu;
#Override
protected void onCreate(Bundle savedInstanceState) {
bill = new MyBilling(this);
bill.onCreate();
}
private void displaySelectedScreen(int itemId) {
///calls fragment and inflates
}
public void UpdateUI(){
//Make some changes to UI
nav_Menu.findItem(R.id.remove_ad_button).setVisible(false);
//Recall fragment
displaySelectedScreen(R.id.distance_check);
}
}
Billing helper class (Google In App Billing)
public class MyBilling {
Activity activity;
public MyBilling(Activity launcher) {
this.activity = launcher;
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null)
return;
// Is it a failure?
if (result.isFailure()) {
// complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
/*
* Check for items we own. Notice that for each purchase, we check
* the developer payload to see if it's correct! See
* verifyDeveloperPayload().
*/
// Do we have the premium upgrade?
Purchase removeAdsPurchase = inventory.getPurchase(SKU_REMOVE_ADS);
AdCheck = (removeAdsPurchase != null && verifyDeveloperPayload(removeAdsPurchase));
//Yes there has been purchases!
if(AdCheck == true){
removeAds(); // sets global flag
//// Want to call UI change here....
MainActivity main = new MainActivity();
main.UpdateUI();
}
}
};
}
What I understand from debugging is IabHelper.QueryInventoryFinishedListener is called during oncreate of billing class, it is asynchronous, so my main activity continues to compile. When setup and reading of purchases are complete I need to go back to my MainActivity and make changes.
If anyone can point me in the right direction here please - I've tried to keep the code straight forward by just provided the needed snippets.
Use an Interface. Add the UpdateUI() method declaration in the Interface. Then create instance of the Interface in the MyBilling class and initialise using constructor. Call the UPdateUI() method throught the the instance of the Interface. Then implement the Interface in your MainActivity. If you are unclear about implementing, I'd be happy to post the code.
The code :
In your MyBilling class, add the following inside the class
public interface Update {
void UpdateUI();
}
Use this to call the UpdateUI() method from the MyBilling class.
myActivity.UpdateUI();
Use this as the constructor in MyBilling class
Update myActivity;
public MyBilling (Update activity) {
myActivity = activity;
}
Now change to the following in the MainActivity
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, MyBilling.Update
If a red line appear beneath it, Alt+enter it to implement the Update interface in MainActivity. I'm guessing since you already has UpdateUI() in your MainActivity, there wont be a red line error. Hope it helps!

Fragment communicating with an Activity

I am confused about how communication with a Fragment and an Activity is made. For example, an interface was defined here (https://developer.android.com/training/basics/fragments/communicating.html).
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
...
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// The user clicked on a list item.
mCallback.onArticleSelected(position);
}
}
Eventually, the following method is called from the MainActivity.
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// Do something
}
}
My questions are:
How does mCallback "know" which onArticleSelected method to call (as there might be other classes that have implemented OnHeadlineSelectedListener).
mCallback.onArticleSelected(position);
I wouldn't be confused if it went:
mCallback = new OnHeadSelectedListener() {
#Override
public void onArticleSelected(int position)
// Do something
}
and then mCallback is referred in some way in MainActivity to utilize the onArticleSelected method in some way. In the example code, however, the line intelligently sticks to "an" interface. How does that happen?
Also, I found that the Log I implemented onArticleSelected method from MainActivity is called previously to the one in onListItemClick method in HeadlineFragment. Is it expected?
Since you defined the OnHeadlineSelectedListener interface with only one method, and your activity implements it, there's no ambiguity in choosing the method when you use the activity as instance of this interface, 'cause all you know about activity while using it as instance of OnHeadlineSelectedListener interface is a presence of onArticleSelected(int) method in it.
It depends on when you call the logging function - before or after calling the callback method.
P.S. While this kind of communication between Activity and Fragment (or any other objects) is perfectly fine, personally I prefer the Event Bus approach, 'cause it gives us a possibility to organize code in a low coupled manner. Here are some nice implementations of Event Bus pattern:
https://github.com/greenrobot/EventBus
http://square.github.io/otto/
Take a look at them if you are interested in this approach.
Your mCallback is your activity, in the onAttach method of your fragment, you will set the activity as listener for your fragment. By this way, this is normal that the activity is notified when you call mCallback.onArticleSelected(position);
For your first part of question
You should have a look on onAttach and onDetach methods-
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallbacks = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement OnHeadlineSelectedListener.");
}
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
Second part-
The behavior is not expected.
You should not make your activity class static as well.

Categories

Resources