问题
For training purposes, I am currently replicating the 2048 game. I got the logic and interaction done, but print (and simply refresh) it inside a single TextView
which looks ridiculous, of course.
When thinking about how to build a UI for the game, I am uncertain which LayoutManager
to choose. What I need to be doing in the game's 4 x 4 grid is to merge and add cells inside rows and columns with animation. It seems to me that:
GridLayout
won't work since it is dealing with the full dataset (16 tiles in four rows/columns) when I need to be able to deal with single rows and columns. However, I am not sure about this. I have worked withGridLayout
before, but never manipulated the number of items (add, remove) and never used animation with that manipulation. Is there a way of manipulating single rows and columns?LinearLayout
won't work since I get in trouble when I want to manipulate columns, but have four horizontalLinearLayout
s, or rows, but have four verticalLinearLayout
s.RelativeLayout
orConstraintLayout
would be possible - maybe. Here the trouble seems to be that I have to keep track of my 16 views,TextView
s probably, and programmatically construct full layouts so I can tell animation what to do. Certainly viable, but challenging.CustomLayout
is a choice, but on which superclass should I build it and why?
Am I missing the easy solution? If not, what would be the most "natural" LayoutManager
for my data structure (which is currently an array of 16 integers, but could easily be changed to a 4 x 4 array of integers).
回答1:
Update: Included demo of a tile appearing and two tiles being combined.
I suggest that you go with ConstraintLayout
. It will allow you to position your views efficiently and can provide you with some easy animation. I have mocked up a quick sample below to demonstrate this approach.
Here is a video of the results:
The XML layout uses ConstraintLayout
as the view group. The two boxes are simply text views but could easily be image views or another type of view.
The two boxes simply move vertically between horizontal guidelines at 24dp
(gdln0
) and 520dp
(gdln100
) for textView1
and 148dp
(gdln25
) and 520dp
(gdln100
) for textView2
. These movement are animated using ConstraintSet
and TransitionManager
through a click handler attached to the "animate" button. Here is the XML followed by the code:
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;
}
}
});
}
}
回答2:
I think I would go with plain drawing the whole thing without using any TextView
or similar.
Let me explain you why.
Assuming you have a set of 16 TextView
, which is the maximum that you can have in your grid, you should manage which one is visible and which one is not, hide it or not, move it inside a layout or not. This would take up many resources and if you're using a layout like the ones you've described, a "free" transition would not be possible too.
Assume you have two tiles and you want one to slide on the other and merge them. The only usable layout I can think of is RelativeLayout
because it's the one with the most easily manipulable constraints.
You'll have to deal with every TextView
though, managing the gradual transition and the overlap, while making Android consider ALL the stuff a TextView
can do.
Now, you'll have to do all that movement/overlapping stuff anyway, so why don't you do that by drawing each tile? You'll have to write some more code to draw an hypothetic Tile
class into a canvas (note that you might change your data structure a bit).
After you have your drawing mechanism you'll have to make the same stuff you would do with TextViews, but in a different, freer, way of thinking, with fewer constraints and by using less Android resources.
I know you were asking advice about some Layout
to use and I'm proposing to draw the tiles by yourself, this might not be what you needed and if I bothered you with this long talk I apologise, but if you were asking for advice in a "wider" manner I think this is a good idea to keep in mind.
And it would be a great occasion to learn about 2d drawing on Android too!
Happy coding!
来源:https://stackoverflow.com/questions/46359987/which-layoutmanager-for-the-animations-of-a-2048-game