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
Related
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"/>
I am developing an Android app where I have two different views. Both views are actually canvas drawings (drawn programmatically) with a bunch of text views and buttons on top. The positions of these buttons and text views need to be changed depending on the view selection. I created a relative layout with my drawing and the buttons and text views. I use one of the text views as an anchor for the others and programmatically change its position.
Here is a part of the xml:
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:id="#+id/tvAnchor"
android:layout_above="#id/btnCtrl"
android:layout_marginBottom="130dp"
android:textSize="15sp"
android:textColor="#color/lightGrey"
android:gravity="center"
android:text="#string/Select1"
android:background="#drawable/controls_border_not_active"
android:clickable="true"/>
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:id="#+id/tvAlign"
android:layout_alignBottom="#id/tvAnchor"
android:layout_toStartOf="#id/tvAnchor"
android:layout_marginEnd="2dp"
android:textSize="15sp"
android:textColor="#color/lightGrey"
android:gravity="center"
android:text="#string/Select2"
android:background="#drawable/controls_border_not_active"
android:clickable="true"/>
In my main I have the following code to change the position of the text views accordingly:
private void PositionControls(){
RelativeLayout.LayoutParams lopAnchor = (RelativeLayout.LayoutParams) tvAnchor.getLayoutParams();
if (isView1Selected){
lopAnchor.resolveLayoutDirection(relativeLayout.getLayoutDirection());
lopAnchor.removeRule(RelativeLayout.ALIGN_START);
lopAnchor.setMargins(0,0,0,370);
lopAnchor.addRule(RelativeLayout.CENTER_HORIZONTAL);
}else{
lopAnchor.setMargins(0,0,0,300);
lopAnchor.removeRule(RelativeLayout.CENTER_HORIZONTAL);
lopAnchor.addRule(RelativeLayout.ALIGN_START,swDoIt.getId());
}
tvAnchor.setLayoutParams(lopAnchor);
}
Basically, in View1, the tvAnchor should be centered horizontally and in the other view it should be positioned off center - in the middle of 'center-to-right screen edge'. In order to do that when the second view is selected I remove the CENTER_HORIZONTAL rule and add a rule 'ALIGN_START' with a switch that is already positioned in the desired X coordinate. It works just fine. The problem occurs when View 1 is selected again and I switch from View 2 to View 1. In this situation I remove the ALIGN_START rule and add the CENTER_HORIZONTAL one. The thing is that tvAnchor remains aligned to the switch as if the ALIGN_START rule was not removed. I tried to get the direction of the layout, used requestLayout and other things that could think of, but nothing seems to remove that rule.
Did you check that there isn't coming some mystery values enabled??
I had case where is moved clockwidget place in layout
I did add ALIGN_PARENT_END and removed CENTER_IN_PARENT but when i set layout back i could not get clock widget centered.
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) mTextClockWidget.getLayoutParams();
p.addRule(RelativeLayout.ALIGN_PARENT_END, 1);
p.removeRule(RelativeLayout.CENTER_IN_PARENT);
mTextClockWidget.setLayoutParams(p);
Then noticed that from somewhere my layout params got ALIGN_PARENT_RIGHT enabled and when tried to remove ALIGN_PARENT_END and enable CENTER_IN_PARENT it didn't work. So i had to also add remove for ALIGN_PARENT_RIGHT to get it work.
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) mTextClockWidget.getLayoutParams();
p.removeRule(RelativeLayout.ALIGN_PARENT_END);
p.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT);
p.addRule(RelativeLayout.CENTER_IN_PARENT, 1);
mTextClockWidget.setLayoutParams(p);
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
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.
I want to do the Dashboard pattern. I currently do this for each home button:
<FrameLayout
android:layout_weight="1"
android:focusable="true"
android:onClick="onHomeButtonClicked"
android:background="#drawable/button_background">
<TextView
android:text="#string/button_text"
android:drawableTop="#drawable/button_icon"
android:layout_gravity="center"
android:gravity="center"
android:background="#android:color/transparent" />
</FrameLayout>
The reason I wrap my button inside a FrameLayout is I want to:
Maximize the clickable area
Make the icon and text properly
centered.
I tried doing this in the past but gave up because I couldn't figure out a screensize-independent way of centering the text and icon:
<Button
android:layout_weight="1"
android:background="#drawable/button_background"
android:text="#string/button_text"
android:onClick="onHomeButtonClicked"
android:drawableTop="#drawable/button_icon"
android:drawablePadding="DONT_KNOW_WHAT_TO_PUT_IN_HERE" />
My question: Is it possible to do all these:
Not wrap the Button inside other
layout (using only 1 view per
button)
Maximize the clickable area
Properly center the icon and text in
a screensize-independent way
Many thanks.
Here's some code that I use to build a dashboard programmatically:
#Override public View getView(int position, View convertView,
ViewGroup parent) {
TextView v = (TextView) convertView;
if (v == null) {
v = new TextView(DashboardActivity.this);
v.setGravity(Gravity.CENTER);
}
v.setCompoundDrawablesWithIntrinsicBounds(0,
mIcons[position].drawableId, 0, 0);
v.setText(mIcons[position].labelId);
return v;
}
You could instead use android:drawableTop in an XML file if you could build the dashboard beforehand.