I'm trying to generate a bitmap to show in the notification. The layout is a bit complex, however, I need to set the width according to the screen size. In the XML, I have a LinearLayout in the root and I inflate the view with
LinearLayout v = (LinearLayout) i inflater.inflate(R.layout.notification_expanded_detail, null, false);
This layout has quite a bunch of elements which I need to fill. And their width depends on the width of the layout itself and the width of the layout varies with phone. I set the width for the inflated layout with
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) available_width, LinearLayout.LayoutParams.WRAP_CONTENT);
v.setLayoutParams(params);
However, when I try to generate a bitmap from this, it appears that the size set with the setLayoutParams is not affecting the child views of the inflated layout. There are many questions around i in SO, however, all of them deals with inflating a layout that already has a rootView and nothing worked for me. A couple of answers suggested that I call the measure() method on the view. However, after calling the measure method, the getMeasuredWidth returns a width lesser that what I had set with the setLayoutParams call.
Update
A little more context about the problem. I was trying to show a notification that had a placeholder ImageView which I would later fill in with a bitmap generated from a view inflated from an XML. The remote view layout is
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sliderViewBottom"
android:layout_width="match_parent"
android:layout_height="256dp"
android:background="#color/detail_page_blue"
android:clickable="true"
android:focusable="false"
android:orientation="vertical"
android:gravity="center"
android:padding="10dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#color/white"
>
<ImageView
android:id="#+id/progressImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#D05757"
android:gravity="center"
android:paddingBottom="8dp"
android:paddingTop="8dp"
>
<TextView
android:id="#+id/overAllDelay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delayed by "
android:textColor="#color/white"
/>
<TextView
android:id="#+id/overAllDelayVal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/overAllDelay"
android:text="-"
android:textColor="#color/white"
/>
</RelativeLayout>
</LinearLayout>
The layout that I was trying to inflate and generate a bitmap from to set in the progressImage was
<RelativeLayout
android:id="#+id/swipe_page_track_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingLeft="#dimen/swipe_layout_info_card_track_margin"
android:paddingRight="#dimen/swipe_layout_info_card_track_margin"
>
<ImageView
android:id="#+id/fromGreenCircle"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="1dp"
android:src="#drawable/from_green_circle"/>
<ImageView
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_alignParentRight="true"
android:layout_marginTop="1dp"
android:src="#drawable/station_end"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="4.5dp"
android:background="#drawable/swipe_page_empty_track"
/>
<ImageView
android:id="#+id/fromGreenTrack"
android:layout_width="105dp"
android:layout_height="3dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="4.5dp"
android:src="#drawable/from_green_track"
/>
<ImageView
android:id="#+id/trackNavigationIcon"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginLeft="100dp"
android:src="#drawable/ic_track_detail_card_current_station_pointer"
/>
<TextView
android:id="#+id/currentStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/trackNavigationIcon"
android:layout_marginTop="10dp"
android:textColor="#color/black"
/>
</RelativeLayout>
I was trying to inflate this second layout with
LinearLayout v = (LinearLayout) i inflater.inflate(R.layout.notification_expanded_detail, null, false);
And then was using
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) available_width, LinearLayout.LayoutParams.WRAP_CONTENT);
v.setLayoutParams(params);
This available_width was computed dynamically. However, when I later tried to generate a Bitmap from this inflated view by using v.layout(), it kep t generating an image which was not honouring the width that I set.
Later, I came across this SO thread where I noticed that while inflating, instead of specifying null as the parentView, new LinearLayoutParams(contex) was specified. I tried that and now, the dynamically set width is being honoured.
I came across this SO thread where I noticed that while inflating, instead of specifying null as the parentView, new LinearLayoutParams(contex) was specified. I tried that and now, the dynamically set width is being honoured
Related
After creating a LinearLayout with ImageViews as its children, I noticed that only the first row of items are shown. I thought the LinearLayout would automatically wrap its children onto a new line as necessary? The width seems fine but not the height.
Expected result
Expected blueprint (ImageView count is not to scale)
For some reason, when I create a LinearLayout inside another view, the width is shown correctly, but it never seems to adjust its height to fit & show all the children inside it.
Current result
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cv_facilities">
<LinearLayout
android:id="#+id/ll_facilities"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:id="#+id/ll_titlerow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/iv_expandcollapsearrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp" />
<ImageView
android:id="#+id/iv_topicsymbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp" />
<LinearLayout
android:id="#+id/ll_symbols"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/iv_symbol_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_language" />
<ImageView
android:id="#+id/iv_symbol_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_pets" />
<ImageView
android:id="#+id/iv_symbol_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_verified_user" />
<ImageView
android:id="#+id/iv_symbol_d"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_transport" />
<ImageView
android:id="#+id/iv_symbol_e"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_seat" />
<ImageView
android:id="#+id/iv_symbol_f"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_fingerprint" />
<ImageView
android:id="#+id/iv_symbol_g"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_areoplane_depart" />
<ImageView
android:id="#+id/iv_symbol_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_areoplane_arrive" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
This behavior of LinearLayout is "as intended": it will display its children in a horizontal or vertical line.
Since all of the child Views in your blueprint seem to be of a similar size, consider switching to GridLayout, it is available as androidx-library (e.g. androidx.gridlayout:gridlayout:1.0.0).
For child Views with varying dimensions, FlexboxLayout is a good alternative. It was introduced in a blog post in February 2017. There is a version for androidx available: 'com.google.android:flexbox:1.1.0'
A horizontal LinearLayout will not automatically wrap to a second line to fit it's children. Per the Android documentation it only supports a single direction:
LinearLayout is a view group that aligns all children in a single
direction, vertically or horizontally
What you could do is use flexbox-layout to achieve the wrapping behavior.
Don't make horizontal linearLayout with 6 imageView's
Make new vertical linearlayout and put on it two horizontal linear layout for each one 3 imageView .
How can I make this layout in android programmatically.
I want just an idea not whole coding thing.
Here is the answer but not the direct answer. This is what I personally do to create complex layout programatically.
Create the same layout in XML.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="#drawable/some_big_image" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="#DD000000" >
<ImageView
android:id="#+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/imageView2"
android:layout_toRightOf="#+id/imageView2"
android:text="TextView"
android:textColor="#android:color/white" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/imageView2"
android:layout_toRightOf="#+id/imageView2"
android:text="TextView"
android:textColor="#android:color/white" />
</RelativeLayout>
Now start from top parent to child.
RelativeLayout mParent = new RelativeLayout(this);
RelativeLayout.LayoutParams mParentParams = new RelativeLayout.LayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
mParent.setLayoutParams(mParentParams);
ImageView mBigImageView = new ImageView(this);
mBigImageView.setLayoutParams(mParentParams);
mParent.addView(mBigImageView);
As you practice you can directly code the same without creating xml.
Basic Idea:
Create a RelativeLayout which contains one ImageView and two TextView which is aligned bottom
set the alpha of the RelativeLayout to adjust the transparency
You can have one relative layout which contain one image view for big image and below it one more linear layout containing image and text as "pearl Continental ..." and background of liner layout should be 50-60% transparent.
Create the background for LL using shapes. and set it as align bottom of parent (relative) layout.
Hope this helps you
I have a custom view/layout that need to be sized based on the contents of the children. The layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="64dp"
android:orientation="vertical">
<LinearLayout
android:id="#+id/rectangle"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#drawable/rectangle"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:id="#+id/iconImageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical|left"
android:contentDescription="Icon"
android:src="#drawable/iconplaceholder"
android:scaleType="fitCenter"/>
<TextView
android:id="#+id/labelTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:textColor="#color/white"
android:gravity="center|left"
android:textSize="16dp"
android:text="Hello"/>
</LinearLayout>
<View
android:id="#+id/triangle"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_horizontal"
android:background="#drawable/triangle"
android:rotation="180"/>
</LinearLayout>
What I would like to do is:
List item
Allow the view to have a max width
Make sure that the entire text of #+id/labelTextView of the text view is visible in the view and allow it to grow taller if needed, but not get any wider than the max width.
If the text is really short, the view should just be as wide as needed
Set the size of the #+id/iconImageView from code and make sure that the parent view is resized accordingly
I guess I need to override onMeasure of my view, but please advice if this is the correct way. Also, how can I measure the size of a TextView given a set of constraints, like a max width?
I would think you would be able to set the layout_height for the parent to wrap_content. This way, it sets its view to be the size it needs to be depending on the sizes of the children inside it.
I've got a ListActivity with a custom Adapter. This adapter uses the following list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/item_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/ll1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/default_margin"
android:contentDescription="#string/checkbox_content_description"
android:src="#drawable/checkbox_unchecked"
android:background="#layout/transparent_background"
android:focusableInTouchMode="false"
android:adjustViewBounds="true"
android:clickable="false"
android:focusable="false" />
<TextView
android:id="#+id/tv_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginTop="#dimen/default_margin"
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false"
android:textIsSelectable="false" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginTop="#dimen/default_margin">
<TextView
android:id="#+id/tv_product_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false"
android:textIsSelectable="false" />
</LinearLayout>
<TextView
android:id="#+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginTop="#dimen/default_margin"
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false"
android:textIsSelectable="false" />
</LinearLayout>
<LinearLayout
android:id="#+id/ll2"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_below="#id/ll1">
<Space
android:id="#+id/filler_space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/default_margin"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginBottom="#dimen/default_margin"
android:focusableInTouchMode="false"
android:clickable="false"
android:focusable="false" />
<EditText
android:id="#+id/et_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginBottom="#dimen/default_margin"
android:inputType="number" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginBottom="#dimen/default_margin"
android:padding="0dp">
<AutoCompleteTextView
android:id="#+id/actv_search_product"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:singleLine="true"
android:ellipsize="end"
android:inputType="text" />
</LinearLayout>
<EditText
android:id="#+id/et_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/default_margin"
android:layout_marginBottom="#dimen/default_margin"
android:inputType="numberDecimal"
android:digits="0123456789.,-" />
</LinearLayout>
</RelativeLayout>
In the Adapter's getView-method I've added the following piece of code:
// Change the width of the Filler-Space to match the Image
// and leave the height as is
Space space = (Space) view.findViewById(R.id.filler_space);
space.setLayoutParams(new LinearLayout.LayoutParams(
imageView.getMeasuredWidth(), space.getMeasuredHeight()));
This gives the following result at first:
When I change the Visibility of my second LinearLayout (ll2) to VISIBLE, it gives the following result:
What I want instead is:
As it seems the Space-View width isn't changed at all.. While I know for fact that the getView methods are successfully called thanks to Log-messages and other things I do in the getView method.
Does anyone know what I did wrong?
PS: When I use View instead of Space I have the same result. Never used Space before, so I thought it might have to do something with that.
Solution thanks to Demand's option 4:
// Since we can't access Measured widths and heights before the View is completely loaded,
// we set up an Observer that will be called once the ListItem's layout is ready
ViewTreeObserver observer = view.getViewTreeObserver();
if(observer.isAlive()){
// In order to access the view in the onGlobalLayout, it needs to be final, so we create a final copy here
final View v = view;
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
// This will be called once the layout is finished, prior to displaying it
#Override
public void onGlobalLayout() {
ImageView imageView = (ImageView) v.findViewById(R.id.image);
Space space = (Space) v.findViewById(R.id.filler_space);
// Change the width of the Filler-Space to match the Image and leave the height as is
space.setLayoutParams(new LinearLayout.LayoutParams(imageView.getMeasuredWidth(), space.getMeasuredHeight()));
}
});
}
You set width and height if your space to wrap_content. It's mean that space will have width as their children, but there is no children and you have width = 0. It's not about space, it's about layout measuring in android.
When you call your code:
Space space = (Space) view.findViewById(R.id.filler_space);
space.setLayoutParams(new LinearLayout.LayoutParams(
imageView.getMeasuredWidth(), space.getMeasuredHeight()));
Your imageView havn't measured yet and return width = 0. It will measured later before drawing. In getView() in adapter you only create views, but measuring, layout and drawing will be later.
You have several ways to fix it:
set width of your space in dp, instead of wrap_content.
using relative layout instead of three linear layouts.
Use TableLayout.
add GlobalTreeObserver and getMeasuredWidth() at right time.
Post your runnable to view's handler to get width after drawing.
I think the best ways is 2 and 3, because 4 and 5 will cause measuring several times.
In my android app, I try to publish some data on ButtonClick.
I've created separate layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TableLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3" >
<TableRow>
<TextView
android:id="#+id/TV1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#string/strTV1" />
</TableRow>
</TableLayout>
<ImageView
android:id="#+id/overallPortraitIV"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:contentDescription="#string/strPortrait"/>
</LinearLayout>
My onClick event:
View view = getLayoutInflater().inflate(R.layout.overall_rfid_layout, null);
overallResultTab.addView(view);
ImageView portraitImage=(ImageView)view.findViewById(R.id.overallRfidPortraitIV);
decodedByte = scaleToFitWidth(decodedByte, portraitImage.getMeasuredWidth());
portraitImage.setImageBitmap(decodedByte);
As you see, my ImageView width is set to 0, but it has weight, so, factical width should not be 0. So how to get amount of pixels, which factically are occupied by the ImageView?
As shown in code, I've tried to getMeasuredWidth, also, getWidth, but they return 0;
Since you are measuring ImageView's width before setting any image to the view thats why getMeasuredWidth returning 0. In your XML, you set the layout_width to "0dip" so before setting any image to this ImageView it's width will remain "0dip".