我是Android开发领域的新手,我正试图借助http://api.tvmaze.com/search/shows?q=girls的rertofit库来解析复杂的JSON结构。但是我的项目无法加载数据。我想问题出在GSON模型中,但我完全不知道到底是什么问题。 我只需要获取图片和标题 我已经在清单中包含了互联网许可 响应如下:
[
{
"score": 17.314238,
"show": {
"id": 139,
"url": "http://www.tvmaze.com/shows/139/girls",
"name": "Girls",
"type": "Scripted",
"language": "English",
"genres": [
"Drama",
"Romance"
],
"status": "Ended",
"runtime": 30,
"premiered": "2012-04-15",
"officialSite": "http://www.hbo.com/girls",
"schedule": {
"time": "22:00",
"days": [
"Sunday"
]
},
"rating": {
"average": 6.7
},
"weight": 94,
"network": {
"id": 8,
"name": "HBO",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
}
},
"webChannel": null,
"externals": {
"tvrage": 30124,
"thetvdb": 220411,
"imdb": "tt1723816"
},
"image": {
"medium": "http://static.tvmaze.com/uploads/images/medium_portrait/31/78286.jpg",
"original": "http://static.tvmaze.com/uploads/images/original_untouched/31/78286.jpg"
},
"summary": "<p>This Emmy winning series is a comic look at the assorted humiliations and rare triumphs of a group of girls in their 20s.</p>",
"updated": 1538265422,
"_links": {
"self": {
"href": "http://api.tvmaze.com/shows/139"
},
"previousepisode": {
"href": "http://api.tvmaze.com/episodes/1079686"
}
}
}
},
{
"score": 13.229476,
"show": {
"id": 23542,
"url": "http://www.tvmaze.com/shows/23542/good-girls",
"name": "Good Girls",
"type": "Scripted",
"language": "English",
"genres": [
"Drama",
"Comedy",
"Crime"
],
"status": "Running",
"runtime": 60,
"premiered": "2018-02-26",
"officialSite": "https://www.nbc.com/good-girls?nbc=1",
"schedule": {
"time": "22:00",
"days": [
"Monday"
]
},
"rating": {
"average": 7
},
"weight": 92,
"network": {
"id": 1,
"name": "NBC",
"country": {
"name": "United States",
"code": "US",
"timezone": "America/New_York"
}
},
"webChannel": null,
"externals": {
"tvrage": null,
"thetvdb": 328577,
"imdb": "tt6474378"
},
"image": {
"medium": "http://static.tvmaze.com/uploads/images/medium_portrait/141/354343.jpg",
"original": "http://static.tvmaze.com/uploads/images/original_untouched/141/354343.jpg"
},
"summary": "<p><b>Good Girls</b> follows three \"good girl\" suburban wives and mothers who suddenly find themselves in desperate circumstances and decide to stop playing it safe, and risk everything to take their power back.</p>",
"updated": 1532345475,
"_links": {
"self": {
"href": "http://api.tvmaze.com/shows/23542"
},
"previousepisode": {
"href": "http://api.tvmaze.com/episodes/1425058"
}
}
}
},
......
]
我建立我的模型类来获取图像和标题,因为我只需要两个项目: 公共课程图片:
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Image {
@SerializedName("medium")
@Expose
private String medium;
@SerializedName("original")
@Expose
private String original;
public String getMedium() {
return medium;
}
public void setMedium(String medium) {
this.medium = medium;
}
public String getOriginal() {
return original;
}
public void setOriginal(String original) {
this.original = original;
}
}
公共课电影:
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Movie {
@SerializedName("name")
@Expose
private String name;
@SerializedName("image")
@Expose
private Image image;
public String getTitle() {
return name;
}
public void setTitle(String name) {
this.name = name;
}
public Image getImageUrl() {
return image;
}
public void setImageUrl(Image image) {
this.image = image;
}
}
用于这些物品的recyclerview适配器:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MyViewHolder> implements Filterable {
private List<Movie> movieList;
private List<Movie> movieListFiltered;
private Context context;
public void setMovieList(Context context,final List<Movie> movieList){
this.context = context;
this.movieList = movieList;
this.movieListFiltered = movieList;
}
@Override
public MovieAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(MovieAdapter.MyViewHolder holder, int position) {
holder.title.setText(movieListFiltered.get(position).getTitle());
GlideApp.with(context).load(movieList.get(position).getImageUrl()).listener(new RequestListener() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
// Log the GlideException here (locally or with a remote logging framework):
Log.e(TAG, "Load failed", e);
// You can also log the individual causes:
for (Throwable t : e.getRootCauses()) {
Log.e(TAG, "Caused by", t);
}
// Or, to log all root causes locally, you can use the built in helper method:
e.logRootCauses(TAG);
return false; // Allow calling onLoadFailed on the Target.
}
@Override
public boolean onResourceReady(Object resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
// Log successes here or use DataSource to keep track of cache hits and misses.
return false; // Allow calling onResourceReady on the Target.
}
}).into(holder.image);
}
@Override
public int getItemCount() {
if(movieList != null&& movieListFiltered!=null){
return movieListFiltered.size();
} else {
return 0;
}
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
String charString = charSequence.toString();
if (charString.isEmpty()) {
movieListFiltered = movieList;
} else {
List<Movie> filteredList = new ArrayList<>();
for (Movie movie : movieList) {
if (movie.getTitle().toLowerCase().contains(charString.toLowerCase())) {
filteredList.add(movie);
}
}
movieListFiltered = filteredList;
}
FilterResults filterResults = new FilterResults();
filterResults.values = movieListFiltered;
return filterResults;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
movieListFiltered = (ArrayList<Movie>) filterResults.values;
notifyDataSetChanged();
}
};
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView title;
ImageView image;
public MyViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.title);
image = (ImageView)view.findViewById(R.id.image);
}
}
}
我的MainActivity:
public class MainActivity extends AppCompatActivity {
private SearchView searchView;
private RecyclerView recyclerView;
private MovieAdapter movieAdapter;
private List<Movie> movieList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
movieList = new ArrayList<>();
recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
movieAdapter = new MovieAdapter();
recyclerView.setAdapter(movieAdapter);
ApiInterface apiService = TvMazeApiClient.getClient().create(ApiInterface.class);
Call<List<Movie>> call = apiService.getMovies();
call.enqueue(new Callback<List<Movie>>() {
@Override
public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
if(movieList != null) {
movieList = response.body();
Log.d("TAG", "Response = " + movieList);
movieAdapter.setMovieList(getApplicationContext(), movieList);
}
else{
Toast.makeText(MainActivity.this,"error", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<List<Movie>> call, Throwable t) {
Log.d("TAG","Response = "+t.toString());
Toast.makeText(MainActivity.this,t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options_menu, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView = (SearchView) menu.findItem(R.id.action_search)
.getActionView();
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
searchView.setMaxWidth(Integer.MAX_VALUE);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
movieAdapter.getFilter().filter(query);
return false;
}
@Override
public boolean onQueryTextChange(String query) {
movieAdapter.getFilter().filter(query);
return false;
}
});
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_search) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
if (!searchView.isIconified()) {
searchView.setIconified(true);
return;
}
super.onBackPressed();
}}
我的xml文件:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="wrap_content"
>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="160dp"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_marginRight="8dp"
android:scaleType="fitXY" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"
android:textSize="16sp"
android:textStyle="bold"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
接口:
public interface ApiInterface {
@GET("search/shows?q=girls")
Call <List<Movie>> getMovies();
}
客户:
public class TvMazeApiClient {
public static String BASE_URL ="http://api.tvmaze.com/";
private static Retrofit retrofit;
public static Retrofit getClient(){
if(retrofit == null){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
在此先感谢您的帮助
答案 0 :(得分:0)
您要解析的字段“名称”和“图像”嵌套在JSON对象“ show”中。您还需要解析此字段。
public interface ApiInterface {
@GET("search/shows?q=girls")
Call <List<MovieResponse>> getMovies();
}
创建一个新的Java对象
public class MovieResponse {
@SerializedName("show")
@Expose
private Movie show;
public Movie getShow() {
return show;
}
}