studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones android listview android-activity android-animation android-transitions

para - manual de programacion android pdf



Android: ¿Cómo crear una transición de un elemento en la vista de lista a una actividad completa? (4)

Lo que quiero es que cuando el usuario haga clic en un elemento de la lista en un ListView, se convierta en una actividad completa (como puede ver en el siguiente ejemplo), pero no pude encontrar un tutorial que explique esto y, en realidad, lo hago. No sé cómo se llama este movimiento.

En otras palabras, lo que quiero lograr es:

  1. Incrementa la elevación del elemento de la lista cuando se hace clic (como se puede ver en el gif derecho)

  2. Expanda y transforme el elemento de la lista al siguiente diseño de fragmento / actividad que contenga información detallada sobre el elemento seleccionado.

He intentado muchas transiciones pero sin suerte. ¿Puede alguien ayudarme a lograr esto?


Construyo una pequeña aplicación de muestra que hace la transición entre dos actividades con el efecto deseado:

Sin embargo, las transiciones en los gifs proporcionados son ligeramente diferentes. La transición en el gif en el lado izquierdo transforma el elemento de lista en el área de contenido de la segunda actividad (la barra de herramientas permanece en su lugar). En el gif del lado derecho, la transición transforma el elemento de la lista en la pantalla completa de la segunda actividad. El siguiente código proporciona el efecto en el gif izquierdo. Sin embargo, debería ser posible adaptar la solución con modificaciones menores para lograr la transición en el gif correcto.

Tenga en cuenta que esto solo funciona en Lollipop. Sin embargo, es posible simular un efecto diferente en dispositivos más antiguos. Además, el único propósito del código provisto es mostrar cómo se podría hacer. No uses esto directamente en tu aplicación.

Actividad principal:

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>

DetailActividad:

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

Entonces, ¿cuáles son las diferentes partes aquí: Tenemos dos actividades. Durante la transición se transicionan cuatro vistas entre las actividades.

  • Barra de herramientas: como en el gif izquierdo, la barra de herramientas no se mueve con el resto del contenido.

  • Vista del elemento ListView -> se convierte en la vista de contenido de DetailActivity

  • Fondo de StatusBar y NavigationBar: si no agregamos estas vistas al conjunto de vistas en transición, éstas se desvanecerán y volverán a aparecer durante la transición. Sin embargo, esto requiere retrasar la transición de ingreso (consulte: TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar )

En MainActivity las vistas de transición se agregan al paquete que se utiliza para iniciar DetailActivity . Además, las vistas de transición deben ser nombradas (nombre de transitionName ) en ambas actividades. Esto se puede hacer en el xml de diseño, así como programáticamente.

El conjunto predeterminado de transiciones, que se utiliza durante la transición del elemento compartido, afecta a diferentes aspectos de la vista (por ejemplo: ver límites - ver 2 ). Sin embargo, las diferencias en la elevación de una vista no están animadas. Es por esto que la solución presentada utiliza la ElevationTransition personalizada.


La animación que necesita se llama Transiciones de actividad entre elementos compartidos. Por investigación encontré que usted debería:

  1. Ponga su vista de ListView en un relativoLayout
  2. OnClick, infla una copia de tu renderizador
  3. Encuentre las coordenadas globales para la ubicación del renderizador en relación con el padre de ListView
  4. Agregue el procesador copiado a RelativeLayout (padre de ListView)
  5. Animar la lista
  6. Al final de ese animado, anima tu nuevo renderizador
  7. ¡Lucro!

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


Prueba esta página web espectacular @ Cómo comenzar con las transiciones de actividad y fragmentos (parte 1) . Aquí hablaron sobre la actividad y las transiciones de fragmentos. No lo he probado. Mi opinión es que Fragment Transitions es mejor y requiere menos uso de computadoras, por lo que es un buen comienzo. Y es posible que no necesite cambiar las barras de herramientas, puede mostrarlas u ocultarlas.

Otro buen enlace SO es @ Animar la transición entre fragmentos , mira la mejor respuesta. En ese post, hablaron de objectAnimator .

Otra opinión es sobre la muestra de animación que publicaste, no muestra una animación suave de un arte a otro. Es menos impresionante cuando la animación no es suave.

Buena suerte, diviértete, mantennos informados.


prueba esto ... 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()); } });