I have an Activity with a single Fragment that asks the user to login. Once the person logs in, a new Activity is launched. My question is, once the the person enters their credentials and hits the login button on the Fragment should
A)the fragment alert its current Activity first and then from there start the new Activity. For example, here is my Fragment:
public class LoginFragment extends Fragment implements View.OnClickListener {
private Button loginButton;
private ClickedInterface clickedInterface;
public LoginFragment() {
// Required empty public constructor
}
static interface ClickedInterface{
public void buttonClicked(View v);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_login, container, false);
loginButton = (Button)view.findViewById(R.id.fragment_login_loginButton);
loginButton.setOnClickListener(this);
return view;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.clickedInterface = (ClickedInterface)activity;
}
#Override
public void onDetach() {
super.onDetach();
clickedInterface = null;
}
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.fragment_login_loginButton:{
clickedInterface.buttonClicked(v);
break;
}
}
}
And Here is the Activity using the ClickedInterface method:
#Override
public void buttonClicked(View v) {
switch (v.getId()){
case R.id.fragment_login_loginButton:{
//Do Stuff
break;
}
}
}
OR
B)launch the new Activity right from the Fragment?
Thank you
It totally depends on what business logic you have in your fragment, and whether you're using it in multiple activities. For example, you might have a share button, and a button that sends an Intent to rate your app in the Play Store.
In the sharing scenario, you might want each activity to have a different share text. In this case, you would get a reference of the activity, check that it implements an interface, then delegate everything to the activity:
// share scenario (delegate to Activity)
if (getActivity() instanceof MyCallback) {
((MyCallback)getActivity()).launchMyIntent(); // TODO handle callback in activity
}
However, if you know for a fact that you only want one behaviour regardless of where you are in the app (like with a rating button), it might make sense to just send your intent straight from the Fragment.
// rating scenario (send intent from fragment
getActivity().startActivity(myIntent);
The best way is to pass the control to MainActivity and then let the activity do the rest.
Second way of doing is--> Create a static method like openPostLoginActivity inside MainActivity and call it from your fragment. This way you can ensure that the global action is always residing in parent. This would mostly help you when you will be having multiple fragments. I have seen this type of approach used by pubnub sample android app
Third is the one which you mentioned. Opening it from fragment itself.
Frankly speaking in your scenario you can use any one of above. It depends whether you want to stuck with the standards or just want your work to get done. All the best!!1
Related
Here is my situation. I have a fragment which has two buttons on it. When you tap on either button, a DialogFragment appears which contains a single EditText with ok/cancel buttons. Both buttons open the same DialogFragment, but the data input into the EditTexts needs to be kept separate.
I recently started implementing the fragment event callback pattern from the Android docs seen here, but have run into an issue - I have two buttons using the same event callback and am not sure how to differentiate which one the user has just finished using. So using the docs as an example, I can open FragmentA from two buttons on the same screen, but need to handle the result differently depending on which button I clicked.
In my fragment:
public static class FragmentA extends DialogFragment {
public interface OnEditNameListener {
public void onPositiveButtonClicked(String newName);
}
}
#Override
public void onAttach(Context context){
super.onAttach(context);
try {
mListener = (OnEditNameListener ) context;
}
catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement OnEditNameListener ");
}
}
In my Activity, which implements the OnEditNameListener:
button1.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View view) {
(new EditNameDialog.Builder())
.setTitle(getContext().getString(R.string.title))
.setValue(currentText)
.show(mParentFragmentActivity.getSupportFragmentManager());
}
});
button2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
(new EditNameDialog.Builder())
.setTitle(getContext().getString(R.string.title2))
.setValue(currentText2)
.setInputType(InputType.TYPE_CLASS_NUMBER)
.show(mParentFragmentActivity.getSupportFragmentManager());
}
});
#Override
public void onPositiveButtonClicked(String newName) {
... //does stuff with the name.
//Currently no way to determine whether this came from button1 or button2.
}
Currently, both callbacks hit the same OnPositiveButtonClicked method with the input from the DialogFragment, but I do not know how to determine which of the two buttons this came from.
First of all you have to add an argument to your onPositiveButtonClicked(String name,int buttonId),and the pass to FragmentA an argument based on the button pressed like:
FragmentA fragment=new FragmentA();
Bundle args = new Bundle();
args.putInt("buttonId", 1 or 2);
fragment.setArguments(args);
//open the fragment from the activity
Then in your FragmentA onCreate method try:
int buttonId = getArguments().getInt("buttonId");
and finally when positive button pressed call:
onPositiveButtonClicked(newName,buttonId)
UPDATE
An even better solution is to create a setter in your DialogFragment and use anonymus interfaces like:
(new EditNameDialog.Builder())
.setTitle(getContext().getString(R.string.title))
.setValue(currentText).setOnEditNameListener(new EditNameListener{
#Override
onPositiveButtonClicked(String newName){
//handle action
}
});
And in your DialogFragment add the setter:
EditNameListener listener;
public DialogFragment setOnEditNameListener(EditNameListener listener){
this.listener=listener;
return this;
}
I have method in fragment activity and if that method trigger, I need to update fragment listView. I am dealing with database. Where I am clearing the database of particular user and i will update fragment.
Problem is: if user is in same screen means, how to update fragment listview if fragment activity method triggers? It only works when I need to go back to activity and once again need to come to same screen.
Here is code:
public class ActivityExpertDasboard extends ActivityBase {
// this method is calling when particular user closes the screen. when I am in fragment screen..
#Override
protected void onChatInvitation(String msgKeys, String userId) {
String msgKey = mApplication.returnEmptyStringIfNull(msgKeys);
LogMessage.e("username", mPreference.getStringFromPreference(Constants.CLOSE_CHAT_USERNAME));
if (userId.equalsIgnoreCase(mPreference.getStringFromPreference(Constants.CLOSE_CHAT_USERNAME))) {
if (msgKey.equalsIgnoreCase(Constants.CODE_CHAT_END)) {
AAEDatabaseHelper.deleteUsername(mPreference.getStringFromPreference(Constants.CLOSE_CHAT_USERNAME));
// I need to update in Fragment screen if this is triggered.
}
}
super.onChatInvitation(msgKey, userId);
}
}
FragmentExpertLiveChats:
public class FragmentExpertLiveChats extends Fragment {
private List<LiveChatDetails> list;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_chat_history, container,
Constants.ATTACH_TO_ROOT);
list = AAEDatabaseHelper.getLiveChatDetails(Constants.TABLE_LIVE_CHAT);
}
#Override
public void onStart() {
LogMessage.e("onStart", "onStart");
super.onStart();
updateUI();
}
}
If phone is in FragmentExpertLiveChat screen without doing any perfomance and if method in activity calls, how to update the row? I need to use broadcast receiver? If yes, where and how?
For that, and many more cases, where you need to communicate amongst different components, I suggest using EventBus. It's usage is very simple:
Define events: public class MessageEvent { /* Additional fields if
needed */ }
Prepare subscribers Register your subscriber (in your onCreate or in a
constructor): eventBus.register(this);
Declare your subscribing method: #Subscribe public void
onEvent(AnyEventType event) {/* Do something */};
Post events: eventBus.post(event);
Don't forget to unregister afterwards. I suggest you do registration/unregistration in start/stop or pause/resume, or, in case of fragments, attach/dettach.
In your case, register in Fragment, and in Activity, when user does his things, post event.
I have 2 fragments which are instantiated from the same class as the layouts are identical like so:
getSupportFragmentManager().beginTransaction().
add(R.id.leftContainer,new LeftFragmentClass(),"leftFrag").commit();
getSupportFragmentManager().beginTransaction().
add(R.id.rightFrag,new LeftFragmentClass(),"rightFrag").commit();
Within LeftFragmentClass there is a callback method which is called when the button within the fragment is pressed. After this some processing is done and data is displayed, however, right now the callback cannot distinguish which button was pressed. Is there a function which can return which fragment button was pressed?
For this type of condition i create a function inside fragment which will return me the instance of fragment and make the fragment constructor private something like:-
public class LeftFragmentClass extends Fragment{
private String fragmentTag = null;
public LeftFragmentClass(){}
public static LeftFragmentClass newInstance(String tag){
LeftFragmentClass mLeftFragmentClass = new LeftFragmentClass();
Bundle bundle = new Bundle();
bundle.putString ("tag",tag);
mLeftFragmentClass.setArgument(bundle);
return mLeftFragmentClass;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
tag = getArguments().getString("tag")
}
}
So i used newInstance function to create instance of LeftFragmentClass and pass the tag to it which i m setting to Fragment argument using bundle and inside onCreate get bundle using getArguments and from it the tag value. Pass this tag value as one of the parameter to your callback method to identify which button was clicked.
So from activity for getting instance of LeftFragmentClass you can write as
LeftFragmentClass mLeftFragmentClassLeft = LeftFragmentClass.newInstance("left")
LeftFragmentClass mLeftFragmentClassRight = LeftFragmentClass.newInstance("Right")
==== Edit ====
keep the fragment class constructors always public don't make it private as i suggested above in my sample code. Making it private will cause application to crash with exception
java.lang.RuntimeException: Unable to start activity
ComponentInfo{MainActivity}:
android.support.v4.app.Fragment$InstantiationException: Unable to
instantiate fragment com.thatswhy.AppAlertDialog: make sure class name
exists, is public, and has an empty constructor that is public
Fragment fragment = getFragmentManager().findFragmentByTag("Tag")
As per provided the info you can do something like this, in your callback method pass the button object and check accordingly,
Some code snippet to explain the same :
Suppose your callback method is onButtonClick() then you can pass button object like :
public void onButtonClick(Button button){
// check here with button id
if(button.getId() == R.id.button1) {
} else if(button.getId() == R.id.button1) {
}
}
Hope this makes things clear..
The cleanest way of doing this I've seen is to create two distinct View.OnClickListener(s) in the Activity.
Have a getter() for each. public View.OnClickListener getLeftButtonPressed(), public View.OnClickListener getRightButtonPressed()
Then when you instantiate your left and right instances of your fragment, just pass in the appropriate 'View.OnClickListener' to the constructor of the Fragment. This not only reduces the code in the Fragment(s), it also centralizes the 'logic' of what to do when buttons are pressed.
public class MyActivity extends Activity {
// create the two listeners
View.OnClickListener leftButtonListener = new View.OnClickListener() {
public void onClick(View v) {
leftButtonClicked(v);
}
});
View.OnClickListener rightButtonListener = new View.OnClickListener() {
public void onClick(View v) {
rightButtonClicked(v);
}
});
// 2 getters
public View.OnClickListener getLeftListener() { return this.leftButtonListener; }
public View.OnClickListener getRightListener() { return this.rightButtonListener; }
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.content_layout_id);
}
// actual logic of what to do when each button is pressed.
private void leftButtonClicked(View v){
// some logic here
}
private void rightButtonClicked(View v){
// some logic here
}
}
This removes you later having to keep track of which button was pressed by making use of strings and if/then/else blocks, etc.
Add a parameter to interface callback function in your fragment;
interface Interfacecallback{
public void callbackfunction(int fragid);
}
Interfacecallback interfacecallback;
//in your button click
//pass 1 for fragment right
//pass 2 for fragment left
interfacecallback.callbackfunction(1);
You can check the fragment tag using this line of code if it exists:-
Fragment mapFragment = getFragmentManager().findFragmentByTag("MapRestaurantFragment");
Here is what I would like to do:
1) Inside an Activity a dialog is shown. I use DialogFragment and FragmentManager for this, by calling:
dialogFragment.show(fragmentManager, "edit_task_list");
2) Inside the Dialog I have layout with a custom Button. I would like to perform some action when the button is clicked and later close the dialog.
How should I connect everything? I see two options:
1) onclick attribute in the Button and a method inside the Actvity. That was my original plan, but I don't how to get the Dialog from the Activity to dismiss it. Even if this is not the right way, how could this be done? I would like to understand how this works.
2) set on click listener on the button when the Dialog is created in DialogFragment. This will require me to pass some context from the Activity to the DialogFragment, so I would like to avoid it (and keep the DialogFragment as simple as possible).
Which of those options should I take?
Number 2 Doesn't require you to pass any context (and you shouldn't). You define an interface that can act as a contract between fragments and activities and make your activity implement it.
From your dialog and in your button.onClick(), you do something like this (untested code):
if ( getActivity() != null
&& !getActivity().finishing()
&& getActivity() instanceOf YourInterface) {
((YourInterface)getActivity()).onSomeNiceMethod();
dismiss(); // close the dialog (if this is what you want).
}
The interface looks like:
public interface YourInterface {
void onSomeNiceMethod();
}
And your Activity…
public class YourActivity implements YourInterface {
void onSomeNiceMethod() {
// Hey! The Button In The Dialog Has Been Pressed!
}
}
All Activity and Fragment classes have a built-in callback method for you to use when you start another Activity, Fragment, Dialog, or DialogFragment.
void onActivityResult(int requestCode, int resultCode, Intent data)
Since you want to start the Dialog from an Activity, using the Dialog class is better than the DialogFragment class. The latter is better for starting a dialog from a Fragment, because it has two methods for communicating back to the Fragment (get/set TargetFragment())
The Dialog class has the getOwnerActivity() method. This is the Activity you use when creating the Dialog with one of its constructors.
You set a onClickListener on the button in the Dialog class. To pass the result back to the Activity:
getOwnerActivity().onActivityResult(intIdentifyingme, Activity.RESULT_OK,
intent);
dismiss(); // close the dialog
You put additional info you want to send in an Intent.
1) onclick attribute in the Button and a method inside the Actvity.
That was my original plan, but I don't how to get the Dialog from the
Activity to dismiss it. Even if this is not the right way, how could
this be done? I would like to understand how this works.
Basically your Activity has to remember/know which dialog is active at the moment with something like curDialog=dialogFragment;, then when handling the button onclick action you'll know which dialog to dismiss. But this is really not a good idea since basically the Button View would "leak" from your DialogFragment to your Activity, which breaks object encapsulation.
2) set on click listener on the button when the Dialog is created in
DialogFragment. This will require me to pass some context from the
Activity to the DialogFragment, so I would like to avoid it (and keep
the DialogFragment as simple as possible).
As a previous answer mentioned, you don't need to pass any Context to it, especially since you can get the Activity by calling getActivity().
The solution depends on whether or not this dialog would be used by multiple Activities:
Used by a single Activity: #Martin's solution will work just fine
Used by multiple Activity: abstraction can be used such that only the user's decision is passed to a listener. This is a (modified) solution I came up for the same problem:
public class BaseDialogFragment extends DialogFragment {
protected TextView dialogEn;
protected Button dialogYes;
private Button dialogNo;
protected OnSelectListener listener;
public interface OnSelectListener {
public void onSelect(int type, boolean yes);
}
public void setOnSelectListener(OnSelectListener listener) {
this.listener = listener;
}
public BaseDialogFragment() {
super();
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_confirm, container, false);
dialogYes = (Button) v.findViewById(R.id.yes);
dialogNo = (Button) v.findViewById(R.id.no);
dialogEn = (TextView) view.findViewById(R.id.dialog_en);
dialogEn.setText(getArguments().getString("text_en"));
dialogYes.setOnClickListener(this);
dialogNo.setOnClickListener(this);
return v;
}
public void onClick(View v) {
if (listener != null) {
listener.onSelect(getArguments().getInt("type"),
v == dialogYes ? true : false);
}
getDialog().dismiss();
}
}
To use it some additional info needs to be provided:
Bundle bundle = new Bundle();
bundle.putInt("type", type); //type: an unique integer value that helps differentiate result from different dialogs
bundle.putString("text_en", en); //en: String to be displayed
dialog.setArguments(bundle);
dialog.setOnSelectListener(this);
So if the type value above is set to 115, then a dialogYes button click would trigger public void onSelect(int type, boolean yes) method to be called with 115 and true as the 1st & 2nd parameters.
Your first point about the onClick attribute in the xml should be avoided. Because handling a Dialog that way could be really painfull if you respect events like screen rotation or a setup with multiple dialogs. This leads into leaked window errors most of the time and needs unnecessary code overhead to avoid this. Because you have to keep track of the Dialog which is actually shown yourself.
To be able to dismiss the Dialog this way you can use the Tag you setted as you called dialogFragment.show(fragmentManager, "edit_task_list");
DialogFragment frag = (DialogFragment)getFragmentManager().findFragmentByTag("edit_task_list");
if(frag != null)
frag.dismiss();
The proper solution is to use an interface as a callback for the communication between the DialogFragment and the Activity. This keeps the Dialog modular and the code easy. Here is an example from the docs. For this you don't need a Context. You simply pass the interface to the dialog in the onAttach() callback. It has a reference of the Activity as a parameter, which called that Dialog.
// Example interface for the communication
public interface OnArticleSelectedListener {
public void onButtonClicked(/*any Parameters*/);
}
public static class FragmentA extends DialogFragment {
OnArticleSelectedListener mListener;
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity; // get the interface of the Activity
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnArticleSelectedListener");
}
}
...
}
Handle the Button click in the Dialog and call dismiss() in it, that the Dialog can dismiss itself. Have a look at this question why to use dismiss() instead of getDialog().dismiss().
yourButton.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v){
if(mListener != null) // check if the listener is still valid
mListener.onButtonClicked(...); // calls the Activity implementation of this callback
dismiss(); // dismiss the Dialog
}
});
In onPause() of the Dialog set the reference of the interface to null. This way you can be sure that the callback will only be used if the Dialog is showing.
Your Activity looks something like this to be able to handle the callback:
public class MyActivity extends Activity implements OnArticleSelectedListener{
...
#Override
public void onButtonClicked(...){
// your implementation here
}
}
I don't know your overall setup but if you would use an AlertDialog a click on the Buttons dismiss the Dialog automatically when the method returns.
I have two Fragments in my Activity: fragment A with button X and fragment B with button Y.
How can I change button X's background image when I click button B? Is it possible?
From the documentation,
Because each fragment defines its own layout and its own behavior with its own lifecycle callbacks, you can include one fragment in multiple activities, so you should design for reuse and avoid directly manipulating one fragment from another fragment.
That being said, what you want to do is create event callbacks to the activity. A good way to do that is to define a callback interface inside the fragment and require that the host activity implement it. When the activity receives a callback through the interface, it can share the information with other fragments in the layout as necessary. This is the recommended way to share events between two separate Fragments--that is, sharing the event through the activity.
Check out the link above... it provides a couple nice examples. If you are still having trouble, let me know and maybe I can be more explicit.
Edit #1:
Let's say you click a button in fragment A and you want this to cause changes to a button in fragment B. Here's some sample code illustrating the concept:
The callback interface:
public interface OnButtonClickedListener {
public void onButtonClicked();
}
The activity:
public class SampleActivity extends Activity implements OnButtonClickedListener {
/* Implementation goes here */
public void onButtonClicked() {
// This method is called from fragment A, and when it is called,
// it will send information to fragment B. Remember to first
// check to see if fragment B is non-null.
/* Make call to a method in fragment B that will update its display */
}
}
Fragment A:
public class FragmentA extends Fragment {
OnButtonClickedListener mListener;
/* Implementation goes here */
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnButtonClickedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnButtonClickedListener ");
}
}
public void clickButton() {
// When the button is clicked, notify the activity.
// The activity will then pass the information to fragment
// B (if it has been created).
mListener.onButtonClicked();
}
}
Edit #2:
Now, you might be wondering, "Why would anyone ever go through all of this trouble? What's the point of creating a separate activity callback method when you could just have fragment A directly manipulate fragment B?"
The main reason you want to do this is to ensure that each fragment is designed as a modular and reusable activity component. This is especially important because a modular fragment allows you to change your fragment combinations for different screen sizes. When designing your application to support both tablets and handsets, you can reuse your fragments in different layout configurations to optimize the user experience based on the available screen space. For example, on a handset, it might be necessary to separate fragments to provide a single-pane UI when more than one cannot fit within the same activity. Making use of activity callbacks ensures that you will easily be able to reuse your fragments in situations where fragment B is not visible on the screen. For example, if you are on a handheld device and there is not enough room to display fragment B, then you can easily have your activity check to see if fragment B is currently being shown on the screen.
Sorry if this isn't clear... I'm finding it difficult to describe :P. Working your way through this tutorial might help... Activity callbacks make your life especially easier as a developer when you are working with interactive multi-pane layouts.
Base on Alex Lockwood's answer:
The activity:
public class SampleActivity extends Activity{
public interface OnButtonClickedListener {
public void onButtonClicked();
}
private OnButtonClickedListener onButtonClickedListener = null;
public OnButtonClickedListener getOnButtonClickedListener () {
return onButtonClickedListener
}
public void setOnButtonClickedListener (
OnButtonClickedListener onButtonClickedListener {
this.onButtonClickedListener = onButtonClickedListener;
}
}
Fragment A:
public class FragmentA extends Fragment {
private OnButtonClickedListener onButtonClickedListener = null;
private OnClickListener actionBarClickListener = new OnClickListener() {
#Override
public void onClick(View view) {
if (onButtonClickedListener == null){
onButtonClickedListener = ((SampleActivity) getActivity()).onButtonClickedListener ();
}
if (onButtonClickedListener != null) {
onButtonClickedListener
.onButtonClicked();
}
}
};
}
Fragment B:
public class FragmentB extends Fragment {
private OnButtonClickedListener onButtonClickedListener = new OnButtonClickedListener() {
#Override
public void onButtonClicked() {
Toast.makeText(getActivity(), "Button clicked", Toast.LENGTH_SHORT).show();
}
};
#Override
public void onResume() {
super.onResume();
SampleActivity sampleActivity = (SampleActivity) getActivity();
sampleActivity.setSearchBoxTextChangedListener(onButtonClickedListener);
}
}
Hope can help someone.
Setting the onClick attribute for a button in your layout, even your fragment's layout, will call the appropriate method on your Activity.
Your app can then send this signal from your Activity to fragment B.