code reuse for activity is confusing. In normal situation we can design a parent activity and put all common method in it. like follow :
public class BaseActivity extends Activity{
#Override
protected void doExit() {
showDialog(DIALOG_EXIT_ALTER);
}
protected Dialog onCreateDialog(int id, Bundle args) {
switch (id) {
case DIALOG_EXIT_ALTER:
return new AlertDialog.Builder(BaseUIActivity.this)
.setTitle("Exit?")
.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialoginterface, int i) {
close();
}
})
.setNeutralButton("No",null).create();
default:
return null;
}
}
protected void close() {
finish();
}
}
then other activities extend BaseActivity will show a alertdialog instead of exit immediately when back button press.
But in android framework there are more than one build-in activites such like PreferenceActivity,ListActivity,ActivityGroup,etc.
if my activity extend those activities then it can't use the common code defined in BaseActivity.because of Java's single inheritance.
So is there other way recommend to do code reuse for activity in android?
Create a new class ActivityHelper.
public class ActivityHelper {
Activity activity;
public ActivityHelper(Activity activity) {
this.activity = activity;
}
public Dialog onCreateDialog(int id, Bundle args) {
// do many usefull things
return result;
}
}
Use it in all your activities.
protected Dialog onCreateDialog(int id, Bundle args) {
return activityHelper(id, args);
}
Since PreferenceActivity, ListActivity, ActivityGroup are specialized form of Activity, and you have to use them in their relative context.
So IMHO workaround is to have one copy of Base+[all above Activity] if you have to use them more then one time in your project, and extend your child ListActivity or whatever Specialized Activity it is.
Related
I've searched for a solution for my question all over the internet but I haven't been able to find one and I hope you can help me out
I am trying to create a master detail flow application in android with 2 activities and the second activity contains a fragment. Can anyone please tell me how I can simultaneously update the value in the MainActivity() when I make a change in the fragment's EditText field? I have tried using an Intent but when the 2 activities are side by side that doesnt seem to work well.
Screenshot of Emulator
Any suggestions?
It seems you are in a context as follows:
When A happens, it triggers B
As a result, I suggest you to use EventBus library in your project.
The installation is easy. First, add the following code in your build.gradle file:
compile 'org.greenrobot:eventbus:3.0.0'
Second, let's see what we are going to add in our codes.
In the Fragment which you wanted to make changes:
/* When A happens */
myButton.setOnClickListener(new View.OnClickListener() { // complete entering the content, update it
EventBus.getDefault.post(MyUpdateEvent(myContent));
});
Create your custom class MyUpdateEven:
public class MyUpdateEvent{
private String myContent;
public MyUpdateEvent(String myContent) {
this.myContent = myContent;
}
public String getUpdateContent() {
return myContent;
}
}
In the Activity you wanted to update:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault.register(this); // add this code to monitor the update
}
/* It triggers B */
#Subscribe // don't forget to add #Subscribe
public void onEvent(MyUpdateEvent event){
// this is your custom method
myTextView.setText(event.getUpdateContent()); // do your update
}
#Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault.unregister(this); // when you leave this lifecycle, cancel the monitoring
}
}
EventBus is a good library that I've been used a lot in my projects.
I think it can solve your problem.
Try to define a interface.
public interface OnEditActivity {
public void onEdit(ActivityObject activityObject, boolean isEditing);
}
And on your another class for example DetailActivity, then you have to override the method onEdit that you created in your interface:
public class DetailActivity extends AppCompatActivity implements OnEditActivity{
//IN HERE --- Create method.
#Override
public void onEdit(ActivityObject activityObject, boolean isEditing) {
if(isEditing){
displayView(activityObject,true);
}else{
displayView(activityObject,false);
}
}
}
And in your EditFragment for example will look like this:
public class EditFragment extends Fragment{
//Define your interface in your fragment
private OnEditActivity onEditActivity;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_edit_activity, container, false);
return v;
}
public void onAttach(Activity a) {
super.onAttach(a);
onEditActivity =(OnEditActivity) a;
}
}
And if you want to call onEdit method just call:
onEditActivity.onEdit(activityObjectNew,false)
I hope this help you !
Right now i'm having :-
1) 1 activity which is the main activity that extends from AppCompactActivity.
2) 1 fragment class that extends from fragment, this is the fragment that being called from main activity (1) - ProfileTeacherActivity.java
3) 1 fragment class that extends from DialogFragment, this dialog getting called from fragment (2) - ModalBox.java
So, basically, this is just a simple flow of execution. At start, the applications showing the main activity (1) having drawer that have a few links as example a profile link, when click this link, the application call the fragment (2) showing details of profile with one edit button. After clicking edit button, the applications will invoke DialogFragment (3) that contains some of EditText for editing user's profile.
What i want to achieve is, after editing user's profile and successful saved into database, i tried to send user's data back to fragment (2) just to show latest updated info, unfortunately it didn't work.
Here is what i'm tried :
1) Creating Interface inside DialogFragment (3) - ModalBox.java
public class ModalBox extends DialogFragment{
....
public interface EditProfileModalBoxInterface {
void onFinishEditProfile( HashMap<String, String> dataPassing );
}
...
...
}
2) Inside DialogFragment also i have .setPositiveButton function for OK button. - ModalBox.java
public class ModalBox extends DialogFragment{
...
...
public Dialog onCreateDialog(Bundle savedInstanceState ) {
...
builder
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, int id) {
// At here i'm using retrofit2 http library
// to do updating stuff
// and inside success's callback of retrofit2(asynchronous)
// here i call the below function to send data
// dataToSend is a HashMap value
sendBackResultToParent( dataTosend );
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
.....
}
// Function called inside success's callback of retrofit2
public void sendBackResultToParent( HashMap<String, String> data ) {
// instantiated interface
EditProfileModalBoxInterface ls=(EditProfileModalBoxInterface)getTargetFragment();
// declaring interface's method
ls.onFinishEditProfile( data );
}
}
3) Finally, i'm implements those interface inside fragment (2) - ProfileTeacherActivity.java
public class ProfileTeacherActivity extends Fragment
implements ModalBox.EditProfileModalBoxInterface{
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState ) {
.....
.....
}
// At here the interface's method did't triggered
#Override
public void onFinishEditProfile( HashMap dataPassedFromDialog ) {
Toast.makeText(getActivity(), "Testing...." , Toast.LENGTH_LONG).show();
}
}
What i'm confuses right now is, the problem happens only when i called this function sendBackResultToParent( dataTosend ); inside retrofit2 success's callback, it does triggered when calling outside of it. I'm assumed the async called caused this. If i could use Promise or something like that or is there any workaround on this?
The following existing solutions didn't work in my case :
Callback to a Fragment from a DialogFragment
How to send data from DialogFragment to a Fragment?
Send Data from DialogFragment to Fragment
Ask me for more inputs if above use case didn't clear enough or misunderstanding. Thanks for the helps. Regards.
This is a sample DialogFragment code used to send message to selected contact. I too required to capture the click event on the DialogFragment and redirect.
Ideally to achieve this , this is what needed to be done
Override the positive/negative button clicks of AlertDialog.Builder and do no action
After this , using getButton method mention AlertDialog.BUTTON_NEGATIVE or AlertDialog.BUTTON_POSITIVE and assign an action
public class SMSDialogFrag extends DialogFragment {
private static String one="one";
private EditText messageContent;
private AlertDialog dialog;
private String mobNumber;
public static SMSDialogFrag showDialog(String mobNumber){
SMSDialogFrag customDialogFrag=new SMSDialogFrag();
Bundle bundle=new Bundle();
bundle.putString(one, mobNumber);
customDialogFrag.setArguments(bundle);
return customDialogFrag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.sms_dialog, null);
alertDialogBuilder.setView(view);
setupUI(view);
alertDialogBuilder.setTitle("");
alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Do nothing here because we override this button later
}
});
alertDialogBuilder.setPositiveButton("Send", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Do nothing here because we override this button later
}
});
dialog = alertDialogBuilder.create();
dialog.show();
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dialog.dismiss();
//else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
}
});
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendMessage();//IN YOUR USE CASE YOU CAN REDIRECT TO YOUR CALLER FRAGMENT
}
});
return dialog;
}
void setupUI(View view){
TextView textViewMob=(TextView)view.findViewById(R.id.mobNumber);
messageContent=(EditText)view.findViewById(R.id.messageContent);
mobNumber=getArguments().getString(one);
textViewMob.setText("Send message to : "+mobNumber);
}
void sendMessage(){
if( ! TextUtils.isEmpty(messageContent.getText())){
try {
SmsManager smsManager = SmsManager.getDefault();
Log.v(Constants.UI_LOG,"Number >>>>>>> "+mobNumber);
smsManager.sendTextMessage(mobNumber, null, messageContent.getText().toString(), null, null);
} catch (Exception e) {
e.printStackTrace();
}
Toast.makeText(getActivity(),"Message Sent!",Toast.LENGTH_SHORT).show();
dialog.dismiss();
}else{
Toast.makeText(getActivity(),"Please enter message to send!",Toast.LENGTH_SHORT).show();
}
}
}
Consider using eventBus, for example
Otto
The usage is very simple. All you need to do is create an evenbus:
public static Bus bus = new Bus(ThreadEnforcer.MAIN); //use Dagger2 to avoid static
Then create a receiver method (in fragment 2 in your case):
#Subscribe
public void getMessage(String s) {
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}
Send you message by calling(from DialigFramgent):
bus.post("Hello");
And don't forget to register your eventBus inside onCreate method(of your Fragment):
bus.register(this);
And that is!
From architectural standpoint, 2 fragments should not directly communicate with one another. Container Activity should be responsible for passing data between it's child fragments. So here's how i would do it:
Implement your interface in the container Activity and just attach your interface implementation in the Activity to the Dialog class and call that interface method when required. Something like this :
public static class ModalBox extends DialogFragment {
EditProfileModalBoxInterface mListener;
// Container Activity must implement this interface
public interface EditProfileModalBoxInterface {
void onFinishEditProfile( HashMap<String, String> dataPassing );
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (EditProfileModalBoxInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement EditProfileModalBoxInterface");
}
}
}
Then call mListener.onFinishEditProfile(....) where ever it's required in the DialogFragment class.
This way you will receive the result back in your Activity class from where you can call your desired fragment's relevant method to pass the results to that fragment.
This whole flow has been described here
Finally the culprit founds. All the answers mentioned above were right. And my script also actually works, the problem is related with my API json's response that did't coming with right structure. In my case, i'm using retrofit2 with GSON converter for parsing into POJO. Found the info on log saying about :
Expected BEGIN_OBJECT but was BEGIN_ARRAY
Means that, GSON expecting the json object, which is my API was returned JSON array. Just change the API's structure then all goods to go. Thanks guys for your hard time. Really appreciated, as i'm right now knowing how to deal with eventBus, replacing default OK and Cancel button and the correct way for communicating between fragments.
I have searched SO for this problem but was not able to find anything which would solve my problem. My problem is, I have a activity which contains FrameLayout which is constantly updated with different fragments. The top view and bottom view are going to remain same hence they are in the layout of the
activity.
As you can see bottom view has a button on click of that i want to make changes in the fragments which will be present in the FrameLayout.
I have created a interface
public interface ShowFormula {
void showFormula(boolean show);
}
which i will use to implement in the fragment.
Now the main problem in my MainActivity class i am trying to initialize the interface but not able to as i am getting class cast exception
showFormula = (ShowFormula) this;//yes i know this is wrong
How should i initialize this in order to communicate with the fragment.
Main goal is to toggle the view in fragments on click of the button in activity.
Thanks in advance.
You don't need to use an interface to make calls from an Activity to a Fragment. Just keep a reference to the current Fragment, and call into a public method in the Fragment from the Activity.
If you have multiple Fragments and you don't want to keep a reference for each one, you can create a Fragment base class, declare the common method in the base class, and then implement that method override in all of your Fragments that inherit from the base Fragment. Then, keep one reference of the base Fragment type, and always have it set to the Fragment that is shown currently.
Activity ---> Fragment
Communication from Activity to Fragment is pretty straightforward. You
really don't need a listener.
Let's say you have a method inside Fragment share()
public class MyFragment extends Fragment{
public static MyFragment getInstance()
{
return new MyFragment();
}
........
public void share()
{
// do something
}
}
How to call share() method from an Activity?
Get the reference of the Fragment and call the method. Simple!
MyFragment myFragment = MyFragment.getInstance();
myFragment.share();
You can see the full working code for Fragment to Fragment Communication
Just to add to Daniel Nugent's brilliant answer, here are snippets from my working code for delegating calls from Activity to Fragment.
I have a MVP architecture and I have defined the error handling method showError on the BaseView class and the code below demonstrates how to handle the UI on a TargetFragment class. I, specifically needed to hide my progress spinner on the fragment upon any error scenario. Here's the code snippets for the base classes:
public interface BaseView {
void showError(ErrorResponse errorResponse);
}
public abstract class BaseActivity implements BaseView {
#Override
public void showError(ErrorResponse errorResponse) {
// Check error condition or whatever
// ...
MaterialDialog dialog = new MaterialDialog.Builder(this)
.title(R.string.dialog_error_title)
.content(R.string.error_no_internet)
.positiveText(R.string.dialog_action_ok)
.build();
dialog.show();
}
}
public abstract class BaseFragment implements BaseView {
#Override
public void showError(ErrorResponse errorResponse) {
((BaseView) getActivity()).showError(errorResponse);
}
}
And, this is how I handle UI inside my TargetFragment class:
public final class TargetFragment extends BaseFragment implements TargetView {
#Override
public void showError(ErrorResponse errorResponse) {
super.showError(errorResponse);
hideSpinner();
// Do other UI stuff
// ...
}
private void hideSpinner() {
spinner.setVisibility(View.INVISIBLE);
}
}
a clean solution:
public interface ShowFormula {
public void showFormula(boolean show);
}
public class MyActivity implements ShowFormula {
...
#Override
public void showFormula(boolean show) {
/** Your Code **/
}
...
}
public class MyFragment {
private ShowFormula listener;
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (ShowFormula) activity;
// listener.showFormula(show?);
} catch (ClassCastException castException) {
/** The activity does not implement the listener. **/
}
}
...
}
simple thing make public method in fragments then call it on from your activity.
e.g
MyFragment fragment = new MyFragment();
fragment.doSomeThing();
doSomeThing() is a public method in MyFragment.
Activity to Fragment Communication via Interface:
public class MyActivity {
private ShowFormula showFormulaListener;
public interface ShowFormula {
public void showFormula(boolean show);
}
public void setListener(MyFragment myFragment) {
try {
showFormulaListener = myFragment;
} catch(ClassCastException e) {
}
}
}
public class MyFragment implements ShowFormula{
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
((MyActivity) activity).setListener(this);
} catch (ClassCastException e) {
Log.e(TAG, e.toString());
}
}
#Override
public void showFormula(boolean show) {
/** Your Code **/
}
}
Once you are done setting this, you can call 'showFormulaListener.showFormula(boolVal)'
I'm having problems getting data from my dialog to the calling activity. Been following the android documents, but haven't had luck. I just want to capture a string that someone enters in an edit text box of my dialog. Here is my setup.
public class CrossoverMainActivity extends Activity implements CrossoverSaveDialog.NoticeDialogListener {
final CrossoverSaveDialog dialog_save = new CrossoverSaveDialog();
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
// User touched the dialog's positive button
}
#Override
public void onDialogNegativeClick(DialogFragment dialog) {
// User touched the dialog's negative button
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
if (id == R.id.action_save) {
dialog_save.show(getFragmentManager(), "Dialog_save");
return true;
}
if (id == R.id.action_delete) {
CrossoverDeleteDialog dialog_delete = new CrossoverDeleteDialog();
//Fragment fragment = new Fragment();
dialog_delete.show(getFragmentManager(), "Dialog_delete");
return true;
}
return super.onOptionsItemSelected(item);
}
}
Then, in another .java file, I have my dialog:
public class CrossoverSaveDialog extends DialogFragment {
public interface NoticeDialogListener {
void onDialogPositiveClick(DialogFragment dialog);
void onDialogNegativeClick(DialogFragment dialog);
}
// Use this instance of the interface to deliver action events
NoticeDialogListener mListener;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View dialogSave = inflater.inflate(R.layout.crossover_save_dialog, null);
builder.setView(dialogSave);
builder.setPositiveButton(R.string.dialog_save, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogPositiveClick(CrossoverSaveDialog.this);
}
})
.setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogNegativeClick(CrossoverSaveDialog.this);
}
});
return builder.create();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
mListener = (NoticeDialogListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString()
+ " must implement NoticeDialogListener");
}
}
}
Here are my two questions:
The problem I'm getting is that the compiler is complaining that I'm not implementing the methods of my interface... which I seem to be. Is there something I'm missing here?
I have to do this for a second dialog which is different than the one I've shown here. But, I'll be doing it in the same fashion. Do I just look in the dialog of the callback method to determine which dialog is being used? That way I can handle my logic for each dialog. Or, should I have each dialog with a seperate interface that uses it's each unique methods, and just implement each of those?
You must set your activity to be the Listener for your newly member "mListener"
So in Dialogclass you should create a getter/setter for your listener like this:
public NoticeDialogListener getListener() { return mListener; }
public void setListener (NoticeDialogListener listener) { mListener=listener; }
and in your activity you would do
CrossoverDeleteDialog dialog_delete = new CrossoverDeleteDialog();
dialog_delete.setListener(this);
dialog_delete.show(getFragmentManager(), "Dialog_delete");
I actually figured out the answer here.
#blender: Maybe I'm mis-understanding this, but I don't think I need to do that right? I mean, I'm implementing the interface already in my activity class, and I'm getting attached to that via the onAttach within my dialog class aren't I?
Anyhow, here was the blunder. I was importing the android.support.v4.app.DialogFragment. Then, in my main activity, I was importing the android.app.DialogFragment. That was causing my two method callbacks in my main activity to not actually get registered by the compiler it seemed. I used Android Studio so when you auto import stuff, it can be easy to dismiss the fact that you didn't import the correct namespace. Doh!
Secondly, I found out that you can just call the dialog.getTag() in the onDialogPositiveClick(DialogFragment dialog). You can check that against whichever dialog it is you show. In this way, you can have multiple dialogs using the same interface methods, and just incorporate your "checking" logic there.
ie, I have this line:
dialog_save.show(getFragmentManager(), "Dialog_save");
So I can simply do this to check which dialog I clicked the positive button from:
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
// User touched the dialog's positive button
if (dialog.getTag().equals("Dialog_save"){
//..do stuff...
}
if (dialog.getTag().equals("Dialog_cancel"){
//..do stuff...
}
}
Consider the below piece of code (which works fine by the way) from an Activity:
class ParentActivity extends Activity {
...
#Override
public void btnOK_onClick(final View view) {
// do stuff
}
}
class ChildActivity extends ParentActivity {
...
public void superClick(View view) {
super.btnOK_onClick(view);
}
#Override
public void btnOK_onClick(final View view) {
final BarcodePickActivity me = this;
if (confirmation_needed == true) {
new AlertDialog.Builder(this)
.setTitle(R.string.are_you_sure_)
.setMessage(R.string.are_you_sure_you_want_to_continue)
.setNegativeButton(R.string.no, null)
.setPositiveButton(R.string.yes, new OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
me.superClick(view);
}
}).create().show();
} else {
super.btnOK_onClick(view);
}
}
}
In the layout that is used, there is a Button with an onClick set:
<Button
android:id="#+id/btnOK"
android:onClick="btnOK_onClick"
android:text="#string/ok" />
as you see, I have to jump through quite some hoops to get this working:
define a final variable to have this available in the OnClickListener of the AlertDialog,
define an extra method to be able to call super.btnOk_onClick.
As I said, it works fine, but it doesn't look pretty. Is there a way to do this nicer?
Use classname.this from the inner class. So if you class is MySuperClass you can reference MySuperClass.this from an instance inner class.
There is no reason you need to define superClick as you could always just call super.btnOK_onClick(view);.
So all told, you should be able to do something like MySuperClass.super.btnOK_onClick()