Android FrameLayout Детский Отрицательный Маржина Нечетный эффект

Я сделал заказ View это используется для отображения обратной связи в пользовательском интерфейсе (обычно в ответ на предпринимаемое действие). когда FeedbackView.showText называется, это оживит View в течение 2 секунд, а затем оживить его. Это сделано с помощью translationY,

Если я применю к нему отрицательное поле, которое больше его высоты в первый раз FeedbackView.showText называется это не оживляет правильно; он просто появляется (или в некоторых случаях вообще не отображается). Последующие звонки FeedbackView.showText вызвать правильную анимацию.

В activity_main.xml ниже FeedbackView имеет верхнюю границу -36dp, который больше, чем его высота (при отсутствии отрицания). Если вершина поля изменяется на -35dp правильно оживляет даже в первый раз FeedbackView.showText называется.

Кто-нибудь знает, почему что-то подобное произошло?

Ромэн Гай сказал, что можно использовать отрицательные поля на LinearLayout а также RelativeLayout, Мое единственное предположение, что они не в порядке с FrameLayout s.

FeedbackView.java

public class FeedbackView extends FrameLayout {
  public static final int DEFAULT_SHOW_DURATION = 2000;

  private AtomicBoolean showing = new AtomicBoolean(false);
  private AtomicBoolean animating = new AtomicBoolean(false);

  private float heightOffset;

  private Runnable animateOutRunnable = new Runnable() {
    @Override
    public void run() {
      animateContainerOut();
    }
  };

  public FeedbackView(Context context) {
    super(context);
    init();
  }

  public FeedbackView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  public FeedbackView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
  }

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public FeedbackView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    init();
  }

  private void init() {
    setAlpha(0);
  }

  public boolean isShowing() {
    return showing.get();
  }

  public void showText(Context context, String text) {
    removeCallbacks(animateOutRunnable);

    heightOffset = getMeasuredHeight();

    removeAllViews();

    final TextView tv = new TextView(context);
    tv.setGravity(Gravity.CENTER);
    tv.setTextColor(Color.WHITE);
    tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
    tv.setText(text);

    addView(tv);

    if(!showing.getAndSet(true)) {
      animateContainerIn();
    }
    else {
      tv.setTranslationY(-getHeight());
      tv.animate().translationY(0).start();
    }

    postDelayed(animateOutRunnable, DEFAULT_SHOW_DURATION);
  }

  private void animateContainerIn() {
    if(animating.getAndSet(true)) {
      animate().cancel();
    }

    ViewPropertyAnimator animator = animate();
    long startDelay = animator.getDuration() / 2;

    animate()
        .alpha(1)
        .setStartDelay(startDelay)
        .start();

    animate()
        .translationY(heightOffset)
        .setStartDelay(0)
        .withEndAction(new Runnable() {
          @Override
          public void run() {
            animating.set(false);
            showing.set(true);
          }
        })
        .start();
  }

  private void animateContainerOut() {
    showing.set(false);

    if(animating.getAndSet(true)) {
      animate().cancel();
    }

    ViewPropertyAnimator animator = animate();
    long duration = animator.getDuration();

    animate()
        .alpha(0)
        .setDuration(duration / 2)
        .start();

    animate()
        .translationY(-heightOffset)
        .setDuration(duration)
        .withEndAction(new Runnable() {
          @Override
          public void run() {
            animating.set(false);
          }
        })
        .start();
  }
}

MainActivity.java

public class MainActivity extends Activity {
  private FeedbackView feedbackView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    feedbackView = (FeedbackView) findViewById(R.id.feedback);

    findViewById(R.id.show_feedback).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        feedbackView.showText(MainActivity.this, "Feedback");
      }
    });
  }
}

activity_main.xml

<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:background="#000"
    android:clickable="true"/>

  <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <FrameLayout
      android:layout_width="match_parent"
      android:layout_height="90dp"/>

    <FrameLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:clickable="true"
      android:background="#e9e9e9"/>

    <negative.margin.FeedbackView
      android:id="@+id/feedback"
      android:layout_width="match_parent"
      android:layout_height="35dp"
      android:layout_marginTop="-36dp"
      android:background="#20ACE0"/>

  </FrameLayout>

  <Button
    android:id="@+id/show_feedback"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|center"
    android:text="Show Feedback"/>

</LinearLayout>

1 ответ

Я думаю, что отрицательный margin не является прямой причиной неудачи вашей анимации.

Вы, вероятно, достигните того же (нежелательного) эффекта - анимация не выполняется - если вы установите, например: layout_marginLeft до значения, равного Activitywidth (так что положительное значение).

Проблема в том, что ваш View полностью "вне" видимой области, поэтому, когда ваш Activity создается, View не отображается сразу. И запустить анимацию на View то, что еще не было обработано, не будет выполнено.

Более подробная информация (например) здесь.

Что вы можете сделать, чтобы это исправить:

  • Восстановите ваш макет так, как ваш View находится в визуализированной области (так в основном в видимой области), но его visibility установлен в View.INVISIBLE, В начале анимации (используйте AnimationListener или же AnimatorListener или что-то еще;)) установите его visibility в View.VISIBLE,

  • Перестройте свою анимацию, чтобы она не использовалась ViewPropertyAnimator (animate() вызов метода), но AnimationObject, И начать это по другому View (на одном, который, как вы уверены, уже отрисован) - например, на View'sViewParent (который вы можете получить с getParent()).

  • Вы можете попробовать (мои смелости говорят мне, что это должно работать, но вам нужно будет проверить это), чтобы установить макеты clipChildren а также clipToPadding в false, заставляя ваши виды отображаться, даже если вы находитесь за пределами видимой области. Если вы попробуете это решение (и я думаю, что вам следует, потому что вам не придется так сильно менять - просто добавьте android:clipChildren="false",android:clipToPadding="false" всем вашим layouts в этом Activity) скажите пожалуйста, сработало ли это.

Другие вопросы по тегам