Случайный размер и положение кнопки: неожиданное поведение - PullRequest
0 голосов
/ 15 февраля 2019

Мой 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, а также исключая нижнюю панель навигации).

При запуске моего текущего кода Button на самом деле некоторыевремена заканчивают тем, что были вне экрана.Что здесь не так?

Ответы [ 2 ]

0 голосов
/ 17 февраля 2019

Wang ответ правильный (и более краткий / изящный), но, поскольку я действительно понял это всего за несколько минут до того, как он написал, я также опубликую свое решение.

В качестве глобальных переменных:

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 .

0 голосов
/ 17 февраля 2019

Я попробовал ваш код, заменив корневой узел макета на 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" />

Надеюсь, это будет полезно для вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...