Android 属性 当 imageview 的宽度使用 dp 单位时动画不起作用

Android property animations does not work when imageview's width uses dp units

我正在创建一个游戏,其中会有很多可点击的ImageView和一个不可点击的ImageView,当可点击的ImageView被点击时,它应该与不可点击的ImageView交换位置(在这个例子中,它叫做 imageView2).

当我为我的 imageView 使用 px 单位时一切正常。但是,如您所知,使用 px 单位会导致在不同设备上显示不同。但是,当我使用 dp 单位时,属性 动画不起作用。我如何让它与 dp 单位一起工作?

MainActivity.java

public class MainActivity extends Activity implements View.OnClickListener {

    ImageView imageView1, imageView2;
    float x, x_9, y, y_9;

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

        imageView1 = (ImageView) findViewById(R.id.imageView1);
        imageView2 = (ImageView) findViewById(R.id.imageView2);

        imageView1.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {

        // Get x position of clicked imageView
        x = v.getX();
        // Get x position of imageView2
        x_9 = imageView2.getX();

        // Get y position of clicked imageView
        y = v.getY();
        // Get y position of imageView2
        y_9 = imageView2.getY();


        // Check if imageViews are align with each other either horizontally or vertically
        if((x == x_9 && y + 100 == y_9) || (x == x_9 && y - 100 == y_9) || (x + 100 == x_9 && y == y_9) || (x - 100 == x_9 && y == y_9)) {

            // If they are aligned, swap their position with property animation

            PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("translationX", x_9);
            PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("translationY", y_9);

            PropertyValuesHolder pvhX9 = PropertyValuesHolder.ofFloat("translationX", x);
            PropertyValuesHolder pvhY9 = PropertyValuesHolder.ofFloat("translationY", y);

            ObjectAnimator blockAnim = ObjectAnimator.ofPropertyValuesHolder(v, pvhX, pvhY);
            blockAnim.setDuration(500);
            blockAnim.setRepeatCount(0);
            blockAnim.start();

            ObjectAnimator blockAnim2 = ObjectAnimator.ofPropertyValuesHolder(imageView2, pvhX9, pvhY9);
            blockAnim2.setDuration(500);
            blockAnim2.setRepeatCount(0);
            blockAnim2.start();
        }
    }
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"
android:layout_height="match_parent"     android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/relative"
        android:background="@color/grey"
        android:layout_width="400dp"
        android:layout_height="400dp"
        >

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@drawable/smiley1"/>

        <ImageView
            android:id="@+id/imageView2"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@drawable/smiley2"
            android:translationX="100dp"/>

    </RelativeLayout>

</RelativeLayout>

我该如何解决这个问题?非常欢迎所有建议。谢谢。

根据 Nikola Milutinovic 的建议,我添加了如下所示的 GlobalLayoutListener

        this.findViewById(R.id.relative).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                x = v.getX();
                x_9 = imageView2.getX();

                y = v.getY();
                y_9 = imageView2.getY();
            }
        });

请参阅下面这两种方法的文档,找出最适合您的方法。 container 将是您的根布局(顶部的 RelativeLayout 或 id relative

    container.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

        }
    });

    container.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
        @Override
        public void onDraw() {

        }
    });

这是我实现这个监听器的例子。 我在里面做各种各样的事情。请看一下。

public class CustomOnGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {

private Context context;
private View fragment;
private float x1;
private float y1;
private float x2;
private float y2;

public CustomOnGlobalLayoutListener(Context context, View view,  float x1, float y1, float x2, float y2) {
    this.context = context;
    this.fragment = view;
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
}

@Override
public void onGlobalLayout() {
    fragment.setX(x1);
    fragment.setY(y1);
    removeOnGlobalLayoutListener();
    FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) fragment.getLayoutParams();
    fragment.setLayoutParams(layoutParams);
    fragment.setPivotX(0);
    fragment.setPivotY(0);
    fragment.setScaleX((x2 - x1) / fragment.getMeasuredWidth());
    fragment.setScaleY((y2 - y1) / fragment.getMeasuredHeight());
    fragment.setAlpha(0f);
    PropertyValuesHolder pA = PropertyValuesHolder.ofFloat(View.ALPHA, 1f);
    PropertyValuesHolder pX = PropertyValuesHolder.ofFloat(View.X, 0f);
    PropertyValuesHolder pY = PropertyValuesHolder.ofFloat(View.Y, 0f);
    PropertyValuesHolder pSx = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
    PropertyValuesHolder pSy = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
    final ObjectAnimator animator1 = ObjectAnimator.ofPropertyValuesHolder(fragment, pA, pX, pY, pSx, pSy);

    animator1.setInterpolator(new AccelerateDecelerateInterpolator());
    animator1.setDuration(200);
    animator1.start();

}

private void removeOnGlobalLayoutListener() {
    if (Build.VERSION.SDK_INT < 16) {
        //noinspection deprecation
        fragment.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    } else {
        fragment.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
}

}

不要忘记删除全局布局侦听器;

如果您在实施时遇到任何问题,请写信给我们,我们会想出办法:)

您还可以查看我的 github 存储库,我实际使用它来实现从触摸点使用 viewpager 打开片段。

https://github.com/mrnmilutinovic/HumanityTestProject

检查 ImagePagerFragment class 以查看其用法。 此代码是可编译的,因此您也可以 运行 看看它是否有效。

我认为这样做的方法是通过 imageViews 包含的实际值将硬编码引用更改为宽度和高度。在您的 if 条件中将 100 更改为 getHeight() 或 getWidth();

int height = imageView1.getHeight();
int width = imageView1.getWidth();

if((x == x_9 && y + height == y_9) || (x == x_9 && y - height == y_9) ||
    (x + width == x_9 && y == y_9) || (x - width == x_9 && y == y_9)){

}

这样你就没有对 px 的引用。您使用的“100”值表示 100 像素。 hdpi 设备的实际宽度或高度大概是 1.5*100 = 150px。通过使用 getWidth() 和 getHeight(),您将在屏幕上使用 px 的实际值。我希望它是清楚的。超过2个imageView就需要修改你的if条件,但是原理是一样的

最后,如果你想让你的动画同步,最好使用 AnimatorSet。

ArrayList <Animator> animations = new ArrayList<Animator>();
animations.add(ObjectAnimator.ofPropertyValuesHolder(v, pvhX, pvhY));
animations.add(ObjectAnimator.ofPropertyValuesHolder(imageView2, pvhX9, pvhY9));

AnimatorSet animatoreSet = new AnimatorSet();
animatorSet.setDuration(500);
animatorSet.setRepeatCount(0);
animatorSet.playTogether(animations);
animatorSet.start();

如果您需要添加监听器,只需添加一次即可。