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

Posted

技术标签:

【中文标题】Android -- 如何将视图定位到屏幕外?【英文标题】:Android -- How to position View off-screen? 【发布时间】:2011-02-03 00:50:50 【问题描述】:

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

这是我目前用作布局和滑入动画的内容(目前不渲染 ImageView 的顶部 50px):

布局:

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

动画:

<?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>

提前致谢。

【问题讨论】:

另见view.setTranslationX()view.offsetLeftAndRight()mainViewPanel.animate().x(500); 【参考方案1】:

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

android:layout_marginTop="-40dp"

【讨论】:

负边距可以与RelativeLayout和LinearLayout一起使用,但不能与ConstraintLayout一起使用;这是根据谷歌 Android 图形团队负责人 Romain Guy 所说:***.com/a/10673572/2559202【参考方案2】:

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

活动(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));
    

新建线性布局(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)));
    

布局(/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_ 
      android:layout_ 
      android:id="@+id/QuickPlayClipLayout">
      <ImageView android:id="@+id/Clip"
         android:background="@drawable/clip" 
         android:layout_ 
         android:layout_>
      </ImageView>
   </com.games.mygame.CustomLinearLayout>

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

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

动画(/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>

该程序现在完全可以满足我的需要。整个布局从屏幕底部开始,在 1 秒内滑入并停止,布局的顶部实际上距离屏幕顶部 50px(即LL.getTop() = -50),而布局的底部在屏幕底部(即LL.getBottom() = 530 = 480 + 50)。

【讨论】:

【参考方案3】:

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

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);

此代码将 myView 定位到屏幕外的 amountOffscreen,在这种情况下,将 80% 的视图置于屏幕外,仅留下 20% 的屏幕。

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

【讨论】:

这是一个很棒的技术,但是当在 myView 上调用 setLayoutParams 时它确实会导致闪烁(因为 setLayoutParams 又会调用 requestLayout)。有没有避免闪烁的好方法? @PacificSky 我删除视图并将其添加到布局中,然后是 invalidate()。这对我有用,我看不到任何闪烁。代码:layout.removeView(view);layout.addView(view, layoutParams);view.invalidate(); 带负值的边距可以完成这项工作:) 警告:您不能在 onCreate() 中使用此代码,因为 myView.getWidth() 将始终返回 0,因为尚未设置视图。这篇文章的前两个答案为您提供了两种获得正确视图宽度的策略:***.com/questions/4142090/… 它也对我有用。如果您希望视图向右移出屏幕,请确保将 -1*xOffsetxOffset 交换。【参考方案4】:

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

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

    在 XML 文件中放置一个自定义视图,使用类似 &lt;your.package.AnimatingView&gt; 的东西,将其大小设置为 fill-parent。

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

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

您可以在我对动画问题的其他答案中看到我的意思的一些示例:关于 usefulness of using SurfaceView 和 example of the loop to use 的基本回复。注意:第二个问题是关于轮换的,因此我所说的一些困难与你无关。祝你好运!

【讨论】:

首先,感谢您的回复。你上面描述的也是我认为我必须做的,但希望有一些其他/更简单的方法可以获得相同的结果。事实证明,我确实找到了一种更简单的方法(请参阅我发布的解决方案)。它可能不是最好的或最优雅的,但它确实有效。【参考方案5】:

使用android:translationXandroid:translationY

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

【讨论】:

没有意识到您可以从 xml 设置翻译,这正是我所需要的。谢谢!【参考方案6】:

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

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

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

【讨论】:

以上是关于Android -- 如何将视图定位到屏幕外?的主要内容,如果未能解决你的问题,请参考以下文章

屏幕外的Android动画视图不起作用

防止屏幕外的div调整大小

将 SwiftUI 视图渲染到屏幕外并将视图另存为 UIImage 以共享

Swift iOS -CollectionView 如何将单元格滚动到屏幕外

iOS如何在屏幕外启动图像并将其动画到屏幕上

Android - 软键盘将我的活动布局推到屏幕外