我制作了一个自定义View
,用于在UI中显示反馈(通常是为了响应正在采取的操作)。调用FeedbackView.showText
时,它会为View
设置动画2秒,然后将其设置为动画。这是使用translationY
完成的。
如果我在第一次调用FeedbackView.showText
时对其应用的负边距大于其高度,则它不会正确显示动画;它只是出现(或在某些情况下根本不显示)。对FeedbackView.showText
的后续调用会导致正确的动画。
在activity_main.xml
下方,FeedbackView
的上边距为-36dp
,大于其高度(非取消时)。如果边距顶部更改为-35dp
,即使第一次调用FeedbackView.showText
,它也会正确设置动画。
有谁知道为什么会发生这样的事情?
Romain Guy说it is OK to use negative margins on LinearLayout
and RelativeLayout
。我唯一的猜测是他们对FrameLayout
没有问题。
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>
答案 0 :(得分:0)
我的猜测是负面margin
不是导致动画失败的直接原因。
你可能会达到相同的(不受欢迎的)效果 - 动画没有被执行 - 如果你设置例如:layout_marginLeft
到等于Activity
width
的值(所以是积极的值)。
问题在于,您的View
完全位于可见区域的“外部”,因此在创建Activity
时,View
不会立即呈现。 1}}尚未进行渲染的动画将无法执行。
更多信息(例如)here。
你可以做些什么来解决它:
以View
在渲染区域内的方式重建布局(因此基本上在可见区域内),但其View
设置为visibility
。在动画开始时(使用View.INVISIBLE
或AnimationListener
或其他内容;))将其AnimatorListener
设置为visibility
。
重建动画,使其不使用View.VISIBLE
(ViewPropertyAnimator
方法调用),而是使用animate()
Animation
。然后在另一个Object
(在您确定已经渲染的那个)上启动它 - 例如在View
View's
上(您可以使用ViewParent
获得)
您可以尝试(我的胆量告诉我应该有效,但您需要对其进行测试)将布局getParent()
和clipChildren
设置为{ {1}},强制您的视图即使在可见区域之外也能呈现。如果您尝试使用该解决方案(我认为您应该这样做,因为您不必更改那么多 - 只需将clipToPadding
,false
添加到此android:clipChildren="false"
中的所有android:clipToPadding="false"
请告诉我它是否有效。