Problem by refreshing listView: UnsupportedOperationException - java

why I am not able to refresh/reload my ListView?
I' ve written an app, which read out html-websites, saves the information in a String-Array an present it in my ListView.
But every time I close and re-open the app the new content append to the existing content.
In the first run I get, e. g., "1 2 3 4" and in the second run "1 2 3 4 1 2 3 4" and then " 1 2 3 4 1 2 3 4 1 2 3 4" and so on.
I google a lot and find the methods to clear an ArrayAdapter (aa) and refill it with new data
-> aa.clear() / aa.setModifyDataChanged() / aa.remove(String Object) / aa.add(String)
but every time i call one Method my app does a force close and LogCat shows the exception: java.lang.UnsupportedOperationException.
Why? This really grind my gears. The whole saturday afternoon I try to fix it -without success...
Maybe somebody could help me!?
Here is my code-snippet
public class viewband extends ListActivity
{
private static final int AKTUALISIEREN = 0;
private static ArrayList<String> al = new ArrayList<String>();
static String[] TST;
static ArrayAdapter<String> ad;
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case AKTUALISIEREN:
getIt();
//here a ad.close does a force close
setListAdapter(ad);
ListView lv = getListView();
lv.setTextFilterEnabled(true);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getIt();
ad = new ArrayAdapter<String>(this, R.layout.list_item, TST);
setListAdapter(ad);
// here, when I try ad.clear() my app does a force close
ListView lv = getListView();
lv.setTextFilterEnabled(true);
}
public static void getIt ()
{
// Here I get the source-code of the html-site and parse my content
// All necessary Information I write in my String Array TST, declared above in a static way
}
I hope, somebody could help me...
Many thanks and a nice sunday.

You need to back your ArrayAdapter by an ArrayList or List rather than a fixed-size array - the size of a fixed-size array cannot change nor can it be cleared (without re-creating it, which defaults the whole purpose)
So set the content of your adapter to be al rather than TST (on a side note you really need to name your variables with sensible names). Then call al.clear(), al.add(String), to modify the dataset backing the adapter. Once the dataset has been changed call ad.notifyDataSetChanged - this will then cause the list adapter and list to update the display.
Also your adapter should not be static (nor should TST or al probably) -- this will lead to memory leaks since the Adapter holds onto a reference to the current Context. NEVER make anything with a context static (drawables, adapters etc). Unless you know why you want something to be static keep it as a normal variable.

clear TST all time you update your listview

Related

Android - Open Activity and wait for Result before continue in calling class

I am quite new to Android and facing a problem.
I have a class A from where i would like to call another activity. I have found some post saying that there is no way to pause the calling Activity and wait for the result.
public class A extends AppCompatActivity {
[...]
private List<String> list = new ArrayList<String>();
private void doSomething() {
list.add("a");
list.add("b");
for(String tmp:list) {
Intent intent = new Intent(this, OtherActivity.class);
intent.putStringExtra("TAG", tmp);
startActivityForResult(intent, 1);
}
}
This is not a complete example but basically the problem I am facing.
I have a loop and try to open another activity. The loop does not stop when i start the "OtherActivity".
The first thing i see is the OtherActivity for the last element of the list (here String "b"). When i finish this Activity i see the OtherActivity with String "a" in wrong order.
I considered a callback for this, but i am not sure how to implement it because the callback handler wouldn't be within the loop.
Again I am not sure if a callback would be a good idea because many people say i should not Pause the "calling" activity for the sub activity.
You are doing it totally wrong, if you want to send data to other activity and do some work then get the result , i would prefer that send the whole data as a list , do the work and then get the data from that activity , you shouldn't be doing it in a loop. Either pass it is as intent or save it in database then retrieve from database.
If you want to pass the whole list of string to the other activity I suggest you do this
You can pass an ArrayList the same way, if the E type is
Serializable.
You would call the putExtra (String name, Serializable value) of
Intent to store, and getSerializableExtra (String name) for retrieval.
Example:
ArrayList<String> myList = new ArrayList<String>();
intent.putExtra("mylist", myList);
In the other Activity:
ArrayList<String> myList = (ArrayList<String>) getIntent().getSerializableExtra("mylist");
Please note that serialization can cause performance issues: it takes time, and a lot of objects will be allocated (and thus, have to be garbage collected)
Source: Passing a List from one Activity to another
Or as Abdul suggested, save the data to a database and retrieve it from there in the other activity

Android Toolbar item OnClickListener

I have a Toolbar and an item (add) which, when clicked, adds a view in listView below. However, the onOptionsItemSelected gives you the effect of a single click so it only adds one view, and in my case, I need multiple views, thus multiple clicks are required. How do I set up everything so that the item behaves as an onClickListener rather than a single click?
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.addButton){
final TextView noProject = (TextView) findViewById(R.id.NOPROJECT);
final ArrayList<String> listItems=new ArrayList<String>();
final ListAdapter addAdapter = new ArrayAdapter<String>(this,
R.layout.list_item, R.id.listFrame, listItems);
final ListView lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(addAdapter);
noProject.setVisibility(View.GONE);
lv.setVisibility(View.VISIBLE);
listItems.add("New Project");
((ArrayAdapter) addAdapter).notifyDataSetChanged();
}
if (id == R.id.addPeople) {
return true;
}
return super.onOptionsItemSelected(item);
}
Android is always listening for menu item clicks. And on click your action will happen, so you'll need to click multiple times anyways if you want this add feature in the menu.
I usually setup my list adapter in onCreate or onCreateView. Once it's established you can do addAdapter.clear() and addAdapter.add(item). You shouldn't need to reference your listitems directly since the ArrayAdapter.add() method is setup to append to that list anyways and then if i'm not mistaken you can get rid of notifyDataSetChange() - I've never had to use this method with any of the default list adapters or the custom adapters I've written. .clear(), .add(), .insert(), and .remove() should be sufficient.
My listview is usually filled out using a for loop. If you want multiple views added then could you just setup a loop instead of waiting/requiring for more clicks?
Maybe I'm not fully understanding the usecase but a basic for loop seems like the answer here.
Edit:
//For Each Loop - "For each individualItem in itemHolder"
listadapter.clear();
for(ItemType individualItem : itemHolder){
listAdapter.add(individualItem.getText());
}
or you can do a traditional for loop
//"For i(index) starting at index 0, run until index < itemHolder.getItemCount() is false"
//for(initialize index variable : condition check : increment after each iteration)
for(int index =0; index<itemHolder.getItemCount(); index++)
{
listAdapter.add(itemHolder.getItemAt(index));
}
Something like that. I made up method names obviously it's going to depend on your data structures.

ConcurrentModificationException when updating a ListView that extends an ArrayAdapter

I have a ListView that has an adapter for each cell. This adapter extends ArrayAdapter (my object class).
The app also has 2 tabs so far. Tab 1 has the list, Tab 2 has a map.
Each cell in the adapter has a check box, which by default is unchecked.
The issue I have is that when I switch tabs the checked boxes return to be unchecked, and if I iterate an array of checked boxes, I get a ConcurrentModificationException when I try to mark a box as checked.
Here's my code of what I'm doing:
value_checkBox.setTag(route.getRouteShortName());
value_checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
if (isChecked == true)
{
RouteListLV.arrayOfRoutesEnabled.add(route);
}
else
{
RouteListLV.arrayOfRoutesEnabled.remove(route);
}
}
});
This part (the sample code above) works, and here's where I am adding the checked routes into a static array that can be used by multiple views.
for (Route routeFromArray : RouteListLV.arrayOfRoutesEnabled)
{
if (routeFromArray.getRouteShortName().equals(route.getRouteShortName() ) )
{
System.out.println("Match!");
value_checkBox.setChecked(true); <--- causes the ConcurrentModificationException
}
}
This loop (the for loop above) doesn't work and the exception happens when I set the checkBox to be checked.
I have tried an iterator as well, but I get the same result:
Iterator<Route> iterator = RouteListLV.arrayOfRoutesEnabled.iterator();
while (iterator.hasNext())
{
Route routeFromArray = iterator.next();
if (routeFromArray.getRouteShortName().equals(route.getRouteShortName() ) )
{
System.out.println("Match!");
value_checkBox.setChecked(true); <--- causes the ConcurrentModificationException
}
}
Any suggestions on this issue?
Just to be clear, I have 2 arrayLists. One is the one with the entire list of "routes" which is being passed to the ArrayAdapter to form the list, and I have a second arrayList for just the selected routes.
You're iterating through an ArrayList (RouteListLV.arrayOfRoutesEnabled) and modifying it as you go along. That causes a ConcurrentModificationException, so don't do it. Find a way to first find the element or elements you're going to modify and then modify the list (and never use the iterator after that). Or build a "shadow" copy of the list that has everything you want it to have at the end, then call removeAll() and then addAll(newList) on the original list, which is just that same idea in a slightly different form.

Adding an array of views crashes an android app

I have some part of an android app here which crashes for no apparent reason.
RL0 happens to be some LinearLayout defined in XML which already contains some other irrelevant stuff. To be honest with you, I've mostly worked with C++ so I might not initially know much about why some things are done significantly different in android, but I'm putting effort. Any help on how I can fix that crash?
Error message states NullPointerException.
Thanks.
public class Osteoporoza extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_osteoporoza);
LinearLayout RL0=(LinearLayout)findViewById(R.id.RL0);
page[] pages=new page[10];
RL0.addView(pages[0].pageLL0);//doesn't crash without this line, yet i need to have some way of adding n objects that follow a pattern, i.e. a class.
class page
{
public LinearLayout pageLL0;
public ScrollView pageUpperScroll1;
public TextView pageTextView2;
public ScrollView pageLowerScroll1;
public LinearLayout pageAnswerButtonLL2;
public Button AnswerButton3_1;
public Button AnswerButton3_2;
public Button AnswerButton3_3;
public Button AnswerButton3_4;
page()
{
pageAnswerButtonLL2.addView(AnswerButton3_1);
pageAnswerButtonLL2.addView(AnswerButton3_2);
pageAnswerButtonLL2.addView(AnswerButton3_3);
pageAnswerButtonLL2.addView(AnswerButton3_4);
pageLowerScroll1.addView(pageAnswerButtonLL2);
pageUpperScroll1.addView(pageTextView2);
pageLL0.addView(pageUpperScroll1);
pageLL0.addView(pageLowerScroll1);
}
}
All elements in an Object array are null by default.
I.e. when you create the array:
page[] pages = new page[10];
you are only setting the size of the array but not setting any instances within the array itself so every element will be null. To instantiate each element you need to use:
for (int i=0; i < pages.length; i++) {
pages[i] = new page();
}
Note Java naming conventions show that class names start with an uppercase letter, for example
Page[] pages = new Page[10];
- You have declared the Array but didn't initialize it.
Eg:
page[] pages = new page[10]; // Tell that this is an Array of page of length 10
- You will need to Initialize it,
Eg:
for (page p : pages){
p = new page();
}
- Please use the Collection like ArrayList instead of Array, as its far more flexible than using an Array.
- ArrayList can hold null values, and unlike Array, its size can increased.
ArrayList<page> p = new ArrayList<page>();
- Always make the 1st letter of a class, enum , interface as Capital.
Eg:
It should Not page but Page

Android - Refresh data in an AlertDialog?

So, if I create an AlertDialog like so:
AlertDialog.Builder b = new AlertDialog.Builder();
b.setItems(MyStringArray, MyListener);
b.create().show();
And then I want to update the items in the list, i.e. MyStringArray has changed to have more or fewer items. I can't seem to find a way to do this. So far, I've tried getting the ListView from the AlertDialog, but I can't seem to get .setAdapter to work. Is this the right approach, or is there a better way to do this?
I haven't tried this out myself, but from all the other apps I've built I'm pretty sure this will solve your problem.
Instead of using setItems, try using the setAdapter() method and pass in an ArrayAdapter that has been initialized with the data from your Array of String. Then, when you know that the data has changed, you can use getListView() to get your View object and from there call getAdapter() so that now you're working directly with the dataset. You can clear it, and re-initialize it if you like, or just add / remove the items as you like. From the adapter object, if you call notifyDataSetChanged() it should trigger a re-draw using the new data set that you just supplied to the adapter.
Hope that helps you out. Let me know if it doesn't.
DSC
If you are like me and you would like to use default adapter for example for multichoice items, then there is also a way.
Just as with any other adapter just update the string array object, get adapter from the dialog instance, cast it to appropriate adapter and invalidate it.
AlertDialog.Builder b = new AlertDialog.Builder();
b.setItems(MyStringArray, MyListener);
AlertDialog instance = b.create();
instance.show();
// Later when you need to update
MyStringArray[0] = "puf";
ListView list = instance.getListView();
// Now according to whether you used cursor or array for supplying items to the builder
// you have to cast adapter to either CursorAdapter or ArrayAdapter
ArrayAdapter adapter = (ArrayAdapter)list.getAdapter();
adapter.notifyDataSetChanged();
You can find out more here.
This is how I did it in Kotlin:
AlertDialog.Builder(context).apply {
val actions = arrayListOf("aa", "bb")
val onItemClickListener = DialogInterface.OnClickListener { dialog, which ->
// code
}
val adapter = ArrayAdapter(requireContext(),
android.R.layout.simple_list_item_1, actions)
setAdapter(adapter, onItemClickListener)
executeGetRequest() {
actions.add("cc")
adapter.notifyDataSetChanged()
}
}.show()

Categories