简体   繁体   中英

How to call Json using Retrofit

I have JSON as below:

{
  "Search": [
    {
      "Title": "Iron Man",
      "Year": "2008"
    },
    {
      "Title": "Iron Man2",
      "Year": "20010"
    },
    {
      "Title": "Iron Man3",
      "Year": "2013"
    }
  ]
}

Then I've created APIservice as below:

public interface MyApi {

    @GET("/movies")
    Call<ArrayList<MovieSearch>> getartistdata();
}

My data classes as per below

public class MovieSearch {

    @SerializedName("Search")
    public List<Search> Search =null;

}

public class Search {

    @SerializedName("Title")
    public String Title="";

    @SerializedName("Year")
    public String Year="";

    @SerializedName("Poster")
    public String Poster="";

    public Search() {
    }

    public Search(String Title, String Year, String Poster) {
        this.Title = Title;
        this.Year = Year;
    }
}

Now I'm trying to implement viewmodel class as below

public class MyListViewModel extends ViewModel {
    public String Title = "";
    public String Year= "";
    public String Poster = "";
    public MutableLiveData<ArrayList<MyListViewModel>> mutableLiveData = new MutableLiveData<>();
    private ArrayList<MyListViewModel> arrayList;
    private ArrayList<MovieSearch> myList;

    public String getImageurl() {
        return Poster;
    }

    @BindingAdapter({"imageUrl"})
    public static void loadimage(ImageView imageView, String imageUrl) {
        Glide.with(imageView.getContext()).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(imageView);
        //Picasso.with(imageView.getContext()).load(imageUrl).into(imageView);
    }

    public MyListViewModel() {

    }

    public MyListViewModel(MovieSearch myList) {
        this.Title = myList.Title;
        this.Poster = myList.Poster;
        this.Year= myList.Year;
    }

    public MutableLiveData<ArrayList<MyListViewModel>> getMutableLiveData() {

        arrayList = new ArrayList<>();

        MyApi api = MyClient.getInstance().getMyApi();
        Call<ArrayList<MovieSearch>> call = api.getartistdata();
        call.enqueue(new Callback<ArrayList<MovieSearch>>() {
            @Override
            public void onResponse(Call<ArrayList<MovieSearch>> call, Response<ArrayList<MovieSearch>> response) {
                myList = new ArrayList<>();
                myList = response.body();
                for (int i = 0; i < myList.size(); i++) {
                    MovieSearch myk = myList.get(i);
                    MyListViewModel myListViewModel = new MyListViewModel(myk);
                    arrayList.add(myListViewModel);
                    mutableLiveData.setValue(arrayList);
                }

            }

            @Override
            public void onFailure(Call<ArrayList<MovieSearch>> call, Throwable t) {

            }
        });

        return mutableLiveData;
    }
}

But I'm getting "Cannot resolve symbol" error on the following:

public MyListViewModel(MovieSearch myList) {
        this.Title = myList.Title;
        this.Poster = myList.Poster;
        this.Year= myList.Year;
    }

I'm trying to get the data from JSON and bind it to view holder. But I couldn't figure it out how to call it. Someone please help me to fix it.

You are pretty close, you just have to make few changes in service class and viewmodel to make this work. Make below changes

MyApi interface: -> becoz your json is not arraylist but an object of MovieSearch so make change accordingly inside that the arraylist of search is there.

public interface MyApi {

    @GET("/movies")
    Call<MovieSearch> getartistdata();
}

MyListViewModel class:

    public class MyListViewModel extends ViewModel {

    public MutableLiveData<ArrayList<Search>> mutableLiveData = new MutableLiveData<>();

    @BindingAdapter({"imageUrl"})
    public static void loadimage(ImageView imageView, String imageUrl) {
        Glide.with(imageView.getContext()).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(imageView);
        //Picasso.with(imageView.getContext()).load(imageUrl).into(imageView);
    }


    public MyListViewModel() {
        //do something else, your view model is not POJO it's the handler not storage.
    }

    public MutableLiveData<ArrayList<Search>> getSearchResults() {

        MutableLiveData<ArrayList<Search>> localData = new MutableLiveData<>();

        MyApi api = MyClient.getInstance().getMyApi();
        Call<MovieSearch> call = api.getartistdata();
        call.enqueue(new Callback<MovieSearch>() {
            @Override
            public void onResponse(Call<MovieSearch> call, Response<MovieSearch> response) {
               List<Search> myList = response.body().Search; 
               mutableLiveData.setValue(myList);
               localData.setValue(myList);
            }

            @Override
            public void onFailure(Call<ArrayList<MovieSearch>> call, Throwable t) {
               //handle the error
            }
        });

        return localData;
    }
}

Call the above view model by creating an object of view model and inside some function. this part usually goes inside some UI class activity or fragment:

  //listening for change in live data
  // this would be in ui layer
    myListViewModelObject.getSearchResults().observe(this, new Observer<ArrayList<Search>>() {
        @Override
        public void onChanged(@Nullable ArrayList<Search> obj) {
           // handle changes here, to use the data write code below
        }
    });

Advice : Don't use your viewmodel to store the data it's not what is made for. It is for handling the data and actions not the keeper of data. Managing the data and business logic in viewmodel can cause many problems. Always if possible break things into smaller parts like, get retrofit object from some other class or mathod don't put it in the calling method itself this will code duplication and may impact the performance also.

Note : I haven't tested the code just made the best possible changes and it should run. Please add the missing parts if i have removed any.

Here I noticed that you are keeping an array of ViewModels. The main thing is ViewModels are not meant to be used like that.

I think its better if you read some documentation and sample implementations of ViewModels to have a better understanding.


Coming to your implementation, everything looks fine but one thing that is converting your list of MovieSearch into a list of MyListViewModel .

In ViewModel

public class MyListViewModel extends ViewModel {
    private MutableLiveData<MovieSearch> mutableLiveData = new MutableLiveData<>();

    @BindingAdapter({"imageUrl"})
    public static void loadimage(ImageView imageView, String imageUrl) {
        Glide.with(imageView.getContext()).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(imageView);
        //Picasso.with(imageView.getContext()).load(imageUrl).into(imageView);
    }

    public LiveData<MovieSearch> getMutableLiveData() {
        loadData();
        return mutableLiveData;
    }

    private void loadData(){
MyApi api = MyClient.getInstance().getMyApi();
        Call<MovieSearch> call = api.getartistdata();
        call.enqueue(new Callback<MovieSearch>() {
            @Override
            public void onResponse(Call<MovieSearch> call, Response<MovieSearch> response) {
                MovieSearch movieSearch = response.body();
                mutableLiveData.setValue(myList); 

            }

            @Override
            public void onFailure(Call<ArrayList<MovieSearch>> call, Throwable t) {

            }
        });
    }
}

In activity or fragment, you can access the liveData through getMutableLiveData() and set the List<MovieSearch> to the adapter.


One more thing, given your response JSON, your API should like below

public interface MyApi {

    @GET("/movies")
    Call<MovieSearch> getartistdata();
}

MovieSearch is not in a list


Adapter

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private ArrayList<Search> arrayList = new ArrayList<>;
    private LayoutInflater layoutInflater;

    public void submitList(ArrayList<Search> searchList){
        this.arrayList = searchList;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (layoutInflater==null){
            layoutInflater=LayoutInflater.from(parent.getContext());
        }
        MyListBinding myListBinding= DataBindingUtil.inflate(layoutInflater, R.layout.mylist,parent,false);
        return new ViewHolder(myListBinding);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Search search =arrayList.get(position);
        holder.bind(search);
    }

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

    class ViewHolder extends RecyclerView.ViewHolder{
        private MyListBinding myListBinding;
        public ViewHolder(@NonNull MyListBinding myListBinding) {
            super(myListBinding.getRoot());
            this.myListBinding=myListBinding;
        }
        public void bind(Search myli){
            this.myListBinding.setMylistmodel(myli);
            myListBinding.executePendingBindings();
        }
        public MyListBinding getMyListBinding(){
            return myListBinding;
        }
    }
}

Activity

adapter = MyAdapter();

recyclerview=(RecyclerView)findViewById(R.id.recyclerView);
recyclerview.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerview.setAdapter(adapter);

myListViewModel = ViewModelProviders.of(this).get(MyListViewModel.class);
        myListViewModel.getSearchResults().observe(this, new Observer<MovieSearch>() {
            @Override
            public void onChanged(@Nullable MovieSearch movieSearch) {
                // handle changes here, to use the data write code below
                adapter.submitList(movieSearch.Search);

            }
        });

And one small thing, please don't start variable names with capital letters. It is better to follow naming conventions from the start.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM