En un post previo les prometí escribir acerca de hacer una lista interminable con RecyclerView y mostrando una barra de progreso en el pie de página , mientras que la descarga de datos desde una base de datos externa . Después de jugar con él durante horas por fin logré hacerlo y lo estoy compartiendo con ustedes.
Haremos el mismo efecto usando la misma técnica de lo que hicimos con listviews . La única diferencia es que RecyclerViews no tienen pies de página así que tenemos que utilizar dos ViewHolders diferentes . Pero eso era mi problema de averiguar , sólo puede copiar y pegar el código :)
Hay dos cosas que tenemos que lograr para la experiencia de usuario perfecta:
- En cada descarga de desplazamiento sólo una pequeña cantidad de datos para mostrar sin mantener la espera de usuario
- Mostrar una Cargando ... mensaje con una barra de progreso mientras se descargan los datos
rv = (RecyclerView)view.findViewById(R.id.rv); rv.setHasFixedSize(true); llm = new LinearLayoutManager(getActivity()); rv.setLayoutManager(llm);
arr_userid = new ArrayList(); arr_username = new ArrayList (); arr_photo = new ArrayList (); arr_level = new ArrayList (); arr_date = new ArrayList (); num_loadstart = 0; num_loadfinish = 8;
OnScrollListener
Este fragmento onScrollListener está listo para copiar y pegar. Se ocupa de la carga de nuevos registros cuando el usuario llega al final de los registros ya descargados en la aplicación. Esto es más simple de lo que necesitábamos para usar con listviews.
rv.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); visibleItemCount = rv.getChildCount(); totalItemCount = llm.getItemCount(); firstVisibleItem = llm.findFirstVisibleItemPosition(); if (loading) { if (totalItemCount > previousTotal) { loading = false; previousTotal = totalItemCount; } } if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) { //End has been reached //Below we need the -1 otherwise 1 item will be missed the first time we scroll. I don't know why only the first //time but the point is it's working this way DownloadUsersClassic(String.valueOf(totalItemCount-1), String.valueOf(num_loadfinish)); loading = true; } } });
Descarga de los datos
Yo uso Volley para descargar los datos de una fuente externa porque es más rápido y más eficiente que AsyncTask . Si usted no utiliza Volley, por favor, eche un vistazo a este video para entender cómo funciona. Olvídese OnPreExecute () , doInBackground () y onPostExecute (). Volley es mucho más fácil de usar.
private void DownloadUsersClassic(final String start, final String finish) { StringRequest postReq = new StringRequest(Request.Method.POST, download_leaderboard, new Response.Listener() { @Override public void onResponse(String response) { if (response.length() > 10) { //if there are any records retrieved, otherwise null try { jArray = new JSONArray(response); for(int i=0;i getParams() { Map params = new HashMap (); params.put("start", start); params.put("finish", finish); return params; } }; postReq.setShouldCache(false); AppController.getInstance().addToRequestQueue(postReq); }
Adapter
La clave es usar dos ViewHolders diferentes. Uno de los elementos normales y otro para el pie de página. Utilizamos getItemViewType para determinar cuál queremos mostrar . Una vez hemos descargado todos los datos de nuestra base de datos externa que llamamos mAdapter.hideFooter ( ) para eliminar el FooterView.
public class CustomAdapter extends RecyclerView.Adapter{ ArrayList arr_userid = new ArrayList (); ArrayList arr_username = new ArrayList (); ArrayList arr_photo = new ArrayList (); ArrayList arr_level = new ArrayList (); ArrayList arr_date = new ArrayList (); boolean show_footer; public LayoutInflater inflater; public static final int TYPE_ITEM = 1; public static final int TYPE_FOOTER = 2; Context context; public CustomAdapter(Context context) { this.context = context; } public CustomAdapter(Context context, ArrayList arr_userid, ArrayList arr_username, ArrayList arr_photo, ArrayList arr_level, ArrayList arr_date, boolean show_footer) { super(); this.arr_userid = arr_userid; this.arr_username = arr_username; this.arr_photo = arr_photo; this.arr_level = arr_level; this.arr_date = arr_date; this.show_footer = show_footer; this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public class GenericViewHolder extends RecyclerView.ViewHolder { public TextView txtName, txtPoints, txtDate; public ImageView ivUser; public LinearLayout llLoadmore; public GenericViewHolder(View v) { super(v); txtName = (TextView) v.findViewById(R.id.txtName); txtPoints = (TextView) v.findViewById(R.id.txtPoints); txtDate = (TextView) v.findViewById(R.id.txtDate); ivUser = (ImageView) v.findViewById(R.id.ivUser); llLoadmore = (LinearLayout) v.findViewById(R.id.loadmore); } } class FooterViewHolder extends RecyclerView.ViewHolder { TextView tvloadmore; public FooterViewHolder (View itemView) { super (itemView); this.tvloadmore = (TextView) itemView.findViewById (R.id.tvloadmore); } } public void add(int position, String item) { arr_userid.add(position, item); notifyItemInserted(position); } public void remove(String item) { int position = arr_userid.indexOf(item); arr_userid.remove(position); notifyItemRemoved(position); } public CustomAdapter(ArrayList myDataset) { arr_userid = myDataset; } @Override public RecyclerView.ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) { if(viewType == TYPE_FOOTER) { View v = LayoutInflater.from (parent.getContext ()).inflate (R.layout.loadmore, parent, false); return new FooterViewHolder (v); } else if(viewType == TYPE_ITEM) { View v = LayoutInflater.from (parent.getContext ()).inflate (R.layout.leaderboard_row, parent, false); return new GenericViewHolder (v); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if(holder instanceof FooterViewHolder) { FooterViewHolder footerHolder = (FooterViewHolder) holder; footerHolder.tvloadmore.setText("Loading"); } else if(holder instanceof GenericViewHolder) { GenericViewHolder genericViewHolder = (GenericViewHolder) holder; genericViewHolder.txtName.setText(arr_username.get(position)); genericViewHolder.txtPoints.setText(arr_level.get(position) + " Levels"); String year = arr_date.get(position).substring(0, 4); String month = arr_date.get(position).substring(5, 7); String day = arr_date.get(position).substring(8, 10); genericViewHolder.txtDate.setText(month + "." + day + "." + year); if (!arr_photo.get(position).equals("")) { Picasso.with(getActivity()) .load(arr_photo.get(position)) .into(genericViewHolder.ivUser); } else { genericViewHolder.ivUser.setImageDrawable(null); } } } @Override public int getItemCount() { if (show_footer) { return arr_userid.size() + 1; //+1 is for the footer as it's an extra item } else { return arr_userid.size(); } } @Override public int getItemViewType (int position) { if (show_footer) { if (isPositionFooter(position)) { return TYPE_FOOTER; } return TYPE_ITEM; } else { return TYPE_ITEM; } } private boolean isPositionFooter (int position) { //if position == arr_userid.size then we need to show footerView otherwise we'll get indexOutOfBoundsException return position == arr_userid.size (); } public void hideFooter() { show_footer = false; }
Mi objetivo con este tutorial es para mostrar que la creación de un desplazamiento sin fin, con poderosa RecyclerView de Android por suerte no es tan difícil como parece a primera vista ser. No he encontrado realmente alguna buena tutoriales sobre esto, así que decidieron disfrutar de sufrir durante horas y compartirlo con ustedes. ¡Espero que ayude!
0 comentarios:
Publicar un comentario