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

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.

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.

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.

Refresh all view in android?

I working on messaging platform like whatsup.When ı send message ı must update the screen because ı am getting data in db.When i press send button ı must update View.
I googled but ı can not exact solution.Can anybody help me?
EDIT:
my code:
RelativeLayout layout=new RelativeLayout(this);
LayoutParams lparams = new LayoutParams(
1200,LayoutParams.WRAP_CONTENT);
layout.setLayoutParams(lparams);
//layout.setBackgroundResource(R.drawable.bubble);
// Creating a new TextView
TextView tv = new TextView(this);
tv.setText(msgdesc[i]);
layout.setGravity(Gravity.RIGHT);
tv.setPadding(30, 10, 0, 0);
layout.addView(tv);
bubbleLayout.addView(layout);
You will need to call either requestLayout() or invalidate() depend on what you update exactly in your view
If you just need the View to redraw so call invalidate()
If you change the View bounds (e.g. size) call requestLayout()
if you use a listview with a listadapter, then you have to use listadapter.notifyDataSetChanged(); this will update your listview with the new data
Have you try with view.invalidate()? http://developer.android.com/reference/android/view/View.html
Drawing
Drawing is handled by walking the tree and rendering each view that
intersects the invalid region. Because the tree is traversed in-order,
this means that parents will draw before (i.e., behind) their
children, with siblings drawn in the order they appear in the tree. If
you set a background drawable for a View, then the View will draw it
for you before calling back to its onDraw() method.
Note that the framework will not draw views that are not in the
invalid region.
To force a view to draw, call invalidate().
you can try like the reply in this post invalidate the viewgroup:
How to force an entire layout View refresh?
Finally if you have only a TextView in your layout try this:
//Supposing that msgdesc is a class field.
void myRefreshFunction(RelativeLayout l)
{
if(l != null)
{
l.removeAllViews();
TextView tv = new TextView(this);
tv.setText(msgdesc[i]);
layout.setGravity(Gravity.RIGHT);
tv.setPadding(30, 10, 0, 0);
layout.addView(tv);
}
}
see Dianne's answer:
invalidate() just causes the views to be redrawn: http://developer.android.com/reference/android/view/View.html#invalidate()
requestLayout() asks that the views go through a new measure/layout pass (and then redraw): http://developer.android.com/reference/android/view/View.html#requestLayout()
If you're building a messaging app and items are added to some kind of list, I suggest using a RecyclerView and Recyclerview Adapter to achieve what you're trying to do. When the list grows in size, your relative layout won't be scrollabe, a RecyclerView however would. On top of that RecyclerView gives you more performance with very long list because from the name ~Recycle~rView, it recycles previous items and thus increases preformance.
Here's a reasonable tutorial on how to achieve what you want:
https://blog.sendbird.com/android-chat-tutorial-building-a-messaging-ui
(I'm not affiliated with SendBird or anything, it's just the first result when you google: RecyclerView chat example)

How to bring Animation to front: bringToFront and zAdjustment/Z-ordering

I have a LinearLayout with two views in it, next to each other. When a view is tapped upon, it animates to full screen. This goes fine for the view on the right, but it fails for the left view:
I am unable to animate the left view on top of the right view
Without corrupting my layout with bringToFront(). Adjusting the Z-order of my animation does not seem to work.
Not a solution: The problem is gone when I use "brintToFront()" on the left view, but this causes my layout to be completely corrupted afterwards, and there is no brintToBack() function or whatsoever. => brintToFront = not a good solution?
Adjusting the Z-order of my animation does not seem to work (does not change anything).
scaleAnimation.setZAdjustment(Animation.ZORDER_TOP);
translateAnimation.setZAdjustment(Animation.ZORDER_TOP);
AnimationSet set = new AnimationSet(true);
set.addAnimation(scaleAnimation);
set.addAnimation(translateAnimation);
set.setZAdjustment(AnimationSet.ZORDER_TOP);
myFrameLayout.startAnimation(set);
Why does Z-ordering not work as expected?
This should be possible if you extend LinearLayout and override the following method:
getChildDrawingOrder (int childCount, int i)
To make sure layout uses your function you need to call setChildrenDrawingOrderEnabled(true)
See ViewGroup javadoc
For your z reordering will apply you gonna need to request a new layout on the view, but try doing it before starting your animation:
AnimationSet set = new AnimationSet(true);
// init animation ...
scaledView.bringToFront();
scaledView.requestLayout();
myFrameLayout.startAnimation(set);
I guess there is no good answer to this.
I am now animating everything else on the screen too, so if view 1 has to grow larger on top of view 2 than I animate view 2 also, in such a way that it looks like view 1 is growing on top of view 2, while actually view 2 is getting smaller while view 1 is getting larger.
Quite a lot of work and a bad solution, but I guess the only solution.

layout function doesn't change View dimension.

I am trying to create a view that slides up from the bottom of the screen. I tried setting the initial position of the view (which should be offscreen) in xml, but instead of placing the imageview where I specified, it truncated it. My second thought was to set the position of the view programatically inside the onWindowFocusChanged method. Here's my code
#Override
public void onWindowFocusChanged(boolean hasFocus) {
if(hasFocus) {
slide_dock.layout(slide_dock.getLeft(), phone_height - 70, slide_dock.getRight(), phone_height + 230);
}
}
The problem is that this only works SOME of the time. I've been debugging it, and I believe the issue is that the layout values of slide_dock get altered after my onWindowFocusChanged function completes, I'm just not sure where. Can anyone here help me out? Or link me to somewhere that explains the layout process in-depth? I've been searching around to no avail.
Have you tried using the Animation framework? Use a RelativeLayout and align your child view to the bottom of the parent. Then use the following animation, maybe showing and hiding your view appropriately with View.setVisibility(int)
View myView = View(this);
TranslateAnimation slideUp = new TranslateAnimation(myView.getHeight(), 0, 0, 0);
slideUp.setDuration(250); // millis
slideUp.setFillAfter(true); // Required for animation to "stick" when done
myView.startAnimation(slideUp);
You might have to play with the TranslateAnimation constructor parameters to get it to work right (this is from the top of my head).
So I figured out the cause for the issue above, and I'm posting it here in case anyone ever runs into the same problem.
The reason why the ImageView was resizing was because in ImageView's onMeasure function, it resizes itself if it doesn't think that it will fit onto the screen. You can view the ImageView source here and see how it works: http://www.google.com/codesearch/p?hl=en#uX1GffpyOZk/core/java/android/widget/ImageView.java&d=3
To work around this, I created a custom view that extended ImageView and overrode the onMeasure method. In my new onMeasure method, I simply called setDimension to give my new view the dimensions that I wanted it to have, this effectively stopped the view from resizing as it was doing earlier
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec), 300);
}

Categories

Resources