Updating ListView On Custom ArrayAdapter - java

I apologize if this is similar to other threads, but I have yet to find a good solution on how to update the ListView in my custom ArrayAdapter.
I'm extending FragmentActivity in order to create a custom Dialog popup for adding / updating new List entries (which works fine), but I can't seem to get the ListView in ServerActivityArrayAdapter to update after a change is made.
Obviously, adapter.notifyDataSetChanged(); in the ServersActivity class isn't enough, but I'm stumped as to how to get my ListView to update after the Save button is clicked in the ServerDialog popup. Any help would be greatly appreciated!
public class ServersActivity extends FragmentActivity {
private List<Server> server;
private ListView lv;
private ServerActivityArrayAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.server_list);
server = getServers();
lv = (ListView) findViewById(android.R.id.list);
adapter = new ServerActivityArrayAdapter(this, R.layout.server_list_item, server);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showServerDialog(position);
}
});
}
...
private void showServerDialog(int position) {
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
ServerDialog serverDialog = new ServerDialog(position);
ServerDialog.newInstance();
serverDialog.show(fragmentManager, "server_dialog");
}
ServerDialog class for adding new List entries
public class ServerDialog extends DialogFragment {
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.settings_fragment, container);
context = getActivity().getApplicationContext();
dbh = new DbHandler(context);
etName = (EditText) view.findViewById(R.id.etName);
etAddress = (EditText) view.findViewById(R.id.etAddress);
etPort = (EditText) view.findViewById(R.id.etPort);
swtchNumDoors = (Switch) view.findViewById(R.id.swtchNumDoors);
Button btnSave = (Button) view.findViewById(R.id.btnSave);
loadData();
String title = (!newServer) ? "Edit Server" : "Add A New Server " + ID;
getDialog().setTitle(title);
btnSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
server[ID] = String.valueOf(ID);
server[SERVER_NAME] = etName.getText().toString();
server[ADDRESS] = etAddress.getText().toString();
server[PORT] = etPort.getText().toString();
if (server[ADDRESS].isEmpty() || server[PORT].isEmpty()) {
Toast.makeText(context, "Invalid Entry", Toast.LENGTH_LONG).show();
} else {
saveData();
// TODO - notify ServersActivity of change
}
}
});
return view;
}
public class ServerActivityArrayAdapter extends ArrayAdapter<Server> {
private int layout;
private String[][] values;
private int defServer;
private static LayoutInflater inflater;
ServerActivityArrayAdapter class for displaying ListView
public ServerActivityArrayAdapter(Context context, int layout, List<Server> servers) {
super(context, layout, servers);
this.layout = layout;
int i = 0;
values = new String[servers.size()][5];
for (Server server : servers) {
values[i][0] = Integer.toString(server.id);
values[i][1] = server.name;
values[i][2] = server.address;
values[i][3] = Integer.toString(server.port);
values[i][4] = Integer.toString(server.num_doors);
defServer = (server.default_server) ? i : 0;
i++;
}
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
private static class ViewHolder {
private TextView tvServerName;
private CheckBox cbDefault;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View v = convertView;
try {
if(v == null) {
v = inflater.inflate(layout, parent, false);
holder = new ViewHolder();
holder.tvServerName = (TextView) v.findViewById(R.id.tvServerName);
holder.cbDefault = (CheckBox) v.findViewById(R.id.cbDefault);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
holder.tvServerName.setText(values[position][1]);
holder.cbDefault.setChecked(defServer==position);
} catch (Exception e){
System.out.println(e);
}
return v;
}

You could use an interface and have a method in it maybe named updateListView().
Implement this interface in your FragmentActivity. In the implemented method, add code to update the listView.
And in the button code of your dialog, call ((Interfacename) getActivity).updateListView()

Your problem is in the implementation of the adapter. You are instantiating the ArrayAdapter with a List<Server> object (The super call) but then creating your own two dimensional array values to contain the data. You use the values array for the getView() method yet make no mention or show through code how you actually modify that data to update the adapter.
You should be calling back into the adapter to update it. EG, calling add(), remove(), etc. You only mention updating the DB which has nothing to do with updating your adapter. It's not linked in anyway to the DB. Even if you were to call into the previously mentioned mutate methods on the adapter, they wouldn't work unless you specifically override them. The default ArrayAdapter implementation will modify the List<Server> data and not the values two-dim array you created.
But if you find yourself override those mutate methods to support your values two-dim array...there's no point in subclassing the ArrayAdapter. Part of the point of the ArrayAdapter is handling all the data management for you. In such a situation you should be instead subclassing BaseAdapter.

Related

How to add items dynamically to a custom adapter for ListView

I made adapter for ListView. If I fill the elements array before creating the adapter and linking it to the listView, the elements are displayed.
But if I use the updateItems () method to add items when the button is clicked, nothing happens.
Code of adapter:
public class ListAdapter extends ArrayAdapter<Lf> {
private LayoutInflater inflater;
private int layout;
private List<Lf> lfs;
public ListAdapter(Context context, int resource, List<Lf> lfs) {
super(context, resource, lfs);
this.lfs = lfs;
this.layout = resource;
this.inflater = LayoutInflater.from(context);
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView==null){
convertView = inflater.inflate(this.layout, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
}
else{
viewHolder = (ViewHolder) convertView.getTag();
}
Lf lf = lfs.get(position);
viewHolder.name.setText(lf.getLf());
viewHolder.freq.setText((int)lf.getFreq() + "");
return convertView;
}
public void updateItems(List<Lf> lfs) {
this.lfs.clear();
this.lfs.addAll(lfs);
this.notifyDataSetChanged();
}
private class ViewHolder {
final TextView name, freq;
ViewHolder(View view){
name = (TextView) view.findViewById(R.id.text_item_1);
freq = (TextView) view.findViewById(R.id.text_item_2);
}
}
}
Code of MainActivity:
public class MainActivity extends AppCompatActivity {
EditText editText1;
ListView listView;
ListAdapter adapter;
List<Lf> elements = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText1 = (EditText) findViewById(R.id.edit_text);
listView = (ListView) findViewById(R.id.fragment_list);
// Working... Elements print on screen
for(int i = 0; i < 10; i++) {
Lf temp = new Lf();
temp.setLf("mean");
temp.setFreq(100);
elements.add(temp);
}
adapter = new ListAdapter(this, R.layout.list_item, elements);
listView.setAdapter(adapter);
}
public void activity_button(View view) {
adapter.updateItems(elements);
}
}
If I click on the button, the existing items on the screen are cleared instead of a new one added. But in debug I see that elements normally passed to ListAdapter.
The problem is that your adapter's lfs's field and your activity's elements field both refer to the same List instance. This happens because you pass elements to the ListAdapter constructor, and then simply assign this.lfs = lfs.
So let's look at what happens when you pass elements to updateItems()...
public void updateItems(List<Lf> lfs) {
this.lfs.clear(); // this.lfs and the input lfs are the same list, so this clears both
this.lfs.addAll(lfs); // input lfs is now empty, so addAll() does nothing
this.notifyDataSetChanged();
}
Probably the best thing to do is to create a copy of the list in your adapter's constructor.
this.lfs = new ArrayList<>(lfs);
Now your adapter and activity will reference different lists, so this.lfs.clear() won't accidentally clear out the very list you're passing to it.

Android: delete from database using index from listview - Can't access array of id's that is created with the view but not the basis for the list

I have a simple list view where each item is a view that has a title, from an ArrayList of strings and button, so that each entry in the ArrayList creates a new list item.
I also have another ArrayList of corresponding primary keys, which I want to use to delete specific items from an SQLite database but which isn't used in the list view(I don't want to display the ID's, but the strings that poplulate the list might not necessarily be unique so I can't use them to delete).
I have a onClick listener and method in the getView method for the list view, so that when someone clicks the delete button, I know the position in the list that the button was pressed in, so hopefully, I can then call a delete method on the database using id[position], however, I think due to the list view itself being created after the activity it's inside of, it can't resolve the id array, so I can't call delete.
public class TodayListActivity extends AppCompatActivity {
private ArrayList<String> names = new ArrayList<>();
FoodDB Db = null;
int deleteId;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_todaylist);
ListView lv = (ListView) findViewById(R.id.today_meal_list);
Bundle a = this.getIntent().getExtras();
String[] id = a.getStringArray("idArray"); //used to delete
String[] mealNames = a.getStringArray("mealNamesArray"); //displayed
Collections.addAll(names, mealNames);
//call the list adapter to create views based off the array list 'names'
lv.setAdapter(new MyListAdapter(this, R.layout.list_item, names));
}
protected class MyListAdapter extends ArrayAdapter<String> {
private int layout;
private MyListAdapter(Context context, int resource, List<String> objects) {
super(context, resource, objects);
layout = resource;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
viewHolder viewholder;
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(layout, parent, false);
viewholder = new viewHolder();
viewholder.title = (TextView) convertView.findViewById(R.id.report_meal_name);
viewholder.delButton = (Button) convertView.findViewById(R.id.button_delete_meal);
viewholder.delButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = (Integer)v.getTag();
//int deleteId derived from id[position]
deleteId = Integer.parseInt(id[position]);
idToDelete(deleteId);
//update the list view to exclude the deleted item
names.remove(position);
notifyDataSetChanged();
}
});
convertView.setTag(viewholder);
} else {
viewholder = (viewHolder) convertView.getTag();
}
//set string value for title
viewholder.title.setText(getItem(position));
viewholder.delButton.setTag(position);
return convertView;
}
}
public class viewHolder {
TextView title;
TextView delButton;
}
//delete from database
public void idToDelete(int DeleteId){
Db.deleteFoods(deleteId);
}
}
Any suggestions as to how or where to get either the position index out of the list view (to the activity, where the id array is) or get access to the id array inside the listview would be appreciated!
You can pass the id array to the MyListAdapter adapter, by changing this class' constructor to accept it as a parameter. Also, you are already passing the names list as a parameter, you should keep a reference to it so you can access it when the button is pressed.
Here is an example:
protected class MyListAdapter extends ArrayAdapter<String> {
private int layout;
private List<String> names;
private String[] ids;
private MyListAdapter(Context context, int resource, List<String> names, String[] ids) {
super(context, resource, names);
layout = resource;
this.names = names;
this.ids = ids;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
...
viewholder.delButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int deleteId = Integer.parseInt(ids[position]);// the "position" variable needs to be set to "final" in order to access it in here.
idToDelete(deleteId);
names.remove(position);
notifyDataSetChanged();
}
});
....
}
}
and here is how you can create an instance of this adapter:
lv.setAdapter(new MyListAdapter(this, R.layout.list_item, names, id));

Having problems while trying to populate a todo listview using a custom adaptor?

I want a user to input data through an editable text and I want to receive that data through a custom made listview, for that I am trying to use a custom adapter to add a textfield into my listview, through the tostring() method I have converted the data from the editable textview to a string and I am adding that string within my custom adapter to an Arraylist variable values and I’m trying to display that data through get(0) but either the Arraylist is not populating correctly or the data is not displaying properly because whenever I type something within my editable text and press the add button nothing happens, before this I added the string to an Array Adapter and the listview was populating normally, what am I doing wrong?
public class todoFragment extends ListFragment {
private EditText mToDoField;
private Button mAdd;
UsersAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.todo_title);
}
public class UsersAdapter extends ArrayAdapter<String> {
public Context context;
public ArrayList<String> values;
public UsersAdapter(Context context, ArrayList<String> values) {
super(context, 0, values);
this.context = context;
this.values = values;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = LayoutInflater.from(getContext()).inflate(R.layout.todo_list, parent, false);
TextView todoTextView = (TextView) convertView.findViewById(R.id.todo_TextView);
todoTextView.setText(values.get(0));
return convertView;
}
}
#TargetApi(9) // remember this for isEmpty()
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_todo, container, false);
ArrayList<String> todoList = new ArrayList<String>();
mAdapter = new UsersAdapter(getActivity(), todoList);
ListView listViewToDo = (ListView) v.findViewById (android.R.id.list);
listViewToDo.setAdapter(mAdapter);
mToDoField = (EditText) v.findViewById(R.id.todo_editText);
mAdd = (Button)v.findViewById(R.id.add_button);
mAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String toDo = mToDoField.getText().toString().trim();
if (toDo.isEmpty()){
return;
}
mAdapter.values.add(toDo);
mToDoField.setText("");
}
});
return v;
}
}
Firstly, you should not be doing
todoTextView.setText(values.get(0));
Because this will always return the first element of the values list. You should do
todoTextView.setText(values.get(position));
Secondly,
mAdapter.values.add(toDo);
is not really right. It will work, but its not the best practise. Try using something like
mAdapter.add(toDo);
or
values.add(toDo);
Now once you've added the data to the list, you need to notify the adapter that the data set has been changed. This is done by
mAdapter.notifyDataSetChanged();
When you manually update the data don't forget to call:
mAdapter.notifyDataSetChanged();
Instead of mAdapter.values.add(toDo); UsemAdapter.add(toDo);
Look at the Add Method Of ArrayAdpter Class, it Itself use notifyDataSetChanged() so need to write any extra line of code:
public void add(T object) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.add(object);
} else {
mObjects.add(object);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}

Refresh ListView Items upon button triggered

I was having some problem when trying to refresh the items in list view after the button was triggered. Here is how I populate the listview onCreate:
public class EventChat extends Fragment {
Context context;
View eventChat;
String userID, eventID;
private ListView listview;
public ArrayList<EventComment> _commentlist = new ArrayList<EventComment>();
TextView txtDisplayCommentBy, txtDisplayDateTime, txtDisplayCommentDesc,
txtEventChat;
Button btnChatSubmit;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
eventChat = inflater.inflate(R.layout.event_chat, container, false);
context = getActivity();
listview = (ListView) eventChat.findViewById(R.id.listview);
txtEventChat = (TextView) eventChat.findViewById(R.id.txtEventChat);
btnChatSubmit = (Button) eventChat.findViewById(R.id.btnChatSubmit);
btnChatSubmit.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
onSubmitChatClicked();
}
});
Intent i = getActivity().getIntent();
_commentlist = (ArrayList<EventComment>) i
.getSerializableExtra("eventCommentObj");
Event eventModel = (Event) i.getSerializableExtra("eventObj");
userID = "Gab";
eventID = eventModel.getEventID();
listview.setAdapter(new ListAdapter(getActivity()));
return eventChat;
}
private class ListAdapter extends BaseAdapter {
LayoutInflater inflater;
ViewHolder viewHolder;
public ListAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
public int getCount() {
return _commentlist.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.eventchat_listview_row,
null);
viewHolder = new ViewHolder();
viewHolder.txt_dcommentBy = (TextView) convertView
.findViewById(R.id.txtDisplayCommentBy);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.txt_dcommentBy.setText(_commentlist.get(position)
.getCommentBy().trim());
return convertView;
}
}
private class ViewHolder {
TextView txt_dcommentBy;
TextView txt_ddateTime;
TextView txt_dcommentDesc;
}
}
When my button was triggered and insert a new record into database, at the same time, I wanted the list view items to be refreshed:
public void onSubmitChatClicked() {
EventComment commentModel = new EventComment();
String currentDate = EventDateTime.getCurrentDate();
String currentTime = EventDateTime.getCurrentTime();
String commentDesc = String.valueOf(txtEventChat.getText());
commentModel.setCommentBy(userID);
commentModel.setEventID(eventID);
commentModel.setCommentDate(currentDate);
commentModel.setCommentTime(currentTime);
commentModel.setCommentDesc(commentDesc);
new CreateCommentAsyncTask(context).execute(commentModel);
txtEventChat.setText("");
}
However, it does not refresh. Any ideas?
Thanks in advance.
Well this looks pretty straightforward.
First make your adapter a class member, so you can access it later:
private ListAdapter mAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mAdapter = new ListAdapter(getActivity();
listview.setAdapter(mAdapter);
...
}
Then, when you submit the item, also add it to your ArrayList and update your adapter:
public void onSubmitChatClicked() {
EventComment commentModel = new EventComment();
String currentDate = EventDateTime.getCurrentDate();
String currentTime = EventDateTime.getCurrentTime();
String commentDesc = String.valueOf(txtEventChat.getText());
commentModel.setCommentBy(userID);
commentModel.setEventID(eventID);
commentModel.setCommentDate(currentDate);
commentModel.setCommentTime(currentTime);
commentModel.setCommentDesc(commentDesc);
// Add the new element to your DB
new CreateCommentAsyncTask(context).execute(commentModel);
// Add the new element to your current ArrayList
_commentlist.add(commentModel)
// Update theListView, by updating the adapter
mAdapter.notifyDataSetChanged();
txtEventChat.setText("");
}
EDIT:
Little more explanation:
When your fragment is created you are passed an array of EventComment items. I guess these are the elements from your DB. When you update the database, however, your ArrayList won't get updated, unless you reload the whole fragment. That's why you add the item to the DB, and to the list manually, and with notifyDataSetChanged, you force your adapter to update the ListView.
You cant do listview.setAdapter(new ListAdapter(getActivity())); to achieve this. You should create an instance of adapter and call adapter.add + adapter.notifyDataSetChanged
This is a very good tutorial :
http://www.vogella.com/tutorials/AndroidListView/article.html
http://www.javacodegeeks.com/2013/09/android-listview-with-adapter-example.html

ListView using BaseAdapter not showing in Activity

I'm trying to inflate a list using baseadapter within an activity. The list just doesn't inflate. From the logs implemented within the class, the getView() function doesn't even execute. Here's the code. -
public class CallLog extends Activity {
ListView logList;
List mList;
Context mCtx;
ArrayList<String> logName;
ArrayList<String> logNumber;
ArrayList<String> logTime;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.reject_call_log);
mCtx = getApplicationContext();
ListView logList = (ListView) findViewById(R.id.log_list);
mList = new List(mCtx, R.layout.log_row);
logList.setAdapter(mList);
SharedPreferences savedLogName = PreferenceManager.getDefaultSharedPreferences(mCtx);
SharedPreferences savedLogNumber = PreferenceManager.getDefaultSharedPreferences(mCtx);
SharedPreferences savedLogTime = PreferenceManager.getDefaultSharedPreferences(mCtx);
try{
logName = new ArrayList(Arrays.asList(TextUtils.split(savedLogName.getString("logName", null), ",")));
logNumber = new ArrayList(Arrays.asList(TextUtils.split(savedLogNumber.getString("logNumber", null), ",")));
logTime = new ArrayList(Arrays.asList(TextUtils.split(savedLogTime.getString("logTime", null), ",")));
Collections.reverse(logName);
Collections.reverse(logNumber);
Collections.reverse(logTime);
}catch(NullPointerException e){
e.printStackTrace();
//TextView noLog = (TextView)findViewById(R.id.no_log);
}
}
public class List extends BaseAdapter {
LayoutInflater mInflater;
TextView nameText;
TextView numberText;
TextView timeText;
int timePos = 1;
public List(Context context, int resource) {
}
#Override
public int getCount() {
return 0;
}
#Override
public Object getItem(int i) {
return null;
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (convertView == null) {
v = mInflater.inflate(R.layout.row, null);
}
nameText = (TextView) v.findViewById(R.id.log_name);
numberText = (TextView) v.findViewById(R.id.log_number);
timeText = (TextView) v.findViewById(R.id.log_time);
nameText.setText(logName.get(position));
numberText.setText(logNumber.get(position));
timeText.setText(logTime.get(timePos) + logTime.get(timePos+1));
Log.d("RejectCall", "ListView");
timePos+=2;
return v;
}
}
}
Where is it all going wrong? Also, is there a better way to do what I'm trying to do?
Please replace the following code :
#Override
public int getCount() {
return 0;
}
with
#Override
public int getCount() {
return logName.size();
}
As list view only show the numbers of rows that is returned by this method and right now you are returning 0;
And after fetching the data in arraylist please use adapter.notifyDataSetChanged() to notify the list view.
You have to call notifyDataSetChanged() as you are filling data in array list after setting the adapter. so to notify the list view that data has been changed you have to call notify method(as above)
Your getItem() and getCount() haven't been implemented. If you want any kind of adapter to work for the list, these need to be implemented. Your list is also not holding any actual data, so getItem() has nothing to set.
Don't forget to call notifiyDataSetChanged() in your adapter after you set appropriate implementations for the above two functions.

Categories