【问题标题】:Moving inflated Layout/View to a new position in an animated way以动画方式将膨胀的布局/视图移动到新位置
【发布时间】:2016-07-27 14:13:07
【问题描述】:

我正在膨胀一个布局以在屏幕上显示它。 现在我想将该布局部分移出屏幕。我尝试在膨胀的布局上使用.animate().translationX(-500)。它完全按照我想要的样子移出屏幕:

之前:

之后:

但是现在我的问题是布局最初占用的区域不可点击(例如不能在主屏幕之间滑动,只能在蓝色标记区域之外工作)。

我该如何解决这个问题,所以蓝色区域在移动后是可点击的,但布局的剩余区域(红色区域)仍然可以注册点击(例如,如果我想使用 onClickListener 将其移回)? 我想我必须使用windowManagerupdateViewLayout() 函数,但我不知道我应该对参数进行哪些更改。

编辑 1:

这里是代码。翻译由按钮触发:

overlay.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#d8ff0000"
    android:weightSum="1">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="245dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Lorem ipsum "
            android:id="@+id/textView6"
            android:layout_marginLeft="10dp" />

        <Button
            android:layout_width="55dp"
            android:layout_height="wrap_content"
            android:text="X"
            android:id="@+id/button"
            android:layout_gravity="center_horizontal|top"
            android:layout_alignParentTop="true"
            android:layout_toEndOf="@+id/textView6"
            android:layout_marginStart="27dp" />
    </RelativeLayout>
</LinearLayout>

使布局膨胀的服务:

OverlayService.java

public class FloatingMenu extends Service{

    private WindowManager wm;
    private LinearLayout ll;
    private boolean isPushedToSide = true;


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        ll = new LinearLayout(this);


        LinearLayout.LayoutParams llParameters = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        ll.setBackgroundColor(Color.argb(50, 255, 255, 255));
        ll.setLayoutParams(llParameters);

        final WindowManager.LayoutParams parameters = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);

        parameters.gravity = Gravity.LEFT | Gravity.TOP;

        LayoutInflater li = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
        ll = (LinearLayout) li.inflate(R.layout.overlay, null);
        final Button b = (Button) ll.findViewById(R.id.button);
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (isPushedToSide) {
                    ll.animate().translationX(0);
                    isPushedToSide = false;
                }else {
                    ll.animate().translationX(-450);
                    isPushedToSide = true;
                }
            }
        });
        wm.addView(ll, parameters);
    }
}

编辑 2:

在继续我的研究时,我发现.animate().translationX() 只会移动视图呈现的位置,而不是视图本身。这也解释了为什么在使用.animate().translationX() 之后,onclick 事件仍会在与“移动”布局之前相同的位置触发。

现在我需要找到一种结合动画将实际视图移动到所需位置的方法。任何想法如何做到这一点?

编辑 3: 我发现很多有类似问题的帖子,如果我是对的,问题的解决方案是使用ObjectAnimator 而不是.animate().translationX()。 我尝试在我的代码中替换该部分,以便 onClickListener 现在看起来像这样:

b.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (isPushedToSide) {
            //ll.animate().translationX(0);
            ObjectAnimator.ofFloat(ll, "translationX", 0).setDuration(250).start();
            isPushedToSide = false;
        }else {
            //ll.animate().translationX(-450);
            ObjectAnimator.ofFloat(ll, "translationX", -450).setDuration(250).start();
            isPushedToSide = true;
         }
    }
});

ll 是我膨胀的LinearLayout,包含Lorem ipsum 文本和Button。 同样,动画本身工作正常,但行为方式仍然相同。我仍然需要点击按钮的原始位置才能触发onClickListener,蓝色区域仍然不可点击。

编辑 4:

正在尝试使用.invalidate() 更新实际位置的建议。然而它没有用。我不确定它是否正确使用。

b.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (isPushedToSide) {
            //ll.animate().translationX(0);
            ObjectAnimator.ofFloat(ll, "translationX", 0).setDuration(250).start();
            ll.invalidate();
            isPushedToSide = false;
        }else {
            //ll.animate().translationX(-450);
            ObjectAnimator.ofFloat(ll, "translationX", -450).setDuration(250).start();
            ll.invalidate();
            isPushedToSide = true;
         }
    }
});

我还尝试更改LayoutParams 的x 值并调用wm.updateViewLayou(ll, updatedParameters),效果很好。当我将叠加层向右移动 100 像素时,触发 onClickListener 的区域也被向右推 100 像素。问题是我只能在实际屏幕的边界内移动叠加层,而我需要将其移出屏幕。绑定负值,但这也不起作用。

我本质上需要的是抽屉式导航,但高度有限(至少我认为这是我需要的)。我会看看我是否可以让某些东西以这种方式正常工作。

编辑 5:

更正了我的代码,以便在动画完成时调用 .inflate(),但它的行为仍然相同:

ObjectAnimator anim = ObjectAnimator.ofFloat(ll, "translationX", 0).setDuration(250);

anim.addListener(new Animator.AnimatorListener() {

    @Override
    public void onAnimationStart(Animator animator) {}

    @Override
    public void onAnimationEnd(Animator animator) {
        b.invalidate();
        text.invalidate();
        frame.invalidate();
        ll.invalidate();
    }

    @Override
    public void onAnimationCancel(Animator animator) {}

    @Override
    public void onAnimationRepeat(Animator animator) {}
});
anim.start();

我还录制了两个显示问题的 .gif。我在第二个上将gravity 设置为CENTER

【问题讨论】:

    标签: android android-layout android-animation layout-inflater


    【解决方案1】:

    我认为解决该问题的简单方法是动画更改属性“宽度”而不是转换您的布局。

    希望对你有帮助

    【讨论】:

    • 但是那个方法不会还是显示布局的所有内容,只是挤在一起以适应新的宽度吗?
    • 是的,它会调整您的布局大小,您需要完全隐藏部分布局吗?你能显示你的代码的sn-p吗?动画部分
    • 是的,如果我只更改宽度属性,那么稍后添加内容时它看起来并不好。稍后我会将我的代码添加到开始帖子中。
    • 认为你需要在动画完成后调用 invalidate() 。你必须为每个子视图调用它。现在它在动画开始的同时调用。您是否使用开发人员选项中的“显示布局边界”选项?有时很有帮助..
    • 哇,这是非常奇怪的行为。在动画开始时为您的按钮尝试 setOnclickListener(null) ,并在动画结束时再次设置。当然,这不是解决方案,但它可能有助于定位问题
    【解决方案2】:

    我询问了我用于创建此类SYSTEM_ALERT_WINDOW 窗口的指南的创建者,他帮助了我。

    代码如下:

    public class Fw  extends Service {
    
        private WindowManager wm;
        private boolean isPushedToSide = true;
    
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        WindowManager.LayoutParams parameters;
        Button b;
        View inflating_layout;
        LayoutInflater li;
        ValueAnimator animator;
    
        @Override
    
        public void onCreate() {
            super.onCreate();
    
            wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    
            li = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
            inflating_layout = li.inflate(R.layout.overlay, null);
    
            b = (Button) inflating_layout.findViewById(R.id.button);
    
            parameters = new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT);
            parameters.gravity = Gravity.LEFT | Gravity.TOP;
    
            isPushedToSide = true;
    
            b.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View view) {
                    if (isPushedToSide) {
    
                        isPushedToSide = false;
    
                        animator  = ValueAnimator.ofInt(parameters.x, parameters.x-450);
                        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                            @Override
                            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                                int val = (Integer) valueAnimator.getAnimatedValue();
                                updateView(inflating_layout, val);
    
                            }
                        });
                        animator.setDuration(250);
                        animator.start();
    
                    } else {
    
                        animator = ValueAnimator.ofInt(parameters.x, parameters.x+450);
                        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                            @Override
                            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                                int val = (Integer) valueAnimator.getAnimatedValue();
                                updateView(inflating_layout, val);
    
                            }
                        });
                        animator.setDuration(250);
                        animator.start();
    
    
                        isPushedToSide = true;
                    }
                }
            });
            wm.addView(inflating_layout, parameters);
        }
    
        public void updateView(View view, Integer x) {
            if (view != null) {
                if (x != null) parameters.x = x;
                wm.updateViewLayout(view, parameters);
            }
        }
    }
    

    现在一切似乎都正常。只有滑动动画在屏幕边缘闪烁了一下。以较慢的速度,例如.setDuration(2500) 这不会发生。不知道是模拟器的原因还是其他原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-18
      • 1970-01-01
      • 2016-03-28
      • 1970-01-01
      • 2019-06-27
      • 2021-08-02
      • 2015-03-15
      • 1970-01-01
      相关资源
      最近更新 更多