Which LayoutManager for the animations of a 2048 game?

那年仲夏 提交于 2019-12-03 12:52:40

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;
                }
            }
        });
    }
}

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!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!