Invalidate () does not redraw children

So, I have a rather strange setting, and I get some strange visual errors from it. Basically, I have two views in relative layout: the first is just an ImageView background image; the second is the same background image, but blurry to give the appearance of a frozen glass effect:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/profile_holder" android:layout_width="match_parent" android:layout_height="match_parent" > <!-- background --> <ImageView android:id="@+id/backgroundImage" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="0dp" android:layout_marginTop="0dp" android:scaleType="centerCrop" android:src="@drawable/default_logo" /> <!-- blurry background --> <com.custom.MaskedBlurredBackgroundView_ android:id="@+id/blurred_background" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="0dp" android:layout_marginTop="0dp" /> <ScrollView android:id="@+id/scrolly" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" > <com.custom.HalvedLinearLayout_ android:paddingTop="100dp" android:id="@+id/profileHolder" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <Button android:id="@+id/profileButtonTracks" style="@style/ProfileButtons" android:drawableLeft="@drawable/ic_profile_menu_music" android:text="@string/profile_button_tracks" /> ... 

The blurry background should only be watching the HalvedLinearLayout_, which scrolls up, so I need a blurry background to mask myself above HalvedLinearLayout_ so that the non-blurry background is shown through:

 scrolly = ((ScrollView) rootView.findViewById(R.id.scrolly)); scrolly.setOnScrollListener(new ScrollView.OnScrollListener() { private boolean hasScrolled = false; @Override public void onScrollChanged(int l, int t, int oldl, int oldt) { if (!hasScrolled) { hasScrolled = true; Logger.action(Logger.Action.BioView, band); } positionMask(); } }); ... protected void positionMask() { if (blurredBackground == null) return; Logger.d(TAG, "positionMask() " + rootView.getId()); //blurredBackground.setMaskBottom(blurredBackground.getHeight() - profileHolder.getPaddingTop() - scrollY); blurredBackground.setMaskBottom(profileHolder.getTop() + profileHolder.getPaddingTop() - scrolly.getScrollY()); //blurredBackground.invalidate(); //profileHolder.invalidate(); rootView.postInvalidate(); } 

Now the problem is that everything works as it should, EXCEPTION for the huge egregious fact that when scrolling through the ScrollView, blurredBackground actually draws on HalvedLinearLayout_, erasing the buttons in it. (but only half, sometimes. Sometimes it will be fine, sometimes half of the button will be drawn and the other half saved ... all kinds of weird crashes.)

I started debugging, and I noticed something interesting: calling invalidate () on the root directory does not actually cancel () all the children. I tried all the invalidate and draw functions with some registration materials:

 public class MaskedBlurredBackgroundView extends BlurredBackgroundView { /*** * Sets the mask to cover everything above the given Y coordinate * @param t Y Coordinate in pixels */ public void setMaskBottom(int y) { maskBottom = y; } private void clipCanvas(Canvas canvas) { Logger.d(TAG, "dispatchDraw clipping: " + maskBottom + ", " + getWidth() + "," + getHeight()); canvas.clipRect(0, maskBottom, getWidth(), getHeight(), Region.Op.REPLACE); } @Override protected void dispatchDraw(Canvas canvas) { canvas.save(); clipCanvas(canvas); super.dispatchDraw(canvas); canvas.restore(); } @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { Logger.d(TAG, "drawChild: " + child + getWidth() + "," + getHeight()); return super.drawChild(canvas, child, drawingTime); } @Override public void invalidate() { Logger.d(TAG, "invalidate: " + getWidth() + "," + getHeight()); super.invalidate(); } 

And now, when I look at the log, rootView.invalidate () is triggered when scrolling, but children never redraw:

 07-23 12:36:49.328: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.348: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.348: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.368: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.368: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.378: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.398: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.418: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.448: D/BandDetailFrag(12982): positionMask() 2131165298 07-23 12:36:49.478: D/BandDetailFrag(12982): positionMask() 2131165298 

How to force children to redraw, in the RIGHT ORDER? Right now I assume that they are out of order and why the blurry background rests on everything else.

Here is a glitch screenshot:

screenshot of glitch: see the blurred background masked correctly, but drawing OVER the HalvedLinearLayout_

+6
source share
2 answers

I ran into this problem with invalidate() , and since there is still no good answer in SO, I decided that I would post my solution.

This is a recursive function that will go through all the child views of the ViewGroup . If the child is a ViewGroup , it will be recursive and look for the child. Otherwise, it will call invalidate() on the child (this will be the completion case).

 public void invalidateRecursive(ViewGroup layout) { int count = layout.getChildCount(); View child; for (int i = 0; i < count; i++) { child = layout.getChildAt(i); if(child instanceof ViewGroup) invalidateRecursive((ViewGroup) child); else child.invalidate(); } } 

The layout parameter should be something like ViewGroup ( LinearLayout , RelativeLayout , etc.), which makes this a pretty general solution.

It works for my purposes, but I did not bring it to rigorous testing. If there are any errors, please comment and let me know.

+2
source

Well, I figured out how to get rid of crossbreeding, at least: all I had to do was change “Region.Op.REPLACE” to “Region.Op.INTERSECT”, and now it works!

Invalidate () still does not redraw the children, but I think now I will allow this slide to work.

+1
source

Source: https://habr.com/ru/post/950117/


All Articles