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
до значения, равного Activity
width
(так что положительное значение).
Проблема в том, что ваш View
полностью "вне" видимой области, поэтому, когда ваш Activity
создается, View
не отображается сразу. И запустить анимацию на View
то, что еще не было обработано, не будет выполнено.
Более подробная информация (например) здесь.
Что вы можете сделать, чтобы это исправить:
Восстановите ваш макет так, как ваш
View
находится в визуализированной области (так в основном в видимой области), но егоvisibility
установлен вView.INVISIBLE
, В начале анимации (используйтеAnimationListener
или жеAnimatorListener
или что-то еще;)) установите егоvisibility
вView.VISIBLE
,Перестройте свою анимацию, чтобы она не использовалась
ViewPropertyAnimator
(animate()
вызов метода), ноAnimation
Object
, И начать это по другомуView
(на одном, который, как вы уверены, уже отрисован) - например, наView's
ViewParent
(который вы можете получить сgetParent()
).Вы можете попробовать (мои смелости говорят мне, что это должно работать, но вам нужно будет проверить это), чтобы установить макеты
clipChildren
а такжеclipToPadding
вfalse
, заставляя ваши виды отображаться, даже если вы находитесь за пределами видимой области. Если вы попробуете это решение (и я думаю, что вам следует, потому что вам не придется так сильно менять - просто добавьтеandroid:clipChildren="false"
,android:clipToPadding="false"
всем вашимlayouts
в этомActivity
) скажите пожалуйста, сработало ли это.