I have an activity which contains a fragment.
This is a part of the fragment:
public class NotificationFragment extends DialogFragment {
private static final String EDIT_TEXT_KEY = "EDIT_TEXT_KEY";
private static final String SPINNER_KEY = "SPINNER_KEY";
private static final String IMAGE_VIEW_KEY = "IMAGE_VIEW_KEY";
private static final List<String> arraySpinner = Arrays.asList("item 1", "item 2", "item 3", "item 4", "item 5");
// private String text;
private String spinnerValue;
private Bitmap bitmap;
#Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
System.out.println("NotificationFragment#onActivityCreated");
if (savedInstanceState != null) {
final EditText editText = (EditText) getView().findViewById(R.id.edit_text);
editText.setText(savedInstanceState.getString(EDIT_TEXT_KEY));
editText.setText("gebroken");
final Spinner spinner = (Spinner) getView().findViewById(R.id.spinner);
spinner.setSelection(savedInstanceState.getInt(SPINNER_KEY));
final ImageView imageView = (ImageView) getView().findViewById(R.id.image_view);
imageView.setImageBitmap((Bitmap) savedInstanceState.getParcelable(IMAGE_VIEW_KEY));
System.out.println("1: " + ((EditText) getView().findViewById(R.id.edit_text)).getText().toString());
System.out.println(getView().hashCode());
}
}
In my test I want to check if the view gets restored after restoring the activity/fragment.
My test (debug state) looks like this:
#Config(manifest = IConfig.MANIFEST_PATH, emulateSdk = IConfig.SDK_VERSION, reportSdk = IConfig.SDK_VERSION)
#RunWith(RobolectricTestRunner.class)
public class AbstractFragmentTest {
#Test
public void test() {
final ActivityController<NotificationActivity> controller = Robolectric.buildActivity(NotificationActivity.class).setup().visible()
.start().resume();
final NotificationActivity activity = controller.get();
final ShadowActivity shadowActivity = Shadows.shadowOf(activity);
shadowActivity.recreate();
assertEquals("gebroken", ((EditText) shadowActivity.findViewById(R.id.edit_text)).getText().toString());
}
I would expect to have "gebroken" as a result, as this is the output from the test:
NotificationActivity#onCreate
NotificationFragment#onCreateView
NotificationFragment#onViewCreated
2:
NotificationFragment#onActivityCreated
NotificationFragment#onSaveInstanceState
NotificationActivity#onSaveInstanceState
NotificationActivity#onCreate
NotificationFragment#onCreateView
NotificationFragment#onViewCreated
2:
NotificationFragment#onActivityCreated
1: gebroken
NotificationActivity#onRestoreInstanceState
But instead of being "gebroken", the result is "".
Am I missing a refresh over here?
Change the test to this:
#Config(manifest = IConfig.MANIFEST_PATH, emulateSdk = IConfig.SDK_VERSION, reportSdk = IConfig.SDK_VERSION)
#RunWith(RobolectricTestRunner.class)
public class AbstractFragmentTest {
private static final int EDIT_TEXT_ID = R.id.edit_text;
private static final String EDIT_TEXT_VALUE = "this text value should be restored";
private ActivityController<NotificationActivity> controller;
#Before
public void setupFreshFixture() {
controller = Robolectric.buildActivity(NotificationActivity.class).setup();
}
#Test
public void test() {
setEditText(controller.get(), EDIT_TEXT_ID, EDIT_TEXT_VALUE);
final Bundle state = restartActivity();
assertEquals(EDIT_TEXT_VALUE, state.getString(NotificationFragment.EDIT_TEXT_KEY));
assertEquals(EDIT_TEXT_VALUE, getEditText(controller.get(), EDIT_TEXT_ID));
}
private Bundle restartActivity() {
final Bundle state = new Bundle();
controller.saveInstanceState(state).stop().destroy();
controller = Robolectric.buildActivity(NotificationActivity.class).setup(state);
return state;
}
//TODO move to utilities
public static String getEditText(final Activity activity, final int id) {
return ((EditText) activity.findViewById(id)).getText().toString();
}
public static void setEditText(final Activity activity, final int id, final String value) {
((EditText) activity.findViewById(id)).setText(value);
}
}
And I had to modify my activity too:
public class NotificationActivity extends FragmentActivity {
private NotificationFragment fragment;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("NotificationActivity#onCreate");
setContentView(R.layout.activity_notification);
if (savedInstanceState == null) {
fragment = new NotificationFragment();
getSupportFragmentManager().beginTransaction().add(R.id.notification_container, fragment).commit();
}
}
#Override
protected void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
System.out.println("NotificationActivity#onSaveInstanceState");
fragment.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
System.out.println("NotificationActivity#onRestoreInstanceState");
getSupportFragmentManager().findFragmentById(R.id.notification_container).onActivityCreated(savedInstanceState);
}
}
All this together makes sure that you can test the saving and restoring of the state within a fragment with robolectric
Related
I am very very tired
I can't change visibility or an object in the fragment from the class controller
exmple addIteamsAutomatic.progressBar.setVisibility(View.GONE); return nullpointer
FragmentAddIteamsAutomatic :
public class FragmentAddIteamsAutomatic extends Fragment {
private EditText ssid, paswd;
public TextView afichage;
public Button parainage;
public Button validation;
public ProgressBar progressBar ;
public LinearLayout linearLayoutParm;
public static String sSSID,pWD;
private ControllerAddIteam controleAdd=null;
public FragmentAddIteamsAutomatic()
{
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.add_iteams_automatic, container, false);
controleAdd.getInstance(getActivity());
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
ssid = (EditText) view.findViewById(R.id.ssid);
paswd = (EditText) view.findViewById(R.id.password);
parainage = (Button) view.findViewById(R.id.btnParainage);
validation = (Button) view.findViewById(R.id.btnValid);
afichage = (TextView) view.findViewById(R.id.affichage);
linearLayoutParm = (LinearLayout) view.findViewById(R.id.linearLayParam);
progressBar.setVisibility(View.GONE);
afichage.setVisibility(View.GONE);
validation.setVisibility(View.GONE);
parainage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sSSID = ssid.getText().toString();
pWD = paswd.getText().toString();
if (sSSID.equals(""))
Toast.makeText(getActivity(), "Vous Dever Remplir Tous les champs", Toast.LENGTH_LONG).show();
else
parainer();
}
});
validation.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
controleAdd.addSwitchToBase();
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
ControllerAddIteam.accesDistant.send("getIteams", new JSONArray());
// finish();
}
});
return view;
}
private void parainer(){
controleAdd.getInstanceExecuteHandle();
}
}
ControllerAddIteam :
public class ControllerAddIteam {
private static ControllerAddIteam instanceAdd = null;
private static Context context;
private static WifiUtils wifiUtils;
public static String SSID = null;
public static AccesDistant accesDistant;
public static Handler mHandler;
public static final ControllerAddIteam getInstance(Context context) {
if (context != null)
ControllerAddIteam.context = context;
if (ControllerAddIteam.instanceAdd == null) {
ControllerAddIteam.instanceAdd = new ControllerAddIteam();
accesDistant = new AccesDistant();
}
return ControllerAddIteam.instanceAdd;
}
public static void getInstanceExecuteHandle() {
new ParainageHandle().execute();
}
static class ParainageHandle extends AsyncTask<String, String, String> {
FragmentAddIteamsAutomatic addIteamsAutomatic=new FragmentAddIteamsAutomatic();
#Override
protected void onPreExecute() {
super.onPreExecute();
addIteamsAutomatic.progressBar.setVisibility(View.GONE);
addIteamsAutomatic.afichage.setVisibility(View.GONE);
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
addIteamsAutomatic.progressBar.setVisibility(View.GONE);
if(s.equals("valid"))
{
addIteamsAutomatic.linearLayoutParm.setVisibility(View.GONE);
addIteamsAutomatic.validation.setVisibility(View.VISIBLE);
addIteamsAutomatic.parainage.setVisibility(View.GONE);
}
else if(s.equals("notvalid"))
{
addIteamsAutomatic.parainage.setVisibility(View.VISIBLE);
}
}
#Override
protected void onProgressUpdate(String... values) {
addIteamsAutomatic.afichage.setVisibility(View.VISIBLE);
addIteamsAutomatic.progressBar.setVisibility(View.VISIBLE);
if (values[0].equals("actwifi")) {
if (values[1].equals("true"))
addIteamsAutomatic.afichage.setText("WIFI DEJA ACTIVEE");
else
addIteamsAutomatic.afichage.setText("ACTIVATION WIFI EN COURS...");
} else if (values[0].equals("scan"))
addIteamsAutomatic.afichage.setText("START SCAN FOR Iteams STiTo ... Please Wait");
else if (values[0].equals("find"))
addIteamsAutomatic.afichage.setText("STiTo : "+getTypeFromSsid(SSID)+" DETECTEE : "+SSID);
else if (values[0].equals("connect"))
addIteamsAutomatic.afichage.setText("CONNECTION WITH " + SSID + "En cours ...");
else if (values[0].equals("connectOk"))
addIteamsAutomatic.afichage.setText("CONNECTION WITH " + SSID + "ETABLISHED");
else if (values[0].equals("connectKo"))
addIteamsAutomatic.afichage.setText("PROBLEM OF CONNECTION WITH " + SSID);
else if (values[0].equals("config")) {
addIteamsAutomatic.afichage.setText("SENDING OF CONFIGURATION TO: "+getTypeFromSsid(SSID)+"AND SAVING DATA");
accesDistant.sendConfig(addIteamsAutomatic.sSSID,addIteamsAutomatic.pWD);
....
You declare fragment in AsyncTask and doesn't call replace or add, it mean this fragment never show and it not call onCreateView
FragmentAddIteamsAutomatic addIteamsAutomatic=new FragmentAddIteamsAutomatic();
Maybe you should pass reference addIteamsAutomatic to class ControllerAddIteam. but please make sure it will be call on MainThread, because AsyncTask has method doInBackground in background Thread. best practice is wrap fragment reference by WeakReference
public class AddIteamActivity extends AppCompatActivity {
ViewPager pager;
TabLayout tab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_iteam);
pager = findViewById(R.id.pager);
tab = findViewById(R.id.tab);
AddIteamsAdapter viewPagerAdapter = new AddIteamsAdapter(getSupportFragmentManager());
pager.setAdapter(viewPagerAdapter);
tab.setupWithViewPager(pager);
}
}
I have this code. It displays two recycler views and wants to store their scroll positions for the rotation purpose. So here is my code.
public class DetailActivity extends AppCompatActivity implements DetailViewInteface, TrailersAdapter.TrailersAdapterOnClickHandler {
public static final String IMAGE_URL = "http://image.tmdb.org/t/p/";
public static final String IMAGE_SIZE_185 = "w185";
private static final String TAG = "DetailActivity";
private static final String TRAILERS_LAYOUT = "DetailsActivity.trailer.layout";
private static final String REVIEWS_LAYOUT = "DetailsActivity.review.layout";
private Parcelable listState1;
private Parcelable listState2;
#BindView(R.id.movie_image)
ImageView movieImageTv;
String mImage;
#BindView(R.id.movie_title)
TextView movieTitleTv;
String mTitle;
#BindView(R.id.movie_rating)
TextView movieRatingTv;
double mRating;
#BindView(R.id.movie_date)
TextView movieDateTv;
String mReleaseDate;
#BindView(R.id.movie_overview)
TextView movieOverviewTv;
String mMovieOverview;
#BindView(R.id.recyclerview_trailers)
RecyclerView trailersRecyclerView;
TrailersAdapter trailersAdapter;
#BindView(R.id.recyclerview_reviews)
RecyclerView reviewsRecyclerView;
ReviewAdapter reviewAdapter;
int recyclerViewOrientation = LinearLayoutManager.VERTICAL;
private Parcelable mLayoutManagerSavedState;
DetailPresenter detailPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
ButterKnife.bind(this);
setMVP();
setUpViews();
detailPresenter.getMovieDetails();
detailPresenter.getMovieTrailers();
detailPresenter.getMovieReviews();
}
public void setMVP(){
detailPresenter = new DetailPresenter(this,this);
}
public void setUpViews(){
trailersRecyclerView.setLayoutManager(new LinearLayoutManager(this));
trailersRecyclerView.addItemDecoration(new MyDividerItemDecoration(this,recyclerViewOrientation,16));
reviewsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
reviewsRecyclerView.addItemDecoration(new MyDividerItemDecoration(this,recyclerViewOrientation,16));
}
#Override
public void dipsplayMovieDetails(Movie movie) {
//movieTitleTv.setText(movieTitle);
//movieDescriptionTv.setText(movieDesc);
mImage = movie.getPosterPath();
Picasso.with(this).load(IMAGE_URL + IMAGE_SIZE_185 + mImage).into(movieImageTv);
mTitle = movie.getTitle();
movieTitleTv.setText(mTitle);
mRating = movie.getVoteAverage();
movieRatingTv.setText("Rating: " + mRating);
mReleaseDate = movie.getReleaseDate();
movieDateTv.setText("Date: " + mReleaseDate);
mMovieOverview = movie.getOverview();
movieOverviewTv.setText(mMovieOverview);
}
#Override
public void dipsplayMovieTrailers(TrailersResponse trailersResponse) {
Log.d(TAG, "Trailers size: " + String.valueOf(trailersResponse.getTrailers().size()));
trailersAdapter = new TrailersAdapter(trailersResponse.getTrailers(),DetailActivity.this,this);
trailersRecyclerView.setAdapter(trailersAdapter);
}
#Override
public void displayMovieReviews(ReviewsResponse reviewsResponse) {
if(reviewsResponse != null) {
Log.d(TAG, String.valueOf("Reviews size: " + reviewsResponse.getReviews().size()));
reviewAdapter = new ReviewAdapter(reviewsResponse.getReviews());
reviewsRecyclerView.setAdapter(reviewAdapter);
}else{
Toast.makeText(DetailActivity.this,"No reviews found!",Toast.LENGTH_LONG).show();
}
}
#Override
public void displayError(String s) {
showToast(s);
}
#Override
public void showToast(String s) {
Toast.makeText(DetailActivity.this,s,Toast.LENGTH_LONG).show();
}
#Override
public void onClick(String trailerKey) {
Intent appIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("vnd.youtube:" + trailerKey));
Intent webIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse("http://www.youtube.com/watch?v=" + trailerKey));
try {
startActivity(appIntent);
} catch (ActivityNotFoundException ex) {
startActivity(webIntent);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(REVIEWS_LAYOUT,reviewsRecyclerView.getLayoutManager().onSaveInstanceState());
outState.putParcelable(TRAILERS_LAYOUT,trailersRecyclerView.getLayoutManager().onSaveInstanceState());
}
}
I use the onSaveInstanceState method. But now I need to restore in the scroll positions of recyclerviews.
I am using the onRestoreInstanceState as below but nothing happens. The rotation gets me to the top of the screen.
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
savedInstanceState.getParcelable(REVIEWS_LAYOUT);
savedInstanceState.getParcelable(TRAILERS_LAYOUT);
}
So how can I fix that bug?
Thanks,
Theo.
void addOnScrollListener (RecyclerView.OnScrollListener listener)
This is a listener that will be notified of any changes in scroll state or position for a recycler view.
Refer to this answer for an example implementation.
For more on how to use it correctly.
I'm having difficulties with making my app persistent. When I rotate my phone the data on the screen doesnt change. But after I click on a button to retrieve a new fragment I get an error saying "Can not perform this action after onSaveInstanceState". I have googled and seen similiar problems but I still dont know how to approach and solve this.
I have an activity class, a controller class and two fragment classes.
The activity class has a navigationviewer with 2 buttons that triggers a fragmenttransaction. That is, on each button click it will replace the current fragment with the one set in the button listener. My controller class initalizes the system and the fragments are just the UI.
My activity class:
public class LoggedInActivity extends AppCompatActivity {
private final String TAG = "LoggedInActivity: ";
private Controller controller;
private TextView navName;
private NavigationView navigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logged_in);
if(savedInstanceState == null) {
Log.v(TAG, "savedInstanceState == null");
initComponents();
setNavName();
initListener();
initializeSystem();
} else {
Log.v(TAG, "savedInstanceState != null");
initComponents();
setNavName();
initListener();
this.controller = (Controller)savedInstanceState.getSerializable("controller");
}
}
private void initComponents() {
navigationView = (NavigationView) findViewById(R.id.navigation_view);
View headerView = navigationView.getHeaderView(0);
navName = (TextView) headerView.findViewById(R.id.tv_name_surname);
}
private void initListener() {
navigationView.setNavigationItemSelectedListener(new MyNavigationItemListener());
}
private void initializeSystem() {
Log.v(TAG, "new controller");
controller = new Controller(this, null);
}
public void setFragment(Fragment fragment) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container_logged_in, fragment).commit();
}
private class MyNavigationItemListener implements NavigationView.OnNavigationItemSelectedListener {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch(item.getItemId()) {
case R.id.drawer_summary:
controller.setFragmentSummary();
break;
case R.id.drawer_income:
controller.setFragmentIncome();
break;
}
return false;
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putSerializable("controller", controller);
super.onSaveInstanceState(outState);
Log.v(TAG, "onSaveInstanceState, saving the controller");
}
}
My controller class:
public class Controller implements Serializable {
private final String TAG = "Controller: ";
/********************** Fragments ***********************/
private Fragment_Income fragment_income;
private Fragment_Summary fragment_summary;
/********************************************************/
/********************** Activities **********************/
private LoggedInActivity logged_in_activity;
/********************************************************/
public Controller(LoggedInActivity logged_in_activity) {
this.logged_in_activity = logged_in_activity;
initLoggedInFragments();
setFragmentSummary();
}
}
/* Initializes fragments that are connected to LoggedInActivity */
private void initLoggedInFragments() {
fragment_income = new Fragment_Income();
fragment_income.setController(this);
fragment_summary = new Fragment_Summary();
fragment_summary.setController(this);
}
/* use to replace current fragment with the given one */
private void replaceFragmentWith(Fragment fragment) {
logged_in_activity.setFragment(fragment);
}
/***********************************************************
* METHODS REGARDING FRAGMENT INCOME *
**********************************************************/
public void setFragmentIncome() {
replaceFragmentWith(fragment_income);
}
/* Summary fragment is started at first */
public void setFragmentSummary() {
replaceFragmentWith(fragment_summary);
}
}
Fragment_Income:
public class Fragment_Income extends Fragment implements Serializable{
private final String TAG = "Fragment_Income: ";
private Controller controller;
private FloatingActionButton fab_income;
private ListView lv_income;
private ArrayList<LvData> incomeData;
private LvAdapterIncome lvAdapterIncome;
public Fragment_Income() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.v(TAG, "onCreateView");
View view = inflater.inflate(R.layout.fragment_income, container, false); // Inflate the layout for this fragment
if(savedInstanceState != null) {
this.controller = (Controller) savedInstanceState.getSerializable("controller");
}
initComponents(view);
initListener();
setupListView();
return view;
}
private void initComponents(View view) {
fab_income = (FloatingActionButton) view.findViewById(R.id.fab_income);
lv_income = (ListView) view.findViewById(R.id.lv_income);
}
private void initListener() {
ButtonListener buttonListener = new ButtonListener();
fab_income.setOnClickListener(buttonListener);
}
private void setupListView() {
if (incomeData == null) { // checks if incomeData have been initalized before, if so do not change array to defualt
incomeData = new ArrayList<>();
lvAdapterIncome = new LvAdapterIncome(getContext(), incomeData);
}
lv_income.setAdapter(lvAdapterIncome);
}
public void setController(Controller controller) {
this.controller = controller;
}
#Override
public void onSaveInstanceState(Bundle outState) {
Log.v(TAG, "onSaveInstanceState, saving the controller");
outState.putSerializable("controller", this.controller);
super.onSaveInstanceState(outState);
}
}
Fragment_Summary:
public class Fragment_Summary extends Fragment implements Serializable {
private static final String TAG = "Fragment_Summary: ";
private Controller controller;
private TextView tv_user;
private TextView tv_total_revenue;
private TextView tv_total_expenditure;
private TextView tv_balance;
private float totalRevenue;
private float totalExpenditure;
private float balance;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_summary, container, false);// Inflate the layout for this fragment
initComponents(view);
setUserName();
if(savedInstanceState == null) {
//DO SOMETHING
}
return view;
}
private void addData() {
totalRevenue = controller.getTotalRevenue();
totalExpenditure = controller.getTotalExpenditure();
balance = totalRevenue - totalExpenditure;
tv_total_revenue.setText(String.valueOf(totalRevenue));
tv_total_expenditure.setText(String.valueOf(totalExpenditure));
tv_balance.setText(String.valueOf(balance));
}
private void initComponents(View view) {
tv_user = (TextView)view.findViewById(R.id.tv_user);
tv_total_revenue = (TextView)view.findViewById(R.id.tv_revenue);
tv_total_expenditure = (TextView)view.findViewById(R.id.tv_sum_exp);
tv_balance = (TextView)view.findViewById(R.id.tv_balance);
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("revenue", String.valueOf(balance));
outState.putString("totalExpenditure", String.valueOf(balance));
outState.putString("balance", String.valueOf(balance));
super.onSaveInstanceState(outState);
}
public void setController(Controller controller) {
this.controller = controller;
}
}
I have removed all the header files and some methods from my classes becuase I tought they were not relevant for this problem.
Here is the error log:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1434)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1452)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:708)
at android.app.BackStackRecord.commit(BackStackRecord.java:672)
at com.example.user.my_app.LoggedInActivity.setFragment(LoggedInActivity.java:85)
at com.example.user.my_app.Controller.replaceFragmentWith(Controller.java:89)
at com.example.user.my_app.Controller.setFragmentIncome(Controller.java:99)
at com.example.user.my_app.LoggedInActivity$MyNavigationItemListener.onNavigationItemSelected(LoggedInActivity.java:127)
at android.support.design.widget.NavigationView$1.onMenuItemSelected(NavigationView.java:156)
at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:822)
at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:156)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:969)
at android.support.design.internal.NavigationMenuPresenter$1.onClick(NavigationMenuPresenter.java:342)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
This looks like an activity state loss. See this excellent article by Alex Lockwood entitled "Fragment Transactions & Activity State Loss". I refer to it time and again.
To quote the intro to the posting:
The following stack trace and exception message has plagued StackOverflow ever since Honeycomb’s initial release:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
This post will explain why and when this exception is thrown, and will conclude with several suggestions that will help ensure it never crashes your application again.
I have two variables to save on device orientation. One is current question number as an integer, another is a boolean value if the user cheated. It working fine when I'm storing and retrieving sinle value while storing both value it swaps.
public class QuizActivity extends ActionBarActivity {
// Logging members
private static final String LOG_TAG = QuizActivity.class.getSimpleName();
// The member of Saving the state for rotation of the device
private static final String KEY_BOOLEAN = "index";
private static final String KEY_INT = "index";
// Adding members
private Button mTrueButton;
private Button mFalseButton;
private Button mNextButton;
private Button mBackButton;
private TextView mQuestionTextView;
private ImageButton mImgRightButton;
private ImageButton mImgLeftButton;
private Button mCheatButton;
private TrueFalse[] mQuestionBank = new TrueFalse[] {
new TrueFalse(R.string.question_oceans, true),
new TrueFalse(R.string.question_mideast, false),
new TrueFalse(R.string.question_africa, false),
new TrueFalse(R.string.question_americas, true),
new TrueFalse(R.string.question_asia, true)
};
private int mCurrentIndex = 0;
// To save the CheatActivity returning result
private boolean mIsCheater;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(LOG_TAG, "QuizActivity onCreate(Bundle) called");
setContentView(R.layout.activity_quiz);
if(savedInstanceState != null) {
mCurrentIndex = savedInstanceState.getInt(KEY_INT);
mIsCheater = savedInstanceState.getBoolean(KEY_BOOLEAN);
}
}
// Not to loose activity state when rotating the device
#Override
public void onSaveInstanceState(Bundle saveInstanceState) {
Log.i(LOG_TAG, "onSaveIntanceState");
saveInstanceState.putInt(KEY_INT, mCurrentIndex);
saveInstanceState.putBoolean(KEY_BOOLEAN, mIsCheater);
super.onSaveInstanceState(saveInstanceState);
};
// to get the result from child activity(Cheat activity)
#Override
protected void onActivityResult(int arg0, int arg1, Intent data) {
if(data == null){
return;
}
mIsCheater = data.getBooleanExtra(CheatActivity.EXTRA_ANSWER_SHOWN, false);
};
}
You have the same keys!! Change code like this:
private static final String KEY_BOOLEAN = "index_BOOLEAN";
private static final String KEY_INT = "index_INT";
KEY_INT and KEY_BOOLEAN have the same name, the same value. So when setting those in the bundle one might overwrite the other. Use different names:
private static final String KEY_INT="intindex";
private static final String KEY_BOOLEAN="boolindex";
i wanna pass a string to all fragment(child) from fragment activity (main), may be this picture can explain what exactly what i want to do
https://dl.dropboxusercontent.com/u/57465028/SC20140205-163325.png
so, from above picture...i wanna pass a string from edittext by press a button to all activity in viewpager....how could i do that?
i tried to follow this code https://stackoverflow.com/a/12739968/2003393 but it can't solved my problem..
please help me...i'm stuck
thank in advance.
here is my code from fragment activity (MainActivity)
public class Swipe_Menu extends FragmentActivity {
//String KeyWord;
//private static final String KEYWORD = "keyword";
private ViewPager _mViewPager;
private ViewPagerAdapter _adapter;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.swipe_menu_image);
Button Back = (Button)findViewById(R.id.account);
ImageButton Search = (ImageButton)findViewById(R.id.search);
EditText Keyword = (EditText)findViewById(R.id.keyword);
final String KeyWord = Keyword.getText().toString();
/**
* Back button click event
* */
Back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
finish();
}
});
setUpView();
setTab();
}
protected void sendValueToFragments(String value) {
// it has to be the same name as in the fragment
Intent intent = new Intent("my_package.action.UI_UPDATE");
intent.putExtra("UI_KEY", KeyWord );
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
and here is my fragment (Child Activity)
public class Store_Swipe extends Fragment {
public static final String ACTION_INTENT = "my_package.action.UI_UPDATE";
String KeyWord;
private TextView kata_keyword;
protected BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(ACTION_INTENT.equals(intent.getAction())) {
String value = intent.getStringExtra("UI_KEY");
updateUIOnReceiverValue(value);
}
}
};
private void updateUIOnReceiverValue(String value) {
// you probably want this:
KeyWord = value;
}
public static Fragment newInstance(Context context) {
Store_Swipe f = new Store_Swipe();
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter(ACTION_INTENT);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(receiver, filter);
}
#Override
public void onDestroy() {
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
super.onDestroy();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/*Bundle bundle = this.getArguments();
KeyWord = bundle.getString("keyword");*/
View view = inflater.inflate(R.layout.store_swipe, container, false);
init(view);
return view;
}
void init(View view) {
kata_keyword = (TextView) view.findViewById(R.id.keyword);
//ImageView image = (ImageView) view.findViewById(R.id.image_error);
kata_keyword.setText(KeyWord);
}
}
You don't have access directly to your fragments that reside in ViewPager so you can't reference them directly.
What I am doing in these cases is send a broadcast message from Activity to Fragments. For this reason register a BroadcatReceiver in the fragment (either in onCreate or onCreateView - your decision)m, set a custom action for that receiver (ex. "my_package.actions.internal.BROADCAST_ACTION"), don't forget to unregister the receiver from complementary method.
When you want to send a message from activity, create an intent with above mentioned action, add the string in intent extra and send the broadcast.
In your receiver's onReceive method (within the fragment), get the String from intent paramter and there you have the string.
Makes sense?
EDIT: To provide some code, below are the changes that I would make for fragment:
public class Store_Swipe extends Fragment {
public static final String ACTION_INTENT = "my_package.action.UI_UPDATE";
protected BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(ACTION_INTENT.equals(intent.getAction())) {
String value = intent.getStringExtra("UI_KEY");
updateUIOnReceiverValue(value);
}
}
};
private void updateUIOnReceiverValue(String value) {
// you probably want this:
kata_keyword.setText(value);
}
String KeyWord;
private TextView kata_keyword;
public static Fragment newInstance(Context context) {
Store_Swipe f = new Store_Swipe();
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter(ACTION_INTENT);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(receiver, filter);
}
#Override
public void onDestroy() {
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
super.onDestroy();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle bundle = this.getArguments();
KeyWord = bundle.getString("keyword");
View view = inflater.inflate(R.layout.store_swipe, container, false);
init(view);
return view;
}
void init(View view) {
kata_keyword = (TextView) view.findViewById(R.id.keyword);
ImageView image = (ImageView) view.findViewById(R.id.image_error);
kata_keyword.setText(KeyWord);
}
}
And this code I would have from activity, the parameter is the value from EditText:
protected void sendValueToFragments(String value) {
// it has to be the same name as in the fragment
Intent intent = new Intent("my_package.action.UI_UPDATE");
intent.putExtra("UI_KEY", value);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
You would call this from the click listener that you would set in onCreate:
findViewById(R.id.button_id).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
String valueThatYouWantToSend = null; /// just the value
sendValueToFragments(valueThatYouWantToSend);
}
});
// I think this solution will solved your issue
// In Main activity put your code -----------------------------------
public void onPageSelected(int position)
{
System.out.println("nilesh");
PageOneFragment f = new PageOneFragment();
f.getText();
PageTwoFragment ff = new PageTwoFragment();
ff.setText();
}
//in General Class ------------------------------------------------
public class General
{
public static String name="";
}
// first Fragment ---------------------------------------------
public void getText()
{
General.name = edittext.getText().toString();
}
// second Fragment ----------------------------------------------
public void setText()
{
System.out.println("name**" + General.name);
tv.setText(General.name);
}