I have a few edittexts and textviews in Fragment A. I use edittexts to get value from user and then I calculate based on my formula and show the result in the textview.
For example Edittext A has 1 and Edittext B has 2 and my formula is add so the textview will show the result as 3.
Also, there is a button, info on the Fragment A. On clicking this button a new fragment B is displayed using the below code:
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.contentFrag, fragment);
fragmentTransaction.addToBackStack(backStackStateName);
fragmentTransaction.commit();
Now, when I press the back button, I let the system call super.onBackPressed() so that the current Fragment B is removed and I get back Fragment A.
MY PROBLEM:
Now the edittexts retain all their values, however the value I calculated and displayed on TextViews disappears.
I checked onViewStateRestored(Bundle savedInstanceState) method however the parameter savedInstanceState is null.
My Question:
How do I save/retrieve the values for my textviews?
OR
where can I call that code again that calculates the data for textviews ??
To resolve this problem you need use some data layer like Shared Preference or SQL storage. In some primitive way it looks like:
public override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater?.inflate(R.layout.fragment_blank, container, false)
//update you UI from local STORAGE on create
textView.text = sharedPreference.getInt("result_data", 0).toString()//make request local for date
//calling your calculation
button.setOnClickListener {
calculateData(dataInput, object : Callback {
override fun onCalculated() {
//update you UI from local STORAGE layer in MAIN THREAD
Handler(Looper.getMainLooper()).post {
textView.text = sharedPreference.getInt(
"result_data",0
).toString()
}
}
})
}
}
//you calculate and request to data layer
private fun calculateData(inPutData:Int, callback:Callback){
//use background for calculate It will be available.
Thread{
//calculate
val result = inPutData+ ARG_PARAM
//save result in LOCAL STORAGE
sharedPreference.edit().putInt( " result", result).apply()
//update UI
callback.onCalculated()
}.start()
}
interface Callback{
fun onCalculated()
}
Think also about MVP and Presenter. It is also be available.
In open source you can find amazing MVP library called moxy. It can restore your view state.
Using SharedPreferences will solve your issue just save them in sharedPreferences and clear SharedPreference after Use.
Related
I have activity one where having a empty textview user has click on this textview to select location from list of location so for that when user click on select location textview it will open list of location with checkbox.
When user select location(can select multiple location) and click on done then all selected location will be showing on activity one textView with all selected checked textview value now when user click on same textview to add more location then on recylerview list all previous checked item should be checked. I'm not getting all previous selected checkbox.
I'm not getting how to achieve this. I need all old checkbox should be selected and user can select some more new checkbox if click on same textview. Please help me to get this. Java code will be also helpful for me
Below is my recylerView Adapter code:-
class SelectMedicineAdapter (val medicineList : ArrayList<String>, val context: Context) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var selectedCheckBoxMedicineList : ArrayList<String> = ArrayList()
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemView.textViewSelectMedicineName.text = medicineList.get(position)
holder.itemView.checkboxSelectMedicine.setOnCheckedChangeListener { buttonView, isChecked ->
val itemText = medicineList.get(position)
if (isChecked) {
selectedCheckBoxMedicineList.add(itemText)
} else {
selectedCheckBoxMedicineList.remove(itemText)
}
}
}
fun getSelectedMedicineList(): ArrayList<String> {
return selectedCheckBoxMedicineList
}
override fun getItemCount(): Int {
return medicineList.size
}
override fun onCreateViewHolder(holder: ViewGroup, p1: Int): RecyclerView.ViewHolder {
val v= (LayoutInflater.from(context).inflate(R.layout.row_select_medicine_adapter,holder,false))
return ViewHolder(v)
}
class ViewHolder (itemView: View): RecyclerView.ViewHolder(itemView){
var textViewSelectMedicineName = itemView.textViewSelectMedicineName
var imageViewPlusButton = itemView.imageViewPlusButton
var imageViewMinusButton = itemView.imageViewMinusButton
var checkboxSelectMedicine = itemView.checkboxSelectMedicine
}
}
You need to update views with their items state (selected or not).
In onSaveInstanceState of your activity/fragment where your adapter is you should write adapters state (which items are selected (getSelectedMedicineList)) to the bundle.
Whenever your fragment/activity is restored just update adapter with data you saved restoreSelectedMedicineList(selectedCheckBoxMedicineList: ArrayList<String>)
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemView.textViewSelectMedicineName.text = medicineList.get(position)
holder.itemView.checkboxSelectMedicine.setOnCheckedChangeListener(null)
if(selectedCheckBoxMedicineList.contains(itemText)) {
holder.itemView.setChecked(true)
} else {
holder.itemView.setChecked(false)
}
holder.itemView.checkboxSelectMedicine.setOnCheckedChangeListener { buttonView, isChecked ->
val itemText = medicineList.get(position)
if (isChecked) {
selectedCheckBoxMedicineList.add(itemText)
} else {
selectedCheckBoxMedicineList.remove(itemText)
}
}
}
fun restoreSelectedMedicineList(selectedCheckBoxMedicineList: ArrayList<String>) {
this.selectedCheckBoxMedicineList = selectedCheckBoxMedicineList
notifyDataSetInvalidated()
}
When you start an Activity, it has no knowledge of what is the state of your data. You need to provide the state for the Activity.
By which I mean when the user selects his/her desired locations and goes back to Activity One you to need hold on to these selected locations and when the user again wants to update the locations you need to pass these previously selected locations to the second Activity and then update the RecyclerView's backing data accordingly.
in your list you do maintain a boolean field when select check box then selected position value is true, and notify data and inside onbind viewholder you check first which position is true. if true then show selected checkbox otherwise unselect.
When I'm working with an app, I have faced same problem and also I needed that checked boxes even app closes. So I used SharedPreferences to stored and retrieve values that will indicates states of check boxes then I can easily specify the states of all element. So if you face same problem and have no solution you can use this way.
Just update the model class with a flag on every tick and untick. Check this flag to tick and untick logic for restoring the checkbox state.
I have searched through StackOverflow, but have not found a proper answer yet.
I have created a ListView (iteration of a checkbox + itemview) and populated it through my customAdapter (which extends BaseAdapter).
I have a button which takes the values and print it on the screen via a Toast.
So far, so good.
Next step, I still have the button in the MainActivity, but the ListView is now in a child activity that I reach by clicking an image (ImageView placed in the MainActivity). I can still check the checkboxes, but I face two issues:
I am still not able to pass the values to the MainActivity, where they will be printed on screen (or manipulated)
As soon as I press the back button to go back to the MainActivity and I press again the image, every CheckBox that was checked is not checked anymore (they came back to default state)
I don't think that code is needed, as it comes from a standard implementation (ListView - customAdapter with ViewHolder implementation, ...), but in case just let me know.
Thanks a lot in advance!
You can put which checkboxes are checked into sharedpreferences. Then move the listview initialization code to Activity's onResume method.
Sample class to handle sharedpreferences data:
class DataHandler {
private final SharedPreferences dataStore;
DataHandler(Context mContext) {
dataStore = mContext.getSharedPreferences("appname", Context.MODE_PRIVATE);
}
int which() {
return dataStore.getInt("some_key",0);
}
void setCheckedItem(int itemwhat) {
dataStore.edit().putInt("some_key",itemwhat).apply();
}
}
For multiple values, you can put them into an array then convert them to string using toString() method and save. And, to get the values:
String x = "2,3,4,5"; //assume
String[] y = new String[]{x};
int checkablepositions = Integer.parseInt(y[0]); // y[0]....y[y.length-1]
Now, at MainActivity's onResume(), Assume that you have initialized ListView as 'mainList'.
CheckBox x1y2z3 = (CheckBox)mainList.getChildAt(new DataHandler(getBaseContext).which());
x1y2z3.setChecked(true);
And for Saving item,
I would recommend you to show them in an alert-dialog instead of in a Toast. Then set a Positive button to get the values from below code and save them.
Or, if you directly save the values from listview onClick :
mainList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
new DataHandler(getBaseContext()).setCheckedItem(position);
}
});
That's it. I'm really new at programming (as you can see my StackOverFlow rep) but hope it will be able to help you.
The main concept is to : store the value → get the value → parse the value → show it on UI.
I am experimenting with some Animations with Android. Basically I have this Activity that displays data from an API. Now when the user wishes to refresh that data, the Front card so to speak of which contains the data, flips to the Back card which is a loading animation. The process of loading new data kicks off and when it finishes the method that flips the so to speak of cards is called so the data can be revealed.
With my intentions in place I have everything explained above 99% complete. When I have the data from the API back in the UI thread controlling the cards, I am ready to update those interface objects that are part of the front card.
But setting the text of a TextView no longer works, and I'll explain that. When the front card gets switched out, the back card comes to the foreground, but when it comes time for the front card to come back with new data, it comes back with its default XML values. I can set TextViews all day of this front card but it return blank.
Code is below, please keep in mind I like to reverse engineer things like this. So some code I do not understand, it the sample from the Google Android development guide.
Here I set up some kind of fragment manager in the onCreate method of the Activity.
mShowingBack is a boolean for if the back card is the card in the foreground.
if (savedInstanceState == null) {
// If there is no saved instance state, add a fragment representing the
// front of the card to this activity. If there is saved instance state,
// this fragment will have already been added to the activity.
getFragmentManager()
.beginTransaction()
.add(R.id.container, new CardFrontFragment())
.commit();
} else {
mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
}
// Monitor back stack changes to ensure the action bar shows the appropriate
// button (either "photo" or "info").
getFragmentManager().addOnBackStackChangedListener(this);
Even if I do not update the textviews with the new data, the front card is still bare and empty. Below is the method that flips the the two cards:
public void onClickFlipCard(View view) {
if (mShowingBack) {
getFragmentManager().popBackStack();
return;
}
// Flip to the back.
mShowingBack = true;
// Create and commit a new fragment transaction that adds the fragment for the back of
// the card, uses custom animations, and is part of the fragment manager's back stack.
getFragmentManager()
.beginTransaction()
// Replace the default fragment animations with animator resources representing
// rotations when switching to the back of the card, as well as animator
// resources representing rotations when flipping back to the front (e.g. when
// the system Back button is pressed).
.setCustomAnimations(
R.animator.card_flip_right_in, R.animator.card_flip_right_out,
R.animator.card_flip_left_in, R.animator.card_flip_left_out)
// Replace any fragments currently in the container view with a fragment
// representing the next page (indicated by the just-incremented currentPage
// variable).
.replace(R.id.container, new CardBackFragment())
// Add this transaction to the back stack, allowing users to press Back
// to get to the front of the card.
.addToBackStack(null)
// Commit the transaction.
.commit();
}
When that method is called, even though it is not included the method call for retrieving the new data kicks off. I am not for sure if like creates a new instance of the card layout so when I try to modify it, I am not actually in touch with it or something?
When I get the data for example all I do is use the textview object used before the data load to set the new data:
textview_example.setText("Example, but nothing appears...")
Even further, when I call the textviews getText method, it returns text but none is shown...Any help would be great. If it matters here are the two fragment classes that represent the two cards,(The front where data is displayed and the back where a loading animation is shown).
Front Card Fragment:
public class CardFrontFragment extends Fragment {
public CardFrontFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_card_front, container, false);
}
}
Back Card Fragment
public class CardBackFragment extends Fragment {
public CardBackFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_card_back, container, false);
}
}
Here is also a link to the Android guide that I follow...
Animation works but afterward as I said I can not edit any of the properties of interface objects. Thanks!
Link: http://developer.android.com/training/animation/cardflip.html
I have a frame layout in an activity to which i want to display different fragments inside. I have a sliding drawer with 3 options, each of which lead to a fragment being loaded inside the frame layout. Currently i use the following to accomplish this:
Fragment nextFragment = determineFragmentToSwitchTo(nextFragmentTag);
transaction.replace(R.id.fragment_container, nextFragment);
The first method determines what fragment i need by evaluating the nextFragmentTag string and loading a new fragment like so:
if (fragmentTag.equals(Constants.STUDENTPAGE))
nextFragment = new StudentFragment();
else if (fragmentTag.equals(Constants.TEACHERPAGE))
nextFragment = new TeacherFragment();
else if (fragmentTag.equals(Constants.PARENTPAGE))
nextFragment = new ParentFragment();
Clearly this approach is creating a new fragment each time and running through the whole fragment lifecycle without saving state. So if i am on the student page and scrolling through the student list and i switch to the parent page, when i go back to the student page, it reloads the entire list (i am fetching it from a server) and looses my place in it. How can i get it to persist state and sort of cache that fragment in the manager (if that makes sense)?
You could use the FragmentTransaction's hide(Fragment) and show(Fragment) methods, e.g.:
// In the parent Activity
StudentFragment studentFragment;
TeacherFragment teacherFragment;
ParentFragment parentFragment;
Fragment fragmentOnDisplay;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Initialize fragmentManager, fragmentTransaction, etc.
studentFragment = (StudentFragment) fragmentManager.findFragmentByTag(Constants.STUDENTPAGE);
if (studentFragment == null) {
studentFragment = new StudentFragment ();
fragmentTransaction.add(R.id.your_frame_layout, studentFragment, Constants.STUDENTPAGE);
}
// repeat the same procedure for the other two fragments
// Suppose you want to begin with the teacherFragment on
// display - in that case hide the studentFragment and
// the parentFragment:
fragmentTransaction.hide(studentFragment);
fragmentTransaction.hide(parentFragment);
fragmentOnDisplay = teacherFragment;
fragmentTransaction.commit();
}
Now whenever you need to switch your fragments, simply hide the fragment on display, and show the fragment you need, e.g.:
...
fragmentTransaction.hide(fragmentOnDisplay);
fragmentTransaction.show(parentFragment);
fragmentOnDisplay = parentFragment;
fragmentTransaction.commit();
A navigation drawer with 3 Fragments, the third Fragment has a TextView with an on Click listener. Once it has been clicked a layout activity will open on the top which includes a ListView to allow the user to select/click on a specific Item, so later on this selected item info should be displayed on that TextView within the third fragment.
is there any method to pass data because I have used a class to pass data but the TextView wouldn't be refreshed with the sent data
This works as a design pattern to share arguments between the Activity and third fragment
--------------------------DataHolder Class---------------------------------
public class DataHolder {
private static DataHolder dataHolder = null;
private DataHolder() {
}
public static DataHolder getInstance() {
if (dataHolder == null)
{dataHolder = new DataHolder(); }
return dataHolder;
}
private String item;
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
}
If you find using startActivityForResult not sufficient in your case or using EventBus, you can overcome this by using SharedPreferences and Fragment/Activity lifecycle.
So once you start new Activity first Activity will go in onPause and with it all its Fragments will be put in onPause. When user clicks on one of the ListView items in your second Activity you can store the value inside SharedPreferences like:
PreferenceManager.getDefaultSharedPreferences(SecondActivity.this)
.edit().putString(key, selectedItemInfoHere).apply();
Then override inside your first Activity and in your third Fragment method onResume() and inside just make checking:
#Override
public void onResume() {
super.onResume();
String value = PreferenceManager.getDefaultSharedPreferences(getContext())
.getString(key, "");
if (value != null && !value.isEmpty()) {
//You have selected item value update TextView
}
}
Note that once you don't need this value you will need to remove it, because it will update your TextView every time when onResume is called. To remove value just call:
PreferenceManager.getDefaultSharedPreferences(getContext()).edit().remove(key);
If I understood you correctly, you have flow 3rd fragment --> activity which should update fragment which launched it. In this case, as for me, the most clean solution is from your opened activity call startActivityForResult method to call activity-host of your fragments and handle all what you need in overridden onActivityResult method. Than just call your fragment's updateTextView() or something like that.
On the other hand you can use this library to send messages between components, but you should be careful with usage and think about corner cases related to components lifecycle.
So, choose solution according your needs:)