Android Retrofit: Display in Recyclerview

Viewed 2262

I have a problem on how to display my json data to a recycler view. Here is my JSON data in which I want to display the "errors" in the recycler view. I tried some ways but it always forces close.

{
 "result": true,
 "errors": [
   {
    "id": "e1812696024",
    "offset": 2,
    "length": 2,
    "bad": "is",
    "better": [
      "am"
        ],
    "type": "grammar"
   },
   {
    "id": "e962925648",
    "offset": 8,
    "length": 8,
    "bad": "engeneer",
    "better": [
       "engineer",
       "engender"
        ],
    "type": "spelling"
    }
 ]
}

Here are my codes: Service.class (Interface)

public interface Service {

      @POST("/check.php")
      Call<InputResult> readErrors(@Query("userInput") String userInput,
                                   @Query("apiKey") String apiKey);
 }

Model classes: InputResult.java

public class InputResult {

    @SerializedName("result")
    private String result;

    @SerializedName("errors")
    private List<Errors> errors;

   //getter and setter
}

Errors.java

public class Errors {

   @SerializedName("id")
   private String id;

   @SerializedName("offset")
   private int offset;

   @SerializedName("length")
   private int length;

   @SerializedName("bad")
   private String bad;

   //getter and setter
}

Adapter

public class ResultAdapter extends RecyclerView.Adapter<ResultAdapter.ResultViewHolder>{

Context mContext;
List<Photo> photoList = new ArrayList<>();
List<Errors> errorsList = new ArrayList<>();

public ResultAdapter (Context mContext, List<Errors> errorsList){
    this.errorsList = errorsList;
    this.mContext = mContext;
}

@Override
public ResultViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View view = LayoutInflater.from(viewGroup.getContext())
            .inflate(R.layout.result_card, viewGroup, false);
    return new ResultViewHolder(view);
}

@Override
public void onBindViewHolder(ResultViewHolder resultViewHolder, int i) {

    Errors errors = errorsList.get(i);
    resultViewHolder.mNumErrorsTextView.setText(errorsList.size());
    resultViewHolder.mIdErrorTextView.setText(errors.getId());
    resultViewHolder.mLengthErrorTextView.setText(errors.getLength());
    resultViewHolder.mBadErrorTextView.setText(errors.getBad());
}

@Override
public int getItemCount() {
    return errorsList.size();
}

 //Viewholder
 ...
 }

Fragment class:

ublic class Tab1Fragment_GrammarChecker extends Fragment {

private static final String TAG = "Tab1Fragment";
private PhotoAdapter adapter;

@BindView(R.id.InputTextEditText)
EditText mInputGrammarEditText;

@BindView(R.id.ErrorsRecyclerView)
RecyclerView mErrorsRecyclerView;

List<Errors> errors = new ArrayList<>();
public ArrayList<Errors> errorArrayList = new ArrayList<>();

List<InputResult> inputResults = new ArrayList<>();
public ArrayList<InputResult> inputResultArrayList = new ArrayList<>();

public static String userInput = "I is an engeneer!";
public String apiKey = ""; 

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.tab1_grammar_checker, container, false);
    ButterKnife.bind(this, view);
    RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
    mErrorsRecyclerView.setLayoutManager(layoutManager);
    loadJson();

    return view;
}

public void loadJson(){

    Retrofit.Builder builder = new Retrofit.Builder()
            .baseUrl("https://api.textgears.com")
            .addConverterFactory(GsonConverterFactory.create());

    Retrofit retrofit = builder.build();

    Service serviceAPI = retrofit.create(Service.class);
    Call<InputResult> loadErrorsCall = serviceAPI.readErrors(userInput, apiKey);
    loadErrorsCall.enqueue(new Callback<InputResult>() {
        @Override
        public void onResponse(Call<InputResult> call, Response<InputResult> response) {
            if (response.isSuccessful()){
                inputResults = response.body();
                Log.i("ORIG. ARRAY SIZE", String.valueOf(inputResultArrayList.size()));
                if (inputResultArrayList != null){
                    for (int i = 0; i < 5; i++){
                        inputResultArrayList.add(inputResults.get(i));
                    }

                    Log.i("NEW ARRAY SIZE", String.valueOf(errorArrayList.size()));
                }
                mErrorsRecyclerView.setItemAnimator(new DefaultItemAnimator());
                mErrorsRecyclerView.setAdapter(new ResultAdapter1(getContext(), inputResultArrayList));
                Log.e("Fragment", response.body().toString());
                Log.e("Fragment", "SUCCESS");
            }
            else {
                Log.i("ERROR", "");
            }

        }

        @Override
        public void onFailure(Call<InputResult> call, Throwable t) {
            Log.i("Error: ", t.getMessage());
        }
    });
}

}

Logcat:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: renelyn.austria.grammatika, PID: 32547
java.lang.ClassCastException: renelyn.austria.grammatika.Model.InputResult cannot be cast to java.util.List
    at renelyn.austria.grammatika.Tab1Fragment_GrammarChecker$1.onResponse(Tab1Fragment_GrammarChecker.java:95)
    at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:71)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:5832)
    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:1399)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
2 Answers

the model probably should look alike the JSON input ...

with an ArrayList<Error> errors instead of List:

public class Results {

    @SerializedName("result")
    private Boolean success;

    @SerializedName("errors")
    private ArrayList<Error> errors;
    ...
}

and the other one (here List can be used to map the String items):

public class Error {

    @SerializedName("id")
    private String id;

    @SerializedName("offset")
    private Long offset;

    @SerializedName("length")
    private Long length;

    @SerializedName("bad")
    private String bad;

    @SerializedName("better")
    private List<String> better;

    @SerializedName("type")
    private String type;
    ...
}

then it should work, without any pointless messing around:

Results results = response.body();
if(results.getSuccess())
    ArrayList<Error> errors = results.getErrors();
}

This is not your recyclerView problem , it's about your retrofit call :

You defined inputResults as List :

List<InputResult> inputResults = new ArrayList<>();

But you filled it like :

inputResults = response.body();

This not acceptable. your response as i understood is a list of InputResult but your retrofit call is single InputResult :

Call<InputResult>

You don't need to serialize or deserialize json yourself. Just read more about retrofit and retrofit converters. All you need to do is that initialize retrofit with GsonConverter and create another model that contains list of InputResult and pass it to retrofit call instead of InputResult to get server response without any problem.

Related