Nested fragments: Detect click on outer fragment from within inner fragment - android

My application has two nested fragments like in the picture below:
How is it possible to detect clicks on Fragment1 from within the instance of Fragment2?

Off the cuff, I would say to create a listener interface in Fragment1, then implement that interface in Fragment2 and call the appropriate method in the interface in Fragment1's onClick method.
Edit
This is a pretty barebones example, and I haven't tested it, but here's the general theory. Of course, you'll need to add in your logic and fill in necessary methods like onCreate.
public class SampleActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize your activity here
Fragment1 fragment1 = new Fragment1();
Fragment2 fragment2 = new Fragment2();
// Give fragment1 a reference to fragment2
fragment1.registerListener(fragment2);
// Do your fragment transactions here
}
}
public class Fragment1 extends Fragment implements OnClickListener{
// This is the interface. You can put as many abstract methods here as you want
// with whatever parameters you want, but they all have to be overriden
// in fragment2
public interface FragmentClickListener {
void onFragmentClick();
}
FragmentClickListener mListener;
// This fragment needs to have a reference to the other fragment
// This method can take any class that implements FragmentClickListener
public void registerListener(FragmentClickListener mListener) {
this.mListener = mListener;
}
#Override
public void onClick(View view) {
// You must check to make sure something is listening before you use the interface
if (mListener != null) {
//Let the interface know this fragment was clicked
mListener.onFragmentClick();
}
}
}
public class Fragment2 extends Fragment implements FragmentClickListener {
#Override
public void onFragmentClick() {
// Do whatever you want to do when fragment1 is clicked
}
}

Related

How to implement an interface in Fragment from a non parent Activity?

I have not found a clear solution anywhere on stack for this.
Here's my basic set up
public class Activity1 extends AppCompatActivity
{
private OnAttributesUpdatedListener onAttributesUpdatedListener;
public interface OnAttributesUpdatedListener
{
public void onAttributesUpdated();
}
public void setTargetFragment(Fragment fragment)
{
this.onAttributesUpdatedListener = (OnAttributesUpdatedListener) fragment;
}
private void whenFinishedSomethingCallback()
{
onAttributesUpdatedListener.onAttributesUpdated();
}
}
public class Fragment1 extends Fragment implements Activity1.OnAttributesUpdatedListener
{
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view) {
if(rivalButtonClick == 0)
{
Activity1 activity1 = new Activity1();
activity1.setTargetFragment(Fragment1.this);
startActivity(new Intent(getActivity(), activity1.getClass()));
}
}
});
}
I get a null pointer exception and crashes on : onAttributesUpdatedListener.onAttributesUpdated(); because for some reason my listener never gets set properly. What's the proper way to do this?
You need to set the listener at start of the fragment onCreatView() or in onActivityCreated() only if the Desired Activity is a parent Activity of that particular fragment. Below is an example .
public class Activity1 extends AppCompatActivity {
private OnAttributesUpdatedListener onAttributesUpdatedListener;
public interface OnAttributesUpdatedListener {
public void onAttributesUpdated();
}
public void setListener(OnAttributesUpdatedListener onAttributesUpdatedListener) {
this.onAttributesUpdatedListener = onAttributesUpdatedListener;
}
private void whenFinishedSomethingCallback() {
if(onAttributesUpdatedListener!=null)
onAttributesUpdatedListener.onAttributesUpdated();
}
}
public class Fragment1 extends Fragment implements Activity1.OnAttributesUpdatedListener
{
#Override
public void onAttributesUpdated() {
// Do your stuff here
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((Activity1)getActivity()).setListener(this);
}
}
Read about fragment Life cycle to make use of getActivity(). also remove the listener when fragment is destroyed .
Use LocalBroadcastManager for communicating between in case the Fragment exists in other Activity.
At first create an Interface like this:
public interface Listener{
void doSomething() }
Then implement this interface in your activity.
And also add
Listener listener
In your fragment
And in onAttach method in fragment use this
listener=(Listener)activity
Then call listener whenever you need .

android set interface with fragment and activity

i try to set and interface between fragment and activity first this the interface in my activity
private PLRListener KListener;
public interface PLRListener{
void updateProgress(long e, long z);
void pause();
void play(long e, long z);
}
and i have viewPager with FragmentPagerAdapter now i set the listener on fragment
public class myfragment extends Fragment implements MainActivity.PLRListener {
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
((MainActivity)getActivity()).setListener(this);
} catch (ClassCastException e) {
throw new ClassCastException("www");
}
}
#Override
public void pause() {
//change background on fragment
}
#Override
public void play(long e, long z) {
//change text on fragment
}
}
now its work but work where on the next fragment not on the visible fragment
so its on callback function change the background and text of layout of the visible fragment
ex: i have three fragment and i start the application
fragment1 fragment2 fragment2
when iam on fragment2 and the callback function run its update on fragment3 when its on fragment1 and i run callback function its update fragment2
If you work with FragmentPagerAdapter you have to consider that this class will load more than one fragment because of OffscreenPageLimit which default value is 1 and can't be lower than 1. You are setting the listener everytime onAttach() of fragment is called. ((MainActivity)getActivity()).setListener(this);

How to access viewpager's fragment from activity

Suppose such an scenario, we have an activity and 3 fragments, like so: MyActivity Frg1, Frg2 and Frg3. Frg2 and Frg3 are embedded into a viewPager. My needs is to trigger Frg2 from Frg1. I made an interface TriggerActivityFromFrg1 and MyActivity implements it, when press button in Frg1 I call (getActivity) triggerActivityFromFrg1.trigger() and method trigger() is called in MyActivity, the problem is how to trigger Frg2 from activity?
I'd like to make somehow an interface between MyActivity and Frg2.
p.s. I don't want to use eventbus.
Have your Frg2 class also implement the interface:
public class Frg2 extends Fragment implements TriggerActivityFromFrg1 {
and implement the method
#Override
public void trigger() {
if (getView() != null) { // see comments below
// TODO logic here
}
}
Add a field to your activity to keep track of the target fragment:
private TriggerActivityFromFrg1 mTarget;
Add the register/unregister methods to the activity:
public synchronized void registerTriggerTarget(TriggerActivityFromFrg1 listener) {
mTarget = listener;
}
public synchronized void unregisterTriggerTarget(TriggerActivityFromFrg1 listener) {
if (mTarget == listener) {
mTarget = null;
}
}
Make the trigger method in your activity like this:
public void trigger() {
if (mTarget != null) {
mTarget.trigger();
}
}
Override onAttach() and onDetach() in Frg2 to register/unregister:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((MyActivity) activity).registerTriggerTarget(this);
}
#Override
public void onDetach() {
((MyActivity) getActivity()).unregisterTriggerTarget(this);
super.onDetach();
}
Congratulations, you just built your own mini event bus.
There's a reason you have to add all this code, and that's because ViewPager won't create fragments until it needs them. Also it decouples MyActivity from Frg2.
Another thing to keep in mind if extending FragmentPagerAdapter is that the fragment will stay in memory even if the view is destroyed, so make sure you check that the fragment has a valid view, i.e. if (getView() != null)

How to update view in Fragment from child fragment in android

Have two fragments A and B, Fragment A has Textview and Fragment B has edittext and button.
Click on submit in FragmentB need to update textview in FragmentA with Edittext text.
How to do communication between fragment?
n this example, FragmentA call notify.
INotifier
public interface INotifier {
public void notify(Object data);
}
Utils
public class Utils {
public static INotifier notifier;
}
FragmentA
public FragmentA extends Fragment {
public void onCreateView(...) {
}
public void inSomeMethod() {
if (Utils.notifier != null) {
Utils.notifier.notify(data);
}
}
}
FragmentB
public FragmentB extends Fragment implements INotifier {
public void onCreateView(...) {
Utils.notifier = this;
}
#Override
public void notify(Object data) {
// handle data
}
}
You need to interact the activity first which will interact the second fragment. and also read this article on how to do it.
https://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
The communication between Fragments is done usinng Listeners. When you want to update fragment, use the listener to tell the MainActivity to update the second fragment as recommended by Google http://developer.android.com/training/basics/fragments/communicating.html. Create the interface in Fragment and Implement this in Activity
Listener in Fragment
public interface FragmentUpdateInterface {
void updateFragment(String newText);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (FragmentUpdateInterface ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement FragmentUpdateListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.updateFragment("New Text");
}
MainActivity
Implement fragment in MainActivity as
public static class MainActivity extends Activity
implements MyFragment.FragmentUpdateListener{
public void updateFragment(String newText) {
OtherFragment otherFrag = (OtherFragment)
getSupportFragmentManager().findFragmentById(R.id.other_fragment);
if (otherFrag != null) {
otherFrag.updateFragment(newText);
} else {
// Otherwise, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
OtherFragment otherFrag = new OtherFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
otherFrag.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, otherFrag);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
Hope this helps.
UPDATE:
You can also use LocalBroadcastManager.getInstance().sendBroadcast() to notify to the other fragment as well.

Three Fragments, interface on second Fragment requiring implementation on both Main and Second Activity

Taking the default 'Master/Detail' flow template in Eclipse, and adding a third Fragment (let's call it Edit, launched from the pre-existing Detail Fragment) and I'm now looking to open the Edit Fragment when a user clicks on an item in the Detail Fragment.
I've implemented an interface on the Detail fragment, however depending on whether the application is on a Tablet or Phone (dual-pane or not), the Iterface requires to be implemented by either the Detail Activity, or the Main Activity in order to function. I assume this is due to the way that the template implements opening of the Detail Fragment as an activity when the device is not in dual-pane mode.
Have I implemented this incorrectly, or is there a best practice that would allow me to unify the implementation of the interfaces into the main activity?
Here are some reduced snippets from the Master and Detail Fragments, showing the requirement for dual-implementation of the Detail Fragments interface.
Code for WaveListWactivity.java (first Fragment)
public class WaveListActivity extends FragmentActivity implements
WaveListFragment.Callbacks,WaveDetailFragment.Callbacks {
private boolean mTwoPane;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wave_list);
if (findViewById(R.id.wave_detail_container) != null) {
mTwoPane = true;
((WaveListFragment) getSupportFragmentManager().findFragmentById(
R.id.wave_list)).setActivateOnItemClick(true);
}
}
//Interface from WaveListFragment
#Override
public void onWaveSelected(int id) {
if (mTwoPane) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.wave_detail_container, new WaveDetailFragment()).commit();
} else {
Intent detailIntent = new Intent(this, WaveDetailActivity.class);
startActivity(detailIntent);
}
}
//Interface from WaveDetailFragment
#Override
public void onItemSelected(int id) {
if (mTwoPane) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.wave_detail_container, new WaveEditFragment()).addToBackStack(null).commit();
} else {
Intent detailIntent = new Intent(this, WaveDetailActivity.class);
startActivity(detailIntent);
}
}
}
Code for WaveDetailActivity.java (second Fragment)
public class WaveDetailActivity extends FragmentActivity implements WaveDetailFragment.Callbacks {
private boolean mTwoPane;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wave_detail);
if (findViewById(R.id.wave_detail_container) != null) {
mTwoPane = true;
}
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().add(R.id.wave_detail_container, new WaveDetailFragment()).commit();
}
}
//Callback from WaveDetailFragment
#Override
public void onItemSelected(int id) {
if (mTwoPane) {
getSupportFragmentManager().beginTransaction().replace(R.id.wave_detail_container, new WaveEditFragment()).addToBackStack(null).commit();
} else {
Intent detailIntent = new Intent(this, WaveEditActivity.class);
startActivity(detailIntent);
}
}
}
I think you may be slightly confused as to the difference between FragmentActivity, Fragments and the callback interfaces that you need to implement on your Activity. From the looks of it, all the code snippets are Activity classes and not Fragments. I would expect a Fragment to look something like:
/** From http://developer.android.com/training/basics/fragments/communicating.html */
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
And then your Activity:
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article
}
}
The overall 'flow' of this should be:
Fragments act as blobs of UI code with callbacks for all the interesting stuff.
Callbacks implemented by Activity.
The overall number of actions you can do depends on the fragments which depends on the size of your screen (so you may have 2 fragments which feed into a single Activity). The logic for determining whether to show one or two fragments should be done in the Activity.

Categories

Resources