Ce que je veux, c'est que lorsque l'utilisateur clique sur un élément de liste dans un ListView, il se convertit en une activité entière (comme vous pouvez le voir dans l'exemple suivant), mais je n'ai pas pu trouver de tutoriel expliquant cela et, en fait, je le fais ne sais pas comment ce mouvement est appelé.
En d'autres termes, ce que je veux réaliser, c'est:
Augmentez l'élévation de l'élément de liste lorsque vous cliquez dessus (comme vous pouvez le voir dans le gif de droite)
Développez et transformez l'élément de liste en la disposition de fragment/activité suivante qui contient des informations détaillées sur l'élément cliqué
J'ai essayé pas mal de transitions mais sans succès. Quelqu'un peut-il m'aider à accomplir cela?
Je construis un petit exemple d'application qui fait la transition entre deux activités avec l'effet souhaité:
Cependant les transitions dans les gifs fournis sont légèrement différentes. La transition dans le gif sur le côté gauche transforme l'élément de liste dans la zone de contenu de la deuxième activité (la barre d'outils reste en place). Dans le gif à droite, la transition transforme l'élément de liste en écran complet de la deuxième activité. Le code suivant fournit l'effet dans le gif gauche. Cependant, il devrait être possible d'adapter la solution avec des modifications mineures pour réaliser la transition dans le bon gif.
Notez que cela ne fonctionne que sur Lollipop. Cependant, il est possible de se moquer d'un effet différent sur les appareils plus anciens. En outre, le seul but du code fourni est de montrer comment cela pourrait être fait. Ne l'utilisez pas directement dans votre application.
Activité principale:
public class MainActivity extends AppCompatActivity {
MyAdapter myAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
ListView listView = (ListView) findViewById(R.id.list_view);
myAdapter = new MyAdapter(this, 0, DataSet.get());
listView.setAdapter(myAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
startTransition(view, myAdapter.getItem(position));
}
});
}
private void startTransition(View view, Element element) {
Intent i = new Intent(MainActivity.this, DetailActivity.class);
i.putExtra("ITEM_ID", element.getId());
Pair<View, String>[] transitionPairs = new Pair[4];
transitionPairs[0] = Pair.create(findViewById(R.id.toolbar), "toolbar"); // Transition the Toolbar
transitionPairs[1] = Pair.create(view, "content_area"); // Transition the content_area (This will be the content area on the detail screen)
// We also want to transition the status and navigation bar barckground. Otherwise they will flicker
transitionPairs[2] = Pair.create(findViewById(Android.R.id.statusBarBackground), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME);
transitionPairs[3] = Pair.create(findViewById(Android.R.id.navigationBarBackground), Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
Bundle b = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, transitionPairs).toBundle();
ActivityCompat.startActivity(MainActivity.this, i, b);
}
}
activity_main.xml:
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical">
<Android.support.v7.widget.Toolbar
Android:id="@+id/toolbar"
Android:layout_width="match_parent"
Android:layout_height="?attr/actionBarSize"
Android:background="@color/colorPrimary"
Android:transitionName="toolbar" />
<ListView
Android:id="@+id/list_view"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
</LinearLayout>
DétailActivité:
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
long elementId = getIntent().getLongExtra("ITEM_ID", -1);
Element element = DataSet.find(elementId);
((TextView) findViewById(R.id.title)).setText(element.getTitle());
((TextView) findViewById(R.id.description)).setText(element.getDescription());
// if we transition the status and navigation bar we have to wait till everything is available
TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar(this);
// set a custom shared element enter transition
TransitionHelper.setSharedElementEnterTransition(this, R.transition.detail_activity_shared_element_enter_transition);
}
}
activity_detail.xml:
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical">
<Android.support.v7.widget.Toolbar
Android:id="@+id/toolbar"
Android:layout_width="match_parent"
Android:layout_height="?attr/actionBarSize"
Android:background="@color/colorPrimary"
Android:transitionName="toolbar" />
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:background="#abc"
Android:orientation="vertical"
Android:paddingBottom="200dp"
Android:transitionName="content_area"
Android:elevation="10dp">
<TextView
Android:id="@+id/title"
Android:layout_width="match_parent"
Android:layout_height="wrap_content" />
<TextView
Android:id="@+id/description"
Android:layout_width="match_parent"
Android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
detail_activity_shared_element_enter_transition.xml (/ res/transition /):
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:transitionOrdering="together">
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
<transition class="my.application.transitions.ElevationTransition"/>
</transitionSet>
my.application.transitions.ElevationTransition:
@TargetApi(Build.VERSION_CODES.Lollipop)
public class ElevationTransition extends Transition {
private static final String PROPNAME_ELEVATION = "my.elevation:transition:elevation";
public ElevationTransition() {
}
public ElevationTransition(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
private void captureValues(TransitionValues transitionValues) {
Float elevation = transitionValues.view.getElevation();
transitionValues.values.put(PROPNAME_ELEVATION, elevation);
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Float startVal = (Float) startValues.values.get(PROPNAME_ELEVATION);
Float endVal = (Float) endValues.values.get(PROPNAME_ELEVATION);
if (startVal == null || endVal == null || startVal.floatValue() == endVal.floatValue()) {
return null;
}
final View view = endValues.view;
ValueAnimator a = ValueAnimator.ofFloat(startVal, endVal);
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
view.setElevation((float)animation.getAnimatedValue());
}
});
return a;
}
}
TransitionHelper:
public class TransitionHelper {
public static void fixSharedElementTransitionForStatusAndNavigationBar(final Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop)
return;
final View decor = activity.getWindow().getDecorView();
if (decor == null)
return;
activity.postponeEnterTransition();
decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@TargetApi(Build.VERSION_CODES.Lollipop)
@Override
public boolean onPreDraw() {
decor.getViewTreeObserver().removeOnPreDrawListener(this);
activity.startPostponedEnterTransition();
return true;
}
});
}
public static void setSharedElementEnterTransition(final Activity activity, int transition) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop)
return;
activity.getWindow().setSharedElementEnterTransition(TransitionInflater.from(activity).inflateTransition(transition));
}
}
Alors, quelles sont les différentes parties ici: Nous avons deux activités. Pendant la transition, quatre vues sont transférées entre les activités.
Barre d'outils: comme dans le gif gauche, la barre d'outils ne bouge pas avec le reste du contenu.
Élément ListView View -> devient la vue de contenu de DetailActivity
StatusBar et NavigationBar Contexte: Si nous n'ajoutons pas ces vues à l'ensemble des vues de transition, elles disparaîtront et réapparaîtront pendant la transition. Cela nécessite cependant de retarder la transition d'entrée (voir: TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar
)
Dans le MainActivity
, les vues de transition sont ajoutées au bundle utilisé pour démarrer le DetailActivity
. De plus, les vues de transition doivent être nommées (transitionName
) dans les deux activités. Cela peut être fait dans la mise en page xml ainsi que par programmation.
L'ensemble de transitions par défaut, utilisé lors de la transition de l'élément partagé, affecte différents aspects de la vue (par exemple: limites de la vue - voir 2 ). Cependant, les différences d'élévation d'une vue ne sont pas animées. C'est pourquoi la solution présentée utilise le ElevationTransition personnalisé.
essayez ceci .. Material-Animations
blueIconImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, SharedElementActivity.class);
View sharedView = blueIconImageView;
String transitionName = getString(R.string.blue_name);
ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
startActivity(i, transitionActivityOptions.toBundle());
}
});
L'animation dont vous avez besoin s'appelle Transitions d'activité entre les éléments partagés. Par recherche, j'ai constaté que vous devriez:
Profit!
public class MainActivity extends Activity {
private RelativeLayout layout;
private ListView listView;
private MyRenderer selectedRenderer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
layout = new RelativeLayout(this);
setContentView(layout);
listView = new ListView(this);
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
layout.addView(listView, rlp);
listView.setAdapter(new MyAdapter());
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// find out where the clicked view sits in relationship to the
// parent container
int t = view.getTop() + listView.getTop();
int l = view.getLeft() + listView.getLeft();
// create a copy of the listview and add it to the parent
// container
// at the same location it was in the listview
selectedRenderer = new MyRenderer(view.getContext());
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(view.getWidth(), view
.getHeight());
rlp.topMargin = t;
rlp.leftMargin = l;
selectedRenderer.textView.setText(((MyRenderer) view).textView.getText());
layout.addView(selectedRenderer, rlp);
view.setVisibility(View.INVISIBLE);
// animate out the listView
Animation outAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, -1f, Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 0f);
outAni.setDuration(1000);
outAni.setFillAfter(true);
outAni.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
ScaleAnimation scaleAni = new ScaleAnimation(1f,
1f, 1f, 2f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scaleAni.setDuration(400);
scaleAni.setFillAfter(true);
selectedRenderer.startAnimation(scaleAni);
}
});
listView.startAnimation(outAni);
}
});
}
public class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return 10;
}
@Override
public String getItem(int position) {
return "Hello World " + position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
MyRenderer renderer;
if (convertView != null)
renderer = (MyRenderer) convertView;
else
renderer = new MyRenderer(MainActivity.this);
renderer.textView.setText(getItem(position));
return renderer;
}
}
public class MyRenderer extends RelativeLayout {
public TextView textView;
public MyRenderer(Context context) {
super(context);
setPadding(20, 20, 20, 20);
setBackgroundColor(0xFFFF0000);
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
rlp.addRule(CENTER_IN_PARENT);
textView = new TextView(context);
addView(textView, rlp);
}
} }