java - stick - RecyclerView onCreateViewHolder hizo un llamado excesivo al desplazarse rĂ¡pidamente con DPAD
fire tv stick memory (5)
Estoy desarrollando en Amazon Fire TV.
Debido a que es una aplicación de TV (sin contacto), necesito focables dentro del diseño de la fila para poder navegar.
Tengo un Recyclerview
realmente simple con imagen, texto y un enfoque. Cuando presiono hacia arriba o hacia abajo, todo se desplaza correctamente, pero noté que cuando navego más rápido de lo que el desplazamiento puede continuar, crea nuevos marcadores de vista (pantalla Apagada) y retrasa la IU.
He creado una actividad con números de creación en ella. Cuando me desplazo lentamente, el número de creación más alto es 10. Pero cuando me desplazo rápidamente, obtengo las tarjetas con el número de creación 60 en un segundo. Esto provoca un retraso enorme y la aplicación deja caer muchos marcos. ¿Mi enfoque es totalmente incorrecto?
Usa el código de abajo para probar esto.
/**
* Created by sylversphere on 15-04-15.
*/
public class LandfillActivity extends Activity{
private Context context;
private static int ticketNumber;
private static int getTicket(){
ticketNumber ++;
return ticketNumber;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
setContentView(R.layout.landfill_activity);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
GridLayoutManager glm = new GridLayoutManager(context, 2);
recyclerView.setLayoutManager(glm);
SickAdapter sickAdapter = new SickAdapter();
recyclerView.setAdapter(sickAdapter);
}
public class SickViewHolder extends RecyclerView.ViewHolder{
TextView ticketDisplayer;
public ImageView imageView;
public SickViewHolder(View itemView) {
super(itemView);
ticketDisplayer = (TextView) itemView.findViewById(R.id.ticketDisplayer);
imageView = (ImageView) itemView.findViewById(R.id.imageView);
itemView.findViewById(R.id.focus_glass).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
context.startActivity(new Intent(context, LouisVuittonActivity.class));
}
});
}
public void setTicket(int value){
ticketDisplayer.setText(""+value);
}
}
public class SickAdapter extends RecyclerView.Adapter<SickViewHolder>{
@Override
public SickViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
SickViewHolder svh = new SickViewHolder(getLayoutInflater().inflate(R.layout.one_row_element, null));
svh.setTicket(getTicket());
return svh;
}
@Override
public void onBindViewHolder(SickViewHolder holder, int position) {
String[] image_url_array = getResources().getStringArray(R.array.test_image_urls);
Picasso.with(context).load(image_url_array[position % image_url_array.length] ).fit().centerCrop().into(holder.imageView);
}
@Override
public int getItemCount() {
return 100000;
}
}
}
one_row_element.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@mipmap/sick_view_row_bg" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
android:layout_marginLeft="15dp"
android:orientation="horizontal">
<TextView
android:id="@+id/virusTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Creation #"
android:textColor="#fff"
android:textSize="40sp" />
<TextView
android:id="@+id/ticketDisplayer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:textColor="#fff"
android:textSize="40sp" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/focus_glass"
android:background="@drawable/subtle_focus_glass"
android:focusable="true"
android:focusableInTouchMode="true"/>
</FrameLayout>
</FrameLayout>
test_image_urls.xml (Urls que no son de mi propiedad)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="test_image_urls"
formatted="false">
<item>http://farm4.static.flickr.com/3175/2737866473_7958dc8760.jpg</item>
<item>http://farm4.static.flickr.com/3276/2875184020_9944005d0d.jpg</item>
<item>http://farm3.static.flickr.com/2531/4094333885_e8462a8338.jpg</item>
<item>http://farm4.static.flickr.com/3289/2809605169_8efe2b8f27.jpg</item>
<item>http://2.bp.blogspot.com/_SrRTF97Kbfo/SUqT9y-qTVI/AAAAAAAABmg/saRXhruwS6M/s400/bARADEI.jpg</item>
<item>http://fortunaweb.com.ar/wp-content/uploads/2009/10/Caroline-Atkinson-FMI.jpg</item>
<item>http://farm4.static.flickr.com/3488/4051378654_238ca94313.jpg</item>
<item>http://farm4.static.flickr.com/3368/3198142470_6eb0be5f32.jpg</item>
<item>http://www.powercai.net/Photo/UploadPhotos/200503/20050307172201492.jpg</item>
<item>http://www.web07.cn/uploads/Photo/c101122/12Z3Y54RZ-22027.jpg</item>
<item>http://www.mitravel.com.tw/html/asia/2011/Palau-4/index_clip_image002_0000.jpg</item>
<item>http://news.xinhuanet.com/mil/2007-05/19/xinsrc_36205041914150623191153.jpg</item>
<item>http://ib.berkeley.edu/labs/koehl/images/hannah.jpg</item>
<item>http://down.tutu001.com/d/file/20110307/ef7937c2b70bfc2da539eea9df_560.jpg</item>
<item>http://farm3.static.flickr.com/2278/2300491905_5272f77e56.jpg</item>
<item>http://www.pic35.com/uploads/allimg/100526/1-100526224U1.jpg</item>
<item>http://img.99118.com/Big2/1024768/20101211/1700013.jpg</item>
<item>http://farm1.static.flickr.com/45/139488995_bd06578562.jpg</item>
</string-array>
</resources>
enfoque sutil
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@color/glass_focus"/>
<item android:drawable="@color/glass_normal"/>
</selector>
glass_normal
es #9000
glass_focus
es #0000
Al profundizar en la respuesta de Sam Judd , obligué al reciclador a reciclar las vistas implementando lo siguiente en su adaptador.
@Override
public boolean onFailedToRecycleView(@NonNull VH holder)
return true;
}
Como puedes ver here :
Llamado por RecyclerView si un ViewHolder creado por este adaptador no se puede reciclar debido a su estado transitorio. Al recibir esta devolución de llamada, el Adaptador puede borrar las animaciones que afectan el estado transitorio de la Vista y devolver el valor verdadero para que la Vista se pueda reciclar. Tenga en cuenta que la Vista en cuestión ya se eliminó de RecyclerView.
En algunos casos, es aceptable reciclar una vista aunque tenga un estado transitorio. La mayoría de las veces, este es un caso en el que el estado transitorio se borrará en la llamada onBindViewHolder (ViewHolder, int) cuando View vuelva a una nueva posición. Por esta razón, RecyclerView deja la decisión al Adaptador y usa el valor de retorno de este método para decidir si la Vista debe ser reciclada o no.
Tenga en cuenta que cuando todas las animaciones son creadas por RecyclerView.ItemAnimator, nunca debe recibir esta devolución de llamada porque RecyclerView mantiene esas vistas como secundarias hasta que se completan sus animaciones. Esta devolución de llamada es útil cuando los elementos secundarios de las vistas de elementos crean animaciones que pueden no ser fáciles de implementar utilizando un RecyclerView.ItemAnimator.
Nunca debe solucionar este problema llamando a holder.itemView.setHasTransientState (false); a menos que haya llamado previamente holder.itemView.setHasTransientState (true) ;. Cada llamada View.setHasTransientState (true) debe coincidir con una llamada View.setHasTransientState (false); de lo contrario, el estado de la vista puede volverse incoherente. Siempre debe preferir finalizar o cancelar las animaciones que activan el estado transitorio en lugar de manejarlo manualmente.
Como los comentaristas señalaron, las respuestas pendientes de Picasso podrían estar deteniéndote. Si ese es el caso, puede resolverlo extendiendo ImageView
y anulando el siguiente método. Creo que vale la pena intentarlo.
@Override
protected void onDetachedFromWindow() {
Picasso.with(context).cancelRequest(this);
super.onDetachedFromWindow();
}
Actualizar:
Resulta que esta no es la forma correcta, cualquiera que desee cancelar solicitudes debe hacerlo en la devolución de llamada onViewRecycled()
como se onViewRecycled()
en los comentarios a continuación.
Intente aumentar el número máximo de vistas recicladas en la piscina:
recyclerView.getRecycledViewPool().setMaxRecycledViews(50);
50 es un número arbitrario, puedes intentarlo más alto o más bajo y ver qué pasa.
RecyclerView intenta evitar la reutilización de vistas que tienen un estado transitorio , por lo que si las vistas se invalidan rápidamente o se animan, es posible que no se reutilicen de inmediato.
De manera similar, si tiene muchas vistas más pequeñas, puede terminar con más en la pantalla de lo que puede manejar el tamaño de grupo predeterminado (más común con diseños de cuadrícula).
Para cualquier otra persona que busque un truco rápido, haz esto. Esto retrasará la selección hasta que se haya inflado y seleccionado. No sé cómo, pero funciona. Simplemente devuelve null en onFocusSearchFailed.
/**
* Created by sylversphere on 15-04-22.
*/
public class SomeGridLayoutManager extends GridLayoutManager{
private final Context context;
public SomeGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
this.context = context;
}
public SomeGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
this.context = context;
}
@Override
public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) {
return null;
}
}
Picasso lo está deteniendo, pero la solución sugerida para construir su propio mecanismo no es el camino a seguir.
Picasso se ha retrasado en el último año y hoy existen alternativas mucho mejores en forma de Google Glide y Facebook Fresco que lanzaron específicamente actualizaciones para que funcionen mejor con RecyclerView y han demostrado ser más rápidas y más eficientes en la carga, el almacenamiento en caché y el almacenamiento en muchos Pruebas que están disponibles en línea, tales como:
- http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
- https://code.facebook.com/posts/366199913563917/introducing-fresco-a-new-image-library-for-android/
Espero que haya ayudado. Buena suerte.