Android ImageView size unexpectedly changing at runtime - android

In one of my activities, I have a table layout with cells that are added at runtime via a custom class. The layout for my cells is as follows:
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+cell/style_2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginBottom="5dp" >
<View
android:id="#+cell/divider"
android:layout_width="fill_parent"
android:layout_height="2dp"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="#FF000000" />
<ImageView
android:id="#+cell/image"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="#cell/divider"
android:layout_margin="5dp"
android:contentDescription="#string/row_thumbnail"
android:scaleType="fitCenter"/>
</RelativeLayout>
</TableRow>
This gets inflated by the following class:
public Cell(Context context) {
super(context);
addView(((LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.gallery_row_1, null));
}
When I inflate the cell, I also set an image to be used as a display, the problem is that the size of the image view is not staying as it should, the right edge is nowhere to be found, and the image is never displayed (probably way off to the right somewhere?), and I am not sure where my problem lies.
c = new Cell(this);
c.getImageView().setImageBitmap(BitmapFactory.decodeStream(assetManager.open("categories" + File.separator + sec + File.separator + filename)));
page.addView(c);
getImageView being a function in my Cell that returns the actual ImageView element.
I know the image is being placed in the ImageView, because when layout params are changed, I can see the image, just not sized appropriately.
The desired output should be a view, a dividing view on top, and an ImageView below, that fills the parent and is 100dp tall. The image, no matter the origional size, should be scaled and shown inside.
Also, if I comment out the line where I set the image to the ImageView, the layout bounds are correct, as viewed with Show Layout Bounds enabled.
My overall question is, why is my ImageView being re-sized when I apply an image.
Any and all help is greatly appreciated.

See this post on LayoutInflater for why your layout is getting mixed up. Since it seems your cell class is an inner class of some ViewGroup (since you're calling addView()), try using the following code:
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.gallery_row_1, this);
or
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.gallery_row_1, this, false);
addView (view);
instead of using
inflater.inflate(R.layout.gallery_row_1, null);
The inflate() call uses the second parameter (the container) to determine what type of LayoutParams to use to interpret the XML. If you pass null, all of the layout attributes are ignored. You should instead either call it with the actual container (which will automatically add it to the container) or call it with the container, and a third parameter telling it not to attach the view yet, and then do what you want with the inflated view.

Related

Custom ListView set Background Drawable in XML doesnt work

I'm getting frustrated about this:
When I define a custom ListView Layout,
Android Studio doesn't keep the background drawable I set in there.
Tried many things, and setting background programmatically doesn't work
since it's ignoring the layout_width which must be set to "wrap_content".
Actual style of background
Result without coding
If anyone could help me, I'd be very grateful !:)
EDIT:
I'm creating a Messenger and I want to display messages in a similar way to WhatsApp, where messages are shown in a listView. Depending on message is sent or received, items should be aligned ParentStart or ParentEnd.
But more importantly, if a message only contains a few chars, I don't want the ListItem Background to fill the entire screen, so it should be set dynamically.
I thought I could achieve this through simply setting wrap content in the parent layout file.
Files look like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentEnd="true">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="4dp"
android:layout_marginTop="4dp"
tools:background="#drawable/background_message_sent">
//Here are TextViews
</LinearLayout>
</RelativeLayout>
Here is my ListViewAdapter, where I set background
(#drawable/background_message_sent/received) programmatically.
However, this covers the entire width of ListView, regardless of message length.
#Override
public View getView(int pos, View convertView, ViewGroup parent) {
int currentUserID = 1;
int senderID = messagesArrayList.get(pos).getSenderID();
if (senderID == currentUserID){
View v = View.inflate(context, R.layout.layout_chat_message_sent, null);
v.setBackgroundResource(R.drawable.background_message_sent);
TextView tvMessageText = v.findViewById(R.id.tvMessageText);
TextView tvTimeStamp = v.findViewById(R.id.tvTimeStamp);
tvMessageText.setText(messagesArrayList.get(pos).getMessageText());
tvTimeStamp.setText(messagesArrayList.get(pos).getTimeStamp());
return v;
}
else {
View v = View.inflate(context, R.layout.layout_chat_message_received, null);
v.setBackgroundResource(R.drawable.background_message_received);
TextView tvMessageText = v.findViewById(R.id.tvMessageText);
TextView tvTimeStamp = v.findViewById(R.id.tvTimeStamp);
tvMessageText.setText(messagesArrayList.get(pos).getMessageText());
tvTimeStamp.setText(messagesArrayList.get(pos).getTimeStamp());
return v;
}
}
Well, after trying, I got the solution if anyone comes to this point:
You have to set your background drawable directly for each TextView, not for Parent Layouts.
These two Lines finally solved everything ^^
android:maxEms="14"
android:background="#drawable/background_message_sent"
<TextView
android:id="#+id/tvMessageText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginEnd="0dp"
android:layout_marginTop="5dp"
android:layout_weight="0.22"
android:text="42456456456546"
android:textColor="#color/tentakelPrimary"
android:maxEms="14"
android:background="#drawable/background_message_sent"/>

ImageButton displays differently when created dynamically

I have a GridLayout-based View to which I am dynamically adding several ImageButtons. I'm trying to understand why the ImageButtons are styled correctly when I inflate them from a layout xml file, but not when I create them using the ImageButton constructor directly.
The GridLayout and ImageButtons were previously both defined in the same layout .xml file (and rendered as expected):
<ScrollView
style="#style/my_list_style"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="12dp">
<GridLayout
android:id="#+id/my_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<!-- These ImageButtons are being converted to dynamic. -->
<ImageButton
style="#style/my_button_style"
android:src="#drawable/image1" />
<ImageButton
style="#style/my_button_style"
android:src="#drawable/image2" />
</GridLayout>
</LinearLayout>
</ScrollView>
To convert the ImageButtons to dynamic, I first removed them from the layout file and used code like the following to add them at runtime:
ImageButton imageButton = new ImageButton(context, null, R.style.my_button_style);
imageButton.setImageResource(R.drawable.image1);
parent.addView(imageButton);
But the buttons failed to render properly; they are not centered, and their sizes do not appear to be correct/uniform.
I then tried creating a new layout file, containing nothing but the ImageButton and its style:
<ImageButton
style="#style/my_button_style"/>
When I inflate this layout into the GridView at runtime, everything looks as expected:
LayoutInflater inflater = LayoutInflater.from(context);
ImageButton imageButton = (ImageButton) inflater.inflate(
R.layout.my_button_layout, parent, false);
imageButton.setImageResource(R.drawable.image1);
parent.addView(imageButton);
Why does inflating the view with LayoutInflator give different results than creating the button directly from its constructor?
Because when you create ImageButton manually, you do not specify its parent, hence it doesn't know the layout params of its parent and can't be laid out as you expect.
On the other hand, when you inflate it via LayoutInflater, you are specifying the parent. Then correct layout params are being passed to children. That's why you see difference.
Have a look at detailed article by Dave Smith.

How to get background color for Android ListView item to extend all the way to the end?

I am trying to force the background color for certain ListView items in my app to be different than all the others. The examples here on Stack Overflow and elsewhere are pretty straightforward. But for some reason the background color changes only as far as the width of the top TextView. Notice in the attached pic there are two TextView items on each row.
I decided to log the width of the list view item and oddly it turns out it has a width of 0. Unexpected, of course, but maybe at the time of the logging (execution of getView) it has not yet been expanded to their full with.
I am setting the background color in the getView() call, so its code is below. The xml for the item is below that.
#Override
public View getView(int arg0, View arg1, ViewGroup arg2)
{
if(arg1 == null)
{
LayoutInflater inflater = (LayoutInflater) m_context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
arg1 = inflater.inflate(R.layout.listitem_assessments, arg2, false);
}
TextView title = (TextView)arg1.findViewById(R.id.tvTitle);
TextView date = (TextView)arg1.findViewById(R.id.tvDate);
if (arg0 <= m_list.size())
{
Attrs attrs = m_list.get(arg0);
title.setText(attrs.m_sTitle);
date.setText(attrs.m_sDetail);
if (Character.isDigit(attrs.m_sActivationCount.charAt(0)))
{
if (!attrs.m_sActivationCount.equals("0"))
{
Log.i("Adapter", " Width of list item view is: " + arg1.getWidth());
arg1.setBackgroundColor(Color.YELLOW);
}
else
Log.i("Adapter", "Width of list item view is: " + arg1.getWidth());
}
else
arg1.setTag("");
return arg1;
}
Here is the xml resource for the ListView items:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="10dp"
>
<TextView
android:id="#+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginLeft="23dp"
android:layout_marginTop="10dp"
android:text="Assessment title"
android:textStyle="bold"
android:textSize="16sp" />
<TextView
android:id="#+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tvTitle"
android:layout_alignLeft="#+id/tvTitle"
android:background="#color/LightBlue"
android:text="date or author" />
</RelativeLayout>
Notice I'm using match_parent for the list item width so seems it would stretch out fully, and certainly not be 0. Nor since it is not wrap_content should it extends only as far as the widest TextView.
To be clear, I need the background color, Yellow, in my test, to extend all the way to the right. Any help greatly appreciated.
UPDATE
I have been able to log some accurate width info by moving the code into the OnItemClickListener event. The width of the entire ListView is 1312, but the width of the ListView items are indeed too short, and vary as the yellow backgrounds reveal. The first item is 696. It is clear now that the layout is not stretching things out to match the parent. How to force it to do that? It seems to be behaving as if I am using match_content instead of match_parent for the layout_width. Trying now to verify at run-time (at inflate time) the layout_width.
The problem is in the width of your TextViews as they are set to wrap_content.
Change them to
android:match_parent

Touching or calling setPressed() on parent View changes TextView's visibility

Something very strange is going on with a custom view containing a TextView. Roughly speaking, (relevant code below), I have a custom view (subclass of FrameLayout), which inflates some xml and adds the resulting View as a child. The inflated view contains a TextView, referred to below as "the label". Also possibly relevant (in case you're not going to look at the code) is that the custom view is added as a child to a MapView.
The following really weird stuff happens:
Do nothing: label is not visible, parent looks fine. Interestingly, the parent view is clearly taking the label's width into account (with the label visibility set to GONE the parent view is much narrower). In design mode the label is visible.
Touch the parent view: Label briefly becomes visible, then disappears. Holding your finger on it keeps the label visible until you end the touch.
Call this.setPressed(true), this of course being the parent view, from the parent view's constructor: Label becomes visible, and stays that way until the parent view is touched, after which behavior reverts to "normal".
Here's the relevant code. By way of context, we're sticking a balloon on a map when you touch the map, which should contain something along the lines of "Tap to choose this location". There are some other views in the balloon, but their visibility is set to GONE initially, and the code to fill them in and show them is disabled. There are no map overlays involved, just attaching the view directly to the map with MODE_MAP to stick it to a single location.
From the custom view, BalloonView.java. Nothing else in this class touches the layout/view methods.
public BalloonView(Context context) {
super(context);
if(!isInEditMode()){ // RoboGuice doesn't like edit mode
RoboGuice.getInjector(context).injectMembersWithoutViews(this);
}
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// We're *not* attaching the view with it's default layout params.
View v = inflater.inflate(R.layout.balloon_view, this, false);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.NO_GRAVITY;
addView(v,params);
label = (TextView) v.findViewById(R.id.label);
addressContainer = v.findViewById(R.id.addressContainer);
address1 = (TextView)findViewById(R.id.address1);
address2 = (TextView) findViewById(R.id.address2);
// makes label visible initially
// setPressed(true);
}
// This is the constructor that we actually call, in case it matters...
public BalloonView(Context context, OnClickListener onClick) {
this(context);
setOnClickListener(onClick);
}
balloon_view.xml
<TextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="6dip"
android:layout_marginLeft="6dip"
android:layout_marginRight="6dip"
android:layout_marginTop="6dip"
android:text="#string/map_balloon_label"
android:enabled="true"
android:textAppearance="?android:attr/textAppearanceMedium" />
<LinearLayout
android:id="#+id/addressContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="6dip"
android:layout_marginLeft="8dip"
android:visibility="gone" >
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="#string/place"
android:src="#drawable/location_place" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:orientation="vertical" >
<TextView
android:id="#+id/address1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:visibility="visible" />
<TextView
android:id="#+id/address2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
Here's where we attach the BalloonView to the MapView
private MapView.LayoutParams getBalloonViewLayoutParams(GeoPoint where){
return new MapView.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
where, MapView.LayoutParams.BOTTOM_CENTER);
}
private void showBalloon(QPGeoPoint where) {
latitude = where.getLatitude();
longitude = where.getLongitude();
BalloonView bv = getBalloonView();
if(bv.getParent() != mapView){
Ln.d("bv.getParent()!=mapView");
mapView.addView(balloonView, getBalloonViewLayoutParams(where));
}else{
Ln.d("parent was map view");
balloonView.setLayoutParams(getBalloonViewLayoutParams(where));
}
balloonView.setVisibility(View.VISIBLE);
balloonView.setLocation(where);
}
Other (possibly) relevant things:
We're using ActionBarSherlock, RoboGuice, and the roboguice-sherlock plugin.
The application theme is set to Sherlock.Theme.Light.DarkActionBar, but the activity in question has it's theme set to Theme.DeviceDefault.NoTitleBar.
I am completely baffled, have been trying to figure this out for hours, and will continue to do so and post updates as I find new clues.
Well ladies and gents, here it is:
The text and background were both white. Touching the text set it's color state to pressed, which made it black.
The quick fix is to change the textColor attribute on the text view.
A more general fix might be to use a different theme for this activity, say, Theme.DeviceDefault.Light.NoTitleBar.
Feeling pretty dumb on this one ;P

RelativeLayout does not adhere to set width when inflated

I have a relativeLayout that I would like to use the theme.dialog android theme, it should have a set width of 240dip. When I specify the whole layout and it's children in xml, this works. However, when I try to inflate the xml to add more views (code below), the Layout fills the width of the screen.
Context context = this;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
RelativeLayout header = (RelativeLayout) inflater.inflate(R.layout.headphonepopupheader, null);
headphonepopup.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="240dp"
android:layout_height="wrap_content" >
<ImageButton
android:id="#+id/closebutton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_marginLeft="216dp"
android:background="#drawable/closebutton" />
</RelativeLayout>
Is there a way to solve this?
When inflating a RelativeLayout, do not use the inflate() method signature that you have there. Instead, use the inflate() that takes the parent container as the 2nd parameter and a boolean as the third. Supply the eventual parent for the RelativeLayout in the 2nd parameter, and if you do not want the RelativeLayout added immediately, pass false as the 3rd parameter.
Leastways, this recipe clears up all sorts of RelativeLayout inflation problems when using a RelativeLayout as the basis for a row in a ListView.

Categories

Resources