ste9206 ste9206 - 2 months ago 5
Android Question

why findviewbyposition( ) of recyclerView returns null only somethimes

this is my code:

public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
{

View rootView = inflater.inflate(R.layout.fragment_add_aircraft, container, false);

recyclerView=(RecyclerView)rootView.findViewById(R.id.recyclerAircraftAdd);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter = new AircraftAdapter(getActivity(),path);
recyclerView.setAdapter(adapter);
Button button = (Button)rootView.findViewById(R.id.salvaereo);

button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
adapter = (AircraftAdapter)recyclerView.getAdapter();
ParseObject aircraft = new ParseObject("Aircrafts");

for(int i = 0;i<adapter.getItemCount(); i++)
{
if (i== 0) //first card
{
View view = recyclerView.getLayoutManager().findViewByPosition(i);
ImageView image = (ImageView)view.findViewById(R.id.aircfraftImg);
image.setDrawingCacheEnabled(true);
image.buildDrawingCache();
Bitmap bm = image.getDrawingCache();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] bytes = stream.toByteArray();
ParseFile file = new ParseFile("Image.jpg",bytes);
aircraft.put("aircraft_image",file);

file.saveInBackground(new SaveCallback() {
@Override
public void done(ParseException e) {
Log.d("sa","tutto ok");
}
});

}
else if (i ==1) //second card
{
View view = recyclerView.getLayoutManager().findViewByPosition(i);
EditText immatricolazione =(EditText)view.findViewById(R.id.immatricolazione);
EditText modello = (EditText)view.findViewById(R.id.modello);
EditText manifacturer = (EditText)view.findViewById(R.id.manifacturer);
EditText categoria = (EditText)view.findViewById(R.id.categoria);
EditText classe = (EditText)view.findViewById(R.id.classe);
EditText velocita = (EditText)view.findViewById(R.id.velocita);
EditText autonomia = (EditText)view.findViewById(R.id.autonomia);
EditText consumo = (EditText)view.findViewById(R.id.consumo);
EditText posti = (EditText)view.findViewById(R.id.postitotali);
EditText costo = (EditText)view.findViewById(R.id.costorario);

aircraft.put("re",immatricolazione.getEditableText().toString());
aircraft.put("ml",modello.getEditableText().toString());
aircraft.put("man",manifacturer.getEditableText().toString());
aircraft.put("Aircraft_Type",categoria.getEditableText().toString());
aircraft.put("ar",classe.getEditableText().toString());
aircraft.put("post",Integer.parseInt(posti.getEditableText().toString()));
aircraft.put("meter",Integer.parseInt(consumo.getEditableText().toString()));
aircraft.put("speed",Integer.parseInt(velocita.getEditableText().toString()));
aircraft.put("prezzi",Float.parseFloat(costo.getEditableText().toString()));
aircraft.put("fuel",Integer.parseInt(autonomia.getEditableText().toString()));
}
else if(i==2) //third card
{
View view = recyclerView.getLayoutManager().findViewByPosition(i);
EditText description = (EditText) view.findViewById(R.id.description);
aircraft.put("descrizione", description.getEditableText().toString());

aircraft.put("User", ParseUser.getCurrentUser());

dialog = ProgressDialog.show(getActivity(), "", "Caricamento aereo in corso...", true);

aircraft.saveInBackground(new SaveCallback() {

public void done(ParseException e) {
dialog.dismiss();

if (e == null) {
new AlertDialog.Builder(getActivity())
.setMessage("Caricamento aereo avvenuto con successo!")
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
}).create().show();
} else {
new AlertDialog.Builder(getActivity())
.setMessage("Non è stato possibile caricare l'aereo, riprova più tardi!")
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
}).create().show();
}


}
});
}
}

}
});

return rootView;
}


but there is this problem: if I charge a photo (first card) and press button, it uploads the photo, but if I write into EditText, it crashes and gives me this error:


Process: com.run.flyproject, PID: 10796
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
at com.run.flyproject.Fragments.AddAircraftFragment$1.onClick(AddAircraftFragment.java:78)
at android.view.View.performClick(View.java:4756)
at android.view.View$PerformClick.run(View.java:19749)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5253)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)


how could I fix this error? Thanks

Answer

Not that this is an explicit answer (the explicit answer would be something like "Your adapter count doesn't coincide with the views available from your adapter"), but my comments were probably getting a bit confusing. You could do something like this...

public class YourAdapter extends RecyclerView.Adapter<YourViewHolder>{

    private boolean mSaveImages = false;
    private SparseArray<Boolean> mImageSavedFlags = new SparseArray<>();

    public void toggleSaveStatus(){
        mSaveImages = !mSaveImages;
    }

    // ... your code

    @Override
    public void onBindViewHolder(YourViewHolder holder, final int position) {
        // Could also manage this in a loop or something
        // in your constructor/when you set your adapter data
        // but this will set our SparseArray
        if(mImageSavedFlags.get(position) == null)
            mImageSavedFlags.put(position, false);

        // ... your code
        if(mSaveImages && !mImageSavedFlags.get(position){
            // ... save your image
            mImageSavedFlags.put(position, true);
        }
    }

    // ... your code
}

This way whenever you attempt to bind a view, you can check if you want to save the ImageViews source and if you haven't already saved it, if you haven't it will save the source and update the SparseArray.

To toggle your status just do something like this

button.setOnClickListener(new View.OnClickListener()
{
    @Override
    public void onClick(View v){
        adapter.toggleSaveStatus();
    }
}

It is worth noting that this will only save images that you actually scroll to after you toggle the save setting on. I suppose you could force scrolling of you RecyclerView but that is getting into something else.

It is also important to realize that if you are doing something like loading images from a URL or from your drawables based on position or something, you don't actually need your ImageView to load the source before you can save it, there are plenty of ways (including libraries) to save images that haven't been explicitly loaded into your app yet, which would completely remove the need to include your adapter in the process.

I am sure this isn't the only way to do this, and I am sure this isn't the best answer, but again my comments were getting a bit confusing.