frag1 calls frag 2 and frag 2 calls a method in frag1 that updates a textview. The code works (doesn't crash) but the frag1 UI (when I return from frag2) is not updated/
Is it a valid approach? Is there a way to update the UI?
here is the frag1 and frag2 code:
public class Frag1 extends android.app.Fragment {
private static TextView txt;
public Frag1() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootview = null;
rootview = inflater.inflate(R.layout.fragment_frag1, container, false);
return rootview;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
txt = (TextView) getActivity().findViewById(R.id.txt);
Button btn = (Button) getActivity().findViewById(R.id.btn1);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Frag2 f2 = new Frag2();
ft.replace(R.id.content_frame, f2);
ft.addToBackStack(null);
ft.commit();
}
});
}
void setTxt(String s){
txt.setText(s);
}
}
and frag2 - the setTxt() method updates the textview in frag1:
public class Frag2 extends Fragment {
private Button btn2;
public Frag2() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootview;
rootview = inflater.inflate(R.layout.fragment_frag2, container, false);
Log.i("frag2","createdview");
return rootview;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Frag1 f1 = new Frag1();
f1.setTxt("msg from frag2");
btn2 = (Button) getActivity().findViewById(R.id.btn2);
btn2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Frag3 f3 = new Frag3();
ft.replace(R.id.content_frame, f3);
ft.addToBackStack(null);
ft.commit();
}
});
}
}
You can Broadcast Intent from the frag2
Intent intent = new Intent("key_to_identify_the_broadcast");
Bundle bundle = new Bundle();
bundle.putString("edttext", json.toString());
intent.putExtra("bundle_key_for_intent", bundle);
context.sendBroadcast(intent);
and then you can receive the bundle in your frag 1 by using the BroadcastReceiver class
private final BroadcastReceiver mHandleMessageReceiver = new
BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle =
intent.getExtras().getBundle("bundle_key_for_intent");
if(bundle!=null){
String edttext = bundle.getString("edttext");
}
//you can call any of your methods for using this bundle for your use case
}
};
in onCreateView() of your fragment you need to register the broadcast receiver first otherwise this broadcast receiver will not be triggered
IntentFilter filter = new IntentFilter("key_to_identify_the_broadcast");
getActivity().getApplicationContext().
registerReceiver(mHandleMessageReceiver, filter);
Finally you can unregister the receiver to avoid any exceptions
#Override
public void onDestroy() {
try {
getActivity().getApplicationContext().
unregisterReceiver(mHandleMessageReceiver);
} catch (Exception e) {
Log.e("UnRegister Error", "> " + e.getMessage());
}
super.onDestroy();
}
You can create separate broadcast receivers in all of your fragments and use the same broadcast to broadcast the data to all of your fragments. You can also use different keys for different fragments and then broadcast using particular key for particular fragment.
Related
I have an activity "MainActivity" that contains two fragments. Fragment1 has a EditText and a button. When the user presses the button the text inside the EditText will be sent to Fragment2 which only has a TextView. But I can't quite figureout how to send the text from EditText in Fragment1 to TextView in Fragment2. If someone could help? Appreciated. Thanks
Fragment1
package org.pctechtips.myfragmentapp;
/**
* Created by george on 5/16/17.
*/
public class ButtonFragment extends Fragment {
Button bt;
EditText editText;
TextView txtView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.button_fragment,
container, false);
txtView = (TextView) view.findViewById(R.id.textView_fragment1);
editText = (EditText) view.findViewById(R.id.editText_fragment1);
bt = (Button) view.findViewById(R.id.button_fragment1);
bt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
buttonClicked(v);
}
});
return view;
}
public void buttonClicked (View view) {
.onButtonClick(edittext.getText().toString());
}
}
MainActivity
package org.pctechtips.myfragmentapp;
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*fragments 1 section*/
FragmentManager fm1 = getFragmentManager();
FragmentTransaction ft1 = fm1.beginTransaction();
ButtonFragment bf = new ButtonFragment();
ft1.add(R.id.button_fragment1, bf);
ft1.commit();
/*Fragment 2 section*/
FragmentManager fm2 = getFragmentManager();
FragmentTransaction ft2 = fm2.beginTransaction();
TextviewFragment tf = new TextviewFragment();
ft2.add(R.id.textView_fragment2, tf);
ft2.commit();
}
}
Fragment2
public class TextviewFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.text_fragment, container, false);
}
}
The quick and easy answer is to call getView().findViewById() from the activity (as shown below). The more proper approach is to give your fragments an interface that gets and retrieves the text. Below is the code for the quick and easy method, but when you create a fragment in Android studio it shows you how to do the more proper interface approach.
package org.pctechtips.myfragmentapp;
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*fragments 1 section*/
FragmentManager fm1 = getFragmentManager();
FragmentTransaction ft1 = fm1.beginTransaction();
ButtonFragment bf = new ButtonFragment();
ft1.add(R.id.button_fragment1, bf);
ft1.commit();
/*Fragment 2 section*/
FragmentManager fm2 = getFragmentManager();
FragmentTransaction ft2 = fm2.beginTransaction();
TextviewFragment tf = new TextviewFragment();
ft2.add(R.id.textView_fragment2, tf);
ft2.commit();
EditText et = (EditText) ft1.getView().findViewById(YOUR_EDITTEXT_ID);
Button button = (Button) ft1.getView().findViewById(YOUR_BUTTON_ID);
TextView tv = (TextView) ft2.getView().findViewById(YOUR_TEXTVIEW_ID);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tv.setText(et.getText();)
}
});
}
You can do it by several ways. For example via Activity like in Official Docs. But, I'm prefer send broadcast messages via LocalBroadcastManager like in this answer of war_Hero:
In Sending Fragment(Fragment B - Your ButtonFragment)
public class FragmentB {
private void sendMessage() {
Intent intent = new Intent("custom-event-name");
intent.putExtra("message", "your message");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
And in the Message to be Received Fragment(FRAGMENT A - Your TextviewFragment)
public class FragmentA {
#Override
public void onCreate(Bundle savedInstanceState) {
...
// Register receiver
LocalBroadcastManager.getInstance(this).registerReceiver(receiver,
new IntentFilter("custom-event-name"));
}
// This will be called whenever an Intent with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("message");
}
};
}
And don't forget to unregister receiver:
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
I am trying to initialize a generic fragment in such manner that it can be used globally no matter what the instance of it is. For example:
public MainActivity extends Activity{
private Fragment fragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first_layout);
FragmentManager fragManager = getFragmentManager();
FragmentTransaction transaction = fragManager.beginTransaction();
if(condition1){
fragment = new MyFirstFragment();
transaction
.add(R.id.registrationFragment, fragment); //registrationFragment is FrameLayout
transaction.commit();
}else if(condition2){
fragment = new MySecondFragment();
transaction
.add(R.id.registrationFragment, fragment); //registrationFragment is FrameLayout
transaction.commit();
}
//create intent filter
//create MyBroadcast object
//registerReceiver(receiverObject, intentFilter)
}
//Some action happens
class MyBroadcast extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(action1)) {
fragment.performAction();
}
}
}
public class MyFirstFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(
R.layout.fragment_holder_a, container, false);
//setWidgets etc...
// TODO Auto-generated method stub
return view;
}
//Used from main
public void performAction(String text){
//set some TextView's text from MainActivity event
//different from MySecondFragment performAction method
}
}
public class MySecondFragmentFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(
R.layout.fragment_holder_b, container, false);
//setWidgets etc...
// TODO Auto-generated method stub
return view;
}
//Used from main
public void performAction(String text){
//set some TextView's text from MainActivity event
//different from MyFirstFragment performAction method
}
}
The problem is that in my real application I may have more than 2 Fragments that the Fragment object could become an instance of. I am trying to avoid a lot of if statements to check which fragment instance was inflated with the FragmentManager. Also the problem I am facing is that When i declare the Fragment fragment = new MyFirstFragment(); I cannot access the fragment.performAction("Stringssss") method. This is not the solution I am looking for:
public MainActivity extends Activity{
private MyFirstFragment myFirstFrag;
private MySecondFragment mySecondFrag;
private MyThirdFragment myThirdFrag;
private MyNthFragment myNthFrag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first_layout);
FragmentManager fragManager = getFragmentManager();
FragmentTransaction transaction = fragManager.beginTransaction();
if(condition1){
myFirstFrag = new MyFirstFragment();
transaction
.add(R.id.registrationFragment, myFirstFrag); //registrationFragment is FrameLayout
transaction.commit();
}else if(condition2){
mySecondFrag = new MySecondFragment();
transaction
.add(R.id.registrationFragment, mySecondFrag); //registrationFragment is FrameLayout
transaction.commit();
}else if(condition3){
//...
}else if(conditionNth){
//...
}
//create intent filter
//create MyBroadcast object
//registerReceiver(receiverObject, intentFilter)
}
//Some action happens
class MyBroadcast extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(action1)) {
if(null != myFirstFrag)
myFirstFrag.performAction();
}else if(null != mySecondFrag){
mySecondFrag.performAction();
}else if(null != myThirdFrag){
myThirdFrag.performAction();
}else if(null != myNthFrag){
myNthFrag.performAction();
}
}else if(action.equals(actionNTH)){
if(null != myFirstFrag)
myFirstFrag.performAction();
}else if(null != mySecondFrag){
mySecondFrag.performAction();
}else if(null != myThirdFrag){
myThirdFrag.performAction();
}else if(null != myNthFrag){
myNthFrag.performAction();
}
}
}
}
As you can see in the BroadcastReceiver a lot of If-else statements start to pile up making the code clunky and dirty. I am looking for a clean solution that will not require me to have a bunch of if-else statements and keep the code simple.
Thank you.
Android newbie here. My app crashes when trying to send text from one fragment to another. It says:
java.lang.NullPointerException
at com.example.hang.camera.Camera.sendResults(Camera.java:57)
at com.example.hang.camera.PageOneFragment.sendMethod(PageOneFragment.java:320)
at com.example.hang.camera.PageOneFragment$1.onClick(PageOneFragment.java:104)
Here is my code:
// Activity that implements the interface from fragment A
Camera.java:
public class Camera extends ActionBarActivity implements PageOneFragment.OnComputeResultListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabHost.setup();
mViewPager = (CustomViewPager)findViewById(R.id.pager);
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
mTabsAdapter.addTab(mTabHost.newTabSpec("one").setIndicator("One"), PageOneFragment.class, null);
mTabsAdapter.addTab(mTabHost.newTabSpec("two").setIndicator("Two"), PageTwoFragment.class, null);
if (savedInstanceState != null)
{
mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
}
mViewPager.setPagingEnabled(false);
}
#Override
public void sendResults(String text){
PageTwoFragment fragmentTwo = (PageTwoFragment)getSupportFragmentManager().findFragmentById(R.id.second_fragment);
fragmentTwo.updateText(text);
}
// fragment A:
PageOneFragment.java
OnComputeResultListener mCallback;
public interface OnComputeResultListener{
public void sendResults(String text);
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try{
mCallback = (OnComputeResultListener) activity;
} catch(ClassCastException e){
throw new ClassCastException(activity.toString()
+ " must implement OnComputeResultListener");
}
}
public void sendMethod(){
mCallback.sendResults("the results");
}
// in onCreateView, I have this onclicklistener, and I call "sendMethod" here
lengthButton.setOnClickListener(
new Button.OnClickListener(){
public void onClick(View v){
double length ;
line_x = imageview.x_getter();
line_y = imageview.y_getter();
length = Math.sqrt(Math.pow((line_x[0]-line_x[1]),2) + Math.pow((line_y[0]-line_y[1]),2));
line_length.setText("Length of the line is: " + String.format("%.2f",length) + "dp");
sendMethod();
}
}
);
// fragment B
PageTwoFragment.java
private TextView resultText = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.pagetwo_fragment, container, false);
resultText = (TextView) view.findViewById(R.id.resultText);
return view;
}
public void updateText(String text){
resultText.setText(text);
}
I finally solved the problem. In my camera.java where I implemented "sendResults" method of the interface, I changed the parameter of "findFragmentById" to the id of the viewPager instead of the id of the fragment's xml file.
However, from Android developer doc, for findFragmentById, it does say:
Finds a fragment that was identified by the given id either when inflated from XML or as the container ID when added in a transaction.
So, I don't know why the xml id didn't work.
You can save your Fragment as an object in your Activity class.
In the activity class before doing the fragment transaction, do the following:
PageTwoFragment fragmentTwo = new PageTwoFragment();
// do the transaction:
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, fragmentTwo);
ft.commit();
then later in code, just call
fragmentTwo.updateText(...);
From all the searches I have found on SO stating that you should save your instance state in the #Override public void onSaveInstanceState(Bundle outState)
However This is tightly coupled with the activities lifestyle.
How can I save the state of my listview in a fragment that gets swapped out with another fragment.
I have one main activity which all the fragments are loaded into.
I have tried this so far:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save adapter data so that when the fragment is returned to it can be resused.
ArrayList<CategoryMobileDto> categories = new ArrayList<CategoryMobileDto>();
for(int i=0; i < adapter.getCount();i++)
{
categories.add(adapter.getItem(i));
}
String persistData = new Gson().toJson(categories);
outState.putString("Categories", persistData);
}
and then in my OnCreate();
if(savedInstanceState!=null)
{
String data =savedInstanceState.getString("Categories");
Type collectionType = new TypeToken<ArrayList<CategoryMobileDto>>() {
}.getType();
adapter.addAll(gson.<Collection<CategoryMobileDto>>fromJson(data, collectionType));
adapter.notifyDataSetChanged();
}else{
// Make request to server
}
however savedInstanceState is always null. But this makes sense as my activity is not being destroyed and recreated.
This is how I transition from one fragment to another:
fragment.setArguments(args);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container, fragment, "ProductListFragment");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Is there a way i can save the state of my listview when the fragment is removed and then restore it again when the fragment is popped from the back-stack?
Move this code from onCreate() to onActivityCreated() of Fragment
if(savedInstanceState!=null)
{
String data =savedInstanceState.getString("Categories");
Type collectionType = new TypeToken<ArrayList<CategoryMobileDto>>() {
}.getType();
adapter.addAll(gson.<Collection<CategoryMobileDto>>fromJson(data, collectionType));
adapter.notifyDataSetChanged();
}else{
// Make request to server
}
If you have any query please let me know.
You can use the Arguments with the Fragment(Only if you have the data to show in fragment before the fragment is loaded means attached). You can setArguments to a fragment which will be persisted when you go to another fragment by fragment transaction and when you come back, load the fragment from the getArguments function.
public void setArguments (Bundle args)
Added in API level 11
Supply the construction arguments for this fragment. This can only be called before the fragment has been attached to its activity; that is, you should call it immediately after constructing the fragment. The arguments supplied here will be retained across fragment destroy and creation.
public final Bundle getArguments ()
Added in API level 11
Return the arguments supplied when the fragment was instantiated, if any.
Please find the sample code below for passing data between fragments :
main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/flContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
MainActivity.java
public class MainActivity extends Activity implements IFragContainer {
private static final String FRAG_TAG = "FragTag";
private FragBase mFrag;
private String dataToBePassedBack;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
changeFragment(FragA.class, "Data to Frag A");
}
#Override
public void changeFragment(Class<? extends FragBase> fragClass, String data) {
try {
FragmentTransaction ft = getFragmentManager().beginTransaction();
mFrag = fragClass.newInstance();
Bundle args = new Bundle();
args.putString("DATA", data);
mFrag.setArguments(args);
ft.replace(R.id.flContainer, mFrag, FRAG_TAG);
ft.addToBackStack(mFrag.toString());
ft.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onBackPressed() {
dataToBePassedBack = mFrag.getDataToPassBack();
FragmentManager mgr = getFragmentManager();
mgr.executePendingTransactions();
boolean doCheckAndExit = true;
for (int i = mgr.getBackStackEntryCount() - 1; i > 0; i--) {
BackStackEntry entry = mgr.getBackStackEntryAt(i);
if (!TextUtils.isEmpty(entry.getName())) {
mgr.popBackStackImmediate(entry.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
doCheckAndExit = false;
break;
}
}
if (doCheckAndExit) {
finish();
} else {
mFrag = (FragBase) mgr.findFragmentByTag(FRAG_TAG);
}
}
#Override
public String getDataToBePassedBack() {
return dataToBePassedBack;
}
}
IFragContainer.java
public interface IFragContainer {
void changeFragment(Class<? extends FragBase> fragClass, String data);
String getDataToBePassedBack();
}
FragBase.java
public abstract class FragBase extends Fragment {
public String getDataToPassBack(){
return null;
}
}
FragA.java
public class FragA extends FragBase {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Button btn = new Button(getActivity());
final IFragContainer fragContainer = (IFragContainer) getActivity();
if (TextUtils.isEmpty(fragContainer.getDataToBePassedBack())) {
btn.setText(getArguments().getString("DATA"));
} else {
btn.setText(fragContainer.getDataToBePassedBack());
}
btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
fragContainer.changeFragment(FragB.class, "Data to Frag B");
}
});
return btn;
}
}
FragB.java
public class FragB extends FragBase {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Button btn = new Button(getActivity());
btn.setText(getArguments().getString("DATA"));
btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getActivity().onBackPressed();
}
});
return btn;
}
#Override
public String getDataToPassBack() {
return "Data from Frag B to A";
}
}
I want to know how to stop running thread and asyncTask of fragment safely on android.
here is my code:
public class MainActivity extends FragmentActivity {
public Fragment mFragment;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
Button btn_next = (Button) findViewById(R.id.button_next);
btn_next.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((FragmentOne)mFragment).infoThread.interrupt();
}
});
Intent intent = getIntent();
String start_fragment = intent.getStringExtra("start_fragment");
onShowFragment(start_fragment);
}
public void onShowFragment(String select_fragment) {
Fragment fr = null;
switch (select_fragment) {
case "fragment1":
fr = new FragmentOne();
break;
case "gragment2":
fr = new FragmentTwo();
break;
}
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragment_place, fr);
ft.commit();
}
}
public class FragmentOne extends Fragment {
public Thread infoThread;
public View view_one;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view_one = inflater.inflate(R.layout.fragment_one, container, false);
onGetInfo();
return view_one;
}
public void onGetInfo() {
infoThread= new Thread(new Runnable() {
#Override
public void run() {
String response_info = HTTPUtils.HTTPPost(Global.USER_URL,
"name", "abc",
"password", "1234");
processGetInfo(response_info);
}
});
infoThread.start();
}
}
in here, when click btn_next button, FATAL EXCEPTION ERROR occur.
please help me.
try to use:
infoThread.stop()
instead of:
infoThread.interrupt();
You are never initialising mFragment with a value.
Add mFragment = fr; to onShowFragment
But, you're going to have a whole load of different issues if you carry on down this path. You should consider a more robust framework than performing http requests in threads spawned from a Fragment's onCreateView method.