Android 从JSON获取数据后,当适配器收到数据更改通知时,在RecyclerView中的每个位置上设置相同的数据
我从数据库中获取数据并正确获取JSON。另外,当我在RecyclerView上设置adapter时,除了在每个位置设置相同的数据外,其他一切似乎都正常工作,但是我得到了包含20个不同值的完整ArrayList。我不知道我在哪里搞砸了notifyDatasetChanged,因为这是我第一次使用RecyclerView 我在活动中使用了一个片段,在代码中,我使用了Log语句(我使用了“##############”而不是Log#标记),这告诉我,调用onBindViewHolder时可以使用包含20个电影细节的完整数组列表 代码如下: DefaultMovieFragment.javaAndroid 从JSON获取数据后,当适配器收到数据更改通知时,在RecyclerView中的每个位置上设置相同的数据,android,android-recyclerview,notifydatasetchanged,Android,Android Recyclerview,Notifydatasetchanged,我从数据库中获取数据并正确获取JSON。另外,当我在RecyclerView上设置adapter时,除了在每个位置设置相同的数据外,其他一切似乎都正常工作,但是我得到了包含20个不同值的完整ArrayList。我不知道我在哪里搞砸了notifyDatasetChanged,因为这是我第一次使用RecyclerView 我在活动中使用了一个片段,在代码中,我使用了Log语句(我使用了“##############”而不是Log#标记),这告诉我,调用onBindViewHolder时可以使用包含2
public class DefaultMovieFragment extends Fragment implements LoaderManager.LoaderCallbacks<ArrayList<Movie>> {
private static final int DEFAULT_MOVIE_LOADER_ID = 1;
ArrayList<Movie> movies;
DefaultMovieAdapter mAdapter;
RecyclerView mRecyclerView;
public DefaultMovieFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.v("############", "onCreateView called");
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_default_movie, container, false);
Movie movie = new Movie("ram", 2, "path");
if (savedInstanceState==null){
movies = new ArrayList<>();
}
//First of all check if network is connected or not then only start the loader
ConnectivityManager connMgr = (ConnectivityManager)
getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
/* fetch data. Get a reference to the LoaderManager, in order to interact with loaders. */
startLoaderManager();
Log.v("############", "startLoaderManager called");
}
// Lookup the recyclerview in activity layout
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerViewMovies);
// Create mAdapter passing in the sample user data
mAdapter = new DefaultMovieAdapter(getActivity(), movies);
// Attach the mAdapter to the recyclerview to populate items
mRecyclerView.setAdapter(mAdapter);
// First param is number of columns and second param is orientation i.e Vertical or Horizontal
final StaggeredGridLayoutManager gridLayoutManager =
new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
// Attach the layout manager to the recycler view
mRecyclerView.setLayoutManager(gridLayoutManager);
// That's all!
return rootView;
}
private void startLoaderManager() {
LoaderManager loaderManager = getLoaderManager();
loaderManager.initLoader(DEFAULT_MOVIE_LOADER_ID, null, this);
Log.v("############", "startLoaderManager finished");
}
@Override
public Loader<ArrayList<Movie>> onCreateLoader(int id, Bundle args) {
Log.v("############", "onCreateLoader called");
Uri baseUri = Uri.parse(UrlsAndConstants.DefaultQuery.DEFAULT_URL);
Log.v("############", "baseUri is "+baseUri.toString());
Uri.Builder uriBuilder = baseUri.buildUpon();
Log.v("############", "uriBuilder is "+uriBuilder.toString());
uriBuilder.appendQueryParameter(API_KEY_PARAM, API_KEY_PARAM_VALUE);
Log.v("############", "uriBuilder.toString() is "+uriBuilder.toString());
String urls = "https://api.themoviedb.org/3/discover/movie?api_key=4182aa25bab27d06344e404f65c4ae76";
return new DefaultMovieLoader(getActivity().getApplicationContext(), urls);
}
@Override
public void onLoadFinished(Loader<ArrayList<Movie>> loader, ArrayList<Movie> movie) {
Log.v("############", "startLoaderManager finished");
if (movie.isEmpty()) {
Log.v("******************", "movies isEmpty");
return;
} else {
Log.v("############", "movies are"+movie);
// Attach the mAdapter to the recyclerview to populate items
mAdapter.setMovieData(movie);
mRecyclerView.setAdapter(mAdapter);
}
}
@Override
public void onLoaderReset(Loader<ArrayList<Movie>> loader) {
Log.v("############", "onLoaderReset called");
}
}
public class DefaultMovieAdapter extends RecyclerView.Adapter<DefaultMovieAdapter.ViewHolder>{
// Store a member variable for the movies
private ArrayList<Movie> mDefaultMovie;
// Store the context for easy access
private Context mContext;
private Movie currentMovie;
// Pass in the movies array into the constructor
public DefaultMovieAdapter(Context context, ArrayList<Movie> movies) {
mDefaultMovie = movies;
mContext = context;
}
// Easy access to the context object in the recyclerview
private Context getContext() {
return mContext;
}
/*
Provide a direct reference to each of the views within a data item
Used to cache the views within the item layout for fast access
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
/*
Your holder should contain a member variable
for any view that will be set as you render a row
*/
public final TextView movieTitleTextView;
public final ImageView movieTitleImageView;
/*
We also create a constructor that accepts the entire item row
and does the view lookups to find each subview
*/
public ViewHolder(View itemView) {
/*
Stores the itemView in a public final member variable that can be used
to access the context from any ViewHolder instance.
*/
super(itemView);
movieTitleTextView = (TextView) itemView.findViewById(R.id.grid_item_movie_title);
movieTitleImageView = (ImageView) itemView.findViewById(R.id.grid_item_movie_image);
}
}
@Override
public DefaultMovieAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).
inflate(R.layout.item_movies, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(DefaultMovieAdapter.ViewHolder viewHolder, int position) {
Log.v("############", "onBindViewHolder called");
// Get the data model based on position
currentMovie = mDefaultMovie.get(position);
Log.v("############", "currentMovie called is "+currentMovie.toString());
Log.v("############", "currentMovie's title is "+currentMovie.getMovieTitle().toString());
/*
Set item views based on your views and data model
TextView textView = viewHolder.movieTitleTextView;
*/
viewHolder.movieTitleTextView.setText(currentMovie.getMovieTitle());
Log.v("############", "title is :>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+currentMovie.getMovieTitle());
//ImageView button = viewHolder.movieTitleImageView;
//viewHolder.movieTitleImageView.setImageResource(R.mipmap.ic_launcher);
String url = "https://image.tmdb.org/t/p/w500/"+currentMovie.getMoviePosterPath().toString();
Log.v("############", "poster path is :>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+currentMovie.getMoviePosterPath().toString());
Picasso.with(getContext())
.load(url)
.placeholder(R.mipmap.ic_launcher)
.into(viewHolder.movieTitleImageView);
}
@Override
public int getItemCount() {
Log.v("############", "getItemCount called with size "+ mDefaultMovie.size());
return mDefaultMovie.size();
}
public void setMovieData(ArrayList<Movie> weatherData) {
Log.v("############", "setMovieData Called");
mDefaultMovie = weatherData;
Log.v("############", "mDefaultMovie is "+mDefaultMovie);
notifyDataSetChanged();
Log.v("############", "notifyDataSetChanged Finished");
}
}
public class DefaultMovieLoader extends AsyncTaskLoader {
/**
* Query URL
*/
private String mUrl;
/**
* Constructs a new {@link DefaultMovieLoader}.
*
* @param context of the activity
* @param url to load data from
*/
public DefaultMovieLoader(Context context, String url) {
super(context);
mUrl = url;
Log.v("############", "url is "+mUrl);
}
@Override
protected void onStartLoading() {
forceLoad();
Log.v("############", "onStartLoading called");
}
/**
* This is on a background thread.
*/
@Override
public ArrayList<Movie> loadInBackground() {
if (mUrl == null) {
return null;
}
// Perform the network request, parse the response, and extract a list of news.
ArrayList<Movie> movies = QueryUtils.fetchMovieData(mUrl);
Log.v("############", "loadInBackground called");
return movies;
}
}
公共类DefaultMovieFragment扩展片段实现LoaderManager.LoaderCallbacks
编辑:
public class QueryUtils {
private static String movieTitle;
private static int movieId;
private static String moviePosterPath;
/**
* Create a private constructor because no one should ever create a {@link QueryUtils} object.
* This class is only meant to hold static variables and methods, which can be accessed
* directly from the class name QueryUtils (and an object instance of QueryUtils is not needed).
*/
private QueryUtils() {
}
/**
* Query the GUARDIAN dataset and return an {@link Movie} ArrayList to represent a single Movie.
*/
public static ArrayList<Movie> fetchMovieData(String requestUrl) {
Log.v("############", "fetchMovieData called");
// Create URL object
URL url = createUrl(requestUrl);
// Perform HTTP request to the URL and receive a JSON response back
String jsonResponse = null;
try {
jsonResponse = makeHttpRequest(url);
} catch (IOException e) {
//handle exception
}
// Extract relevant fields from the JSON response and create an {@link Event} object
ArrayList<Movie> movies = extractFeatureFromJson(jsonResponse);
// Return the {@link Event}
return movies;
}
/**
* Returns new URL object from the given string URL.
*/
private static URL createUrl(String stringUrl) {
URL url = null;
try {
url = new URL(stringUrl);
} catch (MalformedURLException e) {
//handle exception
}
return url;
}
/**
* Make an HTTP request to the given URL and return a String as the response.
*/
private static String makeHttpRequest(URL url) throws IOException {
Log.v("############", "makeHttpRequest called");
String jsonResponse = "";
// If the URL is null, then return early.
if (url == null) {
return jsonResponse;
}
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setRequestMethod("GET");
urlConnection.connect();
/*
If the request was successful (response code 200),
then read the input stream and parse the response.
*/
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
//handle exception
}
} catch (IOException e) {
//handle exception
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
}
return jsonResponse;
}
/**
* Convert the {@link InputStream} into a String which contains the
* whole JSON response from the server.
*/
private static String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
/**
* Return a list of {@link Movie} objects that has been built up from
* parsing a JSON response.
*/
public static ArrayList<Movie> extractFeatureFromJson(String jsonResponse) {
Log.v("############", "extractFeatureFromJson called");
Log.v("############", "jsonResponse"+jsonResponse);
// Create an empty ArrayList that we can start adding movies to
ArrayList<Movie> movies = new ArrayList<Movie>();
/*
Try to parse the received jsonResponse. If there's a problem with the way the JSON
is formatted, a JSONException exception object will be thrown. Catch the exception
so the app doesn't crash, and handle exception.
*/
try {
// Parse the jsonResponse string
JSONObject movie_json_response = new JSONObject(jsonResponse);
Log.v("############", "JSONObject is: " + movie_json_response.toString());
if (movie_json_response.has("results")) {
JSONArray resultsArray = movie_json_response.getJSONArray("results");
if (resultsArray.length() > 0) {
for (int i = 0; i < resultsArray.length(); i++) {
JSONObject movieDetail = resultsArray.getJSONObject(0);
if (movieDetail.has("title")) {
movieTitle = movieDetail.getString("title");
}
if (movieDetail.has("id")) {
movieId = movieDetail.getInt("id");
}
if (movieDetail.has("poster_path")) {
moviePosterPath = movieDetail.getString("poster_path");
}
Log.v("############", " title is "+movies + "############ id is"+movieId+" ############ poster path is "+moviePosterPath);
movies.add(new Movie(movieTitle, movieId, moviePosterPath));
}
}
}
} catch (JSONException e) {
//handle exception
}
Log.v("############", "Movies returned is: " + movies.toString());
// Return the list of movies
return movies;
}
}
公共类查询tils{
私有静态字符串电影;
私人静态电影ID;
私有静态字符串moviePosterPath;
/**
*创建私有构造函数,因为任何人都不应该创建{@link QueryUtils}对象。
*此类仅用于保存静态变量和方法,这些变量和方法可以访问
*直接来自类名QueryUtils(不需要QueryUtils的对象实例)。
*/
私有查询单元(){
}
/**
*查询GUARDIAN数据集并返回{@link Movie}ArrayList以表示单个电影。
*/
公共静态ArrayList fetchMovieData(字符串请求URL){
Log.v(“fetchMovieData called”);
//创建URL对象
URL URL=createUrl(请求URL);
//对URL执行HTTP请求并接收返回的JSON响应
字符串jsonResponse=null;
试一试{
jsonResponse=makeHttpRequest(url);
}捕获(IOE异常){
//处理异常
}
//从JSON响应中提取相关字段并创建{@link Event}对象
ArrayList movies=extractFeatureFromJson(jsonResponse);
//返回{@link事件}
返回电影;
}
/**
*从给定的字符串URL返回新的URL对象。
*/
私有静态URL createUrl(字符串stringUrl){
URL=null;
试一试{
url=新url(stringUrl);
}捕获(格式错误){
//处理异常
}
返回url;
}
/**
*对给定URL发出HTTP请求并返回字符串作为响应。
*/
私有静态字符串makeHttpRequest(URL)引发IOException{
Log.v(“makeHttpRequest called”);
字符串jsonResponse=“”;
//如果URL为空,则提前返回。
如果(url==null){
返回jsonResponse;
}
HttpURLConnection-urlConnection=null;
InputStream InputStream=null;
试一试{
urlConnection=(HttpURLConnection)url.openConnection();
setReadTimeout(10000/*毫秒*/);
setConnectTimeout(15000/*毫秒*/);
urlConnection.setRequestMethod(“GET”);
urlConnection.connect();
/*
如果请求成功(响应代码200),
然后读取输入流并解析响应。
*/
if(urlConnection.getResponseCode()==200){
inputStream=urlConnection.getInputStream();
jsonResponse=readFromStream(inputStream);
}否则{
//处理异常
}
}捕获(IOE异常){
//处理异常
}最后{
if(urlConnection!=null){
urlConnection.disconnect();
}
如果(inputStream!=null){
inputStream.close();
}
}
返回jsonResponse;
}
/**
*将{@link InputStream}转换为包含
*来自服务器的整个JSON响应。
*/
私有静态字符串readFromStream(InputStream InputStream)引发IOException{
StringBuilder输出=新的StringBuilder();
如果(inputStream!=null){
InputStreamReader InputStreamReader=新的InputStreamReader(inputStream,Charset.forName(“UTF-8”);
BufferedReader reader=新的BufferedReader(inputStreamReader);
字符串行=reader.readLine();
while(行!=null){
输出。追加(行);
line=reader.readLine();
}
}
返回output.toString();
}
/**
*返回从中建立的{@link Movie}对象列表
*解析JSON响应。
*/
公共静态ArrayList extractFeatureFromJson(字符串jsonResponse){
Log.v(“###############,“extractFeatureFromJson调用”);
Log.v(“jsonResponse”、“jsonResponse”+jsonResponse);
//创建一个空的ArrayList,我们可以开始向其中添加电影
ArrayList电影=新建ArrayList();
/*
尝试解析收到的jsonResponse。如果JSON的方式有问题
如果已格式化,将抛出JSONException异常对象。捕获该异常
所以应用程序不会崩溃,并处理异常。
*/
试一试{
//解析jsonResponse字符串
JSONObject movie_json_response=新的JSONObject(jsonResponse);
Log.v(“#############”,JSONObject是:“+movie#u json#u response.toString());
if(movie_json_response.has(“results”)){
JSONArray resultsArray=movie_json_response.getJSONArray(“结果”);
if(resultsArray.length()>0){
对于(int i=0;ifor (int i = 0; i < resultsArray.length(); i++) {
Change zeroth index to i
//JSONObject movieDetail = resultsArray.getJSONObject(0);
@Override
public void onBindViewHolder(DefaultMovieAdapter.ViewHolder viewHolder, int position) {
// Use Local variable
Movie currentMovie = mDefaultMovie.get(position);
}
JSONObject movieDetail = resultsArray.getJSONObject(0);
JSONObject movieDetail = resultsArray.getJSONObject(i);
for (int i = 0; i < resultsArray.length(); i++) {
JSONObject movieDetail = resultsArray.getJSONObject(0);
//other code
}
for (int i = 0; i < resultsArray.length(); i++) {
JSONObject movieDetail = resultsArray.getJSONObject(i);
//other code
}