Custom view hast strange layout behavior - android

Basically I have a custom View-Class (MessageViewAudio) which extends ConstraintLayout. This custom view contains another ConstraintLayout which contains another custom view (AudioVisualizationView) I made myself. The problem ist that the Top-Layer-Custom View (MessageViewAudio) always has some space to the right. I attached the information the layout inspector gave me. If you need any more code, just let me know.
As already said, I inspected the layout and tried different types of containers and manually tried to remove margins and paddings from every view mentioned above. But the layout inspector already says that there is no margin oder padding that could cause that strange behavior. There are two ways I got this to work One: is to remove all containers and just add the AudioVisualizationView custom view. Two: is to remove the property constrainedWidth = true from the LayoutParams of MessageViewAudio. Now I need constrainedWidth = true but I have that feeling that this property is kind of the problem, I can't just figure out why constrainedWidth results in this behavior.
With containers
Not Working
Hierarchy
ChatMessage
MessageViewAudio
audioContent
visualizationView
Without containers
Working
The only code I can provide for now is the xml of the views:
audioContent + visualizationView:
<com.jooyapp.jooy.custom_views.RoundedConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/audioContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundColor="#color/colorAccent"
app:radius="20">
<!--android:paddingStart="10dp"-->
<!--android:paddingTop="10dp"-->
<!--android:paddingEnd="10dp"-->
<!--android:paddingBottom="10dp"-->
<com.jooyapp.jooy.custom_views.AudioVisualizationView
android:id="#+id/visualizationView"
android:layout_width="wrap_content"
android:background="#color/light_grey"
android:layout_height="wrap_content" />
</com.jooyapp.jooy.custom_views.RoundedConstraintLayout>
All others are created programmatically
All in all my problem is that I don't understand where this space to the right of the custom view comes.

Related

Accessing a view that has a shared ID within shared layouts when only one layout is visible

In my Android app I have a layout for an activity that presents a choice.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/wrapper"
android:orientation="vertical">
<include layout="#layout/choices" />
<include layout="#layout/choice_one" />
<include layout="#layout/choice_two" />
</LinearLayout>
In layout/choices the user sees two buttons. One button shows layout/choice_one and the other shows layout/choice_two. (The parent in layout/choice_one and layout/choice_two is initially set to android:visibility=gone.)
So when a user chooses either choice_one or choice_two, essentially I'm setting the visibility to VISIBLE. That works great.
The issue is that inside of both choice_one and choice_two I have shared elements with the same ID. For example I have a TextView with ID header. (I did this because I figured only one of those layouts would be visible and they use the same things.)
The issue is that I use Butterknife, and it seems like if I Bind header, when I set the visibility on choice_one or choice_two, I have a 50/50 chance of correctly calling header.setText("Blah") on the appropriate header element.
I'm sure I can get around this by giving unique IDs to all elements in the layouts or ditching Butterknife and using findViewById instead. But is there another way I can target a shared ID inside of a layout without ditching Butterknife or my shared layouts?
I figured this out. (Writing it out helped, so I'll keep it here in case someone else stumbles upon it.) Instead of using include in xml and inflating layouts there, I just subclassed ViewGroup for my two layouts and added them to wrapper:
ChoiceOne choiceOne = new ChoiceOne();
wrapper.addView(choiceOne);

Does 'android:animateLayoutChanges="true" ' Still work

I saw that this statement used inside an xml layout will automate animations for you. I want to add this to my app, but when I put it in the layout I want
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/arrow_background"
android:layout_marginTop="7dp"
android:layout_marginBottom="7dp"
android:padding="10dp"
android:animateLayoutChanges="true"
android:visibility="visible"
android:id="#+id/format_help">
And then I change the visibility to gone
view.findViewById(R.id.format_help).setVisibility(View.GONE);
It only just instantly goes away. The information I saw about this was from a few years ago so does anyone know if this is still supposed to work or how to get it to work?
EDIT: Also I added this statement everywhere and it still does not working. If it matters this is inside a fragment/dialog
It works for views inside container which has the property android:animateLayoutChanges="true". If a Linear layout having the above property, whenever a new view added or removed from that container the effect is visible. The animation effect will not be visible to the container itself when container is added or removed. For more information check the below link.usage of animateLayoutChanges

unwanted side margins of my custom listview item layout

I'm getting some weird left and right side margins when using a custom listview item layout. They are margins (or at least not padding of the container), since the background doesn't extend to the edge.
In this layout, I'm using a simple vertical LinearLayout with a bunch of textviews and a progressbar. If I switch back the built-in simple_list_item_activated_1.xml, the margins disappear. The linear layout itself doesn't have any layout margins. I specifically stripped it of any attributes, leaving only the id, the layout_width="match_parent" and layout_height="wrap_content", and the margins were still there.
Is there anything I'm missing here?
A screenshot of the problem can be seen here:
Edit 1: #Grishu: As I've said earlier, these margins appear even with a very simple layout, such as this
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/some_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
I just typed this from memory, so it might contain syntax errors. But you get the idea.
Edit2: I just went over all my layouts. The problematic margin was set on one of the parent containers, thus it has nothing to do with list items. Sorry for the spam.
i had a similar problem with a listview item layout, it looked fine in the graphical layout in eclipse, i solved it by setting:
android:layout_margin="0dp"
android:padding="0dp"
in the main relative layout, hope this helps.

Using layout_gravity="bottom" to place at bottom of LinearLayout

I would like to place a layout on the bottom of a LinearLayout, but I can't seem to get it to work. I know that I can use RelativeLayout to do this, but I should be able to use LinearLayout, shouldn't I?
EDIT: Actually this is more confusing than I thought. The layout below is simplified. In reality, I'm using fragments on a pre-3.0 device, with the compatibility layer.
I used Hierarchy Viewer to examine what's going on, and found that an android.support.v4.app.NoSaveStateFrameLayout was added to my layout, and that layout has layout_height set to wrap_content. That seems to be what's causing my problem, but I haven't yet figured out how to fix it.
Specifically, why doesn't this work? Shouldn't the layout_gravity place it at the bottom?
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
... stuff here ...
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="horizontal">
... more stuff here ...
</LinearLayout>
</LinearLayout>
BTW, changing layout_height to fill_parent or setting layout_weight don't seem to work either. I just want to better understand what is going on, because clearly I'm missing something important. Thanks.
First of all nice question.
Android behaves we can say weird in the situation like this.
if you have selected your parent linear layout's orientation horizontal then you can set its child component at bottom by setting its layoug_gravity=bottom. suppose you have added 2 text views in that horizontal linear layout and second textview's layout_gravity is bottom then it will set to bottom but it work like it is set at bottom in other column then the first text view. NOTE : you can set textview's layout_gravity = "left" or "right" when its parent linearlayout is horizontal but you cant see its result.
Oppositely, if you have selected parent linearlayout's orientation vertical then you can set its child component at left or right by using layout_gravity. but the second textview will shown in you can say next row with left or right gravity as you have set. NOTE you can set textview's layout_gravity = "top" or "bottom" when its linear layout is vertical but you can not see its result.
Try to make sample xml design as i have stated above so you get better idea.
Strange but True!!! Try to understand this behavior. :)
Just add space between what you want at the bottom and all the rest:
<Space
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
So I resolved the problem. It's a two-part solution:
First, the way to do this without using LinearLayout is to provide weight to the element above so that it takes up all of the empty space. BTW, you can see this example in the API demos: http://developer.android.com/resources/samples/ApiDemos/res/layout/linear_layout_3.html
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
... stuff here ...
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weight="1"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
... more stuff here ...
</LinearLayout>
</LinearLayout>
This by itself didn't solve my problem, as I had a NoSaveStateFrameLayout with layout_width="wrap_content" as a parent view, and so I needed to get that fixed first. I'm using code based on the wonderful Google I/O App, and when I searched the code for NoSaveStateFrameLayout, I found this:
// For some reason, if we omit this, NoSaveStateFrameLayout thinks we are
// FILL_PARENT / WRAP_CONTENT, making the progress bar stick to the top of the activity.
mRootView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
Thanks for an awesome comment Google!!! I added this into my source and everything worked great!
The moral of the story: Hierarchy Viewer and comments are your friends.
LinearLayout will just stack things as they are placed in there. Since it is vertical, it will keep placing items one after the next in a vertical manner. Can you change the android:gravity of the linearLayout and not the layout_gravity of the nested one and see if that works.
RelativeLayout of course should be the first way but you stated you didnt want to do that. Is there reason for that?
It could be that, as per https://stackoverflow.com/a/13366783/513038, you need to set the parent LinearLayout to have android:baselineAligned="false". Worked in my case.

How to bring view in front of everything?

I have activity and a lot of widgets on it, some of them have animations and because of the animations some of the widgets are moving (translating) one over another. For example the text view is moving over some buttons . . .
Now the thing is I want the buttons to be always on the front. And when the textview is moving I want to move behind the buttons.
I can not achieve this I tried everything I know, and "bringToFront()" definitelly doesn't work.
note I do not want to control the z-order by the order of placing element to layout cause I simply can't :), the layout is complex and I can not place all the buttons at the begging of the layout
You can call bringToFront() on the view you want to get in the front
This is an example:
yourView.bringToFront();
With this code in xml
android:translationZ="90dp"
I've been looking through stack overflow to find a good answer and when i couldn't find one i went looking through the docs.
no one seems to have stumbled on this simple answer yet:
ViewCompat.setTranslationZ(view, translationZ);
default translation z is 0.0
An even simpler solution is to edit the XML of the activity. Use
android:translationZ=""
bringToFront() is the right way, but, NOTE that you must call bringToFront() and invalidate() method on highest-level view (under your root view), for e.g.:
Your view's hierarchy is:
-RelativeLayout
|--LinearLayout1
|------Button1
|------Button2
|------Button3
|--ImageView
|--LinearLayout2
|------Button4
|------Button5
|------Button6
So, when you animate back your buttons (1->6), your buttons will under (below) the ImageView. To bring it over (above) the ImageView you must call bringToFront() and invalidate() method on your LinearLayouts. Then it will work :)
**NOTE: Remember to set android:clipChildren="false" for your root layout or animate-view's gradparent_layout. Let's take a look at my real code:
.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:hw="http://schemas.android.com/apk/res-auto"
android:id="#+id/layout_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/common_theme_color"
android:orientation="vertical" >
<com.binh.helloworld.customviews.HWActionBar
android:id="#+id/action_bar"
android:layout_width="match_parent"
android:layout_height="#dimen/dimen_actionbar_height"
android:layout_alignParentTop="true"
hw:titleText="#string/app_name" >
</com.binh.helloworld.customviews.HWActionBar>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/action_bar"
android:clipChildren="false" >
<LinearLayout
android:id="#+id/layout_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:gravity="center_horizontal"
android:orientation="horizontal" >
</LinearLayout>
<ImageView
android:id="#+id/imgv_main"
android:layout_width="#dimen/common_imgv_height"
android:layout_height="#dimen/common_imgv_height"
android:layout_centerInParent="true"
android:contentDescription="#string/app_name"
android:src="#drawable/ic_launcher" />
<LinearLayout
android:id="#+id/layout_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal"
android:orientation="horizontal" >
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
Some code in .java
private LinearLayout layoutTop, layoutBottom;
...
layoutTop = (LinearLayout) rootView.findViewById(R.id.layout_top);
layoutBottom = (LinearLayout) rootView.findViewById(R.id.layout_bottom);
...
//when animate back
//dragedView is my layoutTop's child view (i added programmatically) (like buttons in above example)
dragedView.setVisibility(View.GONE);
layoutTop.bringToFront();
layoutTop.invalidate();
dragedView.startAnimation(animation); // TranslateAnimation
dragedView.setVisibility(View.VISIBLE);
GLuck!
Try FrameLayout, it gives you the possibility to put views one above another. You can create two LinearLayouts: one with the background views, and one with foreground views, and combine them using the FrameLayout. Hope this helps.
If you are using ConstraintLayout, just put the element after the other elements to make it on front than the others
i have faced the same problem.
the following solution have worked for me.
FrameLayout glFrame=(FrameLayout) findViewById(R.id.animatedView);
glFrame.addView(yourView);
glFrame.bringToFront();
glFrame.invalidate();
2nd solution is by using xml adding this attribute to the view xml
android:translationZ=""
You can try to use the bringChildToFront, you can check if this documentation is helpful in the Android Developers page.
There can be another way which saves the day. Just init a new Dialog with desired layout and just show it. I need it for showing a loadingView over a DialogFragment and this was the only way I succeed.
Dialog topDialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar);
topDialog.setContentView(R.layout.dialog_top);
topDialog.show();
bringToFront() might not work in some cases like mine. But content of dialog_top layout must override anything on the ui layer. But anyway, this is an ugly workaround.
You can use BindingAdapter like this:
#BindingAdapter("bringToFront")
public static void bringToFront(View view, Boolean flag) {
if (flag) {
view.bringToFront();
}
}
<ImageView
...
app:bringToFront="#{true}"/>
The order of the overlapping views really depends of 4 things:
The attribute android:elevation which is measured in dp/sp
The attribute android:translationZ which is also measured in dp/sp.
In Constraint Layout, the order in which you put the views in your Component Tree is also the order to be shown.
The programmatically order that you set through methods like view.bringToFront() in your kotlin/java code.
The numerals 1 and 2 compite with each other and take preference over the points 3 and 4: if you set elevation="4dp" for View 1 and translationZ="2dp" for View 2, View 1 will always be on top regardless of the numerals 3 and 4.
Thanks to Stack user over this explanation, I've got this working even on Android 4.1.1
((View)myView.getParent()).requestLayout();
myView.bringToFront();
On my dynamic use, for example, I did
public void onMyClick(View v)
{
((View)v.getParent()).requestLayout();
v.bringToFront();
}
And Bamm !
You can use elevation attribute if your minimum api level is 21. And you can reorder view to the bottom of other views to bring it to front. But if elevation of other views is higher, they will be on top of your view.
If you are using a LinearLayout you should call myView.bringToFront() and after you should call parentView.requestLayout() and parentView.invalidate() to force the parent to redraw with the new child order.
Arrange them in the order you wants to show. Suppose, you wanna show view 1 on top of view 2. Then write view 2 code then write view 1 code. If you cant does this ordering, then call bringToFront() to the root view of the layout you wants to bring in front.
Try to use app:srcCompat instead of android:src
You need to use framelayout. And the better way to do this is to make the view invisible when thay are not require. Also you need to set the position for each and every view,So that they will move according to there corresponding position
You can set visibility to false of other views.
view1.setVisibility(View.GONE);
view2.setVisibility(View.GONE);
...
or
view1.setVisibility(View.INVISIBLE);
view2.setVisibility(View.INVISIBLE);
...
and set
viewN.setVisibility(View.VISIBLE);

Categories

Resources