Reputation: 179
Hello im having a big problem in my app at the moment. I have a recyclerView and i populate the adapter with data from back4app. I already checked with them and there is no delay in the querys i make . Anyways when i load the fragment that presents the recycler view with a grid layout patern with two rows the problem is as follows.
This is the XML inflated in onCreateViewHolder
When i run the app in my emulator or phone i see this behaviour on my screen and some miliseconds later the images display correctly, but this has transmitted into a horrible user experience giving it a slow behaviour.
This is my fragment ...
public class PropiedadesInmobiliariasFragment extends Fragment {
private LinearLayoutManager layoutManager;
RecyclerView recyclerInmobiliarias;
ParseObject queryEmpresa;
ParseObject empresa;
String stringEmpresa;
List<ParseObject> allItems;
FragmentManager fm;
ProgressBar pb;
public PropiedadesInmobiliariasFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_propiedades_inmobiliarias, container, false);
pb = view.findViewById(R.id.progress_bar_inmo);
recyclerInmobiliarias=(RecyclerView)view.findViewById(R.id.recycler_inmobiliarias);
//pb.setVisibility(View.VISIBLE);
recyclerInmobiliarias.setVisibility(View.GONE);
pb.setVisibility(View.VISIBLE);
allItems=new ArrayList<>();
queryEmpresa();
layoutManager=new GridLayoutManager(getContext(),2);
recyclerInmobiliarias.setLayoutManager(layoutManager);
recyclerInmobiliarias.setHasFixedSize(true);
recyclerInmobiliarias.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), recyclerInmobiliarias, new RecyclerTouchListener.ClickListener() {
@Override
public void onClick(View view, int position) {
ParseObject item=allItems.get(position);
String selectedObjectId= item.getObjectId();
String nombrePro=item.getString("NombrePropiedad");
ParseFile object1 = item.getParseFile("imagenPrincipal");
ParseFile imagen1 = item.getParseFile("imagen1");
ParseFile imagen2 = item.getParseFile("imagen2");
ParseFile imagen3 = item.getParseFile("imagen3");
ParseFile imagen4 = item.getParseFile("imagen4");
ParseFile imagen5 = item.getParseFile("imagen5");
ParseFile imagen6 = item.getParseFile("imagen6");
ParseFile imagen7 = item.getParseFile("imagen7");
ParseFile imagen8 = item.getParseFile("imagen8");
String precio=item.getString("Precio");
String numHabita=item.getString("numeroDeHabitaciones");
String metrosCuadrados=item.getString("metrosCuadrados");
String numeroBanos=item.getString("numeroBanos");
String descrip=item.getString("descripcionAdicionalPropiedad");
String admin=item.getString("valorAdministracion");
String numPar=item.getString("Parqueaderos");
Bundle bundle=new Bundle();
bundle.putString("objectId",selectedObjectId);
bundle.putString("Nombre",nombrePro);
bundle.putString("primeraFoto", object1.getUrl());
bundle.putString("precio",precio);
bundle.putString("numHab",numHabita);
bundle.putString("metrosCua",metrosCuadrados);
bundle.putString("numBanos",numeroBanos);
bundle.putString("des",descrip);
bundle.putString("admin",admin);
bundle.putString("parq",numPar);
Intent intent = new Intent(getActivity(), DetalleInmobiliaria.class);
intent.putExtra("objectId", selectedObjectId);
intent.putExtra("Nombre", nombrePro);
intent.putExtra("primeraFoto", object1.getUrl());
intent.putExtra("precio", precio);
intent.putExtra("numHab", numHabita);
intent.putExtra("metrosCua", metrosCuadrados);
intent.putExtra("numBanos", numeroBanos);
intent.putExtra("des", descrip);
intent.putExtra("admin", admin);
intent.putExtra("parq", numPar);
startActivity(intent);
}
@Override
public void onLongClick(View view, int position) {
}
}));
return view;
}
private void queryEmpresa(){
/**Ojo no es que no este sirviendo el metodo sino que el tipo de empresa asignado al usuario
* no concuerda para que llene el recycler*/
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.whereEqualTo("objectId",ParseUser.getCurrentUser().getObjectId());
query.include("Empresa");
query.getInBackground(ParseUser.getCurrentUser().getObjectId(), new GetCallback<ParseUser>() {
public void done(ParseUser object, ParseException e) {
if (e == null) {
// object will be your user and you should be able to retrieve Empresa like this
empresa = object.getParseObject("Empresa");
stringEmpresa=empresa.getObjectId();
queryPropInmo();
} else {
// something went wrong. It would be good to log.
}
}
});
}
private void queryPropInmo(){
ParseQuery<ParseObject> query1 = ParseQuery.getQuery("PropiedadesInmobiliarias");
queryEmpresa=ParseObject.createWithoutData("Empresa",stringEmpresa);
query1.whereEqualTo("Empresa",queryEmpresa);
query1.include("Empresa");
query1.include("Imagenes");
query1.findInBackground(new FindCallback<ParseObject>() {
@Override
public void done(List<ParseObject> objects, ParseException e) {
if(objects!=null) {
for (ParseObject obj : objects
) {
allItems.add(obj);
}
}
InmobiliariaAdapter adapter = new InmobiliariaAdapter(getActivity(),allItems);
recyclerInmobiliarias.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
});
pb.setVisibility(View.GONE);
recyclerInmobiliarias.setVisibility(View.VISIBLE);
}
}
This is my adapter...
public class InmobiliariaAdapter extends RecyclerView.Adapter<InmobiliariaAdapter.ViewHolder> {
Context context;
List<ParseObject> inmobiliList;
public InmobiliariaAdapter(Context context, List<ParseObject> inmobiliList) {
this.context = context;
this.inmobiliList = inmobiliList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_carros, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, final int position) {
ParseObject item = inmobiliList.get(position);
viewHolder.imagen.post(new Runnable() {
@Override
public void run() {
loadBitmap(item,viewHolder);
}
});
viewHolder.nombrePropInmobi.setText(item.getString("NombrePropiedad"));
}
@Override
public int getItemCount() {
return this.inmobiliList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView imagen;
TextView nombrePropInmobi;
public ViewHolder(@NonNull View itemView) {
super(itemView);
imagen = (ImageView) itemView.findViewById(R.id.imgCarro);
nombrePropInmobi = (TextView) itemView.findViewById(R.id.txttitulo);
}
}
private void loadBitmap(ParseObject item, ViewHolder viewHolder){
ParseFile parseFile = item.getParseFile("imagenPrincipal");
if (parseFile != null) {
parseFile.getDataInBackground(new GetDataCallback() {
@Override
public void done(byte[] data, ParseException e) {
if (e == null) {
Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
if (bmp != null) {
viewHolder.imagen.setImageBitmap(bmp);
}
}
}
});
}
}
}
I am not using a model class I just get what I need to from the server I even have implemented various things in trying to solve this issue, ive tried progress bar and even handlers ive also thought of implementing async task but the query.findInBackground with its done method already handles asynchronous calls and now I fear that with the implementation of the Runnable might transform into a memory leak. The real problem here is that the app is first displaying the elements from the XML that is inflated in the adapter per position in the recycler view. what I want to accomplish is that the recycler view is shown only when the images from the backend are really finished loading I really don't know what is causing this. At the moment the list has 5 items i dont want to picture it with 3000 items,:(, PLEASE HELP!!!!.
Upvotes: 1
Views: 1667
Reputation: 293
use Glide builder to build the with async loading.
kotlin
Glide.with(context)
.load(url)
.error(R.drawable.error)
.placeholder(R.drawable.placeholder)
.priority(Priority.HIGH)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.override(60, 60)
.transition(DrawableTransitionOptions.withCrossFade(AppConstants.CROSS_FADE_DIRATION))
.transform(//based on requirements)
.into(target) // Async load the image
private val target = object : CustomTarget<Drawable>() {
override fun onLoadCleared(placeholder: Drawable?) {
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
// OnImage ready set the image with the drawable
}
override fun onLoadFailed(errorDrawable: Drawable?) {
// OnFail set the error drawable to the view
}
}
Java Code
Glide.with(getContext())
.load(getUrl()// load content)
.error(R.drawable.error)
.placeholder(R.drawable.placeholder)
.priority(Priority.HIGH)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.override(60, 60)
.transition(DrawableTransitionOptions.withCrossFade(AppConstants.CROSS_FADE_DIRATION))
.transform(//based on requirements)
.into(target) // Async load the image
private CustomTarget<Drawable> target = new CustomTarget<Drawable>() {
@Override
void onLoadCleared(@Nullable Drawable placeholder) {
}
@Override
void onResourceReady(Drawable resource,@Nullable Transition<in Drawable> transistion) {
// OnImage ready set the image with the drawable
}
@Override
void onLoadFailed(@Nullable Drawable errorDrawable ) {
// OnFail set the error drawable to the view
}
}
Upvotes: 0
Reputation: 144
You can make this work by using libraries like Glide and Picasso. These are lightweight, good libraries with caching ability and automatically loads the optimized version of the image into your ImageView. It automatically manages for storing and deleting the cache. Also till the time, your image is not downloaded, they have an option to display placeholder while your image is being downloaded. It can also be used to preview the local image with optimizations according to your ImageView.
Something like this:
Glide.with(context)
.load("https://raw.githubusercontent.com/bumptech/glide/master/static/glide_logo.png")
.placeholder(R.drawable.circularProgressDrawable)
.into(IMAGE_VIEW)
Upvotes: 1