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
Related
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.
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();
}
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.
I have made an Listview populated with list_row_layout.xml(which is populated with json serializable class), i have clickable textview and onclick changing text from "Accept" to "Accepted". But when i click it on first listview item, another textview listview items below are changing.
Here's some photos to descibe you better
this is the adapter class
public class CustomListAdapter extends BaseAdapter {
private ArrayList<FeedItem> listData;
private LayoutInflater layoutInflater;
private Context mContext;
public CustomListAdapter(Context context, ArrayList<FeedItem> listData) {
this.listData = listData;
layoutInflater = LayoutInflater.from(context);
mContext = context;
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.list_row_layout, null);
holder = new ViewHolder();
holder.headlineView = (TextView)convertView.findViewById(R.id.sex);
holder.reportedDateView = (TextView) convertView.findViewById(R.id.confid);
holder.approve = (TextView) convertView.findViewById(R.id.approveTV);
holder.approve.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View argView)
{
holder.approve.setText("Accepted");
}
}
);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
FeedItem newsItem = (FeedItem) listData.get(position);
holder.headlineView.setText(Html.fromHtml(newsItem.getTitle()));
holder.reportedDateView.setText(Html.fromHtml(newsItem.getContent()));
return convertView;
}
static class ViewHolder {
TextView approve;
TextView headlineView;
TextView reportedDateView;
ImageView imageView;
}
}
Remember that views can be recycled via convertView.
In your onClick method you set the approve text to "Accepted" but when the view is recycled, you never set it back to "Accept"
Actually you need to update (something in) the list in response to an click and have the Accept/Accepted value toggle based on that value rather than simply updating what is currently visible on the screen.
-- to answer the "how" question (asked below)--
Add a new field to ViewHolder
static class ViewHolder {
TextView approve;
TextView headlineView;
TextView reportedDateView;
ImageView imageView;
FeedItem newsItem;
}
Change the onClick method:
public void onClick(View argView)
{
// note that holder no longer needs to be final in the parent class
// because it is not used here.
View parent = (View)argView.getParent();
ViewHolder clickedHolder = (ViewHolder)parent.getTag();
clickedHolder .newsItem.setAccepted(true); /// a new method
clickedHolder .approve.setText ("Accepted");
Log.d(TAG, "Accepted item #" + position);
}
After you have convertView created (if necessary)
FeedItem newsItem = (FeedItem) listData.get(position);
holder.newsItem = newsItem; // populate the new field.
holder.headlineView.setText(Html.fromHtml(newsItem.getTitle()));
holder.reportedDateView.setText(Html.fromHtml(newsItem.getContent()));
if(newsItem.isAccepted ()){ // another new method!
holder.approve.setText ("Accepted");
Log.d(TAG, "Set text to Accepted for item #" + position);
}else{
holder.approve.setText("Accept");
Log.d(TAG, "Set text to Accept for item #" + position);
}
Once it is working you should consider removing the Log.d() lines to cut down on the noise in LogCat.
I'm trying to populate a listview with data passed into the activity via a the intent that created it. The println statement you see confirms that the data is passed in correctly (i.e. the expected content is printed, meaning that the ArrayList referenced in the adapter is properly initialized). However, I keep getting a NullPointerException on the line
content.setText(Html.fromHtml(cmts.get(position).content));
There must be something wrong in the adapter - maybe in the getItem(), or perhaps my calls to cmts.get(position) isn't doing what I think it is, but at this point I can't figure it out.
public class CommentsView extends Activity {
ArrayList<Comment> cmts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments_view);
cmts = (ArrayList<Comment>) getIntent().getExtras().getSerializable("clist");
for (Comment c : cmts) {
System.out.println("CMTinCV: " + c.content);
}
ListView lv = (ListView)findViewById(R.id.commentsList);
CommentAdapter ca = new CommentAdapter();
lv.setAdapter(ca);
}
class CommentAdapter extends BaseAdapter {
public CommentAdapter(){
}
#Override
public int getCount() {
return cmts.size()-1;
}
#Override
public Object getItem(int position) {
return cmts.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.commentbox, null);
TextView content = (TextView)findViewById(R.id.commentText);
TextView author = (TextView)findViewById(R.id.commentAuthor);
TextView date = (TextView)findViewById(R.id.commentDate);
content.setText(Html.fromHtml(cmts.get(position).content));
author.setText(cmts.get(position).author.name);
date.setText(cmts.get(position).date);
}
return convertView;
}
}
}
You need to access the textview's in your getview method as below:
convertView.findViewById(R.id.commentText); access it like this.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.commentbox, null);
TextView content = (TextView)convertView.findViewById(R.id.commentText);
TextView author = (TextView)convertView.findViewById(R.id.commentAuthor);
TextView date = (TextView)convertView.findViewById(R.id.commentDate);
content.setText(Html.fromHtml(cmts.get(position).content));
author.setText(cmts.get(position).author.name);
date.setText(cmts.get(position).date);
}
return convertView;
}
Change your Adapter's constructor to this (if it's not an inner class for your activity) :
ArrayList<Comment> cmts;
public CommentAdapter(ArrayList<Comment> mComments){
this.cmts = mComments;
}
and these lines :
TextView content = (TextView)findViewById(R.id.commentText);
TextView author = (TextView)findViewById(R.id.commentAuthor);
TextView date = (TextView)findViewById(R.id.commentDate);
should be like :
TextView content = (TextView) convertView.findViewById(R.id.commentText);
TextView author = (TextView) convertView.findViewById(R.id.commentAuthor);
TextView date = (TextView) convertView.findViewById(R.id.commentDate);
Check if the variable is null or not first:
if(cmts.get(position) != null) {
content.setText(Html.fromHtml(cmts.get(position).content));
author.setText(cmts.get(position).author.name);
date.setText(cmts.get(position).date);
}
To create the adapter like this
CommentAdapter ca = new CommentAdapter(cmts);
And CommentAdapter class to create constructor like this
public CommentAdapter(ArrayList<Comment> cmts){
this.cmts = cmts;
}
And create local variable in CommentAdapter class
private ArrayList<Comment> cmts;