问题
This is an issue in a Java Android application with a very simple custom draggable drawer in MainActivity.
This is how it behaves when it runs on Android 10 (API level 29) emulator, and it is the expected behavior.
But the problem is, when it runs on Android L (API level 21) emulator, it behaves unexpectedly as follows :
During the animation, UI components are not visible. But when app goes to background and comes back, they become visible.
Implementation details of app :
To detect fling / drag touch gesture, GestureDetectorCompat
was used.
When a fling gesture is detected, the custom drawer open animation is being initiated.
Animation is implemented using ConstraintSet
, ConstraintLayout
and TransitionManager
.
This is the implementation of touch gesture detections and TransitionManager animations.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private boolean mIsDrawerOpened;
private ConstraintLayout mRootConstraintLayout;
private final ConstraintSet mDrawerClosedConstraintSet = new ConstraintSet();
private final ConstraintSet mDrawerOpenedConstraintSet = new ConstraintSet();
private GestureDetectorCompat mGestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_drawer_closed);
// Drawer is initially closed
mIsDrawerOpened = false;
mRootConstraintLayout = findViewById(R.id.rootConstraintLayout);
mDrawerClosedConstraintSet.clone(this, R.layout.activity_main_drawer_closed);
mDrawerOpenedConstraintSet.clone(this, R.layout.activity_main_drawer_opened);
mGestureDetector = new GestureDetectorCompat(
getApplicationContext(),
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// Drag / Fling gesture detected
// TODO: Recongnize unwanted drag / fling gestures and ignore them.
TransitionManager.beginDelayedTransition(mRootConstraintLayout);
// Drawer is closed?
if(!mIsDrawerOpened) {
// Open the drawer
mDrawerOpenedConstraintSet.applyTo(mRootConstraintLayout);
mIsDrawerOpened = true;
}
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// Single tap detected
// TODO: If user has tapped on the drawer, do not close it.
TransitionManager.beginDelayedTransition(mRootConstraintLayout);
// Drawer is opened?
if(mIsDrawerOpened) {
// Close the drawer
mDrawerClosedConstraintSet.applyTo(mRootConstraintLayout);
mIsDrawerOpened = false;
}
return true;
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
}
);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
}
This is the layout XML of closed drawer.
res/layout/activity_main_drawer_closed.xml
<ConstraintLayout
android:id="@+id/rootConstraintLayout">
<ConstraintLayout
android:id="@+id/drawerConstraintLayout"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
<!-- Constraint start (left) of drawer to end (right) of parent (drawer is outside the parent) -->
app:layout_constraintStart_toEndOf="parent"
... >
<Button
android:id="@+id/button1"
android:text="1"
... />
<Button
android:id="@+id/button2"
android:text="2"
... />
</ConstraintLayout>
<ImageView
android:id="@+id/notch"
android:src="@drawable/drawer_notch"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/drawerConstraintLayout"
... />
</ConstraintLayout>
This is the layout XML of opened drawer.
res/layout/activity_main_drawer_opened.xml
<ConstraintLayout
android:id="@+id/rootConstraintLayout">
<ConstraintLayout
android:id="@+id/drawerConstraintLayout"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
<!-- Constraint end (right) of drawer to end (right) of parent (drawer is inside the parent) -->
app:layout_constraintEnd_toEndOf="parent"
... >
<Button
android:id="@+id/button1"
android:text="1"
... />
<Button
android:id="@+id/button2"
android:text="2"
... />
</ConstraintLayout>
<ImageView
android:id="@+id/notch"
android:src="@drawable/drawer_notch"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/drawerConstraintLayout"
... />
</ConstraintLayout>
ConstraintSets from those 2 layouts are taken as start and end key-frames for animations.
Min SDK version is set to API level 19.
build.gradle
android {
defaultConfig {
minSdkVersion 19
...
}
...
}
Full implementation can be found in this GitHub gist.
回答1:
To do this works as expected you need to add android:clipChildren="false"
to your root ViewGroup
in your case is ConstaintLayout
in your layout activity_main_drawer_closed.xml
. Of course such solution applicable only when your view is outside of the viewport.
I don't know why this behaviour differ through versions of Android. In theory, from Android Marshmallow, when you start scene transition root view became invalidated by TransitionManager
and being redraw.
来源:https://stackoverflow.com/questions/61729160/ui-components-not-visible-in-transitionmanager-animations