Troubles with events in fragments transactions - android

Im developing an app and on my first activity, I'm using an tablayout and a viewpager. So when the user, change the tabs the fragment change too. But the problem is on the second tab. In that tab I use a "container" so, when the user change to this tab, I add a fragmentOne and it has a button, when I click on that button, I replace the fragment whit a fragmentTwo. Now, on this second fragment I have a textview, when I click in that textview I want to change to the firstFragment. The trouble I think is because both have an onActivityCreated and the belong to the same activity. This is my code.
Containerfragment:
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
RegistroUnoFragment registroUnoFragment = new RegistroUnoFragment();
android.support.v4.app.FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.contenedorRegistro, registroUnoFragment).commit();
}
This is my first fragment:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
btnContinuar = (Button)getView().findViewById(R.id.btnContinuar);
btnContinuar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RegistroDosFragment registroDosFragment = new RegistroDosFragment();
android.support.v4.app.FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.contenedorRegistro, registroDosFragment).commit();
}
});
}
and in this fragment is when I want to return my first fragment
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
tviRetroceder = (TextView)getView().findViewById(R.id.tviRetroceder);
tviRetroceder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RegistroUnoFragment registroUnoFragment = new RegistroUnoFragment();
android.support.v4.app.FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.contenedorRegistro, registroUnoFragment).commit();
}
}

The tviRetroceder OnClickListener creates a new RegsitroUnoFragment called registroUnoFragment. But there is already a RegistroUnoFragment in existence called registroUnoFragment created in your container activity in its onCreate method. You cannot create it again. You have to "find" this fragment and only then add it to the container. So you need to check if the gragment is NULL first.
Use "replace" and not "add" and use a tag so that you can find the fragment e.g.
fragmentTransaction.replace(R.id.contenedorRegistro,registroUnoFragment,"TagUno");
Then in the OnClick
RegistroUnoFragment registroUnoFragment=(RegistroUnoFragment) getActivity().getFragmentManager.FindFragmentByTag("TagUno");
if(registroUnoFragment!=null) {
{fragmentTransaction.replace(R.id.contenedorRegistro,registroUnoFragment,"TagUno");
}

Related

why the app crashes when I use onclicklistener with Fragment?

public class MainActivity extends AppCompatActivity {
Context context;
LinearLayout menuClcick,gallerClcik,eventsClick;
LayoutInflater inflater;`enter code here`
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//we don't need to set view, our fragment will handle it
setPointer();
//Fragment Manger
FragmentManager fm = getFragmentManager();
//create instance of Fragment Transaction to handle fragment replace and animation
FragmentTransaction ft=fm.beginTransaction();
int displayMode = getResources().getConfiguration().orientation;
Log.e("WTF", "onCreate: "+displayMode );
//choose which fragment to display according to screen orientation
if (displayMode==1) //portrait
{
// that's the Fragment that I use to display a layout in the portrait and other layout in the landscape//
//create instance of our portrait fragment
Fragment1 f1=new Fragment1();
//change content of the screen to our new fragment
ft.replace(android.R.id.content,f1);
}
else
{
Fragment2 f2=new Fragment2();
ft.replace(android.R.id.content,f2);
}
//choose animation
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
//commit our changes
ft.commit();
}
private void setPointer() {
this.context=this;
menuClcick=findViewById(R.id.menuClick);
gallerClcik=findViewById(R.id.gallerClcik);
eventsClick=findViewById(R.id.eventsClick);
//this is the problem the app have no problem to find the buttons but it stops working when I try to put onclick listener in it//
menuClcick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "portrait", Toast.LENGTH_SHORT).show();
}
});
}
You are trying to access views that are not yet created,
As the documentation lifecycle shows, you should implement onCreateView() to inflate your layout and only there you have access to your R.id.menuClick.
So basically, you should call your setPointer() method on onCreateView().

OnBackPressed() brings back incorrect activity

Within my Main_Activity, I have a Button which when OnClicked opens the new Game_Activity, within the OnCreate of my Game_Activity I have:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_true_false);
level1GameInstructions = new Level1GameInstructions();
fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.game_activity, instructions).commit();
}
Which opens a Fragment, then within the Fragment class when a Button is clicked i want to close the current Fragment open so I have done this:
View.OnClickListener playBtnListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
getActivity().onBackPressed();
}
};
However, for some reason this returns the Main_Activity rather than the Game_Activity. Does anyone know why?
Be glad to use these two methods instead of your code:
public void removeFragment(#NonNull String tag) {
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag(tag)).commit();
}
public void replaceFragment(#IdRes int containerId, Fragment fragment, #Nullable String tag) {
getSupportFragmentManager().beginTransaction().replace(containerId, fragment, tag).commit();
}
Just replace the needed fragment with some tag, and remove it by that tag.

Cannot able to open second fragment

On click of Button I am trying to open second fragment(YourResultFragment.java),
I had a lots but don't know why it is not working , here is my code which I am using
public class KnowYourBodyFragment extends Fragment {
public KnowYourBodyFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_know_your_body, container, false);
Button button = (Button)rootView.findViewById(R.id.button2);
button.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Fragment mFragment = new YourResultFragment();
getActivity().getSupportFragmentManager().beginTransaction()
.replace(R.id.know_your_body_container, mFragment ).commit();
}
});
// Inflate the layout for this fragment
return rootView;
}
Use this Try to replace it with Default Container android.R.id.content.
Button button = (Button)rootView.findViewById(R.id.button2);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Fragment newFragment = new YourResultFragment();
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
// Replace whatever is in the fragment_container view with this
// fragment,
// and add the transaction to the back stack
transaction.replace(android.R.id.content, newFragment);
transaction.addToBackStack("tag");
// Commit the transaction
transaction.commitAllowingStateLoss();
}
});
What is transaction.addToBackStack("tag") ?
Add this transaction to the back stack. This means that the transaction will be remembered after it is committed, and will reverse its operation when later popped off the stack.

How to include the activity in fragment tabs ?

I am developing an app in which there are two fragmenttabs.when pressing the tabs corresponding fragments will appear.that works fine.but what I want an activity inside the fragmenttabs. I am using ABS library for this.
ActionBar bar = getSupportActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab1 = bar.newTab();
ActionBar.Tab tab2 = bar.newTab();
tab1.setText("Fragment A");
tab2.setText("Fragment B");
tab1.setTabListener(new MyTabListener<FragmentA>(this, "tab1",
FragmentA.class, null));
tab2.setTabListener(new MyTabListener<FragmentB>(this, "tab1",
FragmentB.class, null));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// app icon in action bar clicked; go Location selection
Intent intent = new Intent(FragmentDemoActivity.this,
TestActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getSupportActionBar()
.getSelectedNavigationIndex());
}
listnerclass is
public class MyTabListener<T extends Fragment> implements ActionBar.TabListener {
private final FragmentActivity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public MyTabListener(FragmentActivity activity, String tag, Class<T> clz,
Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
FragmentTransaction ft = mActivity.getSupportFragmentManager()
.beginTransaction();
mFragment = mActivity.getSupportFragmentManager().findFragmentByTag(
mTag);
if (mFragment != null && !mFragment.isDetached()) {
ft.detach(mFragment);
}
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mFragment == null) {
mFragment = Fragment
.instantiate(mActivity, mClass.getName(), mArgs);
ft.add(android.R.id.content, mFragment, mTag);
ft.commit();
} else {
ft.attach(mFragment);
ft.commit();
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mFragment != null) {
ft.detach(mFragment);
ft.commitAllowingStateLoss();
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
Fragmentclass
public class FragmentB extends Fragment {
Button button;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle saved)
{
return inflater.inflate(R.layout.frag_b, group, false);
}
#Override
public void onActivityCreated (Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
button = (Button) getActivity().findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Fragment B");
builder.setMessage("What would you like to do?");
builder.setPositiveButton("Nothing", null);
builder.setNegativeButton("Leave me alone!", null);
builder.show();
}
});
}
}
i wnt to include the following activity in the fragmentB
public class TestActivity extends Activity {
Button b1, b2;
TextView tv;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.testactivity);
b1 = (Button) findViewById(R.id.button1);
b2 = (Button) findViewById(R.id.button2);
tv = (TextView) findViewById(R.id.textView1);
b1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
tv.setText("You Clicked on Button 1");
}
});
b2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
tv.setText("You Clicked on Button 2");
}
});
}
}
I have lot of created activites to include in the fragment..its un imaginable to recreate in onActivityCreated of fragment class. according to this I have to modify my main application.
A Fragment can't host an activity. Instead of activity, you can use Nested Fragment.
A simple tutorial: http://xperiment-andro.blogspot.com/2013/02/nested-fragments.html
You cannot run an activity inside of a fragment. At best, you can have a fragment inside of a fragment.
so the best thing you can do in this situation is instead of creating an Activity you can create a Fragment and add it inside your FragmentB.
Like Faheem Said "
A Fragment can't host an activity. Instead of activity, you can use Nested Fragment"
Fragments are just like Activities if you read the Docs.Changing your activity to Fragment is easy
This is what developer.android says
To create a fragment, you must create a subclass of Fragment (or an existing subclass of it). The Fragment class has code that looks a lot like an Activity. It contains callback methods similar to an activity, such as onCreate(), onStart(), onPause(), and onStop(). In fact, if you're converting an existing Android application to use fragments, you might simply move code from your activity's callback methods into the respective callback methods of your fragment.
Usually, you should implement at least the following lifecycle methods:
onCreate()
The system calls this when creating the fragment. Within your implementation, you should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed.
onCreateView()
The system calls this when it's time for the fragment to draw its user interface for the first time. To draw a UI for your fragment, you must return a View from this method that is the root of your fragment's layout. You can return null if the fragment does not provide a UI.
onPause()
The system calls this method as the first indication that the user is leaving the fragment (though it does not always mean the fragment is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back).
This is the code activity inside Fragment.
YourActivityName.getSupportFragmentManager();

Switching Fragments in Master/Detail Flow

I am attempting to create an app which has a Master/Detail flow using Fragments. Selecting an item will open a detail fragment which may then which to "open" another fragment and add it to the back stack.
I have renamed classes to help illustrate what they do.
public class ListOfDetails extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
//Callback method indicating that an item with the given ID was selected.
public void onItemSelected(String id) {
// Performing logic to determine what fragment to start omitted
if (ifTwoPanes()) {
Fragment fragment = new DetailFragmentType1();
getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
} else {
Intent newIntent = new Intent(this, SinglePaneFragmentWrapper.class);
newIntent.putExtra("id", id);
startActivity(newIntent);
}
}
// My attempt at making it possible to change displayed fragment from within fragments
public void changeDetailFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(null);
transaction.replace(R.id.aContainer, fragment);
transaction.commit();
}
}
An example of one of the detail fragments. There are many different Fragments that may be created in different circumstances.
public class DetailFragmentType1 extends Fragment {
private ListOfDetails parent;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Activity a = getActivity();
if (a instanceof ListOfDetails) {
parent = (ListOfDetails) a;
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button aButton = (Button) getActivity().findViewById(R.id.aButton);
aButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
parent.changeDetailFragment(new SubDetailFragment());
}
});
}
}
When on phone, a wrapper activity is used to hold the fragment
public class SinglePaneFragmentWrapper extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Duplicate logic must be performed to start fragment
// Performing logic to determine what fragment to start omitted
String id = getIntent().getStringExtra("id");
if(id == "DetailFragmentType1") {
Fragment fragment = new DetailFragmentType1();
getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
} else {
...
}
}
}
What is the proper way to change the fragment that is open in the detail pane in this circumstance? My method feels like a hack when using two panes and doesn't even work when using only one pane because getParent() from SinglePaneFragmentWrapper returns null, making me unable to call parent.changeDetailFragment().
This is a complicated question, hopefully I explained it well. Let me know if I missed something. Thanks
There are lots of opinions around this and lots of ways of doing it. I think in this case the problem is "who is responsible for changing the fragment?" on the surface it seems that a listener on the button is the obvious place, but then the fragment shouldn't know what it is hosted in (a symptom of that is getting an undesirable result like null from getParent()).
In your case I would suggest you implement a "listener" interface in the parent and "notify" from the fragment.. when the parent is notified, it changes the fragment. This way the fragment is not changing itself (so doesn't need to know how).. so.. for your case..
Add a new interface:
public interface FragmentChangeListener {
void onFragmentChangeRequested(Fragment newFragment);
}
Implement the interface in your ListOfDetails activity
public class ListOfDetails extends FragmentActivity implements FragmentChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
//Callback method indicating that an item with the given ID was selected.
public void onItemSelected(String id) {
// Performing logic to determine what fragment to start omitted
if (ifTwoPanes()) {
Fragment fragment = new DetailFragmentType1();
getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
} else {
Intent newIntent = new Intent(this, SinglePaneFragmentWrapper.class);
newIntent.putExtra("id", id);
startActivity(newIntent);
}
}
// My attempt at making it possible to change displayed fragment from within fragments
public void changeDetailFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(null);
transaction.replace(R.id.aContainer, fragment);
transaction.commit();
}
// This is the interface implementation that will be called by your fragments
void onFragmentChangeRequested(Fragment newFragment) {
changeDetailFragment(newFragment);
}
}
Added listener to detail fragment
public class DetailFragmentType1 extends Fragment {
private FragmentChangeListener fragmentChangeListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Actually you might not have an activity here.. you should probably be
// doing this in onAttach
//Activity a = getActivity();
//if (a instanceof ListOfDetails) {
// parent = (ListOfDetails) a;
//}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button aButton = (Button) getActivity().findViewById(R.id.aButton);
aButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// parent.changeDetailFragment(new SubDetailFragment());
notifyFragmentChange(new SubDetailFragment());
}
});
}
#Override
public void onAttach(Activity activity) {
// This is called when the fragment is attached to an activity..
if (activity instanceof FragmentChangeListener) {
fragmentChangeListener = (FragmentChangeListener) activity;
} else {
// Find your bugs early by making them clear when you can...
if (BuildConfig.DEBUG) {
throw new IllegalArgumentException("Fragment hosts must implement FragmentChangeListener");
}
}
}
private void notifyFragmentChange(Fragment newFragment) {
FragmentChangeListener listener = fragmentChangeListener;
if (listener != null) {
listener.onFragmentChangeRequested(newFragment);
}
}
}
And implement the same interface to your single pane activity...
public class SinglePaneFragmentWrapper extends FragmentActivity implements FragmentChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Duplicate logic must be performed to start fragment
// Performing logic to determine what fragment to start omitted
String id = getIntent().getStringExtra("id");
if(id == "DetailFragmentType1") {
Fragment fragment = new DetailFragmentType1();
getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
} else {
...
}
}
// My attempt at making it possible to change displayed fragment from within fragments
public void changeDetailFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(null);
transaction.replace(R.id.aContainer, fragment);
transaction.commit();
}
// This is the interface implementation that will be called by your fragments
void onFragmentChangeRequested(Fragment newFragment) {
changeDetailFragment(newFragment);
}
}
Note the similarity between your single pane and your multi-pane activities.. this suggests that you could either put all of the duplicated code (changefragment etc) into a single activity that they both extend or that in maybe they are the same activities with different layouts...
I hope that helps, Good luck.
Regards,
CJ

Categories

Resources