我的/usr
:
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>
预期的行为:
在整个屏幕的开头出现一个按钮。按下该按钮后,将调整该按钮的大小(正方形,侧面至少为屏幕宽度的1/25,最大为屏幕宽度的1/2),并在屏幕上的任意位置移动。每次用户再次按下该按钮时,其行为都相同(新的随机位置和大小)。
我的问题:
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);
}
}
和btn.setX()
的行为似乎不符合预期。我以为他们可以确定按钮在屏幕上的绝对位置,但是对btn.setY()
使用以下示例代码会产生奇怪的行为:
init()
大小似乎还可以,但是位置似乎相对于屏幕中间(尽管它似乎也取决于按钮的大小,因为使用前面介绍的代码可以正确地填满整个屏幕)。我希望这样的位置相对于屏幕的左上角(不包括顶部的ActionBar / TitleBar,也不包括底部的NavigationBar)。
运行当前代码时,btn.setX(0);
btn.setY(0);
btn.setWidth(150);
btn.setHeight(150);
实际上有时会出现在屏幕之外。怎么了?
答案 0 :(得分: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" />
希望这对您有用。
答案 1 :(得分:0)
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 的答案。