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