哪个 LayoutManager 用于 2048 游戏的动画?
Which LayoutManager for the animations of a 2048 game?
出于培训目的,我目前正在复制 2048 游戏。我完成了逻辑和交互,但是在单个 TextView
中打印(并简单地刷新)它当然看起来很荒谬。
在考虑如何为游戏构建UI时,我不确定该选择哪个LayoutManager
。在游戏的 4 x 4 网格中,我需要做的是 使用动画 在行和列中合并和添加单元格。在我看来:
GridLayout
将无法工作,因为当我需要能够处理单行和列。但是,我不确定这一点。我以前使用过 GridLayout
,但从未操纵过项目的数量(添加、删除),也从未在该操纵中使用过动画。有没有办法操纵单个行和列?
LinearLayout
将无法工作,因为当我想操作列时遇到麻烦,但有四个水平 LinearLayout
或行,但有四个垂直 LinearLayout
s.
RelativeLayout
或 ConstraintLayout
是可能的 - 也许吧。这里的问题似乎是我必须跟踪我的 16 个视图,可能 TextView
s,并以编程方式构建完整的布局,以便我可以告诉动画要做什么。当然可行,但具有挑战性。
CustomLayout
是一个选择,但我应该在哪个超类上构建它,为什么?
我错过了简单的解决方案吗?如果不是,那么对于我的数据结构(目前是 16 个整数的数组,但可以很容易地更改为 4 x 4 的整数数组)来说,什么是最 "natural" LayoutManager
。
我想我会在不使用任何 TextView
或类似的情况下直接绘制整个内容。
让我来解释一下原因。
假设你有一组 16 TextView
,这是你可以在你的网格中拥有的最大值,你应该管理哪些是可见的,哪些是不可见的,隐藏与否,移动它是否在布局内。这会占用很多资源,如果您使用的布局与您所描述的布局类似,那么“免费”过渡也是不可能的。
假设您有两个图块,并且您希望一个图块在另一个图块上滑动并合并它们。我能想到的唯一可用的布局是 RelativeLayout
因为它是最容易操纵约束的布局。
尽管如此,您必须处理每个 TextView
,管理逐渐过渡和重叠,同时让 Android 考虑所有的东西 TextView
可以做。
现在,无论如何您都必须完成所有 movement/overlapping 的事情,那么您为什么不通过绘制每个图块来做到这一点呢?您将不得不编写更多代码来将假设的 Tile
class 绘制成 canvas(请注意,您可能会稍微更改数据结构)。
在你有了你的绘图机制之后,你必须你会用 TextViews 做同样的事情,但是用一种不同的、更自由的方式思考,更少的约束和使用更少的 Android 资源。
我知道你在征求一些Layout
使用的建议,我建议你自己画瓷砖,这可能不是你需要的,如果我用这么长的谈话打扰你,我深表歉意,但是如果您以“更广泛”的方式寻求建议,我认为这是一个值得牢记的好主意。
这也是在 Android 上学习二维绘图的好机会!
编码愉快!
更新: 包含显示一个图块和合并两个图块的演示。
我建议你选择 ConstraintLayout
。它将允许您有效地定位您的视图,并可以为您提供一些简单的动画。我在下面模拟了一个快速示例来演示这种方法。
这是结果的视频:
XML布局使用ConstraintLayout
作为视图组。这两个框只是文本视图,但很容易成为图像视图或其他类型的视图。
对于textView1
和[=19,这两个框只是在24dp
(gdln0
)和520dp
(gdln100
)的水平准则之间垂直移动=] (gdln25
) 和 520dp
(gdln100
) 对于 textView2
。这些移动使用 ConstraintSet
和 TransitionManager
通过附加到 "animate" 按钮的点击处理程序进行动画处理。这是 XML 后跟代码:
activity_main.xml
<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/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.constraint.Guideline
android:id="@+id/gdln0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="24dp" />
<android.support.constraint.Guideline
android:id="@+id/gdln25"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="148dp" />
<android.support.constraint.Guideline
android:id="@+id/gdln50"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="272dp" />
<android.support.constraint.Guideline
android:id="@+id/gdln75"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="396dp" />
<android.support.constraint.Guideline
android:id="@+id/gdln100"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="520dp" />
<TextView
android:id="@+id/textView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="2"
android:textColor="@android:color/white"
android:textSize="72sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/gdln0" />
<TextView
android:id="@+id/textView2"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="4"
android:textColor="@android:color/white"
android:textSize="72sp"
app:layout_constraintStart_toEndOf="@+id/textView1"
app:layout_constraintTop_toBottomOf="@id/gdln25" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:text="Animate"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private int mSquareSide;
private int mMargin;
private int mBoardState = 0;
private TextView mNewView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Resources r = getResources();
mSquareSide = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, r.getDisplayMetrics());
mMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, r.getDisplayMetrics());
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.layout);
ConstraintSet newSet = new ConstraintSet();
mBoardState = (mBoardState + 1) % 3;
switch (mBoardState) {
case 0: // Just reset the board to its starting condition.
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(this);
break;
case 1: // Move tiles down and insert new tile.
mNewView = new TextView(layout.getContext());
mNewView.setId(View.generateViewId());
mNewView.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
mNewView.setTextSize(72);
mNewView.setTextColor(getResources().getColor(android.R.color.white));
mNewView.setText("2");
mNewView.setVisibility(View.INVISIBLE);
mNewView.setGravity(Gravity.CENTER);
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(mSquareSide, mSquareSide);
mNewView.setLayoutParams(lp);
layout.addView(mNewView);
newSet.clone(layout);
newSet.connect(mNewView.getId(), ConstraintSet.TOP,
R.id.gdln0, ConstraintSet.BOTTOM);
newSet.connect(mNewView.getId(), ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, mMargin);
newSet.clear(R.id.textView1, ConstraintSet.TOP);
newSet.clear(R.id.textView2, ConstraintSet.TOP);
newSet.connect(R.id.textView1, ConstraintSet.BOTTOM,
R.id.gdln100, ConstraintSet.BOTTOM);
newSet.connect(R.id.textView2, ConstraintSet.BOTTOM,
R.id.gdln100, ConstraintSet.BOTTOM);
TransitionManager.beginDelayedTransition(layout);
newSet.applyTo(layout);
mNewView.setVisibility(View.VISIBLE);
break;
case 2: // Move tiles up and combine two tiles.
newSet.clone(layout);
newSet.clear(R.id.textView1, ConstraintSet.BOTTOM);
newSet.clear(R.id.textView2, ConstraintSet.BOTTOM);
newSet.connect(R.id.textView1, ConstraintSet.TOP,
R.id.gdln0, ConstraintSet.BOTTOM);
newSet.connect(R.id.textView2, ConstraintSet.TOP,
R.id.gdln0, ConstraintSet.BOTTOM);
Transition transition = new AutoTransition();
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
mNewView.setText("4");
// Here you would remove the overlapped view
// with layout.removeView(View);
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
TransitionManager.beginDelayedTransition(layout, transition);
newSet.applyTo(layout);
break;
}
}
});
}
}
出于培训目的,我目前正在复制 2048 游戏。我完成了逻辑和交互,但是在单个 TextView
中打印(并简单地刷新)它当然看起来很荒谬。
在考虑如何为游戏构建UI时,我不确定该选择哪个LayoutManager
。在游戏的 4 x 4 网格中,我需要做的是 使用动画 在行和列中合并和添加单元格。在我看来:
GridLayout
将无法工作,因为当我需要能够处理单行和列。但是,我不确定这一点。我以前使用过GridLayout
,但从未操纵过项目的数量(添加、删除),也从未在该操纵中使用过动画。有没有办法操纵单个行和列?LinearLayout
将无法工作,因为当我想操作列时遇到麻烦,但有四个水平LinearLayout
或行,但有四个垂直LinearLayout
s.RelativeLayout
或ConstraintLayout
是可能的 - 也许吧。这里的问题似乎是我必须跟踪我的 16 个视图,可能TextView
s,并以编程方式构建完整的布局,以便我可以告诉动画要做什么。当然可行,但具有挑战性。CustomLayout
是一个选择,但我应该在哪个超类上构建它,为什么?
我错过了简单的解决方案吗?如果不是,那么对于我的数据结构(目前是 16 个整数的数组,但可以很容易地更改为 4 x 4 的整数数组)来说,什么是最 "natural" LayoutManager
。
我想我会在不使用任何 TextView
或类似的情况下直接绘制整个内容。
让我来解释一下原因。
假设你有一组 16 TextView
,这是你可以在你的网格中拥有的最大值,你应该管理哪些是可见的,哪些是不可见的,隐藏与否,移动它是否在布局内。这会占用很多资源,如果您使用的布局与您所描述的布局类似,那么“免费”过渡也是不可能的。
假设您有两个图块,并且您希望一个图块在另一个图块上滑动并合并它们。我能想到的唯一可用的布局是 RelativeLayout
因为它是最容易操纵约束的布局。
尽管如此,您必须处理每个 TextView
,管理逐渐过渡和重叠,同时让 Android 考虑所有的东西 TextView
可以做。
现在,无论如何您都必须完成所有 movement/overlapping 的事情,那么您为什么不通过绘制每个图块来做到这一点呢?您将不得不编写更多代码来将假设的 Tile
class 绘制成 canvas(请注意,您可能会稍微更改数据结构)。
在你有了你的绘图机制之后,你必须你会用 TextViews 做同样的事情,但是用一种不同的、更自由的方式思考,更少的约束和使用更少的 Android 资源。
我知道你在征求一些Layout
使用的建议,我建议你自己画瓷砖,这可能不是你需要的,如果我用这么长的谈话打扰你,我深表歉意,但是如果您以“更广泛”的方式寻求建议,我认为这是一个值得牢记的好主意。
这也是在 Android 上学习二维绘图的好机会!
编码愉快!
更新: 包含显示一个图块和合并两个图块的演示。
我建议你选择 ConstraintLayout
。它将允许您有效地定位您的视图,并可以为您提供一些简单的动画。我在下面模拟了一个快速示例来演示这种方法。
这是结果的视频:
XML布局使用ConstraintLayout
作为视图组。这两个框只是文本视图,但很容易成为图像视图或其他类型的视图。
对于textView1
和[=19,这两个框只是在24dp
(gdln0
)和520dp
(gdln100
)的水平准则之间垂直移动=] (gdln25
) 和 520dp
(gdln100
) 对于 textView2
。这些移动使用 ConstraintSet
和 TransitionManager
通过附加到 "animate" 按钮的点击处理程序进行动画处理。这是 XML 后跟代码:
activity_main.xml
<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/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.constraint.Guideline
android:id="@+id/gdln0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="24dp" />
<android.support.constraint.Guideline
android:id="@+id/gdln25"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="148dp" />
<android.support.constraint.Guideline
android:id="@+id/gdln50"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="272dp" />
<android.support.constraint.Guideline
android:id="@+id/gdln75"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="396dp" />
<android.support.constraint.Guideline
android:id="@+id/gdln100"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="520dp" />
<TextView
android:id="@+id/textView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="2"
android:textColor="@android:color/white"
android:textSize="72sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/gdln0" />
<TextView
android:id="@+id/textView2"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="4"
android:textColor="@android:color/white"
android:textSize="72sp"
app:layout_constraintStart_toEndOf="@+id/textView1"
app:layout_constraintTop_toBottomOf="@id/gdln25" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:text="Animate"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private int mSquareSide;
private int mMargin;
private int mBoardState = 0;
private TextView mNewView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Resources r = getResources();
mSquareSide = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, r.getDisplayMetrics());
mMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, r.getDisplayMetrics());
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.layout);
ConstraintSet newSet = new ConstraintSet();
mBoardState = (mBoardState + 1) % 3;
switch (mBoardState) {
case 0: // Just reset the board to its starting condition.
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(this);
break;
case 1: // Move tiles down and insert new tile.
mNewView = new TextView(layout.getContext());
mNewView.setId(View.generateViewId());
mNewView.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
mNewView.setTextSize(72);
mNewView.setTextColor(getResources().getColor(android.R.color.white));
mNewView.setText("2");
mNewView.setVisibility(View.INVISIBLE);
mNewView.setGravity(Gravity.CENTER);
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(mSquareSide, mSquareSide);
mNewView.setLayoutParams(lp);
layout.addView(mNewView);
newSet.clone(layout);
newSet.connect(mNewView.getId(), ConstraintSet.TOP,
R.id.gdln0, ConstraintSet.BOTTOM);
newSet.connect(mNewView.getId(), ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, mMargin);
newSet.clear(R.id.textView1, ConstraintSet.TOP);
newSet.clear(R.id.textView2, ConstraintSet.TOP);
newSet.connect(R.id.textView1, ConstraintSet.BOTTOM,
R.id.gdln100, ConstraintSet.BOTTOM);
newSet.connect(R.id.textView2, ConstraintSet.BOTTOM,
R.id.gdln100, ConstraintSet.BOTTOM);
TransitionManager.beginDelayedTransition(layout);
newSet.applyTo(layout);
mNewView.setVisibility(View.VISIBLE);
break;
case 2: // Move tiles up and combine two tiles.
newSet.clone(layout);
newSet.clear(R.id.textView1, ConstraintSet.BOTTOM);
newSet.clear(R.id.textView2, ConstraintSet.BOTTOM);
newSet.connect(R.id.textView1, ConstraintSet.TOP,
R.id.gdln0, ConstraintSet.BOTTOM);
newSet.connect(R.id.textView2, ConstraintSet.TOP,
R.id.gdln0, ConstraintSet.BOTTOM);
Transition transition = new AutoTransition();
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
mNewView.setText("4");
// Here you would remove the overlapped view
// with layout.removeView(View);
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
TransitionManager.beginDelayedTransition(layout, transition);
newSet.applyTo(layout);
break;
}
}
});
}
}