snippetjavaMinor
Using an Adapter with Parse query in Android
Viewed 0 times
androidwithqueryparseadapterusing
Problem
I'm an iOS developer new to Android. I'm creating my first Android app and I have several questions. My app uses Android Studio, gradle, and it's based on Parse (the backend). Basically, takes messages from the backend and shows them. User has the possibility to delete them, or click them and see the detail, and of course unread messages have a different color.
This is the code of one class, called
```
package com.ricardoruizlopez.xxxxx.messages;
import ...
public class MessagesListFragment extends Fragment {
private static final String LOG_TAG = "MessagesListFragment";
MessagesAdapter mAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.messages_list_layout, container, false);
final ListView listView = (ListView) v.findViewById(R.id.list);
// load messages and attach them to the list
ParseQuery messagesQuery= ParseQuery.getQuery(Message.class);
messagesQuery.whereEqualTo("receiver", ParseUser.getCurrentUser());
messagesQuery.whereEqualTo("deleted", false);
messagesQuery.include("sender");
messagesQuery.orderByDescending("createdAt");
messagesQuery.setLimit(1000);
final ProgressDialog progress = ProgressDialog.show(getActivity(), null, getString(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_LOADING_MESSAGES));
messagesQuery.findInBackground(new FindCallback() {
@Override
public void done(List list, ParseException e) {
progress.dismiss();
if (e == null) {
mAdapter = new MessagesAdapter(list);
listView.setAdapter(mAdapter);
} else {
Log.d(LOG_TAG, "Error loading messages. " + e.getLocalizedMessage());
new AlertDialog.Builder(getActivity())
.setTitle(R.string.ALERT_VIEW_ERROR)
.setMessage(R.string.MESSAGE
This is the code of one class, called
MessagesListFragment.java, after that a few questions.```
package com.ricardoruizlopez.xxxxx.messages;
import ...
public class MessagesListFragment extends Fragment {
private static final String LOG_TAG = "MessagesListFragment";
MessagesAdapter mAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.messages_list_layout, container, false);
final ListView listView = (ListView) v.findViewById(R.id.list);
// load messages and attach them to the list
ParseQuery messagesQuery= ParseQuery.getQuery(Message.class);
messagesQuery.whereEqualTo("receiver", ParseUser.getCurrentUser());
messagesQuery.whereEqualTo("deleted", false);
messagesQuery.include("sender");
messagesQuery.orderByDescending("createdAt");
messagesQuery.setLimit(1000);
final ProgressDialog progress = ProgressDialog.show(getActivity(), null, getString(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_LOADING_MESSAGES));
messagesQuery.findInBackground(new FindCallback() {
@Override
public void done(List list, ParseException e) {
progress.dismiss();
if (e == null) {
mAdapter = new MessagesAdapter(list);
listView.setAdapter(mAdapter);
} else {
Log.d(LOG_TAG, "Error loading messages. " + e.getLocalizedMessage());
new AlertDialog.Builder(getActivity())
.setTitle(R.string.ALERT_VIEW_ERROR)
.setMessage(R.string.MESSAGE
Solution
MessagesListFragment
Firstly,
If you want to perform networks calls, you may do so in
For long listeners and callbacks, I usually prefer to make them member variables:
Note that it is almost never necessary to make an adapter a member variable.
Once you've assigned an adapter, you can access it from the ListView using
Previously, your code had the following line:
Unfortunately, things aren't as simple on Android as they are on iOS. Kindly follow this tutorial
to understand how data needs to be passed between Activities in Android.
MessagesAdapter
Your adapter implementation is mostly correct albeit one glaring error: It is NOT an Adapter's job to handle click events.
The adapter should only render the list items. Interaction with the items should be handled in the activity or fragment.
Declare an interface to handle button click events in your Adapter
Set this listener to the delete button in getView()
```
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(getActivity(), R.layout.message_item, null);
}
Message message=mMessages.get(position);
CustomTextView senderTextView = (CustomTextView) convertView.findViewById(R.id.senderTextView);
senderTextView.setText(getString(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_MESSAGE_FROM, message.getSender().getString("displayName")));
CustomTextView dateTextView = (CustomTextView) convertView.findViewById(R.id.dateTextView);
Date createdAt = mMessages.get(position).getCreatedAt();
DateFormat df=DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
dateTextView.setText(df.format(createdAt));
Button deleteButton = (Button) convertView.findViewById(R.id.deleteButton);
deleteButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
if(mDeleteClickListener != null){
mDeleteClickListener.onMessageDeleteClick(message,position,view);
}
Firstly,
onCreateView should only be used for initializing the view.If you want to perform networks calls, you may do so in
onResume():@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.messages_list_layout, container, false);
final ListView listView = (ListView) v.findViewById(R.id.list);
listView.setOnItemClickListener(mMessageItemClickListener);
return v;
}
@Override
public void onResume(){
super.onResume();
requestMessages();
}
private void requestMessages(){
ParseQuery messagesQuery= ParseQuery.getQuery(Message.class);
messagesQuery.whereEqualTo("receiver", ParseUser.getCurrentUser());
messagesQuery.whereEqualTo("deleted", false);
messagesQuery.include("sender");
messagesQuery.orderByDescending("createdAt");
messagesQuery.setLimit(1000);
final ProgressDialog progress = ProgressDialog.show(getActivity(), null, getString(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_LOADING_MESSAGES));
messagesQuery.findInBackground(mFindCallback);
}For long listeners and callbacks, I usually prefer to make them member variables:
public class MessagesListFragment extends Fragment {
//...
private FindCallback mFindCallback = new FindCallback(){
@Override
public void done(List list, ParseException e) {
progress.dismiss();
if (e == null) {
listView.setAdapter(new MessagesAdapter(list));
} else {
Log.d(LOG_TAG, "Error loading messages. " + e.getLocalizedMessage());
new AlertDialog.Builder(getActivity())
.setTitle(R.string.ALERT_VIEW_ERROR)
.setMessage(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_ERROR_LOADING_MESSAGES)
.setPositiveButton(R.string.ALERT_VIEW_OK, null)
.show();
}
}
};
}Note that it is almost never necessary to make an adapter a member variable.
Once you've assigned an adapter, you can access it from the ListView using
getAdapter()private AdapterView.OnItemClickListener mMessageItemClickListener = new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
Log.d(LOG_TAG, "onItemClick");
Message message = (Message) mListView.getItemAtPosition(position);
// mark message as read if needed
if (message.getUnread()) {
message.setUnread(false);
mAdapter.notifyDataSetChanged();
message.saveInBackground();
}
// show message detail
Intent messageDetail = new Intent(getActivity(), MessageDetailActivity.class);
messageDetail.putExtra("message",messageDetail)
startActivity(messageDetail);
}
}Previously, your code had the following line:
MessageDetailActivity.message = message;Unfortunately, things aren't as simple on Android as they are on iOS. Kindly follow this tutorial
to understand how data needs to be passed between Activities in Android.
MessagesAdapter
Your adapter implementation is mostly correct albeit one glaring error: It is NOT an Adapter's job to handle click events.
The adapter should only render the list items. Interaction with the items should be handled in the activity or fragment.
Declare an interface to handle button click events in your Adapter
class MessagesAdapter extends BaseAdapter {
public interface OnMessageDeleteClickListener(){
void onMessageDeleteClick(Message message, int position, View view);
}
private OnMessageDeleteClickListener mDeleteClickListener;
public MessagesAdapter(List messages,OnMessageDeleteClickListener listener ) {
mMessages=messages;
mDeleteClickListener = listener;
}
}Set this listener to the delete button in getView()
```
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(getActivity(), R.layout.message_item, null);
}
Message message=mMessages.get(position);
CustomTextView senderTextView = (CustomTextView) convertView.findViewById(R.id.senderTextView);
senderTextView.setText(getString(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_MESSAGE_FROM, message.getSender().getString("displayName")));
CustomTextView dateTextView = (CustomTextView) convertView.findViewById(R.id.dateTextView);
Date createdAt = mMessages.get(position).getCreatedAt();
DateFormat df=DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
dateTextView.setText(df.format(createdAt));
Button deleteButton = (Button) convertView.findViewById(R.id.deleteButton);
deleteButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
if(mDeleteClickListener != null){
mDeleteClickListener.onMessageDeleteClick(message,position,view);
}
Code Snippets
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.messages_list_layout, container, false);
final ListView listView = (ListView) v.findViewById(R.id.list);
listView.setOnItemClickListener(mMessageItemClickListener);
return v;
}
@Override
public void onResume(){
super.onResume();
requestMessages();
}
private void requestMessages(){
ParseQuery<Message> messagesQuery= ParseQuery.getQuery(Message.class);
messagesQuery.whereEqualTo("receiver", ParseUser.getCurrentUser());
messagesQuery.whereEqualTo("deleted", false);
messagesQuery.include("sender");
messagesQuery.orderByDescending("createdAt");
messagesQuery.setLimit(1000);
final ProgressDialog progress = ProgressDialog.show(getActivity(), null, getString(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_LOADING_MESSAGES));
messagesQuery.findInBackground(mFindCallback);
}public class MessagesListFragment extends Fragment {
//...
private FindCallback<Message> mFindCallback = new FindCallback<Message>(){
@Override
public void done(List<Message> list, ParseException e) {
progress.dismiss();
if (e == null) {
listView.setAdapter(new MessagesAdapter(list));
} else {
Log.d(LOG_TAG, "Error loading messages. " + e.getLocalizedMessage());
new AlertDialog.Builder(getActivity())
.setTitle(R.string.ALERT_VIEW_ERROR)
.setMessage(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_ERROR_LOADING_MESSAGES)
.setPositiveButton(R.string.ALERT_VIEW_OK, null)
.show();
}
}
};
}private AdapterView.OnItemClickListener mMessageItemClickListener = new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d(LOG_TAG, "onItemClick");
Message message = (Message) mListView.getItemAtPosition(position);
// mark message as read if needed
if (message.getUnread()) {
message.setUnread(false);
mAdapter.notifyDataSetChanged();
message.saveInBackground();
}
// show message detail
Intent messageDetail = new Intent(getActivity(), MessageDetailActivity.class);
messageDetail.putExtra("message",messageDetail)
startActivity(messageDetail);
}
}class MessagesAdapter extends BaseAdapter {
public interface OnMessageDeleteClickListener(){
void onMessageDeleteClick(Message message, int position, View view);
}
private OnMessageDeleteClickListener mDeleteClickListener;
public MessagesAdapter(List<Message> messages,OnMessageDeleteClickListener listener ) {
mMessages=messages;
mDeleteClickListener = listener;
}
}@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(getActivity(), R.layout.message_item, null);
}
Message message=mMessages.get(position);
CustomTextView senderTextView = (CustomTextView) convertView.findViewById(R.id.senderTextView);
senderTextView.setText(getString(R.string.MESSAGES_TABLE_VIEW_CONTROLLER_MESSAGE_FROM, message.getSender().getString("displayName")));
CustomTextView dateTextView = (CustomTextView) convertView.findViewById(R.id.dateTextView);
Date createdAt = mMessages.get(position).getCreatedAt();
DateFormat df=DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
dateTextView.setText(df.format(createdAt));
Button deleteButton = (Button) convertView.findViewById(R.id.deleteButton);
deleteButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
if(mDeleteClickListener != null){
mDeleteClickListener.onMessageDeleteClick(message,position,view);
}
}
});
// set colors accoring if it's read or not
CustomTextView fromTextView = (CustomTextView)
convertView.findViewById(R.id.fromTextView);
int color = getResources().getColor(message.getUnread()? R.color.messages_unread : R.color.messages_read);
senderTextView.setTextColor(color);
dateTextView.setTextColor(color);
fromTextView.setTextColor(color);
return convertView;
}Context
StackExchange Code Review Q#93469, answer score: 4
Revisions (0)
No revisions yet.