I'm trying to completely replace a fragment back stack with one I generate based on some information returned via a network connection. I first pop the back stack to the position I want (that works fine... but lets say I pop to the root for simplicity), and then I try to build and apply a fragment stack like this:
ArrayList<JSONObject> crumbsOut = new ArrayList<JSONObject>(count);
//.... pop the back stack to a certain point
//replace entire nav. backstack
final FragmentTransaction transaction = this.getActivity().getSupportFragmentManager().beginTransaction();
for(int i = 0; i<count; i++)
{
final JSONObject item = crumbsOut.get(i);
final String id = item.getString("id");
FolderFragment currentFolder = new FolderFragment();//fragment displays folder contents
Bundle args = new Bundle();
args.putString(DATA_ITEM_ID_KEY, id);
args.putString(DATA_ITEM_NAME_KEY, item.getString("displayname"));
currentFolder.setArguments(args);
transaction.replace(R.id.MasterContainer, currentFolder);
transaction.addToBackStack(id);
}
// Commit the transaction
transaction.commit();
When I run this, the top-most FolderFragment is displayed properly, but when I hit the back button (or pop the stack), the view reverts to the point immediately before the above code is run (i.e. instead of going back in the stack of new fragments I created with the loop, I go back to the state just before trying to add/create this stack).
If it helps, I'm using the Android Compatibility Package in my project.
Please help. Thanks
I found the answer. You have to create unique transactions for each new fragment you want to add to your stack. I originally thought this wouldn't be necessary, but I guess this is not so. So, here is the answer:
ArrayList<JSONObject> crumbsOut = new ArrayList<JSONObject>(count);
//.... pop the back stack to a certain point
//replace entire nav. backstack
for(int i = 0; i<count; i++)
{
//move the transaction into the loop
final FragmentTransaction transaction = this.getActivity().getSupportFragmentManager().beginTransaction();
final JSONObject item = crumbsOut.get(i);
final String id = item.getString("id");
FolderFragment currentFolder = new FolderFragment();//fragment displays folder contents
Bundle args = new Bundle();
args.putString(DATA_ITEM_ID_KEY, id);
args.putString(DATA_ITEM_NAME_KEY, item.getString("displayname"));
currentFolder.setArguments(args);
transaction.replace(R.id.MasterContainer, currentFolder);
transaction.addToBackStack(id);
// Commit the transaction
//move the commit into the loop
transaction.commit();
}
could it be that you are doing everything in the same method and your beginTransaction() call is cancelling the pop (the FragmentManager no doubt begins a transaction to do the pop).-
I would suggest doing the clean up yourself using the same FragmentTransaction and only performing a single commit. Alternatively you could post your replacement calls into the main threads message queue so that it is performed 'later,.
As you're using the compat library you can always debug the source to see what's going on.
Related
I am attempting to Dynamically Create Buttons to match Data Table. I was using this answer as a reference point however I keep getting this error code: Button (android.content.Context) in Button Cannot be Applied to (Java.lang.Object)
I tried multiple things to alleviate error code but I am at a loss of how to fix it I attempted to set the Map to an array but that isn't working either. The Code has successfully counted, and displayed the data but I cannot get it to add the Needed Buttons.
Backendless.Data.of( "Store" ).find( queryBuilder, new AsyncCallback<List<Map>>()
{
#Override
public void handleResponse( List<Map> response )
{
int numBrands = response.size();
Button brandButtons[] = new Button[numBrands];
System.out.println("The Count of Buttons:" + numBrands);
ArrayList<Brands> productList = new ArrayList<>();
Object[] arrayList = {response};
for(int i = 0; i < brandButtons.length; i++)
{
Button brans = new Button(productList[i]);
brans.setOnClickListener();
add(brans);
brandButtons[i] = brans;
//Object element = thisIsAStringArray[i];
System.out.println( "List of Brands" + response );
}
}
Your error is here in this line:
Button brans = new Button(productList[i]); // here
Button class expects Context passed to it's constructor invocation & you're passing Object type instead.
Use like this,
Button brans = new Button(context); // here context can be activity or fragment.
//now use this brans object to set property to your programmatically created Button,
//don't forget to add it to your parent view afterwards
As you can see in Button documentation, to create a button object you need to pass a Context object. In your code your passing an object of Brands which is causing the problem.
The solution would be to pass a context to the Button(Context) constructor. If your in an Activity it would be something like new Button(YourActivity.this), in a fragment you could use new Button(getContext())
I'm attempting to update and ArrayList with new data - however when I attempt to do so - the previous data is overwritten and the new data is never appended to the list and I'm unsure why.
if (keylist.contains(item) && !value.equals("start") && !value.equals("stop") && !value.equals("done")){
final Integer number = keylist.indexOf(item);
this.runOnUiThread(new Runnable() {
#Override
public void run() {
// GET PREVIOUS VALUES
String previous_key = App.sharedPrefs.getString(LIVE_VIEW_KEY, null);
String previous_value = App.sharedPrefs.getString(LIVE_VIEW_VALUE, null);
// ADD NEW AND OLD VALUES TO LIST
mCallElements.clear();
mCallElements.add(new CallElement(previous_key, previous_value));
mCallElements.add(new CallElement(valuelist.get(number), value));
mAdapter.notifyDataSetChanged();
// SAVE NEW VALUES
final SharedPreferences.Editor editor = App.sharedPrefs.edit();
editor.putString(LIVE_VIEW_KEY, valuelist.get(number));
editor.putString(LIVE_VIEW_VALUE, value);
editor.apply();
}
});
}
P.S.
This is part of a Twilio chat implementation - full source can be seen here: https://pastebin.com/imNQdCJ0
You're clearing the ArrayList with .clear() and then calling .notifyDataSetChanged(). This clears the last set of data, and then forces the Adapter to completely reload.
To make your life as a developer significantly easier, I would recommend using DiffUtil. This calculates the difference between two lists, and deals with the changes for you. A really good tutorial on how to implement this can be found on CodePath: https://guides.codepath.com/android/using-the-recyclerview#diffing-larger-changes
The Android Documentation says:
'rely on notifyDataSetChanged() as a last resort'
But because of DiffUtil you don't have to care anymore.
I think if you just want to add new data to the old data,you do not need to use mCallElements.clear(),if the notifydatachanged still doesn't update the data, I think you need to check if your ArrayList is the same object.I hope my answer will help you
I got a few fragments and I tried to sort out the transitions between them. I can go from Main -> A -> B -> C. Then once I am done with the stuffs in C, I wanted to go back to B -> A -> Main. This is the desired transition I wanted to achieve.
However, with my current code, there is something weird with the transition. I go from Main -> A -> B -> C, then inside C I am doing some SQL to create data in database, once I am done, I go back to B. However, from there, when I pressed back button, it go back to C -> B -> A -> Main. There is an unnecessary C in the back transition.
Inside my Main, I am calling A like this:
final SettingActivity settingFragment = new SettingActivity();
ft.replace(R.id.frame,settingFragment);
ft.addToBackStack("tag");
ft.commit();
Inside my A, I am calling B like this:
FragmentTransaction it = getFragmentManager().beginTransaction();
it.replace(R.id.frame,categoryFragment);
it.addToBackStack("tag");
it.commit();
Inside my B, I am calling C like this:
FragmentTransaction et = getFragmentManager().beginTransaction();
et.replace(R.id.frame,editFragment);
et.addToBackStack("tag");
et.commit();
Then inside my C, when I am successfully inserted a record in database, I am calling B like this:
// button on click listener
new GetAllCategoriesAsyncTask(
new GetAllCategoriesAsyncTask.OnRoutineFinished() {
public void onFinish() {
Bundle bundle = new Bundle();
FragmentTransaction it = getFragmentManager().beginTransaction();
bundle.putSerializable("categorylist", GetAllCategoriesAsyncTask.categoryList);
categoryFragment.setArguments(bundle);
it.replace(R.id.frame,categoryFragment);
it.addToBackStack("tag");
it.commit();
}
}).execute();
Then inside my B, I am getting the data like this in onCreate():
categoryList = (ArrayList<Category>) getArguments().getSerializable("categorylist");
On button click pop the current fragment from stack which is C , instead of adding B again in the stack. So, replace the button onClick code with below line of code:
getFragmentManager().popBackStack();
Bundle is used when you transferring data to next screen. For transferring data to previous screen you need to use callbacks.
For reference please find below attached link :Communicating with Other Fragments
I am pretty new to the android programming thing so I am sure that there is something simple that I am not doing correctly. I have two activities that are accessing a shared preferences file; one that reads from it and displays the contents to a list, and the other reads the contents and adds a new item to the list based upon user input. The issue that I am having: when I read the contents from the file and attempt to add a new item to the list my original list is being overwritten. Maybe that's not what is actually happening but it seems to me that this is the issue. When I add a new item from the child activity and return to the original activity which displays the list I am only seeing the newest item that I saved and not any other items that I had created previously.
The first activity is the main activity and it simply reads the shared preferences and adds the items in a string, separated by commas and displays them in a listview. I only showed the method for displaying the items in the listview:
public void addItems() {
String strLists="";
SharedPreferences sharedPref = getSharedPreferences("ListNamesFile", MODE_PRIVATE);
strLists = sharedPref.getString(getString(R.string.edit_message), strLists);
listItems.clear();
String[] myLists = strLists.split(",");
for(int r=0;r<myLists.length;r++){
listItems.add(myLists[r].toString());
}
//test adding an extra list item to make sure that the issue isn't from adding
//items to the listview
//listItems.add("test");
adapter.notifyDataSetChanged();
}
The second activity is for getting a new string from an edit text control, adding it to the end of the string with the list items, and committing the changes to the shared preferences file. I have included the method for retrieving the shared preferences and writing the new item to the end of the string:
public void createListOnClick(View view){
//create a file for storing list items
SharedPreferences sharedPref = getSharedPreferences("ListNamesFile", MODE_PRIVATE);
String strMyLists = "";
sharedPref.getString(getString(R.string.edit_message),strMyLists);
String[] lists = strMyLists.split(",");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lists.length; i++) {
sb.append(lists[i]);
sb.append(",");
}
SharedPreferences.Editor editor = sharedPref.edit();
EditText txtListName = (EditText)findViewById(R.id.txt_list_name);
sb.append(txtListName.getText().toString());
editor.putString(getString(R.string.edit_message), sb.toString());
editor.commit();
finish();
}
It seems that it must be something simple that I am missing but I haven't been able to figure it out so any help would be greatly appreciated.
you still haven't assigned the sharedPref value in the strMyLists.
In the second activity, you're not actually assigning the read shared preferences:
You should replace
sharedPref.getString(getString(R.string.edit_message),strMyLists);
with
strMyLists = sharedPref.getString(getString(R.string.edit_message),strMyLists);
in getString method
sharedPref.getString(getString(R.string.edit_message),strMyLists);
second argument is default value. So you will have to write.
strMyLists = sharedPref.getString(getString(R.string.edit_message),"");
it will work with this. rest of the code is good.
//first i have this method , below is my question
public void addrows(){
TableRow fila;
tabla = (TableLayout)findViewById(R.id.tabla);
TextView txtNombre;
for(int i = 0;i<id;i++){
String x[] = helper.leer();
layoutFila = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT);
caja= new CheckBox(this);
fila = new TableRow(this);
fila.setLayoutParams(layoutFila);
txtNombre = new TextView(this);
txtNombre.setId(i);
txtNombre.setTextSize(17);
txtNombre.setText(x[i]);
txtNombre.setGravity(Gravity.LEFT);
// txtNombre.setLayoutParams(layoutTexto);
caja.setText("");
caja.setId(i);
fila.addView(txtNombre);
fila.addView(caja);
tabla.addView(fila);
}
}
i know that when the oncreate() method start the checkboxes objects are created and then i assign an numerical id from 0 to wherever the for cycle stop , but later in the program i need to retrieve what checkboxes were clicked so first i need the id but eclipse wont let me put the numerical id, please help! and sorry for my English i'm a noob in android and the English language
this.CheckBox = (CheckBox)this.findViewById(R.id.?);
As You may read in View class documentation ID should be unique within a tree You search.
You set same id for TextView and Checkbox.
If You know You are going to access them all later after creation keep references to them in array instead of trying to retrieve them later using findViewById.
But even better solution would be to set onClick event listener for them and keep track of checking/unchecking them.
In #HalR's answer You may read how to set onCheckedChanged event listeners for Your checkboxes. Folowing his solution will have an ArrayList of checked checkboxes.
Next step, You have to increment values of correct TextView so You need to couple CheckBoxes and TextViews.
I think best for this would be to set Tag for CheckBox with value of TextView id.
So after user submits You iterate over List of checkboxes, getTag and use it in findViewById to get TextView and update its value.
Id (short for IDentifier) is an integer to uniquely identify elements, You can use it in findViewById to get view elements. You can read more about ID in this answer
Tag is used to associate View element with some extra data as You may read in getTag documentation. It takes as parameter Object type so You set as tag anything not only numbers. In Your case You could set as ChechBox's tag a TextView instead of its id and it will work too.
You are manually setting your id to the index of the row, which is something I don't think I'd do. I'd normally use setTag() to identify my object.
I think it would be easier to use a listener to detect when the checkboxes have been checked, and you can track the changes when the check happens.
use something like this:
In your Activity, create a ArrayList
ArrayList<CheckBox> checkedBoxes = new ArrayList<CheckBox>();
then in your creation:
caja= new CheckBox(this);
caja.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
int index = buttonView.getId();//pulling back the number you set originally, if that is what you like. I would get the tag I had set, and maybe do something here.
//do something in here!
if (buttonView.isChecked()) {
//including this checkbox in the list of checked boxes
checkedBoxes.addObject(buttonView);
} else {
//take it out of the list of checked boxes
checkedBoxes.removeObject(buttonView);
}
}
};
Some info on Id vs Tag
Id is a numeric value that identifies the view in the view hierarchy. If you are using things in your layout, like aligning one view with another, they look for and expect a view with a specific id. So in layout, you'll have android:id="#+id/bigBox" and that will create some number that it associates with bigBox. When you find that view, with findViewById() that is the number it is looking for. When you manually set those numbers, it seems like you are asking for trouble. If you set a view's id to 2, then you should be able to find it with myView = findViewById(2).
Tag is a nicely little object pointer that you can pass along with your view. Quite often it will be a row number:
Integer saveMe = new Integer(i);
checkBox.setTag(saveMe);
Or it can even be a pointer to your original data object that you used to create that row. If you had created each row using a contact, you could use
myRow.setTag(contact)
and later when you clicked on that row, you would just use
contact = (Contact)myRow.getTag()
and you would have your original contact back. Its way cleaner than keeping big arrays of your rows or checkboxes, or whatevers. Just use listeners that detect when you do something, that is a much better way.
Oh, and if you if you do have an onClick(View view) that is triggered by your CheckBox, that view IS your CheckBox.
CheckBox theBoxIJustChecked = (CheckBox)view;
You don't need to look it up with some id. It's right there.
If you want to go this way than you should just do the apposite operation i.e.:
for(int i = 0; i < n; ++i){
...
...(CheckBox))this.findViewById(i);
...
}
It should work for you
However be careful as if you have number of views with the same id inside the view-tree than findViewById(i) can return an unexpected result such as returning the first view in view-tree with given id (it can be not of CheckBox type which can lead to ClassCAstException)
Update in reply to comment
If you want to make some sort of logical connection CheckBox-TextView there are several options:
You can make a sort of function like the following (assuming that there is the limit of CheckBoxes and TextViews quantity):
Code:
private static int CHECK_BOX_MAX_NUMBER = 10000;
public void int getTextVieIdByCheckBoxId(int checkBoxId){
if(checkBoxId >= CHECK_BOX_MAX_NUMBER){
// you can throw an exception here for example
}
return CHECK_BOX_MAX_NUMBER + checkBoxId;
}
And then you should set id's to your TextViews with that function.
checkBox.setId(i);
textView.setId(getTextVieIdByCheckBoxId(i));
....
// add Views to your layout
....
(CheckBox)this.findViewById(i);
TextView)this.findViewById(getTextVieIdByCheckBoxId(i));
or
2.I think there is a little bit more accurate method:
Just use setTag() of CheckBox instances to set appropreate TextView inside in order to create interconnection. In thiscase you have to store all the created checkBoxes in some List or array:
List<CheckBox> checkBoxList = new ArrayList<CheckBox>();
for(int i = 0; i < n; ++i){
...
CheckBox checkBox = new CheckBox();
TextView textView = new TextView();
checkBox.setTag(textView);
checkBoxList.add(checkBox);
}
Then you can achieve what you want like this:
int textBoxListSize = checkBoxList.size();
for(int i = 0; i < textBoxListSize; ++i){
CheckBox checkBox = checkBoxList.get(i);
if(chechkBox.isChecked()){
TextView textView = (TextView)checkBox.getTag();
//do whatever with textView
}
}
Here you don't need to generate id's and worry about collisions which could accure