I have created on chat screen and i want to display sent message on left side and received messages right side.To do this i have created two xml one for right and one for left and i have bind this two xml with adapter.The problem is that when i am sending a text it resides on left side and when i am receiving a text,the sent text is also aligned on right side.and when i am sending again then whole text(chat) aligns on left.
Here is my ArrayAdapter:
private void setListAdapterL() {
ArrayAdapter<String> adapterL = new ArrayAdapter<String>(this,
R.layout.row_left, messages);
mList.setAdapter(adapterL);
}
private void setListAdapterR() {
// TODO Auto-generated method stub
ArrayAdapter<String> adapterR = new ArrayAdapter<String>(this,
R.layout.row_right, messages);
mList.setAdapter(adapterR);
}
My xml file for row_right.xml.in other row_left.xml just gravity is changed:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingLeft="5dip" />
Any ideas and suggestion will be appreciated.
Thanks
To do this you have to create custom adapter for list view instead of creating two separate adapters for left item (i.e., sent item) and right item (i.e., received item).
It means you have manage sent/received messages in one custom adapter.
for e.g.
// Sample messages inside MessageActivity
void initMessages() {
HashMap<String, String> messageSent = new HashMap<String, String>();
messageSent.put("message", "Hi");
messageSent.put("type", "sent");
messageSent.put("date", "");
HashMap<String, String> messageReceived = new HashMap<String, String>();
messageSent.put("message", "Hello");
messageSent.put("type", "received");
messageSent.put("date", "");
ArrayList<HashMap<String, String>> messages = new ArrayList<HashMap<String, String>>();
messages.add(messageSent);
messages.add(messageReceived);
MessageAdapter adapter = new MessageAdapter(mContext, messages);
setListAdapter(apater);
}
// adapter class
class MessageAdapter extends ArrayAdapter<HashMap<String, String>> {
LayoutInflater inflater;
public MessageAdapter(Context context, ArrayList<HashMap<String, String>> data) {
super(context, R.layout.raw_message, data);
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null) {
convertView = inflater.inflate(R.layout.raw_message, null);
holder = new ViewHolder();
holder.txtSent = convertView.findViewByTag("sent");
holder.txtReceived = convertView.findViewByTag("received");
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
HashMap<String,String> message = getItem(position);
boolean sent = message.get("type").equals("sent");
if(sent) {
holder.txtSent.setVisibility(View.VISIBLE);
holder.txtSent.setText(message.get("message"));
holder.txtReceived.setVisibility(View.GONE);
} else {
holder.txtSent.setVisibility(View.GONE);
holder.txtReceived.setText(message.get("message"));
holder.txtReceived.setVisibility(View.VISIBLE);
}
}
class ViewHolder {
TextView txtSent;
TextView txtReceived;
}
}
// raw_message.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:tag="sent"/>
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:tag="received"/>
</RelativeLayout>
The problem is that when i am sending a text it resides on left side and when i am receiving a text,the sent text is also aligned on right side.and when i am sending again then whole text(chat) aligns on left.
That is probably happening because each time you get a new message you set a new adapter for the ListView with the new gravity(left or right). Because the new adapter holds all messages in the chat they all get aligned left or right.
My advice is to use a data object class that will hold the actual message + its status(it's a received or sent message):
class Message {
String actualMessage;
int status = 0; //(for example 0 for sent messages and 1 for received messages)
public String toString() {
return actualMessage;
}
}
Then implement your own adapter and in the getView method see which type of message do you have and set the orientation for that particular row to left or right. This will also allow you to just update the list of messages instead of creating each time a new ArrayAdapter object. Adapter example:
public class CustomAdapter extends ArrayAdapter<Message> {
public CustomAdapter(Context context, int resId, List<message> data) {
super(context, resId, data);
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = (TextView) super.getView(position, convertView, parent);
Message msg = getItem(position);
if (msg.status == 0) {
tv.setGravity(Gravity.LEFT);
} else {
tv.setGravity(Gravity.RIGHT);
}
}
}
you're facing the problem that ALL you're items always get changed to the new layout. So everytime you're setting the left layout everything moves to the left side vice versa.
What you need is a custom adapter which enables you to have 2 textviews - one for the right side and one for the left one.
Related
I am following this tutorial https://www.simplifiedcoding.net/android-recyclerview-cardview-tutorial/ However my requirements are different.
Instead of having set values like a name, description, rating like the tutorial has, I am wanting to have a spinner, edit text, textbox and checkbox in each Cardview. (when the user presses the FAB button, another card view is added, which also has a spinner, textbox, checkbox and edit text)
But the thing is, the spinner is populated by items in a csv file and this is where I am having trouble. I have a previous program file without card view and recycler view, where the spinners are populated with csv file values and when you press the FAB, spinner appears. However im having trouble integrating the recycler view with this.
In my previous program file I had a MyListAdapter.java.This is the adapter for the spinner i believe.
public class MyListAdapter extends ArrayAdapter<String> {
int groupid;
List<String> items;
Context context;
String path;
public MyListAdapter(Context context, int vg, int id, List<String> items) {
super(context, vg, id, (List<String>) items);
this.context = context;
groupid = vg;
this.items = items;
}
static class ViewHolder {
public TextView textid;
public TextView textname;
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
{
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(groupid, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.textid = (TextView) rowView.findViewById(R.id.txtid);
viewHolder.textname = (TextView) rowView.findViewById(R.id.txtname);
rowView.setTag(viewHolder);
}
// Fill data in the drop down.
ViewHolder holder = (ViewHolder) rowView.getTag();
String row = items.get(position);
//holder.textid.setText(row[0]); //prints aisle number, dont need
holder.textname.setText(row);
return rowView;
}
}
}
Additionally then, in my mainacttivity I had this code which read the csv file.
private class CSVFile {
InputStream inputStream;
public CSVFile(InputStream inputStream) {
this.inputStream = inputStream;
}
public List<String> read() {
List<String> resultList = new ArrayList<String>();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
String line;
while ((line = reader.readLine()) != null) {
String[] row = line.split(",");
//TODO I edited this part so that you'd add the values in our new hash map variable
numberItemValues.put(row[1], row[0]);
resultList.add(row[1]);
}
} catch (IOException e) {
Log.e("Main", e.getMessage());
} finally {
try {
inputStream.close();
} catch (IOException e) {
Log.e("Main", e.getMessage());
}
}
return resultList;
}
}
But the tutorial asks for a two classes, Product.java and product adapter.java. (if you click the tutorial you will see their code) I am confused how to integrate my code above with this tutorial situation?
*-first of all create your recyclerview row layout xml file with items you want
*-load csv file in your main activity or fragment and pass them to your adapter, declare a parameter in your adapter constructor
*-inside your recuclerview adapter class, in onCreateViewHolder method inflate that layouts(just like example)
*-in your RecyclerView.ViewHolder constructor get all your comonent like Spinner and etc, just like example
TextView textViewTitle = itemView.findViewById(R.id.textViewTitle);
...
Spinner spinner = itemView.findViewById(R.id.spinner);
*-in your onBindViewHolder method fill spinner with loaded csv file, same as before
ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_spinner_item, spinnerArray);
spinner.setAdapter(adapter);//mContext should be passed from your activity to your adapter, so you should add it to your adapter constructor
First, you don't need a custom adapter for your spinner if all you need is to display a simple spinner with list of strings. Add the following snippet to your onBindViewHolder() method to populate your spinner.
String[] dataArray = new String[]{"Spinner data1", "data 2"}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_spinner_item, dataArray);
holder.spinner.setAdapter(adapter);
Read the tutorial and follow every steps including creating a Product.java class. Create a layout and replace the layout_product.xml with the new layout. I have created one for you below, copy the code and rearrange the components to meet your want and paste it in the new layout file. Lets call the new file, card_layout.
card_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Spinner
android:padding="7dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:textAppearance="?android:textAppearanceMedium"
android:text="Hey, its me"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
I don't understand the TextBox part, but you can add it yourself if its not already the above code snippet. Now, replace the layout in recyclerview adapter with the new layout. It should work now.
I am developing an android app with a ListView/ArrayAdapter combo with the items being dynamically added when it receives a broadcast from another service.
Here is the initial setup:
postAdapter = new PostAdapter(this.getActivity(), R.layout.item, posts);
ListView list = (ListView) V.findViewById(R.id.listView1);
list.setAdapter(postAdapter);
There are some elements inside the list initially (stored inside posts).
The initial render of the listView from the adapter works perfectly.
However, when a broadcast is received from another service problem occurs.
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String title= intent.getStringExtra("title");
String content= intent.getStringExtra("content");
String senderDesc= intent.getStringExtra("senderDesc");
String receiverDesc= intent.getStringExtra("receiverDesc");
Post newPost = new Post(title, content, senderDesc, receiverDesc);
posts.add(newPost);
postAdapter.notifyDataSetChanged();
}
};
At first, when the first 2 element is added, the listView still looks fine.
Until the listView is added to about 7 elements, then the next element is no longer rendered as the content of the element in question but the content of a previous element.
For example(pseudo for simplicity):
Below is the initial list:
1
2
3
And when the 4th, and 5th post is added, then the listView looks like:
1
2
3
4
5
as expected,
However, when the 6th and sometimes 7th item is added, then it will look like this
1
2
3
4
5
1
2 ...
Below is my custom PostAdapter function:
public class PostAdapter extends ArrayAdapter {
int resource;
public PostAdapter(Context context, int resource, List<Post> posts) {
super(context, resource, posts);
this.resource = resource;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Setting up postView
RelativeLayout postView;
Post post = getItem(position);
String titleString = post.getTitle();
String contentString = post.getContent();
String senderDescString = post.getSenderDesc();
String receiverDescString = post.getReceiverDesc();
if(convertView == null) {
postView = new RelativeLayout(getContext());
String inflater = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater li;
li = (LayoutInflater)getContext().getSystemService(inflater);
li.inflate(resource, postView, true);
TextView contentView = (TextView) postView.findViewById(R.id.postContent);
contentView.setText(contentString);
} else {
postView = (RelativeLayout) convertView;
}
// Check if the post has been previously populated;
return postView;
}
}
I have spend over 10 hours on this and I don't know what is causing the problem.
If expert can point it out to me I really appreciate.
Thank you,
Dennis
I think View-reused make this happen, put the following method below getView() method comment, it will works. BTW, you should use ViewHolder pattern in AdapterView if you have a lot of items to display
TextView contentView = (TextView) postView.findViewById(R.id.postContent);
contentView.setText(contentString);
The problem is the else part when convertView is not null, do
else {
postView = (RelativeLayout) convertView;
setText(contentString);
}
I have created a customized listView using the following tutorial http://www.ezzylearning.com/tutorial.aspx?tid=1763429.
My list view includes two row (each with an image and a TextView). The first row is user, and the second is password.
I am looking for a way to make the password row to masked, something like ****, and to add another row that will enable the user to set it to visible/ mask.
I found the following examples,
How to show hidden password in textview?
How to switch between hide and view password
but I have no idea how to implement this on a specific row.
my rows xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<ImageView android:id="#+id/imgUserAccountIcon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:scaleType="fitStart" />
<TextView android:id="#+id/txtTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
/>
</LinearLayout>
My user class
public class UserAccountData {
public int icon;
public String title;
public UserAccountData(){
super();
}
public UserAccountData(int icon, String title) {
super();
this.icon = icon;
this.title = title;
}
}
My adapter class
public class UserAccountAdapter extends ArrayAdapter<UserAccountData> {
Context context;
int layoutResourceId;
UserAccountData data[] = null;
public UserAccountAdapter(Context context, int layoutResourceId,
UserAccountData[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
UserAccountDataHolder holder = null;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new UserAccountDataHolder();
holder.imgIcon = (ImageView) row.findViewById(R.id.imgUserAccountIcon);
holder.txtTitle = (TextView) row.findViewById(R.id.txtTitle);
row.setTag(holder);
} else {
holder = (UserAccountDataHolder) row.getTag();
}
UserAccountData userAccountData = data[position];
holder.txtTitle.setText(userAccountData.title);
holder.imgIcon.setImageResource(userAccountData.icon);
return row;
}
static class UserAccountDataHolder {
ImageView imgIcon;
TextView txtTitle;
}
}
and the appropriate list view snippet of the activity method
List<UserAccountData> user_data = new ArrayList<UserAccountData>();
user_data.add(new UserAccountData(R.drawable.username_icon,"userName");
user_data.add(new UserAccountData(R.drawable.password_icon,"password");
usersArray = new UserAccountData[user_data.size()];
user_data.toArray(usersArray);
UserAccountAdapter adapter = new UserAccountAdapter(this, R.layout.user_accounts_row, usersArray);
userAccountsListView = (ListView) findViewById(R.id.userAccounts);
userAccountsListView.setAdapter(adapter);
Attaching a picture of what I would like to accomplish
Before click:
after click
thanks
You could fill up a list of TextViews in your adapter while you inflated. After inflation, you'd have access to all the TextViews, and then in your onItemClickListener, when someone clicked the item in in Position 2 (in this case at least), you simply reference your list of TextViews, get the appropriate one, and change the parameters on the fly.
If you have a set, small number of rows like this a TableLayout is a better fit than a ListView. Then you could easily customize the xml of the two TextViews. Another option is to call setRawInputType to set the password on just the second TextView. You'd need to do that in the adapter's getView function based on the view's position.
Well I got around this problem by displaying the password as plain-text in an AlertDialog when the user clicks the ListView entry.
I have a ListFragment where I want certain rows to be a certain color. I basically followed this: Creating a ListView and setting the background color of a view in each row
However, getView is never called. Does anyone know why?
public class TrackerFragment extends ListFragment
{
private String[] list;
#Override
public void onActivityCreated(Bundle myBundle)
{
super.onActivityCreated(myBundle);
list = null;
ListView lv = getListView();
setListAdapter(null);
setEmptyText("Touch a connection to view tracker information.");
lv.setTextFilterEnabled(true);
}
public void updateList(String[] list)
{
this.list = list;
setListAdapter(new ColoredArrayAdapter(getActivity(),R.layout.list_item,list));
}
}
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="7dp"
android:textSize="14sp"
android:id="#+id/line">
</TextView>
I am updating the list like this from my activity:
TrackerFragment tf = (TrackerFragment) fragmentManager.findFragmentById(R.id.tracker1);
tf.updateList(result);
My ColoredArrayAdapter
public class ColoredArrayAdapter extends ArrayAdapter{
private String[] list;
public ColoredArrayAdapter(Context context, int textViewResourceId,
Object[] objects) {
super(context, textViewResourceId, objects);
list = new String[objects.length];
for (int i = 0; i < list.length; i++)
list[i] = (String) objects[i];
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View vi = convertView;
ViewHolder holder;
if (convertView == null)
{
vi = LayoutInflater.from(getContext()).inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.line = (TextView) vi.findViewById(R.id.line);
vi.setTag(holder);
}
else
holder = (ViewHolder) vi.getTag();
for (int i = 0; i < list.length; i++)
{
if (list[i].contains("OUT OF LOCK"))
{
System.out.println("OUT OF LOCK");
holder.line.setText(list[i]);
//holder.line.setTextColor(R.color.white);
holder.line.setBackgroundResource(R.color.red);
}
else if(list[i].contains("IN LOCK"))
{
System.out.println("In LOCK");
holder.line.setText(list[i]);
//holder.line.setTextColor(R.color.white);
holder.line.setBackgroundResource(R.color.green);
}
else
holder.line.setText(list[i]);
}
return vi;
}
}
What am I doing wrong?
Edit: added list_item.xml, where line is found.
Edit2: added extended array adapter
Now my problem is that, every row is either all green or red, when I just want certain individual rows to be either red or green. Also, none of the text is showing up.
Your current getView implementation should be moved into a ListAdapter implementation instead of your TrackerFragment class. Since you're using ArrayAdapter, you can subclass that and put the code in there. ArrayAdapter already implements getView, but you'll override it to provide your specialized behavior.
The reason you're getting a NullPointerException is because you're calling getView and passing in the list view, which does not have a tag associated with it -- so holder = (ViewHolder) vi.getTag(); assigns null to holder. That said, you shouldn't be calling getView directly. The system will call that for you whenever it needs to display a list item. When the system calls the getView method, it initially passes in null to have the views created, and every call where convertView is not null is a view created by that method.
Looks like the same problem as the post you linked: the getView() method isn't nested inside the class.
Or your code doesn't show anything that would call it either.
The more I look over this, the more I wonder about the basic premise you are using. I think you're making it overly complicated. I would do it like this:
public View getView(View convertView)
{
View vi = convertView;
TextView viText = null;
if (vi == null)
vi = LayoutInflater.from(getActivity()).inflate(R.layout.list_item, null);
viText = (TextView)vi.findViewById(R.id.line);
if (viText == null) return vi;
String viString = viText.getText().toString();
if (viString.contains("OUT OF LOCK"))
{
viText.setBackgroundResource(R.color.red);
}
else if (viString.contains("IN LOCK"))
{
viText.setBackgroundResource(R.color.green);
}
return vi;
}
I don't think you are using the holder in the way you think... the loop you have in there will just loop through setting the background resource to whatever the last trigger to set the backgroundResource is, no matter what.
If I have missed the point in this, let me know. But, my basic thought would be to remove as much complexity as you can until it works, and if you've had to remove something important, slowly add it back in.
I have the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView
android:id="#+id/ListView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1.0"
android:textColor="#android:color/white"
android:dividerHeight="1px"
android:listSelector="#drawable/highlight_sel"
/>
</LinearLayout>
And the code:
private ListView lv1;
private String lv_arr[]={"Item 1","Item 2","Item 3","Item 4"};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.newsmenu);
lv1=(ListView)findViewById(R.id.ListView01);
// By using setAdpater method in listview we an add string array in list.
lv1.setAdapter(
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
lv_arr));
}
I want the text color of Item 2 (or 1 or 3 or 4) to appear dynamically as red (denoting a new item) or white (default). Is there a way to do this?
I already have a selector present, which is why I used ListView. I've search the Internet and this site, and I have not seen this question broached.
So is it possible?
Yes everything is possible. you need to write your own adapter implementation basically overriding the getView Method in the adapter. search google and stack you will find many tutorials on how to write an adapter.
Writing a special adapter to override getView in simple adapter is the way to change the text color alternating on the lines of your choice in a listview. I took the example which has been repeated many times on this website and added a way to change the text color. position mod length to select the color position can be replaced with any scheme you like. The text view "business" can be the first line of your layout like mine--or use the android.R.id.text1.
public class SpecialAdapter extends SimpleAdapter {
private int[] colors = new int[] { 0x30FF0000, 0x300000FF };
public SpecialAdapter(Context context, List<HashMap<String, String>> items, int resource, String[] from, int[] to) {
super(context, items, resource, from, to);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
int colorPos = position % colors.length;
//view.setBackgroundColor(colors[colorPos]); //old example
TextView tv1 = (TextView)view.findViewById(R.id.business); //new
tv1.setTextColor(colors[colorPos]); //new
return view;
}
}
Just use SpecialAdapter instead of SimpleAdapter in your app.
Here's an example of a getView method. Note that it's using a viewholder for efficiency. If you want to know more about that, let me know.
public View getView(int position, View convertView, ViewGroup parent) {
tempDeal = exampleBoxArrayList.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = inflator.inflate(R.layout.list_item_example_box, null);
holder = new ViewHolder();
holder.divider = (RelativeLayout) convertView.findViewById(R.id.example_box_divider);
holder.merchantName = (TextView) convertView.findViewById(R.id.example_box_merchant_name);
holder.expireDate = (TextView) convertView.findViewById(R.id.example_box_expire_date);
holder.description = (TextView) convertView.findViewById(R.id.example_box_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (tempDeal.isDivider()) {
holder.divider.setVisibility(View.VISIBLE);
} else {
holder.divider.setVisibility(View.GONE);
}
holder.merchantName.setText(tempDeal.getMerchantName());
holder.expireDate.setText(tempDeal.getExpiryDateString());
holder.description.setText(tempDeal.getPriceOption().getDescription());
return convertView;
}
As you can see, I call the isDivider() method on my custom object (this method looks at a boolean set on data load). This method is used to turn the visibility of part of the layout on or off.
Alternatively, you could load a completely new layout based on this same concept.