Setting a background image to a view stretches my view - android

I created a background image bitmap for a view and now the view is being stretched to the size of the background image....
is this normal?
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/green"
android:tileMode="repeat"/>
here's how I apply it to my view
v.setBackgroundResource(R.drawable.backgroundgreen);
for instance...
if the image is 500px in height
and the view is 200px in height(being set wrap_content as height)
after setting the image as background my view becomes 500px in height...

I have faced this same problem.
If the background image has a size that is bigger than the view's size, the view's size will change to match the image's size.
Solution
Put the view inside a Relative Layout.
Remove the background image.
Add an ImageView before the View inside the Relative Layout
Set the src of the ImageView to your background image
<RelativeLayout
.
.
. >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/yourViewId"
android:layout_alignRight="#+id/yourViewId"
android:layout_alignTop="#+id/yourViewId"
android:layout_alignBottom="#+id/yourViewId"
android:adjustViewBounds="true"
//this will allow the image to resize with same proportions
android:src="#drawable/yourDrawable" />
<YourView
android:id="#+id/yourViewId"
.
..
... />
</RelativeLayout>
This all can be done in code of course.

According to me, the problem you are facing is not a problem, it is the way how Android is used to design the layouts.
This means that you can set the height and width with 3 default constant values:
FILL_PARENT
Special value for the height or width requested by a View. FILL_PARENT means that the View wants to be as big as its parent, minus the parent's padding if any. This value is deprecated starting in API Level 8 and replaced by MATCH_PARENT.
MATCH_PARENT
Special value for the height or width requested by a View. MATCH_PARENT means that the view wants to be as big as its parent, minus the parent's padding if any. Introduced in API Level 8.
WRAP_CONTENT
Special value for the height or width requested by a View. WRAP_CONTENT means that the View wants to be just large enough to fit its own internal content, taking its own padding into account.
Now, when you are setting the View's height/width to WRAP_CONTENT, you are allowing the view to take that much size that is sufficient to show to view's content. The background image is also the View's content, hence you view will be shown of as much size as the image. That's not a problem, that's how it's shown.
Okay, but in your situation that's an issue for you because you have a background to show and view should not be stretched for that. I can suggest few ways:
First and very obvious: make correctly sized images and keep them in different drawable folders.
Specify the size of view not using constants, but in DP. If it becomes necessary, make different layout XML files for different sizes and keep them in layout folders.
You can use a very useful thing for design layout is layout weight.

I suggest to create a wrapper layout and put the background image in there. i'm using it that way and fits very nicely.
see example below
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/settingstab_scroll"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="vertical"
android:background="#drawable/wareninja_wallpaper_img"
>
<!-- your layouts and components goes here -->
</ScrollView>
...
Social Coding # AspiroTV

The reason is quite simple. You gotta see the View::getSuggestedMinimumWidth/Height method.
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
Seeing that, you may know why the background makes a view bigger, especially why assign a BitmapDrawable to it.
and the simple solution is to wrap that Drawable (eg. BitmapDrawable), then returns 0 for getMinimumHeight() and getMinimumWidth(), and better to override getIntrinsicHeight() and getIntrinsicWidth() to returns -1.
support-v7 has a DrawableWrapper which delegates calls to another drawable when necessary. you can extends that one and override methods talked above.
and if you don't use support-v7 (WoW! you are awesome), copy that class to your project is also fine.
https://android.googlesource.com/platform/frameworks/support/+/1949ae9aeaadf52ad7bd7bb74ca5419c67ea7f65/v7/appcompat/src/android/support/v7/internal/widget/DrawableWrapper.java

It's working for me.
< ?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" android:id="#+id/LinearLayoutTest">
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.prueba);
((LinearLayout)findViewById(R.id.LinearLayoutTest)).setBackgroundResource(R.drawable.greenrepeat);
}
in your code, what is v? It has params to fill_parent?

Don't use android:tileMode="repeat". Is your green drawable bigger or smaller than your view? Could add more details?

One good solution that is working perfectly in my case is extending the View and overriding onMeasure().
Here is the steps to do:
Create an own class and extend the View you want to use, here for
example I will use Button.
Override the method onMeasure() and insert the code at the bottom. This will set the background resource after the first measure has been done. For the second measure event, it will use the already measured paramters.
Example code for a custom view which extends Button (change Button to the View you would like to extend)
public class MyButton extends Button {
boolean backGroundSet = false;
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(backGroundSet) {
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
return;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
backGroundSet = true;
setBackgroundResource(R.drawable.button_back_selector);
}
}
The only thing to change here is the type of view you want to extend and in the onMeasure() method the background resource you want to use for the view.
After that, just use this view in your layout xml or add it programatically.

I modify Sherif elKhatib's code, now this works for me:
if we want background picture be stretched as view picture:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignStart="#+id/yourViewId"
android:layout_alignTop="#+id/yourViewId"
android:layout_alignEnd="#+id/yourViewId"
android:layout_alignBottom="#+id/yourViewId"
android:scaleType="fitXY"
android:src="#drawable/bg_very_big_picture" />
<LinearLayout
android:id="#+id/yourViewId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
if we want background picture not to be stretched, but to be cutted to fit view picture:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignStart="#+id/yourViewId"
android:layout_alignTop="#+id/yourViewId"
android:layout_alignEnd="#+id/yourViewId"
android:layout_alignBottom="#+id/yourViewId"
android:scaleType="centerCrop"
android:src="#drawable/bg_very_big_picture" />
<LinearLayout
android:id="#+id/yourViewId"
android:layout_width="match_parent"
android:layout_height="wrap_content"

Related

Set height of children in RecyclerView?

I am trying to use RecyclerView in a similar way to ViewPager (Vertical).
I have setup fling and scroll gestures so the views are scrolled in exactly the same way as ViewPager but now I would like to setup children views (LinearLayoutManager) and LinearLayout used for each child view to fill screen instead of wrap_content. I have made all views to match_parent and fill_parent but that does not work.
I did this:
holder.layout.setMinimumHeight(this.recyclerView.getMeasuredHeight());
Which is nice but when the orientation changes then layout overflows the screen which makes smooth scrolling/gestures flicker etc. I don't want it to overflow the screen.
Any good tips how to make it properly so all children are laid out to maximum height of the screen but do not overflow?
Thanks
Edit: I changed setting minimum height to this:
holder.layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, this.recyclerView.getHeight()));
which solves flickering issue but is there a better way?
I have low reputation so unable to comment down. thats why answering here .
to over come the overflow issue lets say you have XMl of single item like this
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:id="#+id/mainLayout" >
<-- some views --->>
</RelativeLayout>
now in your adapter get this Layout in viewHolder
and make a boolean variable where you are setting adabpter.. which you can use for knowing orientation changed . when orientation get changed then change its value .
in your adapter make a function
public void orientaiotnChange(){
notifyDataSetChanged();
}
in your function
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int arg1) {
if(isOrientationHorizontal){
viewHolder.Mainlayout.setOrientation(horizontal);
}
else{
viewHolder.Mainlayout.setOrientation(vertical);
}
also change the orientation of RecyclerView in activity from where you set adapter and from where you are checking orientation is changed or not .
hopefully it will work so :)

Simulate ListView wrap_content behavior up to specific height in XML

My Activity consists of two parts: 1) A listview at the top. 2) The bottom part (which for simplicity is just a TextView with a black background).
I want the black TextView to be placed right after the last element of the ListView. I can easily achieve this by setting the height of the ListView to wrap_content (please don't tell me that I shouldn't use wrap_content for ListView's height):
Here's where the problem begins: I don't want the ListView to grow more than half a screen (when there are a lot of items in the ListView I want it to behave as if it was a normal ListView (scrollable) that only takes up half of the Activity screen, like here:
Is it possible to do this by just manipulating XML? If yes - how? If not - can you, please, point me to the relevant direction of how to achieve this in code?
This type of behavior is too dynamic to define in XML, but it's easy enough to accomplish with a custom container view. I'm making a couple of assumptions about your application, primarily that the root layout of the Activity only has two children (the ListView and the footer view). Based on that, the following is a custom LinearLayout which will give you what you need:
public class ComboLinearLayout extends LinearLayout {
public ComboLinearLayout(Context context) {
super(context);
}
public ComboLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//We're cheating a bit here, letting the framework measure us first
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//Impose a maximum height on the first child
View child = getChildAt(0);
int newHeightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() / 2, MeasureSpec.EXACTLY);
if (child.getMeasuredHeight() > (getMeasuredHeight() / 2)) {
measureChild(child, widthMeasureSpec, newHeightSpec);
}
//Optional, make the second child always half our height
child = getChildAt(1);
measureChild(child, widthMeasureSpec, newHeightSpec);
}
}
Then you can apply this in your Activity layout like so:
<com.example.myapplication.ComboLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hi Mom!"
android:background="#0A0"/>
</com.example.myapplication.ComboLinearLayout>
The net effect of the container code is that it fixes the measured height of the ListView to exactly half the container height if and only if it measures itself larger than that. Otherwise, it allows the ListView to be smaller.
There's a secondary trick I added in case you needed it, which is an optional block of code that forces the footer view to always be half the screen height. If you are setting the footer view to a fixed height in XML, you can probably remove that second section from onMeasure(). If you use that code, it will work best if the footer view is set to match_parent in XML.
It is not possible only using xml. If you set the fixed height or weight of the ListView, it will always take the fixed position. To achieve this you will have to set listview parent height dynamically as listview grows and stop it when it meets your requirement. Hope it will help you.

Padding doesn't work with certain background resource

Can anyone explain to me why this is happening?
I have a fairly simple class extending TextView. When I set the background colour to Color.BLUE, padding works fine. When I change the background resource to android.R.drawable.list_selector_background, my padding is no longer applied. What the F?
Here is my UI class:
public class GhostDropDownOption extends TextView {
TextView text_view;
public GhostDropDownOption(Context context, AttributeSet attrs) {
super(context, attrs);
setup(context);
}
public GhostDropDownOption(Context context) {
super(context);
setup(context);
}
private void setup(Context context) {
this.setClickable(false);
// THE 2 LINES BELOW ARE THE ONLY THING I'M CHANGING
//this.setBackgroundResource(android.R.drawable.list_selector_background);
this.setBackgroundColor(Color.BLUE);
}
}
And I'm using it in the layout like this:
<trioro.voyeur.ui.GhostDropDownOption
android:id="#+id/tv_dropdown_option_1"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="#string/request_control_dropdown_option_1"
android:textColor="#000000"
android:padding="10dip"/>
And this is the result of changing the background:
The call to:
this.setBackgroundResource(android.R.drawable.list_selector_background);
will remove any previosly set padding (this is to make it work properly with 9-patch assets).
Try to set the padding in code after the line above, like this:
this.setPadding(PADDING_CONSTANT, PADDING_CONSTANT, PADDING_CONSTANT, PADDING_CONSTANT);
Just remember that the values sent to setPadding is in pixels NOT dip!
You should set your background drawable in XML if at all possible. If you set it in code, it will use the padding from your drawable resources rather than what you set in XML, so if it's necessary to do it programmatically, you'll want to retrieve the current padding, store it temporarily, set the background, and then set the padding back as #TofferJ suggests.
The reason for this is that the drawables themselves can have padding, in the case of 9-patch images (where the bottom and right pixel borders define the amount of padding).
Your solution should be to just set your background resource in XML:
android:background="#android:drawable/list_selector_background"
although I believe that may be a private drawable resource that you'll have to copy into your project first.

RelativeLayout in a ListView

Good Afternoon,
I am having trouble getting a RelativeLayout to work within a ListView. If I change the layout to linear etc. I see it behave as I would expect but the RelativeLayout is scrunching everything up into overlapping space. Now my uneducated guess is that it is trying to cram everything into on row. I thought it would expand that row but perhaps not or that is not the problem.
Note my code has been abbreviated for space.
Anyway in my Parent XML:
<LinearLayout ..._width="fill_parent" ..._height="fill_parent" ...orientation="vertical" >
<ListView ...layout_width="fill_parent" ...layout_height="fill_parent" />
</LinearLayout>
So my thought was that this would fill the device screen and in the GraphicalLayout view in Eclipse it appears that it does.
My "row" definition for the ListView is this:
<RelativeLayout width="fill_parent" height="fill_parent" >
<ImageView
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="15dp"
android:layout_marginLeft="5dp" />
.....
</RelativeLayout>
As mentioned I thought this would fill all available space but it seems to only fill some sort of "default" row height?
Not sure what I am missing but any help would be greatly appreciated.
use the layout attributes layout-above, layout-below, layout-toRightOf, layout-toLeftOf in your xml. that way the items will be put next to where you want them to go. right now you are setting everything according to the parent so they are all going to overlap
RelativeLayout by default adds it's children anchored to the top left corner of the layout.
By using the very handy attributes of RelativeLayout, you can arrange the children both
relatively to each other, and
relatively to the parent view.
layout_alignParentLeft, ..Right, ..Top, ..Bottom, layout_centerInParent, layout_centerHorizontal, layout_centerVertical, layout_toLeftOf, ..toRightOf, ..above, ..below
These attributes will help you get the desired display in a very efficient (flat) way, so I'd suggest keep reading it up and consider using it instead of other layout containers for complex views.
I ended up overriding the getView of the ArrayAdapter and then setting the height and width to the dynamically determined screen size
...snip....
int[] iarry_ScrnDim = new int[2];
DisplayMetrics metrics = new DisplayMetrics();
metrics = ctx_Currnt.getResources().getDisplayMetrics();
iarry_ScrnDim[0] = metrics.heightPixels;
iarry_ScrnDim[1] = metrics.widthPixels;
return iarry_ScrnDim;
....snip....
if (vw_BigRow == null)
{
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
vw_BigRow = inflater.inflate(R.layout.liftdatalayout, null);
}
vw_BigRow.setMinimumHeight(mHeight);
vw_BigRow.setMinimumWidth(mWidth);

Android TextView has height and width of 0

I have a small project where I want to reuse a certain UI component a few time so I created a widget by expanding a ViewGroup. In that ViewGroup I inflated a view that contained a TextView inside a LinearLayout and added that inflated view to the ViewGroup trough addView.
The outer LinearLayout expands itself perfectly but the inner TextView have getHeight() = 0 and getWith() = 0 when I view it through Hierarchy Viewer. The strange thing is that layout_height and layout_width is the values I gave them in my xml.
I don't have the code here but it looked something like this:
xml:
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text="random text.."
android:layout_with="200px"
android:layout_height="50px" />
</LinearLayout>
Java:
class MyWidget extends ViewGroup {
...
//In constructor
myView = View.inflate(context, R.layout.xml, null);
addView(myView);
//In layout
myView.layout(l, t, r, b);
I have tried to give my text view fill_parent values for size but it didn't help.
Remember:getHeight() and getWidth()return 0 if components are not drawn yet.
To find the width And height of a View before it being drawn:
First call measure
view.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED)
Now you can get width using getMeasuredWidth and height using getMeasuredHeight
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
I have posted some more ideas here: How to get width/height of a View
1) Here is some links to use Hierarchy Viewer on your dev phone.
http://developer.android.com/guide/developing/debugging/debugging-ui.html
and the class you'll need:
http://github.com/romainguy/ViewServer
2) You can also reuse layout like a component with the include tag:
<include android:id="#+id/your_id" layout="#layout/layout_name" />
So, I put a bounty on this one, and here is what I've found.
Inflating with a null reference is A Bad Idea(TM). Essentially, that View won't get the proper layout parameters it needs (its parent sets a whole bunch of them, with a whole bunch of magic/logic involved). So inflating into null means no parents, and no inherited layout parameters. One can manually set a number of these parameters, but due to the magic involved it might not solve your problem.
The "solution(s)" that I've come up with involve; using include (when you know how many you need) and pulling them into code, or inflating to a parent (when you need true dynamic, N things). And of course, the XML you inflate will have ID collisions, so I go about it by grabbing the last child (e.g. getChildAt(getChildCount()-1) ) of whatever I'm looking for, etc.
Did you try passing yourself as the root:
View.inflate(context, R.layout.xml, this);
Since you will be the parent of this View that complies with the javadoc spec.

Categories

Resources