Proper way of communication between fragments - java

Is this a proper way of communication between fragments ?
public class MainActivity extends AppCompatActivity implements IFragmentsHandler {
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
}
#Override
protected void startFragment1() {
Fragment1 f1 = new Fragment1();
f1.setFragmentsHandler(this);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, f1)
.commit();
}
#Override
protected void startFramgment2() {
Fragment1 f2 = new Fragment1();
f2.setFragmentsHandler(this);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, f2)
.commit();
}
}
public class Fragment1 {
private IFragmentsHadnler fragmentsHandler;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment1, container, false);
//...Code...
fragmentsHandler.startFragment1();
}
public void setFragmentsHandler(IFragmentsHandler fragmentsHandler) {
this.fragmentsHandler = fragmentsHandler;
}
}
public class Fragment2 {
private IFragmentsHadnler fragmentsHandler;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment2, container, false);
//...Code...
fragmentsHandler.startFragment2();
}
public void setFragmentsHandler(IFragmentsHandler fragmentsHandler) {
this.fragmentsHandler = fragmentsHandler;
}
}
[EDIT1] : Posted the Interface (though it was obvious)
public interface IFragmentsHandler {
public void startFragment1();
public void startFragment2();
}
From my Java perspective this will throw OutOfMemoryError but I'm not if it is the same for the Android. Anyway what is the preferred way of communication between fragments?

According to android developer guide, communication between fragments is done through the associated Activity.
A fragment use his interface to communicate with the Activity. And the Activity deliver a message by capturing the Fragment instance with findFragmentById() or creating one if needed, then directly call the other fragment's public methods.
Fragment1 wants to pass some data: uses his interface method implemented by Activity.
Activity executes that method receiving the data, create&replace or find (depending on your layout) the Fragment2 and pass this data or execute some public method on fragment2 class (depending on your layout).
Fragment2 extract data from bundle or execute (depending on your layout) his public method to receive the data.
I think the problem in your code is you are misunderstanding interface purpose. You are using for start the same fragment who is calling the method. Fragment1 is calling startFragment1() in his onCreateView(), but it is already started.
If you needed, in here there is a good tutorial.

To communicate between components consider app architecture MVP, VIPER, etc. On code side it may use event bus for communication or just plain callbacks.
Do navigation in one place
Do business logic in another place
Do present-view logic in presenter
Do view logic in views, fragments, adapters

As you started, you can use interfaces to communicate between Fragments as suggested by Google.
But an easy way to communicate between fragments is by using event bus (which implements the publish/subscribe pattern) like EventBus library.
You can also use RxJava to create your own event bus and thus make communications between components of your app (have a look to this Stackoverflow question: RxJava as event bus?)

Related

How to use base activity in fragment?

Here i implemented a emoji-keyboard.
For the use of this library , activity extended to EmojiCompatActivity.
prepareKeyboard(EmojiCompatActivity activity, EmojiEditText input
This is working well in activity. While using in fragment i tried this one
prepareKeyboard((ActivityName)getActivity,input)
Here is my MainActivity
public class MainActivity extends EmojiCompatActivity {}
And fregment class is
public class PagerEmojKeyboard extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.emoji_keyboard, container, false);
EmojiEditText userMessageInput = getActivity().findViewById(R.id.input_message);
EmojiKeyboardLayout emojiKeyboardLayout=(EmojiKeyboardLayout) v.findViewById(R.id.keyboard_emoj);
emojiKeyboardLayout.prepareKeyboard((MainActivity) this.getActivity(),userMessageInput);
return v;
}
}
You can refer to the base activity as this.activity.
Sometimes, while using fragments, the above method also works.
If you are working in the method onCreateView you shouldn't have problems but if you have other method there is a View variable that you must have as a global for use throughout the class.

Pass Data to Fragment from Activity, when intially loaded with Fragment

So my issue is basically my MainActivity is initially loaded with a Fragment, which we will call MyFragment.
I am loading JSON, from online and wanting to pass into my MyFragment.
The problem is arising when setContentView is called in the MainActivity, it is calling onCreateView in MyFragment, which contains getArguments.getSerializable("myTag"). The key isn't passed because I haven't loaded the JSON yet.
Can you help me resolve this issue?
Here is my code:
In my MyFragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if(getArguments() != null) {
coll = (HashSet<String>) getArguments().getSerializable("myTag");
}
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_my, container, false);
}
MainActivity (assume I loaded my JSON already):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadJSON();
passTagsToFragment(); //passes to the fragment
}
public void passTagsToFragment(){
Bundle bundle = new Bundle();
bundle.putSerializable("myTags", tagsSet);
TagsFragment frag = new MyFragment();
frag.setArguments(bundle);
}
EDIT:
Basically, my issue is that I want to load the MainActivity fully, before even starting to load the Fragment. Not sure how to do that.
EDIT 2:
I fixed the problem here is my code: (Changed the variable names)
MainActivity.java
public TagsFragment passInfoToTagsFramgent(){
Bundle bundle = new Bundle();
bundle.putSerializable("tags", tagsList);
TagsFragment frag = new TagsFragment();
frag.setArguments(bundle);
return frag;
}
in OnPostExecute of MainActvity.java:
Fragment tagFragment = passInfoToTagsFramgent();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, tagFragment);
transaction.commit();
You should call your passTagsToFragment() method in post execute method of your asynctask when all json data gets loaded.
protected void onPostExecute(Long result) {
passTagsToFragment();
}
loadJSON is from online source, so I assume it is an AsyncTask.
I usually do this as a lamda expression (kotlin):
loadJSON({passTagsToFragment()})
loadJSON should take a lamda expression as parameter:
loadJSON(private val callback:()->Unit )
and in the AsyncTask::onPostExecute, you should call the callback:
callback.invoke()
or just
callback()
Then you made sure the fragment is opened when the JSON is loaded and passed to fragment.
OK let me try to make it in Java.
In your AsyncTask which loads JSON, you will need an interface e.g.,
public interface JSONLoadCallback {
void loaded();
}
And the its constructor takes the interface as parameter:
class JSONLoader : AsyncTask<....> {
JSONLoader(JSONLoadCallback callback) {
_callback = callback;
}
#Override
public void onPostExecute() {
_callback.loaded();
}
}
And your Activity implements JSONLoadCallback:
#Override
public void loaded() {
passTagsToFragment();
}
And should pass itself to the AsyncTask:
JSONLoader(this).executeOnExecutor();
This way, the loaded() function is fired when JSON load is finished.
You see, Java codes are very verbal, Kotlin almost removed the necessity of Java interface.
As per my understanding, you can call Loadjson() method on fragment as well and use data accordingly but if you have some specific logic you can use asynctask and on json retrieval with progress bar you can set any MyFragment callback and update your fragment accordingly.

How can I use Fragment's Spinner events in my main class?

the code below named Presenteter is my main class, and I am replacing the Fragment according to questions.
public class Presenteter extends AppCompatActivity {
private final Questions question1Fragment = new Questions();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.present);
FragmentTransaction ft=getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.anim.fade_in,android.R.anim.fade_out);
ft.replace(R.id.flPersonalization, question1Fragment);
ft.commit();
}
This is my Questions class. It is getting the questions from REST server. All questions are listed in in one Spinner. I didn't write them here, in order not to confuse you.
public class Questions extends Fragment implements AdapterView.OnItemSelectedListener {
SearchableSpinner spinner;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.question, container, false);
LinearLayout ln=(LinearLayout)view.findViewById(R.id.listLayout);
spinner=(SearchableSpinner)view.findViewById(R.id.spinner1);
}
Finally I want to use Spinner's onItemSelected() event in my Presenter class.
How can I do this?
Thanks in advance.
You could do something like this in your Fragment:
Presenteter p = (Presenteter) getActivity();
// ...
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
p.yourMethod(); // call a method of your Presenteter class
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
I'm glad your question is answered but there is a better way for doing this.
If you are familiar with Rx stuff, that's one way to do it.
If Rx is not your cup of tea, you can use EventBus which simplifies exactly this kind of communication.
Link:
EventBus
I would suggest going for the latter if you are not sure.

Using Fragments and Creating "Starting Page"

I'm new in Android App developing via Java. I'm using Eclipse. If I create an Activity, Eclipse automatically generates a Placeholderfragment Class and Fragment.xml. Can I disable this function? Or is it not advisable to do that? I delete those files because I find it more complicated to use than just write in one xml file at the moment.
Second question is how do I implement a "starting Page" for my App? For example some sort of a logopage which automatically disables after a few seconds and switches to a new activity. Create a separate Activity for it or do I use something else?
Actually you need two activities, one startup Activity which is used to show your logo or some guide,the other is a MainActivity which should be started by the startUp Activity.
In short You can do something like this:
public class MainActivity extends FragmentActivity {
Fragment fragment;
String className;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("MainActivity", "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Store the name of the class
className=MainActivity.class.getSimpleName();
//First fragment should be mounted on oncreate of main activity
if (savedInstanceState == null) {
/*fragment=FragmentOne.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).addToBackStack(className).commit();
*/
Fragment newFragment = FragmentOne.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, newFragment).addToBackStack(null).commit();
Log.d("FRAGMENT-A", "fragment added to backstack");
}
}
}
FragmentOne.java
public class FragmentOne extends Fragment{
String className;
public static FragmentOne newInstance(){
Log.d("FragmentOne", "newInstance");
FragmentOne fragment = new FragmentOne();
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("FragmentOne", "onCreateView");
View view=inflater.inflate(R.layout.fragment_one, container, false);
//Store the name of the class
className=FragmentOne.class.getSimpleName();
return view;
}
}
Let me know if you need any more info
Well, in a Single Activity setup, the way I did this was the following:
public class SplashFragment extends Fragment implements View.OnClickListener
{
private volatile boolean showSplash = true;
private ReplaceWith activity_replaceWith;
private Button splashButton;
public SplashFragment()
{
super();
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
activity_replaceWith = (ReplaceWith) activity;
}
catch (ClassCastException e)
{
Log.e(getClass().getSimpleName(), "Activity of " + getClass().getSimpleName() + "must implement ReplaceWith interface!", e);
throw e;
}
startSwitcherThread();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_splash, container, false);
splashButton = (Button) rootView.findViewById(R.id.fragment_splash_button);
splashButton.setOnClickListener(this);
return rootView;
}
public void startSwitcherThread()
{
Thread splashDelay = new Thread()
{
#Override
public void run()
{
try
{
long millis = 0;
while (showSplash && millis < 4000)
{
sleep(100);
millis += 100;
}
showSplash = false;
switchToFirstScreen();
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
splashDelay.start();
}
private void switchToFirstScreen()
{
activity_replaceWith.replaceWith(new FirstFragment());
}
#Override
public void onClick(View v)
{
if(v == splashButton)
{
if(showSplash == false)
{
switchToFirstScreen();
}
}
};
}
Where the ReplaceWith interface is the following:
import android.support.v4.app.Fragment;
public interface ReplaceWith
{
public void replaceWith(Fragment fragment);
}
And the replace function is implemented like so:
#Override
public void replaceWith(Fragment fragment)
{
getSupportFragmentManager()
.beginTransaction().replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
}
Now, most people will say this is not a good approach if you're using multiple activities, and/or using multiple orientations and aren't just simply displaying a single Fragment in a single Activity no matter what. And they are completely right in saying so.
Multiple orientations would require the Activity to be responsible for knowing what is the "next" Fragment at a given replace call, and where to place it (which container, or to start it in a new Activity). So this is a valid approach only if you are certain that you only have one container and there is one Fragment shown at a given time.
So basically, if this does not apply to you, then you need to utilize the same approach (make a specific delay before you replace the current Fragment or Activity with another one, this specific code allows you that once the splash has been shown once, then clicking the button will automatically take you to the next screen - typical game splash setup, really), but use activity callbacks specific to the Fragment in order to swap one out for the other.
A Fragment setup I recommend and isn't relying on this special case can be seen here: Simple Android Project or its equivalent on Code Review: Fragment Start-Up Project

Creating an android fragment that opens another fragment based on button activity

I'm trying to create an activity that adds a dynamic fragment at runtime. From that fragment I want to be able to open six other fragments on button click. [Going to use a case to implement this most likely]
Think of it as a windows 8 UI; with 6 buttons, each one opens a new fragment.
Unfortunately I have no idea how to go about this. I can't seem to get the button to pass data back to the main activity. I've also lost quite a bit of my code due to a git mishap. Here's what I recreated.
If you have any tips on coding style, syntax, java, OO- those are all welcome too. I'm coming from a C background. My end goal would be to create a replaceFragment(Frag) method for some easy syntactic sugar later on. Though I couldn't implement that with any success so far.
Another small question with fragments - I'm trying to add them dynamically at run-time - do I need to create all of them at run time? So each one needs a .add [Drink fragment, Menu fragment] or do I just need to do the .replace
SingleFragmentActivity.java
public abstract class SingleFragmentActivity extends FragmentActivity{
protected abstract Fragment createFragment();
FragmentManager fm = getSupportFragmentManager();
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //Lock screen orientation for app
Fragment frag = fm.findFragmentById(R.id.fragment_container);
fm.beginTransaction()
.add(R.id.fragment_container,frag)
.commit();
}
}
Customer_Activity.java
public class Customer_Activity extends SingleFragmentActivity {
public static Context appContext;
#Override
protected Fragment createFragment() {
return new CustomerSelectionFragment();
}
}
CustomerSelectionFragment
public class CustomerSelectionFragment extends Fragment implements OnClickListener{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.customer_selection_fragment, container, false);
//Buttons should be placed here?
Button btnDrink = (Button) v.findViewById(R.id.Drink);
btnDrink.setOnClickListener(this);
Button btnMenu = (Button) v.findViewById(R.id.Menu);
btnDrink.setOnClickListener(this);
return v;
}
//implement the onClick method here
public void onClick(View v) {
// Perform action on click
switch(v.getId()) {
case R.id.Drink:
//Not sure how to pass "Create Drink Fragment to activity?
break;
case R.id.Menu:
//Pass Create Menu fragment to activity?
break;
}
}
}
Totally ok with people editing my post for good-faith reasons [clarity, etc].
Any communication between fragments should be done via activity . Here is the link to developers site http://developer.android.com/training/basics/fragments/communicating.html , the tutorial is about communicating between fragments and pretty much explains everything.

Categories