Basically I've got an ActionBarActivity that loads activity_main.xml which contains my ListFragment
My ActionBar has an Add button on it, to add items to the list.
Problem I've run into now, is how do I handle the Add and pass the information to the ListFragment to populate the ListView?
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//activity_main only contains <fragment ... /> to add my ListFragment
}
....
// This is called from onOptionsItemSelected
private void showAddDialog() {
FragmentManager fm = getSupportFragmentManager();
InputDialog inputDialog = new AddInputDialog();
inputDialog.setOnUpdateListener(new InputDialog.onUpdateListener(){
#Override
public void onUpdate(Item item){
//I'm lost at what to do here...
}
});
inputDialog.show(fm, "fragment_dialog_input");
}
EDIT
Got it working using findFragmentById(), and posted answer below.
I kept trying to get it to work using findFragmentByTag() and even though I had a TAG set in my fragment, and when debugging it(the TAG) showed correctly, for some reason would always return null.
Since the fragment is defined in activity_main.xml use findFragmentById using the specified android:id then you can call your public function within the ListFragment to update the list and notify adapter.
private void showAddDialog() {
final FragmentManager fm = getSupportFragmentManager();
InputDialog inputDialog = new AddInputDialog();
inputDialog.setOnUpdateListener(new InputDialog.onUpdateListener(){
#Override
public void onUpdate(Item item){
ListFragment lf = (ListFragment)fm.findFragmentById(R.id.listFragment);
lf.addItem(item);
}
});
inputDialog.show(fm, "fragment_dialog_input");
}
Related
I have an activity with two fragment and want to be executed first fragment when its back from second fragment using back button. And i am using the add() when navigating first fragment to second fragment. Here is my scenario and code snippet:
First fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.one_fragment, container, false);
final Button button = (Button) view.findViewById(R.id.buttonChange);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
buttonClicked(v);
}
});
return view;
}
public void buttonClicked(View view) {
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_Container, new TwoFragment());
fragmentTransaction.addToBackStack("sdfsf");
fragmentTransaction.commit();
}
Moving to Second fragment and here is the code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.two_fragment, container, false);
return view;
}
The problem is that, When I am navigating from first to second fragment and then back again in the first fragment using back button first fragment lifecycle method is not executing. Instead of using add() if I use replace() then lifecycle method are executing properly. I know its the difference between add() and replace() but I want to use add() and also want to have navigation callback to handle some logic when I back in the first fragment using back button.
Also tried below code:
fragmentManager.addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
Log.e(TAG, "onBackStackChanged: ");
// Update your UI here.
}
});
But its also calling multiple times and creating anomalies.
How can I handle this? Specially handle some logic in first fragment when I back from second fragment.
The easiest way I can think of is to set result when you're done with the second fragment that essentially tells the first fragment to "resume" via its onActivityResult method.
When you create an instance of Fragment B, call #setTargetFragment() and pass in Fragment A as your target fragment. Then when Fragment B is done and going to return to Fragment A, before it exits, you will set the result of it for Fragment A by calling:
getTargetFragment().onActivityResult(getTargetRequestCode(), RESULT_FRAGMENT_B_FINISHED,null)
///// horizontal scroll padding
Note that RESULT_FRAGMENT_B_FINISHED would be some static integer you define somewhere, like
public static final int RESULT_FRAGMENT_B_FINISHED = 123123;
Now in Fragment A all you need to do is override onActivityResult and check that the request code matches the request code integer from setTargetFragment and the result code also matches RESULT_FRAGMENT_B_FINISHED, if so you can run the code that would have been fired from onResume().
#getTargetFragment()
#onActivityResult()
#getTargetRequestCode()
Instead of passing data between the two fragments I recommend you to use a SharedViewModel.
The idea is that the first fragment observe some data for changes and the second one edit this data.
Example:
Shared ViewModel
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new
MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
First fragment
public class FirstFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model =
ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
Second fragment
public class SecondFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model =
ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.select(new Item("value 1","value 2");
}
}
You can read about ViewModels, LiveData and Architecture components starting from here: https://developer.android.com/topic/libraries/architecture/viewmodel#java
This answer is assuming that you want to execute some logic based on some data change. If it's not the case, you can explain what kind of logic do you want to execute and I will edit my answer.
I am trying to build an app with one MainActivity and multiple fragments.
In my MainActivity, I get the data and store it in the Data Model.
For example, getting sunrise time, then display it in Fragment B.
How can I detect the sunrise value changes and update the TextView in Fragment without restarting the app? Is there way can listen to value changed and update the textView?
here are my codes and fragment B layout.
JAVA data model CLASS
public class SunriseTimeClass {
private static final SunriseTimeClass INSTANCE = new SunriseTimeClass();
public String sunrise = "";
private SunriseTimeClass(){ }
public static SunriseTimeClass getInstance(){
return INSTANCE;
}
}
MAINACTIVITY
override fun onCreate(savedInstanceState: Bundle?) {
//this will clear the back stack and displays no animation on the screen
var sunRise = SunriseTimeClass.getInstance()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
supportActionBar!!.setDisplayShowHomeEnabled(true)
getSunriseSunset()
}
fun getSunriseSunset(){
val location = com.luckycatlabs.sunrisesunset.dto.Location("40.9167654", "-74.171811")
val calculator = SunriseSunsetCalculator(location, "America/New_York")
sunRise.sunrise = calculator.getOfficialSunriseForDate(Calendar.getInstance())
}
FRAGMENT B layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text=""
android:gravity="center"
android:id="#+id/DisplaySunrise"/>
</FrameLayout>
(Java code)
Create a setter method in fragment which will be called by activity like:
In fragment:
public void setSunrise(String sunrise){
this.sunrise = sunrise;
//further operations..
}
And in Activity:
Fragment fragment = new Fragment(); //your fragment
String s= calculator.getOfficialSunriseForDate(Calendar.getInstance());
fragment.setSunrise(s);
If I understood you right, you need change/update the fragment when something happens in the Activity.
So I think you could implement an interface ( callback ) between the Activity and the Fragment.
1.You need create an interface in the Activity:
public interface OnActivityAction {
void updateFragment();}
2.You need declare the interface and the setter in the Activity and call it when you want
public OnActivityAction actionListener;
public void setOnActionListener(OnActivityAction fragmentListener) {
this.actionListener = fragmentListener;
}
private void myActionToUpdate(){
if (null != activityListener) {
activityListener.doSomethingInFragment();
}
}
3.Then in your Fragment need implement this interface and set the listener.
public class MyFragment extends Fragment implements OnActivityAction {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(R.layout.fragment_test_two, container, false);
((MainActivity) getActivity()).setOnActionListener(this);
return rootView;
}
#Override
public void updateFragment() {
//Do whatever you want
}
}
I hope this helps you.
Add fragment from activity
FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
//frame_layout put in activity xml
fragmentTransaction.add(R.id.frame_layout, drawer, "").commit();
Get Fragment instance and update your data
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.frame_layout);
((FragmentB)fragment).updateFragment("Updated value");
Make Public method in fragment for update textview
public void updateFragment(String value) {
//update textview here
Textview.setText(value);
}
Thanks for all the great answers.
I have used SharePreference to do my job. working as expected.
There are two possible ways to approach this.
FragmentB gets the sunRise data and updates its own TextView
Have FragmentB retrieve the sunRise values directly and update its TextView, rather than performing the retrieval in MainActivity.
OR
Communicate between Activity and Fragment via an interface
This answer is similar to another one in this thread, but is more explicit on how you would set the fragment's TextView. This solution makes several assumptions and may be an oversimplification due to the limited information in your question:
Assumption 1: This solution assumes that the getSunriseSunset method is synchronous, i.e. not dependent on any service or network call. If it is asynchronous, you would need a separate callback in your MainActivity (e.g. onSunriseRetrieved) to listen for when your data is returned, prior to updating your fragment.
Assumption 2: This solution also assumes that you want access to the fragment's views immediately after it has been added (hence, the executePendingTransactions call). That said, if you have FragmentB perform its own retrieval of the sunRise, then this assumption wouldn't be needed.
Define an interface in the Activity and :
public class MainActivity extends AppCompatActivity
{
public interface OnSunriseChangeListener
{
void onSunriseChange(String newText);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null)
{
getFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, new FragmentB()))
.commit();
//assuming you want access to the fragment's views immediately
getFragmentManager().executePendingTransactions();
}
getSunriseAndUpdateFragment(fragment);
}
/**
* Assumes that getSunriseSunset is synchronous and assumes that FragmentB is created.
*/
private void getSunriseAndUpdateFragment()
{
String newSunRiseValue = getSunriseSunset();
((FragmentB)getFragmentManager().findFragmentById(R.id.fragment_b)).onSunriseChange(newSunRiseValue);
}
// other code not shown...
}
Have FragmentB implement the interface:
public class FragmentB extends Fragment implements OnSunriseChangeListener
{
#Override
public void onSunriseChange(String newSunriseData)
{
TextView textView = getView.findViewById(R.id.DisplaySunrise);
textView.setText(newSunriseText);
}
I'm new Android programming. Earlier I was working with activities, where i could implement onClick on an ImageButton and go to a different activity.
Now I need to do the same but using Fragments. I have a prototype with a menu that always appear on screen and can show different activities to the user. The different lactivities would be inside this container.
Now I want to place an ImageButton inside a fragment and make that the screen shows the next fragment. But I'm confused how to do it.
I have the following components:
Activity_main(java)+activity_main.xml (with menu)
Fragment1(java)+fragment1.xml(working normal)
Inside this layout I have an ImageButton and want to show Fragment2
Fragment2(java)+fragment2.xml
How should look Fragment1 to can call Fragment2?
I will be glad if the answer could be the clearest possible because I'm new on it, and maybe I could forgot an obvious step. Thanks
Simply make a method in your activity which will always change/replace fragment when you invoke it. something like
public void updateFragment(Fragment fragment){
//add fragment replacing code here
}
in your fragment, invoke it some thing like this
((YourActivity)getActivity()).updateFragment(new YourFragment());
since, it is just an idea which works fine but still you can improve the logic.
Actually, going from one fragment to another is almost similar to going from one activity to another. There are just a few extra lines of code.
First, add a new Java class named SingleFragmentActivity which would contain the following code-
public abstract class SingleFragmentActivity extends AppCompatActivity
{
protected abstract Fragment createFragment();
#LayoutRes
protected int getLayoutResId()
{
return R.layout.activity_fragment;
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null)
{
fragment = createFragment();
fm.beginTransaction().add(R.id.fragment_container, fragment).commit();
}
}
}
Make your activities in the following format-
public class SomeActivity extends SingleFragmentActivity
{
#Override
protected Fragment createFragment()
{
return SomeFragment.newInstance();
}
}
And your fragments like this-
public class SomeFragment extends Fragment
{
public static SomeFragment newInstance()
{
return new SomeFragment();
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragment_some, container, false);
return v;
}
}
After this everything has the same code as you have for activities except for one small detail which is your onCreateView(LayoutInflater, ViewGroup, Bundle) class. This is how you would write it-
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragment_some, container, false);
mTextView = (TextView)v.findViewById(R.id.some_text);
mButton = (Button)v.findViewById(R.id.some_button);
mTextView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
check();
}
});
return v;
}
And that is it!
Hi i hope you are already aware about the fragments and their uses but still here is a brief. They are child to an activity and an activity can have more than one fragment so you can update your layout without changing activity just by changing fragments.
You can found more on fragment here : https://developer.android.com/training/basics/fragments/index.html
Back to the original problem, supposed you are in MainActivity.java and you want to load fragment in it, so you do this to load fragment first time.
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame, new Fragment1);
transaction.addToBackStack(null);
transaction.commit();
You will need this method to change fragment from another fragment, so add this in your MainActivity
public void changeFragment(Fragment fragment){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame, new Fragment1);
transaction.addToBackStack(null);
transaction.commit();
}
Now from a button click in this fragment
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((MainActivity)getActivity()).changeFragment(new Fragment2);
}
});
Hope it will help!
I've recently tried to use an interface for fragment-activity communication. The idea is that when a button is pressed in a fragment, it retrieves data from an EditText in the same fragment, it then sends the string to the MainActivty - this controls all my fragments - which then starts another fragment and delivers the string to this fragment for use later, however, I'm having trouble initially setting up the first interface which sends the data. Unfortunately nothing happens, and I cannot therefore get to the next fragment which should be displayed. Additionally I have tried using getActivity() but it cannot find the associated method within the fragment, leading me to believe that the fragments somehow aren't directly connected to MainActivity (I've only just grasped basics of Java and a little of Android, just learning.)
I've listed the relevant information below, thanks for the assistance!
Fragment
public class CreateWorkoutFragment extends Fragment implements OnClickListener {
View rootViewCreateWorkoutFragment;
EditText editTextWorkoutName;
// Using an ImageView for custom button
ImageView buttonNext;
String valueCreateWorkoutEditText;
OnDataPass dataPasser;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootViewCreateWorkoutFragment = inflater.inflate(R.layout.fragment_create_workout, container, false);
buttonNext = (ImageView) rootViewCreateWorkoutFragment.findViewById(R.id.button_workout_name_next);
editTextWorkoutName = (EditText) rootViewCreateWorkoutFragment.findViewById(R.id.edit_text_workout_name);
buttonNext.setOnClickListener(this);
return rootViewCreateWorkoutFragment;
}
public interface OnDataPass {
public void onDataPass(String data);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
dataPasser = (OnDataPass) activity;
}
public void passData(String data) {
dataPasser.onDataPass(data);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_workout_name_next:
valueCreateWorkoutEditText = editTextWorkoutName.getText().toString();
passData(valueCreateWorkoutEditText);
break;
}
}
}
Main Activity
public class MainActivity extends FragmentActivity implements OnClickListener, CreateWorkoutFragment.OnDataPass {
ImageView buttonCreate;
Fragment newFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppThemeBlue);
setContentView(R.layout.activity_main);
buttonCreate = (ImageView) findViewById(R.id.create_foreground);
buttonCreate.setOnClickListener(this);
FragmentManager fragManager = getSupportFragmentManager();
FragmentTransaction tranManager = fragManager.beginTransaction();
CreateWorkoutFragment createWorkoutFrag = new CreateWorkoutFragment();
// fragment_change is just the area in XML where fragments switch
tranManager.add(R.id.fragment_change, createWorkoutFrag);
tranManager.commit();
newFragment = null;
}
#Override
public void onDataPass(String data) {
// CreateFragment is not to be confused with CreateWorkoutFragment
// CreateFragment is the fragment I'm trying to start when any strings
// are obtained from CreateWorkoutFragment
newFragment = new CreateFragment();
}
#Override
public void onClick(View v) {
switch (v.getId()) {
// create_foreground is just an ImageView used as a button
// Additionaly, other buttons are used to create other fragments,
// I've cut them out currently as they are not nessesary which is
// why CreateWorkoutFragment is only button and default currently
case R.id.create_foreground:
newFragment = new CreateWorkoutFragment();
break;
default:
newFragment = new CreateWorkoutFragment();
}
FragmentTransaction tranManager = getSupportFragmentManager().beginTransaction();
tranManager.replace(R.id.fragment_change, newFragment);
tranManager.addToBackStack(null);
tranManager.commit();
}
}
Sorry the code isn't exactly tidy, however, it was the most relevant code cut out from a large class. As I said, I have tried other methods yet cannot get any response from MainActivity either way. Thanks in advance!
Just before I posted: Got the app to write logcat messages to me, it manages to pass the data when the button is clicked - at least I think, and is something to do with the fragment not starting! At MainActivity>onDataPass()>new Fragment = new CreateFragment() Any ideas? As mentioned before, other buttons do exist and manage to change the fragment. However, were cutout to reduce amount of code posted.
getActivity() but it cannot find the associated method within the fragment
This is because getActivity() returns an Activity, not a MainActivity which is your custom subclass. You can easily fix this with a cast. For example, in your fragment, you can do this:
OnDataPass main = (OnDataPass) getActivity();
main.onDataPass(message);
Since such a cast is required, the interface seems to get in the way in my opinion. You can just as easily cast directly to MainActivity:
MainActivity main = (MainActivity) getActivity();
main.onDataPass(message);
I am trying to implement a ListFragment. The idea is to use CursorLoader. The code for FragmentActivity is
public class XYZ extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
// Create the list fragment and add it as our sole content.
if (fm.findFragmentById(android.R.id.content) == null) {
XYZFragment list = new XYZFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
Now I assume that onActivityCreate of XYZFragment should be called but that is not happening instead I am getting a the below image on the Emulator. I am looking for an explanation as to what is happening and what I am doing wrong?
Thanks.
Change onActivityCreate to onActivityCreated and you should get the expected results.