luky luky - 11 days ago 10
Android Question

CoordinatorLayout Custom Behavior onDependentViewChanged is broken when layout is changed

I use Custom CoordinatorLayout Behavior for functionality of tracking the swipe in CoordinatorLayout to slide up two widgets together and it works (something like Custom BottomSheetBehavior + another behavior to follow the position of bottom sheet), but under some circumstances it gets broken.

Layout structure is bit complex but it is like this:

CoordinatorLayout (root tag of activity layout)
- Fragment (DetailFragment)
- CoordinatorLayout (root tag of DetailFragment)
- Fragment (MapFragment - with custom app:layout_behavior)
- CoordinatorLayout (also with another custom app:layout_behavior)
- NestedScrollView
- LinearLayout
- LinearLayout
- Some content


The custom behavior installed on MapFragment element is dependant via layoutDependsOn() method on the following element (CoordinatorLayout). And this following element has own custom behavior which slides it up with swipe gesture. In that case, custom behavior of MapFragment receives event onDependentViewChanged with every movement of its dependency.

But, if i make change in the section i called some content, eg i call setVisibility or i put there ListView and call setAdapter on it (this method interally calls requestLayout method - which has something to do with layout change same like setVisibility it seems) It stops working, that means Custom behavior of MapFragment stops receiving onDependentViewChanged even if the dependant view position has changed.

There is probably also some another hidden condition for this problem to make it occur. So i need to either find the hidden reason, or the workaround how to make it working.

Because, if i use the Detail fragment in activity this problem occurs, but in another activity it seems to working.

I also forgot to say, that it also shows as another extra problem, that the setVisibility method doesn't work - the element remains hidden + behavior of CL is broken (in the problematic activity. Btw - also when i had listview in my layout and called setAdapter on it, it didn't work also - as i said, it is obviously similar somehow with the setVisibility method).

Yes i tried to call setVisibility explicitly via UI thread like

getActivity().runOnUiThread(new Runnable(){
@Override
public void run() {
xxx.setVisibility(View.VISIBLE);
}});


but it didn't help. Thanks

Additional notes:


  • In the problematic Activity, the MapFragment behavior method onDependentViewChanged is called at least once (when the activity is displayed - that should indicate the dependency should be correctly linked) and i think it finds correct CoordinatorLayout as its dependency. But problem is this event is no longer fired if i change position of its dependency.

  • In the fragment code where i call the setVisibility method i also tried to call getView().requestLayout() after it but it didn't help.

  • Also note that the element on which i try to call setVisibility is in that moment out of screen (because the custom behaviour on the CoordinatorLayout after MapFragment is something like BottomSheetBehavior), so if it isn't some problem (in the working activity it is displayed on the screen - the BottomSheet is displayed expanded, not hidden or collapsed as in problematic Activity)



Edit:

It must be something like half dead or lazy UI event messaging system or so, because i found that if i change the visibility of some other element in activity layout, voila - map custom behavior begins work again! - though the element i tried to setVisibility to visible is still hidden..

So this was code in fragment:

getView().findViewById(R.id.info_beer_bdark).setVisibility(View.VISIBLE);


If i change it to:

getView().findViewById(R.id.info_beer_bdark).setVisibility(View.VISIBLE);
getActivity().findViewById(R.id.tabs).setVisibility(View.GONE);


The behavior of MapFragment works again - though as i said, element i tried to make visible is still hidden - maybe it needs some "refreshing injection" too on that layout level. (Tabs are on same layout level as DetailFragment element)

Answer

The reason of the problem is probably because i was animating the CoordinatorLayout (after MapFragment) within its Custom Behavior with code shown below, and during the animation changing the visibility of its children.

 ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));

Pseudo code:

 startCoordinatorUIAnimation(); // it takes like 300ms
 changeVisibilityOfCoordinatorChildren(); // doesn't work while animating + breaks Coordinator Behavior

I don't still know though how to achieve this - change the visibility of children while animating the parent. I tried runOnUIThread, also AsyncTask with runOnUIThread but that doesn't work.