Reputation: 5904
I'm trying to create the reveal effect in my application but without success. What i want is reveal a cardview when i open a fragment. What i tried so far is:
private void toggleInformationView(View view) {
infoContainer = view.findViewById(R.id.contact_card);
int cx = (view.getLeft() + view.getRight()) / 2;
int cy = (view.getTop() + view.getBottom()) / 2;
float radius = Math.max(infoContainer.getWidth(), infoContainer.getHeight()) * 2.0f;
if (infoContainer.getVisibility() == View.INVISIBLE) {
infoContainer.setVisibility(View.VISIBLE);
ViewAnimationUtils.createCircularReveal(infoContainer, cx, cy, 0, radius).start();
} else {
Animator reveal = ViewAnimationUtils.createCircularReveal(
infoContainer, cx, cy, radius, 0);
reveal.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
infoContainer.setVisibility(View.INVISIBLE);
}
});
reveal.start();
}
}
and in the onCreateView
i started the method:
toggleInformationView(view);
this is the cardview:
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/contact_card"
android:visibility="invisible"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:elevation="4dp"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical"
android:padding="10dp"
card_view:cardCornerRadius="2dp" >
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/bg_contact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_weight="0"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@drawable/banner" />
</LinearLayout>
</android.support.v7.widget.CardView>
and the logcat:
11-08 17:25:48.541: E/AndroidRuntime(26925): java.lang.IllegalStateException: Cannot start this animator on a detached view!
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.view.RenderNode.addAnimator(RenderNode.java:817)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.view.RenderNodeAnimator.setTarget(RenderNodeAnimator.java:277)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.view.RenderNodeAnimator.setTarget(RenderNodeAnimator.java:261)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.animation.RevealAnimator.<init>(RevealAnimator.java:37)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.view.ViewAnimationUtils.createCircularReveal(ViewAnimationUtils.java:48)
11-08 17:25:48.541: E/AndroidRuntime(26925): at com.as.asapp.TFragment.toggleInformationView(TFragment.java:64)
11-08 17:25:48.541: E/AndroidRuntime(26925): at com.as.asapp.TFragmentshowInformation(TFragment.java:52)
11-08 17:25:48.541: E/AndroidRuntime(26925): at com.as.asapp.TFragment.onCreateView(TFragment.java:37)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.support.v4.app.Fragment.performCreateView(Fragment.java:1786)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:947)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:739)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1489)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:454)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.os.Handler.handleCallback(Handler.java:739)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.os.Handler.dispatchMessage(Handler.java:95)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.os.Looper.loop(Looper.java:135)
11-08 17:25:48.541: E/AndroidRuntime(26925): at android.app.ActivityThread.main(ActivityThread.java:5221)
11-08 17:25:48.541: E/AndroidRuntime(26925): at java.lang.reflect.Method.invoke(Native Method)
11-08 17:25:48.541: E/AndroidRuntime(26925): at java.lang.reflect.Method.invoke(Method.java:372)
11-08 17:25:48.541: E/AndroidRuntime(26925): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
11-08 17:25:48.541: E/AndroidRuntime(26925): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Checking, seems that getWidth()
and getHeight()
returns 0. But i don't know if it's the real problem. Thanks
Upvotes: 70
Views: 30253
Reputation: 119
looks like your view has not been attached yet. view.post() goes into the main thread queue and gets executed after the other pending tasks are finished and it means after your view gets attached so this will work for you:
view.post(new Runnable() {
@Override
public void run() {
//do what you want
}
});
Upvotes: 0
Reputation: 4116
This is how I solved it, I added a onLayoutChange listener to the view in onCreateView callback, so whenever it is attached to the view and ready to draw, it makes the reveal
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the layout for this fragment
final View view = inflater.inflate(R.layout.fragment_map_list, container, false);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
v.removeOnLayoutChangeListener(this);
toggleInformationView(view);
}
});
}
return view;
}
EDIT: As some people posted, it is better to call toggleInformationView
in onViewCreated
and avoid the View.OnLayoutChangeListener
Upvotes: 78
Reputation: 2757
simplified outside of the scope of the question
private void startAnimation(@NonNull final View view, @NonNull final Runnable onAttachedToWindow) {
if (view.isAttachedToWindow()) {
onAttachedToWindow.run();
} else {
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
onAttachedToWindow.run();
view.removeOnAttachStateChangeListener(this)
}
@Override
public void onViewDetachedFromWindow(View view) {
}
});
}
}
call startAnimation for one or many animations in mind
startAnimation(view, new Runnable() {
@Override
public void run() {
//the code inside toggleInformationView(view)
}
});
Upvotes: 5
Reputation: 1004
Check your Layout is attached to window or not by using rootLayout.isAttachedToWindow()
method.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (rootLayout!= null) {
if(rootLayout.isAttachedToWindow()) {
ViewTreeObserver viewTreeObserver = drawerLayout.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
startRevealAnimation(rootLayout);
rootLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
}
}
Upvotes: 0
Reputation: 1315
ah !!!, so finally here is the solution for this error. Actually, the problem is that we are assigning a view to animation with its height and width, and suddenly we didn't notice that there may be a case, that view won't load properly that time when we are assigning it to animation. So for that, we have to wait until view properly loads.
LayoutInflater inflater = this.getLayoutInflater();
View rowView = inflater.inflate( R.layout.fileselecttype,null, true );
rowView or [Your View]
[Your View].post(new Runnable() {
@Override
public void run() {
//Do your animation work here.
}
});
Upvotes: 4
Reputation: 5600
Use a ViewTreeObserver.
ViewTreeObserver viewTreeObserver = rootLayout.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
revealActivity(revealX, revealY);
rootLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
Upvotes: 1
Reputation: 809
You can use runnable to do the anim . The runnable will run after the view's creation .
view.post(new Runnable() {
@Override
public void run() {
//create your anim here
}
});
Upvotes: 50
Reputation: 4936
Small addition to Hitesh's answer. It's better to use https://developer.android.com/reference/android/support/v4/view/ViewCompat.html#isAttachedToWindow(android.view.View)
android.support.v4.view.ViewCompat
boolean isAttachedToWindow (View view)
Example:
LinearLayout rootView = null;
rootView = (LinearLayout) findViewById(R.id.rootView);
boolean isAttachedToWindow = ViewCompat.isAttachedToWindow(rootView);
Upvotes: 18
Reputation: 45072
I was using custom View so I used isAttachedToWindow()
method to check if view is attached and avoid doing reveal while view is not attached.
Upvotes: 1
Reputation: 1242
How about simply placing the method in the onViewCreated() method:
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
toggleInformationView(view);
}
it works
Upvotes: 7