【问题标题】:Random size and position of button: unexpected behavior按钮的随机大小和位置:意外行为
【发布时间】:2019-02-14 21:10:19
【问题描述】:

我的ConstraintLayout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp"
        android:layout_marginEnd="0dp"
        android:layout_marginBottom="0dp"
        android:minWidth="0dp"
        android:minHeight="0dp"
        android:text="@string/start"
        android:visibility="visible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

我的简化代码:

import android.annotation.SuppressLint;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;

import java.util.Random;


public class MainActivity extends AppCompatActivity {

    final Random rand = new Random();

    private Button btn;
    private float widthPixels, heightPixels;
    private float minBtnSize, maxBtnSize;


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

        /* Obtaining screen's display properties. */
        final DisplayMetrics displaymetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
        widthPixels = displaymetrics.widthPixels;
        heightPixels = displaymetrics.heightPixels; // includes BotNavBar
        heightPixels -= 162; // size of the "Virtual Nav Bar" at the bottom
        float minDim = Math.min(widthPixels, heightPixels);
        minBtnSize = minDim/25;
        maxBtnSize = minDim/2;


        /* Setting up the experiment's Button*/
        btn = findViewById(R.id.btn);
        btn.setOnTouchListener(new View.OnTouchListener(){
            public boolean onTouch(View view, MotionEvent event){

                /* Touch Events. */
                switch (event.getAction()) {

                    case MotionEvent.ACTION_UP:                            
                        /* Move and resize the button. */
                        launchNewAttempt(x, y);
                        break;

                    default:
                        break;
                }
                return true;
            }
        });

        /* Setting up the beginning Button's state. */
        init();
    }


    /**
     * Initializing the Button shown at the very beginning.
     */
    private void init() {
        btn.setX(0);
        btn.setY(0);
        btn.setWidth((int)widthPixels);
        btn.setHeight((int)heightPixels);
    }


    /**
     * Called whenever the user releases the experiment's button (pressed "UP").
     */
    private void launchNewAttempt() {

        /* Random size for the "new" button. */
        int size=(int)(minBtnSize+(maxBtnSize-minBtnSize)*rand.nextDouble());
        btn.setWidth(size);
        btn.setHeight(size);

        /* Random position for the "new" button. */
        float dx = rand.nextFloat() * (widthPixels - size);
        float dy = rand.nextFloat() * (heightPixels - size);
        btn.setX(dx);
        btn.setY(dy);
    }
}

预期行为:

填满整个屏幕的按钮出现在开头。按下后,相同的按钮会调整大小(作为正方形,边至少为屏幕宽度的 1/25,最多为屏幕宽度的 1/2)并在屏幕上随机移动。每次用户再次按下该按钮时,它的行为方式都相同(新的随机位置和大小)。

我的问题:

btn.setX()btn.setY() 似乎没有按预期运行。我原以为他们会确定按钮在屏幕内的绝对位置,但使用 init() 的以下示例代码会有一个奇怪的行为:

btn.setX(0);
btn.setY(0);
btn.setWidth(150);
btn.setHeight(150);

大小似乎没问题,但位置似乎是相对于屏幕中间的(尽管它似乎也取决于按钮的大小,因为使用前面提供的代码它正确地填满了整个屏幕)。我希望这样的位置相对于屏幕的左上角(不包括顶部的 ActionBar/TitleBar,也不包括底部的 NavigationBar)。

在运行我当前的代码时,Button 实际上有时会出现在屏幕外。这里有什么问题?

【问题讨论】:

    标签: android android-layout android-button android-constraintlayout


    【解决方案1】:

    我试过你的代码用LinearLayout替换布局的根节点,它工作正常,所以这意味着问题与ContraintLayout的内部逻辑有关。知道我试图改变按钮的位置考虑约束而不是绝对位置,特别是关于按钮的顶部和开始边距。因此launchNewAttempt() 方法可能类似于:

    private void launchNewAttempt() {
        final int size = (int) (minBtnSize + (maxBtnSize - minBtnSize) * rand.nextDouble());
        final int left = (int) (rand.nextFloat() * (widthPixels - size));
        final int top = (int) (rand.nextFloat() * (heightPixels - size));
        final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) btn.getLayoutParams();
    
        layoutParams.width = size;
        layoutParams.height = size;
        layoutParams.leftMargin = left;
        layoutParams.topMargin = top;
    
        btn.setLayoutParams(layoutParams);
    }
    

    我还只保留了两个约束来将按钮放置在布局中,并删除了末尾和底部的约束:

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp"
        android:layout_marginEnd="0dp"
        android:layout_marginBottom="0dp"
        android:minWidth="0dp"
        android:minHeight="0dp"
        android:text="Start"
        android:visibility="visible"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    

    希望这对你有用。

    【讨论】:

    • 我正在制定解决方案,因为您正在输入:我正在写出来。如果我知道简单地换成LinearLayout 一切都会奏效,我会那样做的。无论如何,感谢您的意见:我相信其他人会从中受益。
    • 我试用了您的解决方案,它更好。非常感谢!
    • 但是,您介意解释一下为什么需要删除 the end and bottom 约束吗?
    • 删除这些约束可能不是强制性的,但如果按钮仅根据顶部和开始放置,则完全没有必要。所以我删除了它们,以确保避免由于这些额外的限制而产生任何副作用。
    【解决方案2】:

    的回答是正确的(而且更简洁/优雅),但由于我实际上在他发布前几分钟就想通了,所以我也会发布我的解决方案。

    作为全局变量:

    private ConstraintSet cSet = new ConstraintSet();
    private ConstraintLayout cLay;
    private float density;
    

    onCreate方法中正确初始化变量。

    /* To be able to manipulate the position and size of the Button. */
    cLay = findViewById(R.id.main);
    cSet.clone(cLay);
    density = displaymetrics.density;
    

    改编的launchNewAttempt方法:

    private void launchNewAttempt(int x, int y) {
    
        /* Random size for the new button. */
        int size = (int)(minBtnSize+(maxBtnSize-minBtnSize)*rand.nextDouble());
        cSet.constrainWidth(btn.getId(), size);
        cSet.constrainHeight(btn.getId(), size);
    
        /* Random position for the new button. */
        float dx = rand.nextFloat() * (widthPixels  - size) / density;
        float dy = rand.nextFloat() * (heightPixels - size) / density;
        /* Possible negatives because position relative to center of screen. */
        final float middle = .5f;
        if(rand.nextFloat() < middle)
            dx = -dx;
        if(rand.nextFloat() < middle)
            dy = -dy;
        cSet.setTranslation(btn.getId(), dx, dy);
    
        /* Applying the changes. */
        cSet.applyTo(cLay);
    }
    

    不过,我还是建议使用 Wang 的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 2021-02-26
      相关资源
      最近更新 更多