Android - comment créer une transition d'un élément dans listview à une activité complète?
ce que je veux, c'est que lorsque l'utilisateur clique sur un élément de liste dans un ListView, il convertisse à 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 ne sais pas comment ce mouvement est appelé.
En d'autres termes, ce que je veux réaliser est:
-
Augmentation de l'Élément de Liste d'élévation quand on clique dessus (comme vous pouvez le voir dans le droit gif)
-
étendre et transformer l'élément de la liste à la disposition suivante des fragments/activités qui contient des informations détaillées sur l'élément cliqué
j'ai essayé beaucoup de transitions, mais sans succès. Quelqu'un peut-il m'aider à accomplir cette mission?
4 réponses
je construis une petite application d'échantillon que les transitions entre deux activités avec l'effet désiré:
toutefois, les transitions dans les GIF fournis sont légèrement différentes. Le la transition dans le gif à gauche fait passer l'élément list 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 list en écran complet de la deuxième activité. Le code suivant fournit l'effet dans le gif de gauche. Toutefois, 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 simuler un effet différent sur des appareils plus anciens. De plus, le seul but du code fourni est de montrer comment il 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>
activité détaillée:
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>
mon.application.transition.Elevationtransion:
@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. Au cours de la transition, quatre points de vue sont échangés entre les activités.
-
barre d'outils: comme dans la gauche gif la barre d'outils ne bouge pas avec le reste du contenu.
-
ListView de Vue de l'élément -> devient l'affichage du contenu de la DetailActivity
-
StatusBar et NavigationBar arrière-plan: si nous n'ajoutons pas ces vues à l'ensemble des vues transitionnées, elles disparaîtront et reviendront pendant la transition. Cela nécessite toutefois de retarder la transition enter (voir:
TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar
)
Dans le MainActivity
la transition vues sont ajoutés à l'offre groupée qui est utilisé pour démarrer le DetailActivity
. En outre, les vues transitionnées doivent être nommées ( transitionName
) dans les deux activités. Cela peut être fait dans la mise en page xml aussi bien que de façon programmatique.
l'ensemble par défaut des transitions, qui est utilisé pendant la transition d'éléments partagés, affecte différents aspects de la vue(par exemple: les limites de la vue - voir 2 ). Cependant, les différences dans l'élévation de vue ne sont pas animées. C'est pourquoi la solution présentée utilise la personnalisation ElevationTransition.
essayez ceci.. Matériel-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 est appelée Transition D'activité entre des éléments partagés. Par la recherche j'ai trouvé que vous devriez:
- Placez votre vue de ListView dans un format relatif
- OnClick, gonfler une copie de votre moteur de rendu
- trouver les coordonnées globales de l'emplacement du renderer relation avec le parent du ListView
- ajouter le renderer copié au RelativeLayout (parent de ListView)
- Animer la listView loin
- Sur la fin de l'animer, animer votre nouveau moteur de rendu
-
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); } } }
essayez cette spectaculaire page web @ pour commencer avec Activity & Fragment Transitions (part 1) . Ici, ils ont parlé de L'activité et des Transitions de fragments. Je n'ai pas essayé. Mon point de vue est que les Transitions de fragments sont meilleures et moins intensives en informatique, donc c'est un bon début. Et vous ne pouvez pas besoin de modifier les Barres d'outils, vous pouvez afficher/cacher.
un Autre bon DONC, le lien est @ Animer la transition entre les fragments , regardez la meilleure réponse. Dans ce post, ils ont parlé de objectanimateur .
une autre opinion est sur l'animation échantillon que vous avez posté, elle ne montre pas une animation lisse d'un art à l'autre. C'est moins impressionnant lorsque l'animation n'est pas fluide.
bonne chance, amusez-vous, tenez-nous au courant.