Custom child view bringToFront not working as expected - android

I have a custom view added as a child of a LinearLayout. I need this custom view to anchor to the bottom irrespective of the parent viewgroup. Here's how I'm doing it.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!drawn) {
drawn = true;
Rect rect = new Rect();
((View) getParent()).getGlobalVisibleRect(rect);
setY(rect.bottom - rect.top - getContext().getResources().getDimension(R.dimen.bottom_bar_height));
setZ(UiUtils.dpToPx(8f));
forceLayout();
bringToFront();
} else {
binding.bottomBarLl.draw(canvas);
}
}
When I add the custom view to a LinearLayout parent, if the children above this child have match_parent height, this one doesn't show.
Below is a screenshot from the layout inspector where it is shown to be laid out but doesn't show.
The inspector also shows that the getZ() of the custom view is 24.
I'm building for API level 21+ but I've also tried ((View) getParent()).invalidate(); and getParent().requestLayout(); in the onDraw() to no good.
From the documentation for View.bringToFront()
bringToFront
Added in API level 1
void bringToFront ()
Change the view's z order in
the tree, so it's on top of other sibling views. This ordering change
may affect layout, if the parent container uses an order-dependent
layout scheme (e.g., LinearLayout). Prior to KITKAT this method should
be followed by calls to requestLayout() and invalidate() on the view's
parent to force the parent to redraw with the new child ordering.
this should be working, but it isn't for me. What am I doing incorrectly?

Like other questions which couldn't get it to work with a LinearLayout, I also used a coordinator layout finally where the linear layout and my custom view are siblings and things are working fine. It would be great if someone could explain the documentation though, since it is misleading otherwise.

Related

Android equivalent to View.setTranslationX, but to adjust width?

I have been spending hours unsuccessfully trying to adjust the width, height, and offset of a simple view in Android as the result of a button press. I have discovered that setTranslationX and setTranslationY always work; the legacy method of setLayoutParams never works once the view is laid out initially. Calls to requestLayout() and invalidate() similarly produce no results.
I have tried to setLayoutParams within the context of posting a runnable, but this does nothing.
Because setTranslationX always works, I would just use that, but unfortunately there is no equivalent method like setWidth or setHeight.
As you can see in the AOSP, setTranslationX makes a call to invalidateViewProperty, which is a private method of View.
Is there an equivalent method to setTranslationX to adjust a view width or view margin, that presumably triggers invalidateViewProperty, and, by extension, works reliably?
EDIT
While in some situations, setLayoutParams may be expected to work after the initial layout, I am in a situation where setLayoutParams has no effect after the initial layout, but setTranslationX does. My setup is as follows:
Running Android KitKat 4.4
The view in question is MATCH_PARENT for both width, height
The view in question is a child of a RelativeLayout
The view in question is a View class with a simple solid-color background drawable
Here is the view:
<View
android:id="#+id/border"
android:background="#drawable/match_background_border_transparent"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
And here is the (non-working) code meant to dynamically alter its margins, but has no effect. Again, if I call setTranslationX, that always works.
holder.toggleButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
imageBorder.post(new Runnable() {
#Override
public void run() {
RelativeLayout.LayoutParams p = (LayoutParams) imageBorder.getLayoutParams();
p.leftMargin = 20;
p.rightMargin = 20;
p.topMargin = 20;
p.bottomMargin = 20;
imageBorder.setLayoutParams(p);
// imageBorder.setTranslationX does have an effect if I included it here
}
});
}
});
I have determined why setTranslationX was working, but setLayoutParams was not. My views were ultimately descendents of an AdapterView. I was able to programmatically manipulate LayoutParams of the AdapterView and his siblings, but none of the AdapterView's descendents.
Additional research showed that this was a common Android question:
Margin on ListView items in android
Why LinearLayout's margin is being ignored if used as ListView row view
What was confusing was that this view was several levels deep; i.e., it went:
AdapterView -> FrameLayout -> RelativeLayout -> View
Anyhow, I was able to accomplish my programmatic layout goals by wrapping view in another view, and using setPadding.

Android - Move a single instance of a View between Parent Views

For an important reason, i'm required to keep only a single instance of a View in the entire application.
Within the application there will be multiple parent views, (each displayed only once at at time). And I need to move the child view around - to the currently active parent.
I tried doing the following:
if (view_to_move_around != null) {
ViewGroup oldParent = (ViewGroup) view_to_move_around.getParent();
if (oldParent != null) {
oldParent.removeView(view_to_move_around);
}
} else {
// Initialise the View
}
newParent.addView(view_to_move_around)
However, that method didn't seem to work? Completely stuck at this point.
I guess my question will be "What do you mean it's not working?" Are you getting an exception? It is not being displayed properly? Does it execute without issue, but is not displaying? Does it display, but it's always initializing?
Are you manipulating the child view within each parent view, before you pass it on to the next view?
Few things to make sure right off the bat:
Layout Params. Are they all set correctly? For both the parent and the child?
Parent View. The code doesn't appear to be faulty from what I can see. So is the parent view being displayed correctly?
Visibility. Are both the parent and child View.VISIBLE?
EDIT
Sweet. Ok, when I'm debugging these things, I like to keep it simple at first. I would take the child view, set it's background color to purple (or a contrasting color from the parent). Then, for simplicity's sake, set it's layout params to match parent. Assuming the ParentView is a FrameLayout:
mChildView.setBackgroundColor(Color.CYAN);
mNewParentView.addView(mChildView,
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT, Gravity.CENTER));
Does it fill up and take over the parent?
In the intermediate steps, does it no longer have a parent when it's been removed?
Log.d("LOGTAG", mChildView.getParent().toString());
Does the new parent show that the child has been added?
Log.d("LOGTAG", mNewParentView.getChildCount()): // before and after
Is it being shown 'behind' the other views within the Parent?
mParentView.bringChildToFront(mChildView);
What may be the problem is that you are using getParent() to store oldParent. Compare for example:
if (view_to_move_around != null) {
ViewGroup oldParent = (ViewGroup) findViewById(R.id.old_parent);
if (view_to_move_around.getParent() == oldParent) {
oldParent.removeView(view_to_move_around);
}
} else {
// Initialise the View
}
newParent.addView(view_to_move_around)
Does this sound like what you're looking for? Let me know if this works.

Dynamically Setting RelativeLayout.LayoutParams.addRule

I have this issue where I have a relative layout that has two child relative layouts (leftpanel and rightpanel). They're inside a custom layout for listview items and each item is updated from a json response from the server. So the size depends on what the server provides.
Issue: I want to have each panel's height to match each other, but it seems that setting layout_height to match_parent doesn't work (actually, if this can be resolved, then no more problems).
What I did: I programmatically set the align top and bottom of each panel to each other -- if the other's bigger, adjust the other one and vice versa. So what I did was to have a view (rightpanel) to listen to rightPanel.getViewTreeObserver().addOnScrollChangedListener(), and call the method below everytime there's a scroll change:
private void updateLayoutAlignmentParams(ViewHolder viewHolder) {
RelativeLayout.LayoutParams leftPanelLayoutParams = (RelativeLayout.LayoutParams)viewHolder.leftPanel.getLayoutParams();
RelativeLayout.LayoutParams rightPanelLayoutParams = (RelativeLayout.LayoutParams)viewHolder.rightPanel.getLayoutParams();
int leftPanelHeight = viewHolder.leftPanel.getHeight();
int rightPanelHeight = viewHolder.rightPanel.getHeight();
if(leftPanelHeight > rightPanelHeight) {
rightPanelLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, 0);
rightPanelLayoutParams.addRule(RelativeLayout.ALIGN_TOP, 0);
leftPanelLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, viewHolder.rightPanel.getId());
leftPanelLayoutParams.addRule(RelativeLayout.ALIGN_TOP, viewHolder.rightPanel.getId());
} else {
leftPanelLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, 0);
leftPanelLayoutParams.addRule(RelativeLayout.ALIGN_TOP, 0);
rightPanelLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, viewHolder.leftPanel.getId());
rightPanelLayoutParams.addRule(RelativeLayout.ALIGN_TOP, viewHolder.leftPanel.getId());
}
}
What happens: not all the views get updated while scrolling; so I get a lop-sided listview item where one is bigger than the other vertically but some do adjust well. Odd thing is, when the item gets out of view, it's lop-sided, then gets corrected consistently.
Note: I also tried
addOnDrawListener() - every item is updated but I get an ArrayList out of bounds index but doesn't point to any line in my code. Wouldn't be the best solution anyway as I need to support devices with API < 16.
setOnGlobalLayoutListener() - Nothing happens.
Please let me know if you know why or have a better solution.
Finally [kindof] fixed it! Replaced the whole method with the code below:
private void updateLayoutAlignmentParams(ViewHolder viewHolder) {
viewHolder.rightPanel.setMinimumHeight(viewHolder.leftPanel.getHeight());
viewHolder.leftPanel.setMinimumHeight(viewHolder.rightPanel.getHeight());
}
Although, I was able to achieve having the left and right panel aligned with each other using the code above. I'm now having issues where the previous view's height and width are retained when I switch views. :(
Edit:
Okay, I ended up using LinearLayout to wrap the whole listview item. Not really sure why RelativeLayout isn't complying with match_parent, though.

Explain this android:orientation:"vertical"

What is android here?
what is Orientation here?
What is Vertical ?
I would be pleased to know if they are classes or packages or methods..?
I am confused?
Can some one explain hierarchy of it?
I am sure you have seen this inside the <LinearLayout>.
It means that whatever view you take inside the LinearLayout will be shown in screen by vertical (like Stake of views).
Every attributes started with android followed by : so here orientation is an attribute and vertical is the value to be assigned this attribute.
Update:
(Answer taken from here.)
For android:orientation="vertical", your views get stacked vertically like this:
View1
View2
View3
View4
etc...
And For android:orientation="horizontal", your views gets placed horizontally like this:
View1 View2 View3 View4 etc...
This is the XML tag for the Layout properties of any Layout Widget for Android UI.
android:orientation is the XML tag and "vertical" is value for the same. so when it will be loaded in UI framework, child of the layout will be arranged in vertical form.
These are Input parameters for XML tags . Although Java is an object oriented language ,but it does not means that you will consider every element of android as Classes . XML Layout structure is view forming technique which use native kit internally .so these #android:something are just identifier to tell the native kit what to do . nothing else .
This is the code written in android.widget.LinearLayout.java
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical();
} else {
layoutHorizontal();
}
}
You can view the SOURCE CODE HERE, Based on the orientation and gravity attribute how the android sets Child Views into Parent.

How to bring a view to front without calling bringToFront()?

There is apparently a bug in Android which breaks View.bringToFront.
If I have Views arranged in a GridView and want to bring a View to front by calling bringToFront(), it may get moved to the bottom right position in the GridView.
Something shady is going on there, so I can't use bringToFront(). When I don't call it, everything works fine. But the Views overlap - if I use scale animation to scale up a View, it's partially obscured by the Views to the bottom and to the right.
I've checked out bringToFront's source and it calls parent.bringChildToFront(...)
it's this method
public void bringChildToFront(View child) {
int index = indexOfChild(child);
if (index >= 0) {
removeFromArray(index);
addInArray(child, mChildrenCount);
child.mParent = this;
}
}
it apparently removes the View from itself! Without mercy! Is a ViewGroup so dumb that it can't manage Z-indexes in any other way that shuffling controls around? Obviously when GridView removes its child and then adds it back again, it gets placed at the end of the Grid!
Is there anything I can do to show a View on top of others without resorting to some hacking? One thing that comes to my mind is to create another View above the GridView, which will appear to be above the one I'm trying to bringToFront(), but I don't like this solution.
apparently I missed the android:clipChildren="false" property in GridView. It solves my problem.
Have you tried making the View focusable and just calling requestFocus()?
Calling bringToFront() changes the ordering in the GridView. Try creating an ImageView with an image of the view you want to animate and animate that instead.
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
final ImageView imageView = new ImageView(getActivity());
imageView.setImageBitmap(bitmap);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(view.getWidth() , view.getHeight());
imageView.setLayoutParams(params);
rootview.addView(imageView);
Add an animation listener to your animation and remove the ImageView at the end of the animation.
From API 21 you can call:
view.setZ(float)
If you target API above 21, you can simply add the attribute android:translationZ="xxx dp" to your XML.
Please note that if you add elevation in views like cardview, you go into the Z axis. And if you want a view come to foreground using this way, you just have to make it higher than the elevation you set.
Example : 6 dp elevation in cardview will require a 7dp in the attribute translationZ of the view you want foreground.

Categories

Resources