Custom view not filling parent - android

I have a custom view like this
public class ButtonBar extends HorizontalScrollView
{
public View mButtonRows;
public ButtonBar(Context context, AttributeSet attrs)
{
super(context, attrs);
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mButtonRows = inflater.inflate(R.layout.toolbar, null);
// button click handling code goes here
addView(mButtonRows);
}
}
which is included in my main xml like this
<com.example.ButtonBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="#+id/pagecontent" />
and inflates an xml file like this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ButtonsRow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/button1"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="0.3"
android:text="button1"
/>
<Button
android:id="#+id/button2"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="0.3"
android:text="button2"
/>
<Button
android:id="#+id/button3"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="0.3"
android:text="button3"
/>
</LinearLayout>
(It currently only has three buttons, but more are going to be needed in later versions, hence the HorizontalScrollView.)
Looking in hierarchyviewer, the custom view does seem to be screen wide, but the LinearLayout is only as wide as the buttons it contains (about 2/3 of the screen at the current button size), despite having the fill_parent width set; the buttons don't stretch. If I set the LinearLayout's background to #android:drawable/bottom_bar (which is a png the width of the screen), the buttons properly resize; I realise I could do the same thing by creating my own images to match, but I'd much rather do it without if possible.
What am I doing wrong?
ETA: if I change HorizontalScollView to ScrollView, it works fine. Do HSVs just not allow their children to "fill_parent"?
ETA2: Setting android:fillViewport="true" in the main xml fixed it!

Setting android:fillViewport="true" in the main xml fixed it!

If you change to this, for each of the buttons, does it work?
android:layout_width="0px"
android:layout_weight="1"

Related

Create a Styled LinearLayout Custom View

Imagine you had the following design for a card that you'd be using through an app. A title, button, a divider, and then space for dynamic content indicated by the blue box. We can add anything we'd need inside the blue region but the frame for holding the content would be consistent. For example:
Card with Placeholder Region
If I was going to put two TextViews inside it might look like this, with the (trimmed down) view layout below:
Card with Two TextViews
<com.google.android.material.card.MaterialCardView>
<LinearLayout android:orientation="vertical">
<LinearLayout android:orientation="horizontal">
<TextView android:text="My Title" />
<com.google.android.material.button.MaterialButton android:text="ACTION" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000" />
<LinearLayout android:id="+#id/contentGoesHere">
<TextView android:text="First element" />
<TextView android:text="Second element" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
Ideally I'd like a custom view so that developers can just do the following and get the consistent stying, or add to the view programatically:
<com.customview.CustomView>
<TextView android:text="First element" />
<TextView android:text="Second element" />
</com.customview.CustomView>
My problem is that just extending LinearLayout and making a custom view wont work - it'll create the layout but there's no way to indicate that the inner LinearLayout is what I want the views to be added to, so any subviews added in the XML are ignored.
Do I need to make a custom ViewGroup and manually inflate the custom holder for the LinearLayout (help! onMeasure and onLayout!?) or is there an easier way to make a custom LinearLayout view with this styled frame around it?
My problem is that just extending LinearLayout and making a custom
view wont work - it'll create the layout but there's no way to
indicate that the inner LinearLayout is what I want the views to be
added to, so any subviews added in the XML are ignored.
You can manually move the child views into nested LinearLayout.
For example:
public class CustomView extends LinearLayout {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
final ViewGroup container = inflate(getContext(), R.layout.card_layout, this)
.findViewById(R.id.inner_content_container);
while (getChildCount() > 1) {
final View child = getChildAt(0);
removeView(child);
container.addView(child, child.getLayoutParams());
}
}
}
card_layout.xml
<com.google.android.material.card.MaterialCardView>
<LinearLayout android:orientation="vertical">
<LinearLayout android:orientation="horizontal">
<TextView android:text="My Title" />
<com.google.android.material.button.MaterialButton android:text="ACTION" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000" />
<LinearLayout
android:id="#+id/inner_content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- content goes here -->
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
...
<com.customview.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:text="First element" />
<TextView android:text="Second element" />
</com.customview.CustomView>

Android, CustomView Layout not showing

I've done this before with no issue so i know my mistake is subtle.
picker_dialog_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left|center_horizontal"
android:id="#+id/pickerDialog_title"
android:text="HELLO WORLD!!"/>
<NumberPicker
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/pickerDialog_title"
android:id="#+id/pickerDialog_selector"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/pickerDialog_selector"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="5"
android:id="#+id/pickerDialog_cancel"
android:gravity="center"
android:text="Cancel"/>
<Button
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="5"
android:id="#+id/pickerDialog_set"
android:gravity="center"
android:text="Set"/>
</LinearLayout>
</merge>
During the Custom Class constructor, PickerDialog(Context context, AttributeSet attrs, int defStyle) i call:
LayoutInflater.from(context).inflate(R.layout.picker_dialog_layout, this);
And in the parent XML:
<com.company.simonaddicott.controlpanel_1.PickerDialog
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/pickerDialog" />
The view itself does show, and from using layout bound tools in developer tools i can identify that the ui elements are present, or at least the boundaries are present (see below)
What am i missing from this to make there UI elements appear like they should??
My mistake was that I extended the custom view by LinearLayout, but was using RelativeLayout positioning on the picker_dialog_layout.xml.
The elements inside the layout were not showing as they had no relative parent element to be positioned against

ImageButton: Force square icon (height = WRAP_CONTENT, width = ?)

In my horizontal LinearLayout I have a TextEdit and an ImageButton. The ImageButton is as high as the TextEdit.
I'd like that the ImageButton is exactly as wide as it's long.
At the moment it looks like the width of the ImageButton is like when there is no scaling (ImageButton width [px] = unscaled drawable width [px]):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="#+id/txtName"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<ImageButton
android:id="#+id/btSet"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="fitEnd"
android:src="#drawable/pin2" />
</LinearLayout>
How it should look like:
Try this, I think this should work:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal" >
<ImageButton
android:id="#+id/btSet"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:scaleType="centerInside"
android:adjustViewBounds="true"
android:src="#drawable/pin2" />
<EditText
android:id="#+id/txtName"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="#id/btSet" />
</RelativeLayout>
Explaination: centerInside will assure that the image will scale proportionally within the bounds of the ImageButton. adjustViewBounds="true" will...well, adjust the view's bounds, if the image needed to be scaled.
try adding
adjustViewBounds="true"
to the ImageButton, that should clip the excess width
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="#+id/txtName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<ImageButton
android:id="#+id/btSet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#drawable/pin" />
</LinearLayout>
Use
android:scaleType="fitCenter"
or android:scaleType="centerInside"
in the ImageButton in xml File.
I might be a bit late to the party.
However there's an easy way to achieve this behavior by overriding the onMeasure().
Here's how it'd look like :
public class MySquareImageButton extends ImageButton {
public MySquareImageButton(Context context) {
super(context);
}
public MySquareImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySquareImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//(1)if you want the height to match the width
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
//(2)if you want the width to match the height
//super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
}
And then you'd simply replace your XML's ImageButton with this custom one :
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="#+id/txtName"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.whatever_your_package.MySquareImageButton
android:id="#+id/btSet"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="fitEnd"
android:src="#drawable/pin2" />
</LinearLayout>
You'd simply put wrap_content to width or height, depending on which one you want to dictate the size of your button.
In the case you want your button to wrap its height to the image, and that the width simply matches the height, you'd use
android:layout_width="wrap_content"
android:layout_height="wrap_content"
and use
//(2)if you want the width to match the height
//super.onMeasure(heightMeasureSpec, heightMeasureSpec);
I had the same problem.
I was trying to create something that looked like this:
But what I was getting was this:
The ImageButton was getting stretched horizontally.
All the top answers didn't work for me. But I noticed people mentioning layout_weight and just looked it up out of curiosity and found the following on Android docs:
Layout Weight
LinearLayout also supports assigning a weight to individual children with the android:layout_weight attribute. This attribute assigns an "importance" value to a view in terms of how much space it should occupy on the screen. A larger weight value allows it to expand to fill any remaining space in the parent view. Child views can specify a weight value, and then any remaining space in the view group is assigned to children in the proportion of their declared weight. Default weight is zero.
For example, if there are three text fields and two of them declare a weight of 1, while the other is given no weight, the third text field without weight will not grow and will only occupy the area required by its content. The other two will expand equally to fill the space remaining after all three fields are measured. If the third field is then given a weight of 2 (instead of 0), then it is now declared more important than both the others, so it gets half the total remaining space, while the first two share the rest equally
So basically, if you set the layout_width to be 0 for an element, it'll appear according to the dimensions of its content.
If you set it to anything else, the element will fight for extra space in the parent element that contains it; with more weighted elements taking up more space.
So, when I set layout_width = 0 for both TextView and ImageButton in my example, neither of them takes up any extra space and they both huddle up to the left.
But when I set it to 1 for TextView and 0 for ImageButton, the ImageButton doesn't take any more space than required by its content; while the TextView takes up all the extra space and pushes ImageButton to the right.
Just the way I want it.
Initially, what had happened was both the elements were set to have a default layout_weight of 1 and hence both were equally competing for the extra space.
Simply use the weightSum to devide the size of controls accordingly...
<?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">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1.0" >
<EditText
android:id="#+id/txtName"
android:layout_weight="0.8"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:inputType="text"
android:singleLine="true"/>
<ImageButton
android:id="#+id/btSet"
android:layout_weight="0.2"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:scaleType="fitEnd"
android:src="#drawable/pin2" />
</LinearLayout>
</LinearLayout>
Hope it will help you.
as you want your ImageButton is stretchable & exactly as wide as it's long, its better to use NinePatch Image. you may find help form here Draw 9-patch & How does Android’s nine-patch tool work ?
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1.0" >
<EditText
android:id="#+id/txtName"
android:layout_weight="0.75" // this work like percentage adjust as u want 70 or 75
android:layout_height="wrap_content"
android:layout_width="0dp"
android:inputType="text"
android:singleLine="true"/>
<ImageButton
android:id="#+id/btSet"
android:layout_weight="0.25" // this work like percentage adjust as u want 25 or 30
android:layout_height="wrap_content"
android:layout_width="0dp"
android:scaleType="fitEnd"
android:src="#drawable/pin2" />
</LinearLayout>
Just add the following line to your ImageButton and the extra background will fade away:
android:layout_gravity="center|clip_horizontal"
hope this works
Just ran into something like this and the other suggestions didn't help. What did help was setting the padding in the ImageButton:
android:padding="5dp"
Besides that, I didn't touch the original layout. The button became square on the 3 emulators that I tested (3.2, 4.4.2, 5.1)
I tried myself,it works
Simply do the Following...
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/editText1"
android:layout_width="0dp"
android:layout_weight="5"
android:layout_height="wrap_content"
android:inputType="text"/>
<ImageButton
android:id="#+id/imageButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent" />
</LinearLayout>
Use Layout Weight ratio 5:1 inside a LinearLayout for EditText and ImageButton
You Can resize the Image button using px... like below...
`android:layout_width="5px"
android:layout_height="5px"`

How to remove unwanted spaces at the right of CheckBox?

I am working on a custom list view. I want to show a CheckBox at the custom view. There is no text for the CheckBox. I found it always have some spaces at the right of the CheckBox.
Here is my layout xml file:
<?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="wrap_content"
android:orientation="horizontal" android:background="#fa9654"
android:paddingTop="65dp" android:paddingBottom="65dp">
<TextView android:id="#+id/bus_route_list_item_num"
android:layout_height="wrap_content" android:layout_width="0dip"
android:gravity="center" android:layout_gravity="center_vertical|center_horizontal"
android:layout_weight="0.15"></TextView>
<TextView android:id="#+id/bus_route_list_item_station"
android:layout_height="wrap_content" android:layout_width="0dip"
android:gravity="left" android:layout_gravity="center_vertical|center_horizontal"
android:layout_weight=".5"></TextView>
<TextView android:id="#+id/bus_route_list_item_fee"
android:layout_height="wrap_content" android:layout_width="0dip"
android:gravity="center" android:layout_gravity="center_vertical|center_horizontal"
android:layout_weight=".15"></TextView>
<CheckBox android:id="#+id/bus_route_list_item_reminder" android:layout_height="wrap_content"
android:layout_width="0dip" android:layout_weight=".20" android:gravity="center"
android:layout_gravity="center" android:paddingRight="0dp" android:paddingLeft="0dp"
android:paddingTop="0dp" android:paddingBottom="0dp" android:background="#0066ff"
android:text=""
/>
</LinearLayout>
The result looks like:
As you can see there are some space at the right of the checkbox. What I want is put the checkbox at the middle of the blue area.
Is it possible to remove the unwanted space? thanks
by default, the Checkbox has minWidth and minHeight value
you can set its value to 0
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="0dp"
android:minHeight="0dp" />
The result will be like that without any extra spaces
You can wrap CheckBox in LinearLayout and then use android:gravity="center" on that layout.
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight=".20"
android:background="#ff0000"
android:gravity="center">
<CheckBox android:id="#+id/bus_route_list_item_reminder"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
</LinearLayout>
As another alternative, you can use RelativeLayout. This would greatly simplify you layout and you will be able to get rid of layout_weight.
Neither of previous solutions worked for me, but I've tried applying a translation to the content and it worked pretty well, no need in additional layout hierarchy, nor implementing own views:
<CheckBox
...
android:text="#null"
android:translationX="12dp" />
Also, it keeps bounds of the element in proper place, so touch area is not shifted.
The translationX seems to work. But it causes problem if you want to support RTL layouts. Another solution would be to set the width of checkbox to a fixed length (e.g. 26dp):
<CheckBox
android:layout_width="26dp"
android:layout_height="wrap_content"
android:text="#null" />
To remove extra space at right of the image (when there is no text) extend CheckBox class and override getSuggestedMinimumWidth() method in order to return there image width. Complete solution:
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.CheckBox;
public class CheckBoxWithoutText extends CheckBox
{
private Drawable buttonDrawable;
public CheckBoxWithoutText(Context context)
{
super(context);
}
public CheckBoxWithoutText(Context context, AttributeSet attrs)
{
super(context, attrs);
}
#Override
protected int getSuggestedMinimumWidth()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
{
return getCompoundPaddingLeft() + getCompoundPaddingRight();
}
else
{
return buttonDrawable == null ? 0 : buttonDrawable.getIntrinsicWidth();
}
}
#Override
public void setButtonDrawable(Drawable d)
{
buttonDrawable = d;
super.setButtonDrawable(d);
}
}
I would use a relative layout here. Aligning checkbox on parent right...
Regards,
Stéphane

Putting a custom view into another View, using XML layouts for both

I'm starting to work on a game, which will have 3 views stacked on top of each other, basically a logo/ad row, a score row, and a game row. To start out with, I'm trying to have a class called Level_Score_Bar that uses an XML layout called score_bar_layout. Right now, my main XML code looks like this (Note, I've been editing this based off of the suggestions below, if I get it fixed, I'll stop editing it):
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent" android:layout_width="fill_parent">
<LinearLayout android:layout_height="wrap_content" android:orientation="horizontal" android:layout_width="fill_parent" android:id="#+id/Title_bar">
<ImageView android:layout_height="wrap_content" android:id="#+id/imageView1" android:layout_width="wrap_content" android:src="#drawable/icon"></ImageView>
<LinearLayout android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="#+id/linearLayout4">
</LinearLayout>
</LinearLayout>
<pearsonartphoto.AJEG.Level_Score_bar android:layout_height="fill_parent" android:layout_width="wrap_content" android:id="#+id/Score_Bar">
</pearsonartphoto.AJEG.Level_Score_bar>
<LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:orientation="vertical" android:id="#+id/Game_Row">
<View android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="#+id/view3"></View>
</LinearLayout>
</LinearLayout>
Level_Score_bar.java looks like this:
public class Level_Score_bar extends RelativeLayout {
public Level_Score_bar(Context context, AttributeSet set, int defStyle) {
this(context,set);
}
public Level_Score_bar(Context context, AttributeSet set) {
super(context, set);
this.addView(View.inflate(context,R.layout.score_bar_layout,null));
Log.d(TAG,"Added view");
}
}
score_bar_layout.xml looks like this
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:text="#string/level" android:id="#+id/Level_text" android:layout_toRightOf="#+id/Level_text"></TextView>
<TextView android:id="#+id/Current_Level" android:layout_width="wrap_content" android:text="TextView" android:layout_height="wrap_content" android:layout_alignParentTop="true"></TextView>
<TextView android:layout_width="wrap_content" android:text="TextView" android:layout_height="wrap_content" android:layout_centerVertical="false" android:layout_centerHorizontal="true" android:id="#+id/Time_Left"></TextView>
<TextView android:id="#+id/Score_Label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:text="#string/score"></TextView>
<TextView android:layout_width="wrap_content" android:text="TextView" android:layout_height="wrap_content" android:layout_toRightOf="#+id/Score_Label" android:id="#+id/Score_Value"></TextView>
</RelativeLayout>
The problem is, I'm not seeing the view at all. I'm sure I'm missing something small, but I can't for the life of me figure it out. I've confirmed that the addView command is being called (Via a Log.d statement), but it's just not seeming to make any difference...
You're using the wrong constructor if I remember correctly, try this one instead:
public Level_Score_bar(Context context, AttributeSet set, int defStyle) {
super(context, set, defStyle);
// ...
}
[Edit]
Forgot to mention that since you're creating the view from XML, Android will call this constructor and it also enables you to use custom attributes if you so wish.
I am not sure of what your trying to do, please add some details :
name of xml files
your intents, what you want to do exactly with both.
But here a re some general advices :
Override other constructors as well in your custom view classes.
One argument parameters are meant to load class from code, with a second, attributeset argument, it will be used for xml constructs.
When you refer to a custom component (child) in xml (parent), use fully qualified class name of your child class. (that will load its child layout). fully qualified is package name + class name
Regards, Stéphane
Did you try to call ?
this.addView(View.inflate(getContext(),R.layout.score_bar_layout,null));
It's so simple, I feel like an idiot...
The problem was, the base layout was oriented horizontal. Simply adding a android:orientation="vertical" was enough to make it work right. Sigh. So, what was happening was the bar was being displayed, but below the screen (Actually, to the right of it), and so it was never visible. Changing the orientation to it's intended method fixed that, so the bar could be seen, and wasn't off screen.

Categories

Resources