removeView doesn't remove View immediately and destroys Layout - android

I'm trying to dynamically remove some components in a LinearLayout in Android. The logic I have implemented is: If someone clicks a Button in my (Sidebar)Fragment I start an Animation that moves the clicked Button up and the Other Buttons out of the left side of the Display. The Animation triggers an AnimationListener that, after the Animation is complete, iterates over all Buttons in the Layout and removes all non clicked Buttons.
The Problem is: if I disable the Fade Out Animation and step in with the debugger i can see, that after removeView() the Views are still there.
The even bigger Problem is that my Button that should still be Visible (the clicked one) disappears and only shows up again, if I set X and Y Position manually at a later Time.
Is there any way i can "fix" the Button while removal and when does removeView() actually remove the View/update the Layout?
I already tried to remove all Views and then add the Button again with the same result.
Some snippets to clear things up a Bit:
//Start Animation over all Components
//Commented out the move outside animation to ensure the Layout doesn't get streched in this process
for (mComponentIterator = mLayout.getChildCount() - 1; mComponentIterator >= 0; mComponentIterator--) {
final ImageTextButton currentButton = (ImageTextButton) mLayout
.getChildAt(mComponentIterator);
ObjectAnimator buttonAnimation;
if (!currentButton.equals(pClickedButton)) {
// buttonAnimation = ObjectAnimator.ofFloat(currentButton, View.X, POSITION_OUTSIDE);
} else {
// buttonAnimation = ObjectAnimator.ofFloat(currentButton, View.Y, POSITION_UPPER);
animations.add( ObjectAnimator.ofFloat(currentButton, View.Y, POSITION_UPPER));
currentButton.setFocused(true);
mSelectedButton = currentButton;
}
// animations.add(buttonAnimation);
}
buttonAnimations.playTogether(animations);
buttonAnimations.setDuration(mShortAnimationDuration);
buttonAnimations.setInterpolator(new DecelerateInterpolator());
buttonAnimations.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator arg0) {
for (mComponentIterator = mLayout.getChildCount() - 1; mComponentIterator >= 0; mComponentIterator--) {
final ImageTextButton currentButton = (ImageTextButton) mLayout
.getChildAt(mComponentIterator);
if (currentButton.equals(mSelectedButton)) {
if (mCurrentFragment != null) {
//This changes the Layout in another
mListener.onChangeLayoutRequest(R.id.maincontent, mCurrentFragment);
}
} else {
mLayout.removeView(currentButton);
}
}
}
});
buttonAnimations.start();
//Callback when Activity is ready
mCurrentFragment.SetOnActivityCreatedListener(new OnActivityCreatedListener() {
#Override
public void onActivityCreated(Activity activity) {
Drawable grayMenuBackground = getActivity().getResources().getDrawable(
R.drawable.bg_menuitem);
Drawable coloredMenuBackground = grayMenuBackground.getConstantState()
.newDrawable();
coloredMenuBackground.setColorFilter(mSelectedMenuColor,
PorterDuff.Mode.SRC_ATOP);
LinearLayout.LayoutParams rightGravityParams = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
rightGravityParams.gravity = Gravity.TOP;
rightGravityParams.weight = 0;
Drawable seperator = getResources().getDrawable(R.drawable.seperator_menu);
seperator.setBounds(new Rect(0, 0, mSelectedButton.getWidth(), 1));
int insertIndex = 1;
for (Button menuButton : mCurrentFragment.getMenuItems()) {
//Some Button setupcode
mLayout.addView(menuButton, insertIndex++);
}
//Here the Button gets visible again
mSelectedButton.setLayoutParams(rightGravityParams);
mSelectedButton.setX(POSITION_LEFT);
mSelectedButton.setY(POSITION_UPPER);
}
});
Im stuck at this problem for two days now. Currently the Buttons Animate in the right manner, then, right after the animation has finished, all Buttons dissapear(also the Clicked Button). Then, after 1/2sec. the other fragment needs to load, the clicked button appears again
PS: I already scheduled the removal with ''mLayout.post()'' with the same result

What happens if you simply do:
button.setVisibility(View.GONE) ;
Added some comments along your code.
//Start Animation over all Components
//Commented out the move outside animation to ensure the Layout doesn't get streched in this process
for (mComponentIterator = mLayout.getChildCount() - 1; mComponentIterator >= 0; mComponentIterator--) {
final ImageTextButton currentButton = (ImageTextButton) mLayout
.getChildAt(mComponentIterator);
ObjectAnimator buttonAnimation;
if (!currentButton.equals(pClickedButton)) {
// buttonAnimation = ObjectAnimator.ofFloat(currentButton, View.X, POSITION_OUTSIDE);
} else {
// buttonAnimation = ObjectAnimator.ofFloat(currentButton, View.Y, POSITION_UPPER);
/*
* You say that you want to keep the clicked button, yet you
* add it to the animations?
*/
animations.add( ObjectAnimator.ofFloat(currentButton, View.Y, POSITION_UPPER));
currentButton.setFocused(true);
mSelectedButton = currentButton;
}
// animations.add(buttonAnimation);
}
buttonAnimations.playTogether(animations);
buttonAnimations.setDuration(mShortAnimationDuration);
buttonAnimations.setInterpolator(new DecelerateInterpolator());
buttonAnimations.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator arg0) {
for (mComponentIterator = mLayout.getChildCount() - 1; mComponentIterator >= 0; mComponentIterator--) {
final ImageTextButton currentButton = (ImageTextButton) mLayout
.getChildAt(mComponentIterator);
if (currentButton.equals(mSelectedButton)) {
if (mCurrentFragment != null) {
//This changes the Layout in another
/*
* What exactly does this do and why does it have to
* be done (numberOfButtons - 1) times?
*/
mListener.onChangeLayoutRequest(R.id.maincontent, mCurrentFragment);
}
} else {
/*
* This does indeed only remove the non selected buttons
* I think, but the animation is still applied to
* the selcted button.
* So it makes sense that you have to reset X and Y
* to see it again.
*/
mLayout.removeView(currentButton);
}
}
}
});
buttonAnimations.start();
//Callback when Activity is ready
mCurrentFragment.SetOnActivityCreatedListener(new OnActivityCreatedListener() {
#Override
public void onActivityCreated(Activity activity) {
Drawable grayMenuBackground = getActivity().getResources().getDrawable(
R.drawable.bg_menuitem);
Drawable coloredMenuBackground = grayMenuBackground.getConstantState()
.newDrawable();
coloredMenuBackground.setColorFilter(mSelectedMenuColor,
PorterDuff.Mode.SRC_ATOP);
LinearLayout.LayoutParams rightGravityParams = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
rightGravityParams.gravity = Gravity.TOP;
rightGravityParams.weight = 0;
Drawable seperator = getResources().getDrawable(R.drawable.seperator_menu);
seperator.setBounds(new Rect(0, 0, mSelectedButton.getWidth(), 1));
int insertIndex = 1;
for (Button menuButton : mCurrentFragment.getMenuItems()) {
//Some Button setupcode
mLayout.addView(menuButton, insertIndex++);
}
//Here the Button gets visible again
mSelectedButton.setLayoutParams(rightGravityParams);
mSelectedButton.setX(POSITION_LEFT);
mSelectedButton.setY(POSITION_UPPER);
}
});

Related

Android - Sliding button (arrow)

I have an "arrow" image on the right edge of the screen, pointing right.
It is a part of a bigger image hidden right, out of the screen.
By cliking on this arrow, i'd like it to point and slide to the left dragging within the screen the rest of the image that was hidden.
If I can, I'd also like to set the sliding speed of the sliding image.
Something like this:
Is it possible?
To answer your question, Yes, it is possible.
Hopefully you are using a fixed width for your item in which case you can just get it in onCreate() and initiate your ValueAnimator. If not you will need to wait for the layout to be drawn and get your width onGlobalLayout callback.
public void onCreate(){
super.onCreate(..)
...
init();
...
}
like so:
private void init(){
mButtonView = findViewById(R.id.button_view);
ImageView arrowButton = mButtonView.findViewById(R.id.arrow_image_container);
arrowButton.setOnClickListener(this);
int visibleLength = arrowButton.getWidth();
int totalLength = mButtonView.getWidth();
int translationDistance = totalLength - visibleLength;
mButtonTranslator = ValueAnimator.ofInt(0,translationDistance);
mButtonTranslator.addUpdateListener((animatedValue)->{
float distanceTraveled = (float)animatedValue.getAnimatedValue();
mButtonView.setTranslationX(distanceTraveled);
});
}
Implement View.OnClickListener and add a toggle method:
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.R.id.arrow_image_container:
toggleButton();
break;
}
}
Have a boolean member (mIsButtonVisible) to hold visibility state:
private void toggleButton(){
if (mIsButtonVisible){
hideButton(true);
} else {
showButton(true);
}
}
show:
private void showButton(boolean animate){
if (!mIsButtonVisible){
mButtonTranslator.setDuration(animate ? ANIMATION_DURATION_IN_MILLIS : 0);
mButtonTranslator.start();
mIsButtonVisible = true;
}
}
and hide:
private void hideButton(boolean animate){
if (mIsButtonVisible){
mButtonTranslator.setDuration(animate ? ANIMATION_DURATION_IN_MILLIS : 0);
mButtonTranslator.reverse();
mIsButtonVisible = false;
}
}
Once initialisation is done and you have your animator initialised you can hide the button off the screen in onViewCreated():
hideButton(false);

Custom circular reveal transition results in "java.lang.UnsupportedOperationException" when paused?

I created a custom circular reveal transition to use as part of an Activity's enter transition (specifically, I am setting the transition as the window's enter transition by calling Window#setEnterTransition()):
public class CircularRevealTransition extends Visibility {
private final Rect mStartBounds = new Rect();
/**
* Use the view's location as the circular reveal's starting position.
*/
public CircularRevealTransition(View v) {
int[] loc = new int[2];
v.getLocationInWindow(loc);
mStartBounds.set(loc[0], loc[1], loc[0] + v.getWidth(), loc[1] + v.getHeight());
}
#Override
public Animator onAppear(ViewGroup sceneRoot, final View v, TransitionValues startValues, TransitionValues endValues) {
if (endValues == null) {
return null;
}
int halfWidth = v.getWidth() / 2;
int halfHeight = v.getHeight() / 2;
float startX = mStartBounds.left + mStartBounds.width() / 2 - halfWidth;
float startY = mStartBounds.top + mStartBounds.height() / 2 - halfHeight;
float endX = v.getTranslationX();
float endY = v.getTranslationY();
v.setTranslationX(startX);
v.setTranslationY(startY);
// Create a circular reveal animator to play behind a shared
// element during the Activity Transition.
Animator revealAnimator = ViewAnimationUtils.createCircularReveal(v, halfWidth, halfHeight, 0f,
FloatMath.sqrt(halfWidth * halfHeight + halfHeight * halfHeight));
revealAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
// Set the view's visibility to VISIBLE to prevent the
// reveal from "blinking" at the end of the animation.
v.setVisibility(View.VISIBLE);
}
});
// Translate the circular reveal into place as it animates.
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("translationX", startX, endX);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("translationY", startY, endY);
Animator translationAnimator = ObjectAnimator.ofPropertyValuesHolder(v, pvhX, pvhY);
AnimatorSet anim = new AnimatorSet();
anim.setInterpolator(getInterpolator());
anim.playTogether(revealAnimator, translationAnimator);
return anim;
}
}
This works OK normally. However, when I click the "back button" in the middle of the transition, I get the following exception:
Process: com.adp.activity.transitions, PID: 13800
java.lang.UnsupportedOperationException
at android.view.RenderNodeAnimator.pause(RenderNodeAnimator.java:251)
at android.animation.AnimatorSet.pause(AnimatorSet.java:472)
at android.transition.Transition.pause(Transition.java:1671)
at android.transition.TransitionSet.pause(TransitionSet.java:483)
at android.app.ActivityTransitionState.startExitBackTransition(ActivityTransitionState.java:269)
at android.app.Activity.finishAfterTransition(Activity.java:4672)
at com.adp.activity.transitions.DetailsActivity.finishAfterTransition(DetailsActivity.java:167)
at android.app.Activity.onBackPressed(Activity.java:2480)
Is there any specific reason why I am getting this error? How should it be avoided?
You will need to create a subclass of Animator that ignores calls to pause() and resume() in order to avoid this exception.
For more details, I just finished a post about this topic below:
Part 1: http://halfthought.wordpress.com/2014/11/07/reveal-transition/
Part 2: https://halfthought.wordpress.com/2014/12/02/reveal-activity-transitions/
Is there any specific reason why I am getting this error?
ViewAnimationUtils.createCircularReveal is a shortcut for creating a new RevealAnimator, which is a subclass of RenderNodeAnimator. By default, RenderNodeAnimator.pause throws an UnsupportedOperationException. You see this occur here in your stack trace:
java.lang.UnsupportedOperationException
at android.view.RenderNodeAnimator.pause(RenderNodeAnimator.java:251)
When Activity.onBackPressed is called in Lollipop, it makes a new call to Activity.finishAfterTransition, which eventually makes a call back to Animator.pause in Transition.pause(android.view.View), which is when your UnsupportedOperationException is finally thrown.
The reason it isn't thrown when using the "back" button after the transition is complete, is due to how the EnterTransitionCoordinator handles the entering Transition once it's completed.
How should it be avoided?
I suppose you have a couple of options, but neither are really ideal:
Option 1
Attach a TransitionListener when you call Window.setEnterTransition so you can monitor when to invoke the "back" button. So, something like:
public class YourActivity extends Activity {
/** True if the current window transition is animating, false otherwise */
private boolean mIsAnimating = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Get the Window and enable Activity transitions
final Window window = getWindow();
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
// Call through to super
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_child);
// Set the window transition and attach our listener
final Transition circularReveal = new CircularRevealTransition(yourView);
window.setEnterTransition(circularReveal.addListener(new TransitionListenerAdapter() {
#Override
public void onTransitionEnd(Transition transition) {
super.onTransitionEnd(transition);
mIsAnimating = false;
}
}));
// Restore the transition state if available
if (savedInstanceState != null) {
mIsAnimating = savedInstanceState.getBoolean("key");
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the current transition state
outState.putBoolean("key", mIsAnimating);
}
#Override
public void onBackPressed() {
if (!mIsAnimating) {
super.onBackPressed();
}
}
}
Option 2
Use reflection to call ActivityTransitionState.clear, which will stop Transition.pause(android.view.View) from being called in ActivityTransitionState.startExitBackTransition.
#Override
public void onBackPressed() {
if (!mIsAnimating) {
super.onBackPressed();
} else {
clearTransitionState();
super.onBackPressed();
}
}
private void clearTransitionState() {
try {
// Get the ActivityTransitionState Field
final Field atsf = Activity.class.getDeclaredField("mActivityTransitionState");
atsf.setAccessible(true);
// Get the ActivityTransitionState
final Object ats = atsf.get(this);
// Invoke the ActivityTransitionState.clear Method
final Method clear = ats.getClass().getDeclaredMethod("clear", (Class[]) null);
clear.invoke(ats);
} catch (final Exception ignored) {
// Nothing to do
}
}
Obviously each has drawbacks. Option 1 basically disables the "back" button until the transition is complete. Option 2 allows you to interrupt using the "back" button, but clears the transition state and uses reflection.
Here's a gfy of the results. You can see how it completely transitions from "A" to "M" and back again, then the "back" button interrupts the transition and goes back to "A". That'll make more sense if you watch it.
At any rate, I hope that helps you out some.
You can add listener to enter transition that sets flag transitionInProgress in methods onTransitionStart() / onTransitionEnd(). Then, you can override method finishAfterTransition() and then check transitionInProgress flag, and call super only if transition finished. Otherwise you can just finish() your Activity or do nothing.
override fun finishAfterTransition() {
if (!transitionInProgress){
super.finishAfterTransition()
} else {
finish()
}
}

how to remove and add a button from the screen without affecting other buttons?

I'm dynamically creating buttons and place them randomly on the screen. I want to remove a button when it's being clicked and create another one, but when I do that, all the other buttons (the ones bellow him to be percise) also change their position (moving up 1 row). How can I modify my code so that the other buttons wont change their position when I click a button? I want to add the new button in the same row of the one that was removed.
I'm using a Linear Layout, so when I add a button it adds it to the bottom and fill the blank row by pushing up all the rows.
In the following code I just remove the button after a few seconds:
private void createGreenButton() {
Random r = new Random();
int hideDelay = r.nextInt(1000) + 1000;
final Button greenButton = new Button(this);
greenButton.setText("button");
greenButton.setOnClickListener(greenButtonClick(greenButton));
layout.addView(greenButton, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
// Place the buttons randomly on the screen
layout.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#SuppressLint("NewApi")
#Override
public void onGlobalLayout() {
Random r = new Random();
layout.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
LinearLayout.LayoutParams layoutParams = (LayoutParams) greenButton
.getLayoutParams();
int horizontalMargin = r.nextInt(SCREEN_WIDTH
- greenButton.getWidth());
layoutParams.setMargins(horizontalMargin, 50, 0, 0);
greenButton.setBackgroundColor(Color.GREEN);
greenButton.setLayoutParams(layoutParams);
}
});
layout.requestLayout();
mHandler.postDelayed(new Runnable() {
public void run() {
((ViewManager) greenButton.getParent()).removeView(greenButton);
createGreenButton();
}
}, hideDelay);
}

Three sliding panes - the Middle pane above Left and Right panes

I am trying to have the same navigation style as Viber's interface (the discussion page), without using a third-part Library such as SlidingMenu.
I thought that they have used SlidingPaneLayout to achieve this nice effect, but when I tried to code it, I noticed that the last pane is always over the second.
My questions :
Is this really a SlidingPaneLayout ?
If yes how to achieve this please ?
If no, is there an android native way to do the same thing ?!
Left Pane
Right Pane
First of all declare this all variable in your Class
/** Sliding Menu */
boolean alreadyShowing = false;
private int windowWidth;
private Animation animationClasses;
private RelativeLayout classesSlider;
LayoutInflater layoutInflaterClasses;
then inside onCreate method declare this, this will help you to get screen's height and width
Display display = getWindowManager().getDefaultDisplay();
windowWidth = display.getWidth();
display.getHeight();
layoutInflaterClasses = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
and then any of your button or image where by clicking you want to open slider put below code.
findViewById(R.id.slidermenu).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (!alreadyShowing) {
alreadyShowing = true;
openSlidingMenu();
}
}
});
and then outside the onCreate declare openSlidingMenu() as below.
private void openSlidingMenu() {
// showFadePopup();
int width = (int) (windowWidth * 0.8f);
translateView((float) (width));
#SuppressWarnings("deprecation")
int height = LayoutParams.FILL_PARENT;
// creating a popup
final View layout = layoutInflaterClasses.inflate(
R.layout.option_popup_layout,
(ViewGroup) findViewById(R.id.popup_element));
ImageView imageViewassignment = (ImageView) layout
.findViewById(R.id.assignment);
imageViewassignment.setOnClickListener(this);
final PopupWindow optionsPopup = new PopupWindow(layout, width, height,
true);
optionsPopup.setBackgroundDrawable(new PaintDrawable());
optionsPopup.showAtLocation(layout, Gravity.NO_GRAVITY, 0, 0);
optionsPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
public void onDismiss() {
// to clear the previous animation transition in
cleanUp();
// move the view out
translateView(0);
// to clear the latest animation transition out
cleanUp();
// resetting the variable
alreadyShowing = false;
}
});
}
just replace
final View layout = layoutInflaterClasses.inflate(
R.layout.option_popup_layout,
(ViewGroup) findViewById(R.id.popup_element));
this above code with your custom screen XML name and by it's ID. and here is other methos's which you need.
private void translateView(float right) {
animationClasses = new TranslateAnimation(0f, right, 0f, 0f);
animationClasses.setDuration(100);
animationClasses.setFillEnabled(true);
animationClasses.setFillAfter(true);
classesSlider = (RelativeLayout) findViewById(R.id.classes_slider);
classesSlider.startAnimation(animationClasses);
classesSlider.setVisibility(View.VISIBLE);
}
private void cleanUp() {
if (null != classesSlider) {
classesSlider.clearAnimation();
classesSlider = null;
}
if (null != animationClasses) {
animationClasses.cancel();
animationClasses = null;
}
}
remember here animationClasses = new TranslateAnimation(0f, right, 0f, 0f); you can play with this parameter for some different effect and also do not forget to change this line's ID with your current screen's ID like for example check below id
classesSlider = (RelativeLayout) findViewById(R.id.classes_slider);
here you need to replace this ID with your Current java screen's XML file's ID.
Hope this will help you.

Animation doesn't show when application starts

I have a main activity which keeps 2 fragments.
On the first fragment i call an animation from the main activity.
If i call it from a button it animates and everything is ok
If i try to call it automatically (like if(x>3) animate()) it doesn't show the animation at all and in addition if i push the button it continues not to showing the animation. the code is below
Parent
public void showAnimation()
{
AnimationSet as = new AnimationSet(true);
Animation up = AnimationUtils.loadAnimation(this.getBaseContext(), R.anim.slide_up);
up.setStartOffset(2000);
Animation down = AnimationUtils.loadAnimation(this.getBaseContext(), R.anim.slide_down);
down.setStartOffset(6000);
as.addAnimation(up);
as.addAnimation(down);
Log.v("FFF","ok");
test.setVisibility(View.VISIBLE);
test.startAnimation(as);
test.setVisibility(View.GONE);
}
Child
if(!settings.getBoolean(BADGE_D7, false)){
if (days >= 7){
days7.setImageResource(R.drawable.days7);
Log.v("FFF","done");
parent.showAnimation();
}
}
and in the constructor of the child i have
public Child(MainActivity p) {
this.parent = p;
}
by the way the log messages are shown up the only problem is the animation. What is the problem??
Well, i kinda found out a solution, it's not the best but it works.
First i put all the code in the child activity and i use it from there. I access the textView from the child and i added a treeObserver
test = (TextView) parent.findViewById(R.id.test1);
ViewTreeObserver vto = test.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
ready = true;
return true;
}
});
and I edited the showAnimation() like this:
public void showAnimation()
{
as = new AnimationSet(true);
Animation up = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_up);
Animation down = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_down);
down.setStartOffset(4000);
as.addAnimation(up);
as.addAnimation(down);
Log.v("FFF","ok");
while(!ready){} //Added this line
test.setVisibility(View.VISIBLE);
test.startAnimation(as);
test.setVisibility(View.GONE);
}
so while the TextView isn't in place it is stucks in a loop until everything is ok.
I had a view that I declared at the end of my layout to keep its Z index above its siblings. I had to touch the page to make the animation work.
So I set the Z index again through Java and it worked.
view.bringToFront();

Categories

Resources