Android -- 如何在屏幕外定位视图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2554871/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 06:17:56  来源:igfitidea点击:

Android -- How to position View off-screen?

androidlayoutanimationimageview

提问by RyanM

I'm trying to animate a simple ImageView in my application and I want it to slide in from the bottom of the screen and come to a resting position where the top 50px of the view is off the top of the screen (e.g. the final position of the ImageView should be -50px in X). I've tried to use the AbsoluteLayout to do this, but this actually cuts off the top 50px of the ImageView such that the top 50px is never rendered. I need to have the top 50px of the ImageView visible/rendered while it's animating and then simply have it come to a rest slightly off-screen. I hope I've explained that well enough.

我试图在我的应用程序中为一个简单的 ImageView 设置动画,我希望它从屏幕底部滑入并到达一个静止位置,其中视图的顶部 50px 离开屏幕顶部(例如最终位置ImageView 的 X 中应为 -50px)。我曾尝试使用 AbsoluteLayout 来执行此操作,但这实际上切断了 ImageView 的前 50 像素,因此永远不会呈现前 50 像素。我需要让 ImageView 的前 50 像素在动画时可见/渲染,然后让它在屏幕外稍微休息一下。我希望我已经解释得够清楚了。

Here is what I'm currently using as a layout and the slide-in animation (this currently doesn't render the top 50px of the ImageView):

这是我目前用作布局和滑入动画的内容(目前不呈现 ImageView 的前 50 像素):

Layout:

布局:

<?xml version="1.0" encoding="utf-8"?>
   <AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_height="fill_parent" 
      android:layout_width="fill_parent" 
      android:id="@+id/QuickPlayClipLayout">
      <ImageView android:id="@+id/Clip"
         android:background="@drawable/clip" 
         android:layout_width="fill_parent" 
         android:layout_height="wrap_content" 
         android:layout_y="-50dp">
      </ImageView>
   </AbsoluteLayout>

Animation:

动画片:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
   <translate android:fromYDelta="100%p" 
       android:toYDelta="0"
       android:duration="1000"/>
   <alpha android:fromAlpha="0.0" 
       android:toAlpha="1.0"
       android:duration="1000" />
</set>

Thanks in advance.

提前致谢。

采纳答案by RyanM

I figured out a solution to this that should be easy to implement. It involves modifying the layout and the Activity inflating the layout... see below:

我想出了一个应该很容易实现的解决方案。它涉及修改布局和 Activity 膨胀布局......见下文:

Activity (QuickPlay.java):

活动(QuickPlay.java):

public class QuickPlay extends Activity implements AnimationListener
{
    private ImageView myImageView;
    private LinearLayout LL;

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

        myImageView = (ImageView) this.findViewById(R.id.Clip);
        LL = (LinearLayout) this.findViewById(R.id.QuickPlayClipLayout);

        //finally
        Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_in_quickplay);
        anim.setAnimationListener(this);
        LL.startAnimation(anim);
    }
    @Override
    public void onAnimationEnd(Animation animation){}

    @Override
    public void onAnimationRepeat(Animation animation){}

    @Override
    public void onAnimationStart(Animation animation)
    {
        // This is the key...
        //set the coordinates for the bounds (left, top, right, bottom) based on the offset value (50px) in a resource XML
        LL.layout(0, -(int)this.getResources().getDimension(R.dimen.quickplay_offset), 
                LL.getWidth(), LL.getHeight() + (int)this.getResources().getDimension(R.dimen.quickplay_offset));
    }
}

New LinearLayout (CustomLinearLayout.java):

新的 LinearLayout (CustomLinearLayout.java):

public class CustomLinearLayout extends LinearLayout
{
    private Context myContext;

    public CustomLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        myContext = context;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec+((int)myContext.getResources().getDimension(R.dimen.quickplay_offset)));
    }
}

Layout (/res/layout/quick_play_screen.xml):

布局(/res/layout/quick_play_screen.xml):

<?xml version="1.0" encoding="utf-8"?>
   <com.games.mygame.CustomLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_height="fill_parent" 
      android:layout_width="fill_parent" 
      android:id="@+id/QuickPlayClipLayout">
      <ImageView android:id="@+id/Clip"
         android:background="@drawable/clip" 
         android:layout_width="fill_parent" 
         android:layout_height="wrap_content">
      </ImageView>
   </com.games.mygame.CustomLinearLayout>

Resource (/res/values/constants.xml):

资源(/res/values/constants.xml):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="quickplay_offset">50dp</dimen>
</resources>

Animation (/res/anim/slide_in_quickplay.xml):

动画(/res/anim/slide_in_quickplay.xml):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
   <translate android:fromYDelta="100%p" 
       android:toYDelta="0"
       android:duration="1000"/>
   <alpha android:fromAlpha="0.0" 
       android:toAlpha="1.0"
       android:duration="1000" />
</set>

The program now does exactly what I need it to do. The entire layout starts off screen at the bottom, slides in in 1 sec and comes to a rest where the top of the layout is actually 50px off the top of the screen (i.e. LL.getTop() = -50) and the bottom of the layout is resting at the bottom of the screen (i.e. LL.getBottom() = 530 = 480 + 50).

该程序现在正是我需要它做的。整个布局从底部的屏幕开始,在 1 秒内滑入并静止,其中布局的顶部实际上离屏幕顶部 50 像素(即LL.getTop() = -50),并且布局的底部位于屏幕的底部屏幕(即LL.getBottom() = 530 = 480 + 50)。

回答by arun

Instead we can simple give negative values to layout_margin(Top/left/right/bottom) eg: If you want your view to be off from top of the screen you can specify

相反,我们可以简单地为 layout_margin(Top/left/right/bottom) 赋予负值,例如:如果您希望您的视图远离屏幕顶部,您可以指定

android:layout_marginTop="-40dp"

回答by esilver

To position my view offscreen I used the following code:

为了在屏幕外定位我的视图,我使用了以下代码:

View myView = /* view you want to position offscreen */
int amountOffscreen = (int)(myView.getWidth() * 0.8); /* or whatever */
boolean offscreen = /* true or false */


int xOffset = (offscreen) ? amountOffscreen : 0;
RelativeLayout.LayoutParams rlParams = 
    (RelativeLayout.LayoutParams)myView.getLayoutParams();
rlParams.setMargins(-1*xOffset, 0, xOffset, 0);
myView.setLayoutParams(rlParams);

This code will position myView offscreen by amountOffscreen, which in this case puts 80% of the view offscreen leaving only 20% onscreen.

这段代码将 myView 按amountOffscreen 定位在屏幕外,在这种情况下,将 80% 的视图放在屏幕外,只留下 20% 的屏幕。

Do not use the layout() method directly - Android will make subsequent calls to invalidate your view for random reasons and only layoutParams are persisted across invalidate calls. If you are curious, check out lines 904 to 912 of this fileto see why you have to modify the layoutParams.

不要直接使用 layout() 方法 - Android 会因随机原因进行后续调用以使您的视图无效,并且只有 layoutParams 会在无效调用中持续存在。如果您好奇,请查看此文件的904 到 912行,了解为什么必须修改 layoutParams。

回答by Steve Haley

This is easy to do if you make to leap to using a Canvas; those support drawing off the screen without any trouble. However, it will be a more complicated to implement. You'd need to implement a custom Viewand write your own animation in code. Essentially this comes down to simple 2D graphics handling rather than Views using built-in XML animations. There may be a way to do it with XML, but I'm much more familiar with canvases. A very good place to see how this is handled in code is the Lunar Lander example game that comes with the SDK.

如果您使用Canvas;这很容易做到。那些支持绘制屏幕没有任何问题。但是,实施起来会比较复杂。您需要实现自定义View并在代码中编写自己的动画。本质上,这归结为简单的 2D 图形处理,而不是使用内置 XML 动画的视图。可能有一种方法可以使用 XML 来实现,但我对画布更为熟悉。SDK 附带的 Lunar Lander 示例游戏是了解如何在代码中处理的一个很好的地方。

Roughly the steps you'll need to follow are:

您需要遵循的步骤大致如下:

  1. Put a custom view in the XML file, using something like <your.package.AnimatingView>, setting its size to fill-parent.

  2. Then define a AnimatingView class, which extends SurfaceView and implements SurfaceHolder.Callback. (This gives you access to the drawing Canvasimmediately rather than by using the invalidate()method. This is important because invalidate() only refreshes when the thread is idle, e.g. at the end of the loop. To implement your animation, you need to have it drawing immediately.)

  3. You can then implement a loop which draws your moving image across the screen. The loop needs to start by drawing the whole background (because the canvas doesn't automatically get erased) and then draw the image at its new position based on the time that has passed. For example, if you want your animation to take 1 second to do, then you know that if 200ms have passed, the view should only have moved 200/1000, or 1/5, of the way from its starting position to the final position.

  1. 在 XML 文件中放置一个自定义视图,使用类似<your.package.AnimatingView>,将其大小设置为 fill-parent。

  2. 然后定义一个 AnimatingView 类,其中extends SurfaceView and implements SurfaceHolder.Callback. (这使您可以Canvas立即访问绘图而不是使用该invalidate()方法。这很重要,因为 invalidate() 仅在线程空闲时刷新,例如在循环结束时。要实现您的动画,您需要让它绘图立即地。)

  3. 然后,您可以实现一个循环,在屏幕上绘制移动图像。循环需要首先绘制整个背景(因为画布不会自动被擦除),然后根据经过的时间在新位置绘制图像。例如,如果你想让你的动画花 1 秒来完成,那么你知道如果 200 毫秒过去了,视图应该只移动 200/1000 或 1/5,从它的起始位置到最终位置.

You can see some examples of what I mean in my other answers to animation questions: basic reply about the usefulness of using SurfaceViewand and example of the loop to use. Note: the second question was about rotating, and hence some of the difficulties I talked about won't be relevant to you. Good luck!

你可以看到我在我其他的答案意味着动画的问题的一些例子:关于基本回复使用SurfaceView的有效性和和示例中的循环中使用的。注意:第二个问题是关于轮换的,因此我谈到的一些困难与您无关。祝你好运!

回答by Pablo Cegarra

With android:translationXand android:translationY

随着android:translationXandroid:translationY

<RelativeLayout
        android:translationX="-600dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent">

回答by Chris Sprague

I had a viewgroup with children and was dragging the view into the screen from the bottom - kind of like a drawer. I then would let go and if the viewgroup top margin were in the top half of the screen I would animate it to the top after the user released the touch.

我有一个带孩子的视图组,正在将视图从底部拖到屏幕上 - 有点像抽屉。然后我会放手,如果视图组上边距在屏幕的上半部分,我会在用户释放触摸后将其动画到顶部。

When this happened the images in the viewgroup's children would get cropped during the animation but would then get shown after the animation.

发生这种情况时,视图组子项中的图像将在动画期间被裁剪,但会在动画之后显示。

The problem: viewgroup's height was wrap_content. I solved this by setting the height to a value that stretched off the screen before the animation started.

问题:视图组的高度是 wrap_content。我通过将高度设置为动画开始前超出屏幕的值解决了这个问题。