Basically I am inserting text along with new line character in mysql db like this:
$message = 'Hello World' . "\n";
$message .= 'This is a test' . "\n";
$message .= 'Thanks;
When saving message in db, I have tried setting value of new line as \n, \\n, \\\n, \\\\n and even <br> instead of \n then used fromHtml on android app but nothing working out.
In db, I can see there is new line.
On Android app I want to have new line character if any showed in TextView, I have tried various things but nothing is working, I've tried things like:
String message = m.getMessage().toString();
message = message.replaceAll("\\n",
System.getProperty("line.separator"));
Here instead of "\\n", I have also tried "\\\n", "\n" and even "\\\\n"
And:
Spanned html = Html.fromHtml(m.getMessage());
And:
message.replaceAll("\n","<br />");
message.replaceAll("\r\n","<br />");
message.replaceAll("\r","<br />");
And:
android:singleLine="false"
With various things tried, in TextView I get text like these permutations:
Hello WorldThis is a testThanks
Hello World\This is a test\Thanks
Hello World\nThis is a test\nThanks
Here is my complete code:
public class MessageListAdapter extends BaseAdapter {
private Context context;
private List<ListMessage> messagesItems;
public MessageListAdapter(Context context, List<ListMessage> navDrawerItems) {
this.context = context;
this.messagesItems = navDrawerItems;
}
#Override
public int getCount() {
return messagesItems.size();
}
#Override
public Object getItem(int position) {
return messagesItems.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#SuppressLint("InflateParams")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
/**
* The following list not implemented reusable list items as list items
* are showing incorrect data Add the solution if you have one
* */
ListMessage m = messagesItems.get(position);
LayoutInflater mInflater = (LayoutInflater) context
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
// Identifying the message owner
if (messagesItems.get(position).isSelf()) {
// message belongs to you, so load the right aligned layout
convertView = mInflater.inflate(R.layout.list_item_message_right,
null);
} else {
// message belongs to other person, load the left aligned layout
convertView = mInflater.inflate(R.layout.list_item_message_left,
null);
}
TextView lblFrom = (TextView) convertView
.findViewById(R.id.lblMsgFromListMessage);
TextView lblTo = (TextView) convertView
.findViewById(R.id.lblMsgToListMessage);
lblFrom.setText(m.getFromName());
// Spanned html = Html.fromHtml(m.getMessage());
// String message = html.toString();
String message = m.getMessage().toString();
message = message.replaceAll("\\n",
System.getProperty("line.separator"));
try {
lblTo.setText(message);
} catch (Exception e) {
lblTo.setText(message);
}
return convertView;
}
}
TextView in layout:
<TextView
android:id="#+id/lblMsgToListMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="50dp"
android:background="#drawable/chat_bg_msg_from"
android:paddingBottom="#dimen/five_dp"
android:paddingLeft="#dimen/ten_dp"
android:paddingRight="#dimen/ten_dp"
android:paddingTop="#dimen/five_dp"
android:textColor="#color/chat_title_gray"
android:textSize="#dimen/eighteen_sp" />
Is something wrong with baseAdaptor? Somewhere I saw this link, it suggested SimpleAdapter has problem setting html but in my case this doesn't help either since I am using BaseAdapter.
I will be really realy thankful for your help as I have wasted five hours on this new line issue :(
Try:
message = message.replaceAll("\\n", "
");
or (only if there really are double backslashes in the input)
message = message.replaceAll("\\n", "
");
Update: Replacements were not working as the search string (\n) had been stripped out (escaped) by the sql database. This answer supplied a workaround which involves running unescape on the string prior to setting it on the TextView.
Related
public class DietSlideAdapter extends PagerAdapter {
Context context;
LayoutInflater layoutInflater;
public DietSlideAdapter(Context context){
this.context = context;
}
//Arrays
public int[] slide_images2 = {
R.drawable.breakfast,
R.drawable.cabbage,
R.drawable.protein,
R.drawable.fish,
R.drawable.tea
};
public String [] slide_headings2 = {
"heading1","heading2","heading3","heading4","heading5"
};
public String [] slide_descs2 = {
"Try fortified ready-to-eat or cooked breakfast cereals with fruit. Fortified cereals have added nutrients, like calcium.",
"Choose a variety of vegetables and fruits, like carrots, cooked greens, bananas, and melon.\n" +
"Eat plenty of beans and whole grains. Try brown rice or oatmeal.",
"Iron keeps your blood healthy. Folic acid helps prevent birth defects.\n" +
"Even before you find out you're pregnant, it's smart to start eating plenty of folate-rich foods like fortified cereals, asparagus, lentils, wheat germ, oranges, and orange juice.",
"Avoid fish and shellfish with high levels of mercury. Common fish that are low in mercury include shrimp, and catfish. ",
"Drink decaffeinated coffee or tea.\n" +
"Drink water or seltzer instead of soda.\n" +
"Don't drink alcohol."
};
#Override
public Object instantiateItem(ViewGroup container, int position){
layoutInflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
View view = layoutInflater.inflate(R.layout.slide2, container, false);
ImageView slideImageView2 = (ImageView) view.findViewById(R.id.slideImage2);
TextView slideHeading2 = (TextView) view.findViewById(R.id.slide_heading2);
TextView slideDescription2 = (TextView) view.findViewById(R.id.slide_desc2);
slideImageView2.setImageResource(slide_images2[position]);
slideHeading2.setText(slide_headings2[position]);
slideDescription2.setText(slide_descs2[position]);
slideDescription2.setMovementMethod(new ScrollingMovementMethod());
container.addView(view);
return view;
}
}
I want to make my app support multiple language.
I want to access string.xml instead of the Hard-coded strings in the Arrays slide_headings2 and slide_desc2.
I looked everywhere and I tried context.getResources().getString(R.string.string_name).
How do i replace the hard coded strings in the array with a R.string from string.xml?
String[] slide_images2 = {
context.getString(R.string.heading1),
context.getString(R.string.heading2),
context.getString(R.string.heading3),
context.getString(R.string.heading4),
context.getString(R.string.heading5),
};
Simple like this ^
I've got an SQLite database with a units table. The units table is set up with only two columns:
create table units (_id INTEGER PRIMARY KEY, desc TEXT)
Example data for a row in this table is:
_id: 4
desc: "Helix #5 [2231]"
The "[2231]" substring is important, and I'd like to change its color to a medium gray color. Id also prefer to do this to the data in the desc column, as opposed to manipulating it with java.
So, I query for the data:
/**
* Get all unit records for display in spinner
*/
public Cursor getAllUnitRecords(){
String sql = "select * from units order by `desc`";
return db.rawQuery(sql, null);
}
My spinner looks like this:
<Spinner
android:id="#+id/UnitSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dropdown" />
And I get the data to the spinner like this:
// Prepare unit dropdown
Cursor units = db.getAllUnitRecords();
MatrixCursor unitsMatrixCursor = new MatrixCursor(new String[] { "_id", "desc" });
unitsMatrixCursor.addRow(new Object[] { 0, "" });
MergeCursor unitsMergeCursor = new MergeCursor(new Cursor[] { unitsMatrixCursor, units });
String[] unitsFrom = new String[]{"desc"};
int[] unitsTo = new int[]{android.R.id.text1};
Spinner unitSpinner = (Spinner) findViewById(R.id.UnitSpinner);
SimpleCursorAdapter unitAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_dropdown_item, unitsMergeCursor, unitsFrom, unitsTo, 0);
unitAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
unitSpinner.setAdapter(unitAdapter);
Since I'd like to color the "[2231]" substring a medium gray color, I thought I might be able to change the value of desc in the database, so that it looks like this:
"Helix #5 <font color='#6e737e'>[2231]</font>"
I did that only because I was searching the internet, and it seemed like it might work. Well, that doesn't work, as the tags are just output, instead of changing the color. What is wrong, and how can I fix it? I guess I'm open to a different solution if necessary, but this Android stuff is hard for me, as I don't work on it very often, so I was trying to go for the easiest solution.
UPDATE #1 ----------------------
So #MartinMarconcini was kind enough to point me in the right direction, and I copy and pasted his colorSpan method into my activity class to test it out. I then looked all around Stack Overflow for any clues as to how to modify the text of my spinner, and then how to modify the text that's in a SimpleCursorAdapter.
I found these questions with answers:
Android, using SimpleCursorAdapter to set colour not just strings
Changing values from Cursor using SimpleCursorAdapter
That gave me some ideas, so I tried to work with that:
// Prepare unit dropdown
Cursor units = db.getAllUnitRecords();
MatrixCursor unitsMatrixCursor = new MatrixCursor(new String[] { "_id", "desc" });
unitsMatrixCursor.addRow(new Object[] { 0, "" });
MergeCursor unitsMergeCursor = new MergeCursor(new Cursor[] { unitsMatrixCursor, units });
String[] unitsFrom = new String[]{"desc"};
int[] unitsTo = new int[]{android.R.id.text1};
Spinner unitSpinner = (Spinner) findViewById(R.id.UnitSpinner);
SimpleCursorAdapter unitAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_dropdown_item, unitsMergeCursor, unitsFrom, unitsTo, 0);
unitAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
/* NEW CODE STARTS HERE */
unitAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View aView, Cursor aCursor, int aColumnIndex) {
if (aColumnIndex == 1) {
String desc = aCursor.getString(aColumnIndex);
TextView textView = (TextView) aView;
final Spannable colorized = colorSpan(desc);
textView.setText(TextUtils.isEmpty(colorized) ? desc + "a" : colorized + "b");
return true;
}
return false;
}
});
/* NEW CODE ENDS HERE */
unitSpinner.setAdapter(unitAdapter);
Notice I added the letter "a" if there was no text, and "b" if there was text. Sure enough, the "a" and "b" were added to my spinner items, but there was no color change! So, I am trying ... but could still use some help. Here is an image of what I'm seeing:
As mentioned in the comments, the presentation shouldn’t be tied to the logic. This is a presentation problem. You want to display a text and you want part of that text to be colored.
So, anywhere in your app where you need to display/present this text to the user, say…
someTextViewOrOtherWidget.setText(yourString);
…you’ll then have to call a method that does the coloring for you.
Example…
I’d move this code into a separate method/place for reuse and make it more re-usable by not hardcoding the [] and such,but this is how a simple example would look:
private Spannable colorSpan(final String text) {
if (TextUtils.isEmpty(text)) {
// can't colorize an empty text
return null;
}
// Determine where the [] are.
int start = text.indexOf("[");
int end = text.indexOf("]");
if (start < 0 || end < 0 || end < start) {
// can't find the brackets, can't determine where to colorize.
return null;
}
Spannable spannable = new SpannableString(text);
spannable.setSpan(
new ForegroundColorSpan(Color.BLUE)
, start
, end
, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
return spannable;
}
And you’d use it like…
String text = "Hello [123123] how are you?";
final Spannable colorized = colorSpan(text);
textView.setText(TextUtils.isEmpty(colorized) ? text : colorized);
I hope this gives you a better idea how to get started.
So, with a lot of help from #MartinMarconcini, I finally achieved what needed to be done, and so I wanted to leave "the answer" here, in case anyone else wants to see what needed to be done. I ended up making a custom cursor adapter, and although I'm still dumbfounded by the complexity of Android Studio, it works!
First, in the activity, the way SimpleCursorAdapter was being used ended up getting changed to the custom cursor adapter (which extends SimpleCursorAdapter).
These lines:
Spinner unitSpinner = (Spinner) findViewById(R.id.UnitSpinner);
SimpleCursorAdapter unitAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_dropdown_item, unitsMergeCursor, unitsFrom, unitsTo, 0);
unitAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
unitSpinner.setAdapter(unitAdapter);
Were replaced with these lines:
Spinner customUnitSpinner = (Spinner) findViewById(R.id.UnitSpinner);
UnitSpinnerCursorAdapter customUnitAdapter = new UnitSpinnerCursorAdapter(this, R.layout.unit_spinner_entry, unitsMergeCursor, unitsFrom, unitsTo, 0);
customUnitSpinner.setAdapter(customUnitAdapter);
I put the custom cursor adapter in its own file, and I put Martin's colorSpan method in there too (for now):
package android.skunkbad.xxx;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class UnitSpinnerCursorAdapter extends SimpleCursorAdapter {
private Context context;
private int layout;
public UnitSpinnerCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
this.context = context;
this.layout = layout;
}
/**
* newView knows how to return a new spinner option that doesn't contain data yet
*/
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
super.newView(context, cursor, parent);
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
int descCol = c.getColumnIndex("desc");
String desc = c.getString(descCol);
final Spannable colorized = colorSpan(desc);
TextView unit_spinner_entry = (TextView) v.findViewById(R.id.custom_spinner_entry_desc);
if (unit_spinner_entry != null) {
unit_spinner_entry.setText(TextUtils.isEmpty(colorized) ? desc : colorized);
}
return v;
}
/**
* bindView knows how to take an existing layout and update it with the data pointed to by the cursor
*/
#Override
public void bindView(View view, Context context, Cursor cursor) {
super.bindView(view, context, cursor);
int descCol = cursor.getColumnIndex("desc");
String desc = cursor.getString(descCol);
final Spannable colorized = colorSpan(desc);
TextView unit_spinner_entry = (TextView) view.findViewById(R.id.custom_spinner_entry_desc);
if (unit_spinner_entry != null) {
unit_spinner_entry.setText(TextUtils.isEmpty(colorized) ? desc : colorized);
}
}
private Spannable colorSpan(final String text) {
if (TextUtils.isEmpty(text)) {
// can't colorize an empty text
return null;
}
// Determine where the [] are.
int start = text.indexOf("[");
int end = text.indexOf("]");
if (start < 0 || end < 0 || end < start) {
// can't find the brackets, can't determine where to colorize.
return null;
}
end++; /* Why do we even need this ? */
Spannable spannable = new SpannableString(text);
spannable.setSpan(
new ForegroundColorSpan(Color.rgb(100,100,100))
, start
, end
, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
return spannable;
}
}
Finally, I had to make a layout file for each entry in the spinner:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:id="#+id/custom_spinner_entry_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16dp"
android:textColor="#FFFFFF"
android:layout_marginLeft="10dp" />
</LinearLayout>
Thanks Martin! It might seem like nothing to you, but it was hard for me, and I couldn't have done it without your help.
One note: I had to put end++; in your colorSpan method, because for some reason it wasn't coloring the closing bracket.
I am fetching JSON Data to listView successfully, but i want to change the color of the textView in that listView by comparing JSON data.
For ex-
if in JSON {"status":"approved"} - green color
else if {"status" : "disapproved"} - red color
I'm able to change it using getView in SimpleAdapter but not able to compare strings there.
when i print the status, its is only showing "approved".
i can't filter "disapproved" in else if condition.
SimpleAdapter adapter = new SimpleAdapter(getActivity(), mylist, R.layout.list_leave_approval,
new String[]{
TAG_STATUS
},
new int[]{
R.id.status}) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//return super.getView(position, convertView, parent);
View v = convertView;
if (v == null) {
// View v = convertView;
LayoutInflater vi = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_leave_approval, null);
TextView test = (TextView) v.findViewById(R.id.status);
// String str = jsonObject.getString("status");
Log.d("Day", "get view STATUS: " + strStatus);
// jsonObject = jsonArray.getJSONObject(i);
if (strStatus.equals(" Approved ")) {
test.setBackgroundColor(getResources().getColor(R.color.green));
// txtStatus.setText("DEC");
} else if (strStatus.equals(" Disapproved ")) {
test.setBackgroundColor(getResources().getColor(android.R.color.holo_red_light));
}
}
return super.
getView(position, v, parent);
}
};
list.setAdapter(adapter);
You can do like this.
Here string will be your parsed json string.
JSONObject jObj = new JSONObject(String);
if (jObj.getString("status").equalsIgnoreCase("approved")) {
textView1.setTextColor(Color.GREEN)
} else {
textView1.setTextColor(Color.RED)
}
I advise you to read about MVC design pattern
You should keep your JSON in a data object (model in MVC) for example Status object and put all the needed data from json to this object
When filling the listView adjust the view according to the statuses:
if(items[index].getStatus.equals("approved"))
textView.setTextColor(ContextCompat.getColor(context, R.color.green));
else if(items[index].getStatus.equals("disapproved"))
textView.setTextColor(ContextCompat.getColor(context, R.color.red));
You will need to override a more complex type of Adapter in order to do what you want, SimpleAdapter only allows setting the text in a very simple way as a String.
Something like ArrayAdapter might be a better fit, in that case you could use the code Michael A has shown in the getView method of ArrayAdapter to change the display of the text.
You might also look into SpannableString which allows adding color to text, but this would require being able to provide code to determine the status of the item.
I'm running into a very strange ListView behaviour. I have a simple chat application which uses a ListView and a custom implementation of BaseAdapter to show messages.
The idea I had was to shade messages from the "local" user grey and have messages from the "remote" user white, to help the user distinguish between the two.
The two screenshots below show what's happening. The second is the exact same activity, xml etc etc, simply scrolled down a bit.
Scrolled up:
Scrolled down:
Look at the message sent by "Me" # 23:05. When at the top it has no contrast to its neighbours, but when it is scrolled to the bottom, the difference is clear to see.
This occurs on a nexus 4 and 7 on 4.2.2, and a GS3 running 4.1.2.
Here is the XML for one of the ListView items:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/activity_view_conversation_message_list_item_wrapper"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="2dp"
android:paddingLeft="5dp"
android:paddingRight="10dp" >
<ImageView
android:id="#+id/activity_view_conversation_message_list_item_user_image"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="5dp"
android:src="#drawable/default_user_image" />
<TextView
android:id="#+id/activity_view_conversation_message_list_item_heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="1dp"
android:layout_toRightOf="#+id/activity_view_conversation_message_list_item_user_image"
android:text="Martyn"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="#+id/activity_view_conversation_message_list_item_contents"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/activity_view_conversation_message_list_item_heading"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp"
android:layout_marginTop="2dp"
android:layout_toLeftOf="#+id/activity_view_conversation_message_list_item_ack"
android:layout_toRightOf="#+id/activity_view_conversation_message_list_item_user_image"
android:text="Hello this is some text"
android:textColor="#333333"
android:textIsSelectable="true"
android:textSize="18sp" />
<TextView
android:id="#+id/activity_view_conversation_message_list_item_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginTop="2dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="2dp"
android:text="12:08"
android:textSize="14sp" />
<ImageView
android:id="#+id/activity_view_conversation_message_list_item_ack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/activity_view_conversation_message_list_item_time"
android:layout_marginRight="2dp"
android:layout_marginTop="8dp"
android:layout_toLeftOf="#+id/activity_view_conversation_message_list_item_time"
android:src="#drawable/red_dot_8dp" />
</RelativeLayout>
And here is where I set the colour of the RelativeLayout:
if(localUserId.equals(remoteUserId)){
itemLayout.setBackgroundColor(Color.parseColor("#F9F9F9"));
}
That code runs inside the getView() method of my Adapter.
I've Googled this a fair bit and turned up nothing, there are a lot of SO questions regarding the android:cacheColorHint issue but I don't think that is what is going on here.
Has anyone run into this before? I'm stumped!
EDIT: Here's the baseadapter code:
public class MessageListAdapter extends BaseAdapter {
private ArrayList<Message> messageList;
Context context;
/**
* Constructor
* #param newConversationsList An ArrayList of Conversation objects that this adapter will use
* #param newContext The context of the activity that instantiated this adapter
*/
MessageListAdapter(ArrayList<Message> newMessageList, Context newContext){
messageList = newMessageList;
//reload();
context = newContext;
}
public int getCount() {
return messageList.size();
}
public Object getItem(int position) {
return messageList.get(position);
}
public long getItemId(int position) {
return position;
}
/**
* Adds a message to the chat list
* #param message A Message object containing all the message's information
*/
public void add(Message message){
//nMessagesToShow++; //A new message has been added, so increase the number to show by one
Log.d(TAG, "COUNT: "+getCount());
//refresh();
}
public void refresh(){
this.notifyDataSetChanged();
}
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if(view!=null){
//return view;
}
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//Get the Message object from the list
Message message = messageList.get(position);
//Get the data from the message
String senderId = message.getFromUser();
int messageType = message.getType();
String senderFirstName;
ImageView userImage, messageImage;
TextView messageHeading, messageBody;
switch(messageType){
case Message.MESSAGE_TYPE_TEXT: //Standard text message
//The layout we inflate for this list item will vary depending on whether this message has the same sender as the previous
if(position>0 && senderId.equals(messageList.get(position-1).getFromUser())){ //True if this is not the first message AND the sender id matches that of the previous message
view = vi.inflate(R.layout.activity_view_conversation_message_list_item_alternate, null); //Inflate an alternate version of the list item which has no heading or user image
}
else{ //This is the first message OR the sender id is different to the previous
view = vi.inflate(R.layout.activity_view_conversation_message_list_item, null); //Inflate the standard version of the layout
userImage = (ImageView) view.findViewById(R.id.activity_view_conversation_message_list_item_user_image);
messageHeading = (TextView) view.findViewById(R.id.activity_view_conversation_message_list_item_heading);
//Use the sender's ID to get the sender's image and first name
Contact contact = database.getContact(senderId);
if(senderId.equals(localUserId)){ //True if the local user sent this message
senderFirstName = "Me";
}
else{
senderFirstName = contact.getFirstName();
}
userImage.setImageBitmap(contact.getImageBitmap(100, 100, 6));
messageHeading.setText(senderFirstName);
}
messageBody = (TextView) view.findViewById(R.id.activity_view_conversation_message_list_item_contents);
messageBody.setText(message.getContents(null));
break;
case Message.MESSAGE_TYPE_IMAGE: //Image message
view = vi.inflate(R.layout.activity_view_conversation_message_list_item_image, null); //Inflate a list item template for displaying an image
userImage = (ImageView) view.findViewById(R.id.activity_view_conversation_message_list_item_user_image);
//Sender's first name
messageHeading = (TextView) view.findViewById(R.id.activity_view_conversation_message_list_item_heading);
Contact contact = database.getContact(senderId);
if(senderId.equals(localUserId)){ //True if the local user sent this message
senderFirstName = "Me";
}
else{
senderFirstName = contact.getFirstName();
}
messageHeading.setText(senderFirstName);
messageImage = (ImageView) view.findViewById(R.id.activity_view_conversation_message_list_item_image);
String imageResourceId = null;
//The message is a JSON object containing several fields, one of which is the file name which we will use to get the image
try {
JSONObject messageJSON = new JSONObject(message.getContents(null));
String imagePath = Environment.getExternalStorageDirectory()+"/epicChat/resources/"+messageJSON.getString("fileName");
int imageWidth = messageJSON.getInt("width"); //We want the dimensions in order to calculate the aspect ratio of the image
int imageHeight = messageJSON.getInt("height");
if(messageJSON.has("resourceId")){
imageResourceId = messageJSON.getString("resourceId"); //This is used when opening the image gallery
}
int displayWidth = 300;
int displayHeight = (int) ((float) imageHeight / (float) imageWidth * (float) displayWidth);
String imagePathFull = imagePath+displayWidth+displayHeight; //For the caching
Bitmap originalImage = null;
//Check the bitmap cache exists. If not, reinstantiate it
if(MainActivity.bitmapCache==null){ //Cache is null
MainActivity.loadBitmapCache();
}
else{ //Cache is not null, so check it to see if this image is in it
originalImage = MainActivity.bitmapCache.get(imagePathFull);
}
if(originalImage==null){ //True if the bitmap was not in the cache. So we must load from disk instead
new Utils.LoadBitmapAsync(imagePath, messageImage, displayWidth, displayHeight, MainActivity.bitmapCache).execute();
messageImage.getLayoutParams().height = displayHeight;
}
else{
messageImage.setImageBitmap(originalImage);
}
}
catch (JSONException e) {
Log.e(TAG, "Error reading image JSON: "+e.toString());
}
if(imageResourceId!=null){ //Only attach the listener if we got a valid resource ID
final String recourceIdFinal = imageResourceId;
final String conversationIdFinal = message.getUserList();
messageImage.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent showConversationImageGalleryIntent = new Intent(context, ViewConversationImageGalleryActivity.class);
showConversationImageGalleryIntent.putExtra("conversationId", conversationIdFinal);
showConversationImageGalleryIntent.putExtra("resourceId", recourceIdFinal);
startActivityForResult(showConversationImageGalleryIntent, ACTION_SHOW_CONVERSATION_IMAGE_GALLERY);
}
});
}
userImage.setImageBitmap(contact.getImageBitmap(100, 100, 6));
break;
case Message.MESSAGE_TYPE_INVALID:
default:
break;
}
//Some layout items are present in all layouts. Typically these are the status indicator and the message time
RelativeLayout itemLayout = (RelativeLayout) view.findViewById(R.id.activity_view_conversation_message_list_item_wrapper);
//If the message is from the local user, give it a subtle grey background
if(localUserId.equals(message.getFromUser())){
itemLayout.setBackgroundColor(Color.parseColor("#E9E9E9"));
}
else{
itemLayout.setBackgroundColor(Color.parseColor("#FFFFFF"));
}
TextView messageTimeText = (TextView) view.findViewById(R.id.activity_view_conversation_message_list_item_time);
messageTimeText.setText(message.getFormattedTime());
ImageView messageStatusImage = (ImageView) view.findViewById(R.id.activity_view_conversation_message_list_item_ack);
//Set the status image according to the status of the message
switch(message.getStatus()){
case Message.MESSAGE_STATUS_PENDING: //Pending messages should have a red dot
messageStatusImage.setImageResource(R.drawable.red_dot_8dp);
messageStatusImage.setVisibility(View.VISIBLE);
break;
case Message.MESSAGE_STATUS_ACK_SERVER: //Messages that reached the server should have an orange dot
messageStatusImage.setImageResource(R.drawable.orange_dot_8dp);
messageStatusImage.setVisibility(View.VISIBLE);
break;
case Message.MESSAGE_STATUS_ACK_RECIPIENT: //Messages that reached the recipient should have an green dot
messageStatusImage.setImageResource(R.drawable.green_dot_8dp);
messageStatusImage.setVisibility(View.VISIBLE);
break;
case Message.MESSAGE_STATUS_NOT_SET: //Not set typically means the message came from another user, in which case the status image should be hidden
default: //Also default here
messageStatusImage.setVisibility(View.INVISIBLE);
break;
}
return view;
}
}
Since there's no else statement matching your if, this is probably due to view recycling. When an item from a ListView scrolls off the screen, the operating system removes it and hands it back to the adapter in the same state it was removed. This means you need to set the background color when it's not the local user's message as well.
I have a database which has a name of an animal, and in an other column a sound of the animal.
The listview works fine, and now I would like to extend it with an image out of the assets.
For this I've read about how to get the assets data and use it.
An example I've tried worked fine for me.
Now I want this "assets" code (at least I think I want this) in the extended BaseAdapter class.
Unfortunately I'm doing something wrong as I can't use the getAssets() in the BaseAdapter.
The problem starts in the try-catch block: " getAssets " doesn't get recognized
Which way would I think of solving this?
Creating another class in which this assets code can run in an extended "Activity"?
Or are there better ways of showing an image via database info in a listview?
Thank you for your support in my quest to get familiar with Android / Java.
public View getView(int position, View convertView, ViewGroup parent) {
// get view reference
View view = convertView;
// if null
if(view == null) {
// inflate new layout
view = mInflater.inflate(R.layout.layout_list_item, null);
// create a holder
ViewHolder holder = new ViewHolder();
// find controls
holder.txtName = (TextView)view.findViewById(R.id.txtName);
holder.txtPicture = (ImageView)view.findViewById(R.id.txtPicture);
// set data structure to view
view.setTag(holder);
}
// get selected user info
UserInfo userInfo = mListUserInfo.get(position);
// if not null
if(userInfo != null) {
// query data structure
ViewHolder holder = (ViewHolder)view.getTag();
// set data to display
holder.txtName.setText(userInfo.getName() + ", " + userInfo.getPicture() );
try {
// get input stream
InputStream ips = getAssets().open( userInfo.getPicture() + ".jpg");
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
// set image to ImageView
holder.txtPicture.setImageDrawable( d );
}
catch(IOException ex) {
Log.e("Imageloading", "Could not load '" + ex.getMessage()+ "'!");
}
}
// return view
return view;
}
I've just edited the code.
To solve the getAssets() thing I've done the following:
holder.txtPicture.setImageDrawable( getSomePicture(null, userInfo.getPicture() + ".jpg") );
public Drawable getSomePicture(Context myContext, String WhichPicture) throws IOException {
// get input stream
InputStream ips = myContext.getAssets().open( WhichPicture );
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
return d;
}
This still is not the solution, researching some more....
Found an interesting source for Lazy Loading
to call getAssets() function in non-activity class you need reference to Context. In your updated code you called function 'getSomePicture()' and passed null to myContext parameter. that means your code will fail because you have myContext.getAssets() later in your method code.
try to do this in your getView method:
Context context = getContext();
holder.txtPicture.setImageDrawable( getSomePicture(context, userInfo.getPicture() + ".jpg") );
You won't have access to the getAssets() method because that is a method that only the Context (and it's subclass, Activity) have. What I find is easiest here is to make your BaseAdapter subclasses private inner classes of the Activity in which it is to be used. That way, you'll have access to the getAssets() method.
Here is what this might look like:
public class YourExampleActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
}
private class YourListAdapter extends BaseAdapter {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// get view reference
View view = convertView;
// if null
if(view == null) {
// inflate new layout
view = mInflater.inflate(R.layout.layout_list_item, null);
// create a holder
ViewHolder holder = new ViewHolder();
// find controls
holder.txtName = (TextView)view.findViewById(R.id.txtName);
holder.txtPicture = (ImageView)view.findViewById(R.id.txtPicture);
// set data structure to view
view.setTag(holder);
}
// get selected user info
UserInfo userInfo = mListUserInfo.get(position);
// if not null
if(userInfo != null) {
// query data structure
ViewHolder holder = (ViewHolder)view.getTag();
// set data to display
holder.txtName.setText(userInfo.getName() + ", " + userInfo.getPicture() );
try {
// get input stream
InputStream ips = getAssets().open( userInfo.getPicture() + ".jpg");
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
// set image to ImageView
holder.txtPicture.setImageDrawable( d );
}
catch(IOException ex) {
Log.e("Imageloading", "Could not load '" + ex.getMessage()+ "'!");
}
}
// return view
return view;
}
// Override other key methods of BaseAdapter here
}
}
Alternatively, you could take in a Context object in the constructor when the BaseAdapter subclass is instantiated, and keep a reference to that with which to call getAssets() on. Please post a comment if you need an example of this.