Recyclerview and OkHttp request synchronisation - java

I'm trying to fetch some data from LastFm API, process it, and display it in a recycler view. My problem is that because of the way my code is structured I think that I initialize my recycler view before data is fetched so nothing is being shown until I reopen my fragment and I don't know how to fix that.
Here is my fragment code:
public class TopArtistsFragment extends Fragment {
private RecyclerView recyclerView;
private ArtistRecyclerViewAdapter adapter;
private TopArtistsViewModel mArtistsViewModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mArtistsViewModel = new ViewModelProvider(this).get(TopArtistsViewModel.class);
mArtistsViewModel.init();
mArtistsViewModel.getArtists().observe(this, new Observer<ArrayList<ArtistData>>() {
#Override
public void onChanged(ArrayList<ArtistData> artistData) {
adapter.notifyDataSetChanged();
}
});
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_top_artists, container, false);
recyclerView = view.findViewById(R.id.artistRecyclerView);
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initRecyclerView();
}
private void initRecyclerView() {
adapter = new ArtistRecyclerViewAdapter(mArtistsViewModel.getArtists().getValue(), getActivity());
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
}
}
This is my ViewModel:
public class TopArtistsViewModel extends ViewModel {
private MutableLiveData<ArrayList<ArtistData>> mArtists;
private ArtistDataRepository mRepo;
public void init() {
if(mArtists != null)
return;
mRepo = ArtistDataRepository.getInstance();
mArtists = mRepo.getArtists();
}
public LiveData<ArrayList<ArtistData>> getArtists() {
return mArtists;
}
}
And this is my repository where the work is being done:
public class ArtistDataRepository {
private static ArtistDataRepository instance;
private ArrayList<ArtistData> dataSet = new ArrayList<>();
public static ArtistDataRepository getInstance() {
if(instance == null)
instance = new ArtistDataRepository();
return instance;
}
public MutableLiveData<ArrayList<ArtistData>> getArtists() {
setArtists();
MutableLiveData<ArrayList<ArtistData>> data = new MutableLiveData<>();
data.setValue(dataSet);
return data;
}
private void setArtists () {
fetchData();
}
private void fetchData () {
final String requestUrl = "https://ws.audioscrobbler.com/2.0/?method=chart.gettopartists&api_key=2124b8e156db20ac7a5035fad9c01b8e&format=json";
final OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(requestUrl).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
Log.d("NETWORK FAILURE", "Artist fetch");
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if(response.isSuccessful())
parseData(response.body().string());
else Log.d("OKHTTP", "Response not successful");
}
});
}
private void parseData (String queryResponse) {
try {
JSONArray artistResponse = new JSONObject(queryResponse).getJSONObject("artists").getJSONArray("artist");
for(int i = 0; i < artistResponse.length(); i++) {
JSONObject artist = artistResponse.getJSONObject(i);
dataSet.add(new ArtistData(artist.getString("mbid"), artist.getString("playcount"), artist.getString("listeners"),
artist.getString("name"), artist.getJSONArray("image").getJSONObject(0).getString("#text")));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
I'm just a beginner so right now I'm basically trying to merge code from different tutorials into one. That's why some things you see here probably don't make sense, so please do tell me what I can fix.

In order to tell the Recycleview that the data has come you need to get the Adapter and set its data then call adapter.notifyDataSetChanged();
mArtistsViewModel.getArtists().observe(this, new Observer<ArrayList<ArtistData>>() {
#Override
public void onChanged(ArrayList<ArtistData> artistData) {
--> adapter.setData(artistData); // Something like that
adapter.notifyDataSetChanged();
}
});

Got through this link
first try to learn how to fetch data using retrofit and you won't to parse data their is a library GSON and Moshi which will do all your work you just need to use POJO. Just go through this example

So to answer the question, part of the solution is, like someone suggested, to add adapter.setData() function to my adapter and call it in onChanged().
mArtistsViewModel.getArtists().observe(this, new Observer<ArrayList<ArtistData>>() {
#Override
public void onChanged(ArrayList<ArtistData> artistData) {
--> adapter.setData(artistData); // Something like that
adapter.notifyDataSetChanged();
}
});
The second part was adding data.postValue() in parseData function in my repo
private void parseData (String queryResponse) {
try {
JSONArray artistResponse = new JSONObject(queryResponse).getJSONObject("artists").getJSONArray("artist");
for(int i = 0; i < artistResponse.length(); i++) {
JSONObject artist = artistResponse.getJSONObject(i);
dataSet.add(new ArtistData(artist.getString("mbid"), artist.getString("playcount"), artist.getString("listeners"),
artist.getString("name"), artist.getJSONArray("image").getJSONObject(0).getString("#text")));
}
data.postValue(dataSet); //THIS WAS ADDED
} catch (JSONException e) {
e.printStackTrace();
}
}
This probably isn't the optimal solution for what I'm doing here, but it works as intended.

Related

RecyclerView not showing any items unless i press homebutton and reenter the activity

The app doesn't show anything in the recycler view the first time I open it, but it shows the items after I press the home button and then press the overview button and open the app from there
here is the code in mainActivity
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mlayoutManager;
private ProgressDialog dialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ArrayList<String> countryNmaeList =new ArrayList<>();
final ArrayList<countryItem> countryList = new ArrayList<>();
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setMessage("Loading data");
mRecyclerView = findViewById(R.id.recyclerView);
mAdapter=new countryAdapter(countryList);
mlayoutManager=new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mlayoutManager);
dialog.show();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://covid-193.p.rapidapi.com/statistics")
.get()
.addHeader("x-rapidapi-host", "covid-193.p.rapidapi.com")
.addHeader("x-rapidapi-key", "xxxxxxxxxxxxx")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
dialog.dismiss();
String response1=response.body().string();
try {
//geting Jason object
JSONObject jsonObject=new JSONObject(response1);
JSONArray jsonArray = jsonObject.getJSONArray("response");
for (int i=0;i<jsonArray.length();i++){
JSONObject country = jsonArray.getJSONObject(i);
JSONObject cases = country.getJSONObject("cases");
int activecaseint = cases.getInt("active");
int recoveredint =cases.getInt("recovered");
JSONObject death= country.getJSONObject("deaths");
int dtotal = death.getInt("total");
//adding items into country items
countryList.add(new countryItem(country.getString("country"),String.valueOf(activecaseint),String.valueOf(recoveredint),String.valueOf(dtotal)));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
//nottifying the dataset changed
mAdapter.notifyDataSetChanged();
mRecyclerView.setAdapter(mAdapter);
}
}
here is my adapter activity
countryAdapter.java
public class countryAdapter extends RecyclerView.Adapter<countryAdapter.countryViewHolder> {
private ArrayList<countryItem> mCountryList;
public static class countryViewHolder extends RecyclerView.ViewHolder{
public TextView mCountryName;
public TextView mActivePatients;
public TextView mRecovered;
public TextView mDeath;
public countryViewHolder(#NonNull View itemView) {
super(itemView);
mCountryName=itemView.findViewById(R.id.CountyNameTv);
mActivePatients=itemView.findViewById(R.id.activePatientsTv);
mRecovered=itemView.findViewById(R.id.recoveredTv);
mDeath=itemView.findViewById(R.id.deathTv);
}
}
public countryAdapter(ArrayList<countryItem> countryList){
mCountryList = countryList;
}
#NonNull
#Override
public countryViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.itemview,parent,false);
countryViewHolder cvh =new countryViewHolder(v);
return cvh;
}
#Override
public void onBindViewHolder(#NonNull countryViewHolder holder, int position) {
countryItem currentItem=mCountryList.get(position);
holder.mCountryName.setText(currentItem.getCountryname());
holder.mActivePatients.setText(currentItem.getActivePatients());
holder.mRecovered.setText(currentItem.getRecovered());
holder.mDeath.setText(currentItem.getDeath());
}
#Override
public int getItemCount() {
return mCountryList.size();
}
public void swapData(ArrayList<countryItem> list) {
if (list != null) {
this.mCountryList.clear();
this.mCountryList.addAll(list);
notifyDataSetChanged();
}
}
}
i have tried putting notifyDataSetChanged inside the try but that didn't work. i hope you can find a way to fix this.
When you have new datalist update after adding it in list as:
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
dialog.dismiss();
String response1=response.body().string();
try {
//geting Jason object
JSONObject jsonObject=new JSONObject(response1);
JSONArray jsonArray = jsonObject.getJSONArray("response");
for (int i=0;i<jsonArray.length();i++){
JSONObject country = jsonArray.getJSONObject(i);
JSONObject cases = country.getJSONObject("cases");
int activecaseint = cases.getInt("active");
int recoveredint =cases.getInt("recovered");
JSONObject death= country.getJSONObject("deaths");
int dtotal = death.getInt("total");
//adding items into country items
countryList.add(new countryItem(country.getString("country"),String.valueOf(activecaseint),String.valueOf(recoveredint),String.valueOf(dtotal)));
}
//adapter.swapData(countryList);
updateData(countryList);
} catch (JSONException e) {
e.printStackTrace();
}
}
you can comment out following line:
mAdapter.notifyDataSetChanged();
Add this function in your adapter class and call it when you need to update list:
public void swapData(ArrayList<countryItem> list) {
if (list != null) {
this.arrayList.clear();
this.arrayList.addAll(list);
notifyDataSetChanged();
}
}
In your global object declaration change type of adapter to:
private countryAdapter mAdapter;
add this method in mainActivity and call when you want to update data:
public void updateData(ArrayList<countryItem> countryList) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
mAdapter.swapData(countryList);
}
});
}
Instead of calling mAdapter.notifyDataSetChanged() at the end of onCreate() you should call it in onRespone() when the new data got set.
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
dialog.dismiss();
String response1=response.body().string();
try {
//geting Jason object
JSONObject jsonObject=new JSONObject(response1);
JSONArray jsonArray = jsonObject.getJSONArray("response");
for (int i=0;i<jsonArray.length();i++){
JSONObject country = jsonArray.getJSONObject(i);
JSONObject cases = country.getJSONObject("cases");
int activecaseint = cases.getInt("active");
int recoveredint =cases.getInt("recovered");
JSONObject death= country.getJSONObject("deaths");
int dtotal = death.getInt("total");
//adding items into country items
countryList.add(new countryItem(country.getString("country"),String.valueOf(activecaseint),String.valueOf(recoveredint),String.valueOf(dtotal)));
}
mAdapter.notifyDataSetChanged(); // ← notify adapter here!
} catch (JSONException e) {
e.printStackTrace();
}
}

Can't display data in ListView using Retrofit

I 've problem with display data in ListView. I get data from Retrofit response, but my activity, which should display this data, is just beeing blank, all the time. I am sure, that I've receiving data, I've checked it, in console.
Model class
public class itemList_model {
#SerializedName("results")
private List<itemList_Results> results;
public List<itemList_Results> getResults() {
return results;
}
public static class itemList_Results{
#SerializedName("title")
String title;
#SerializedName("vote_average")
Double vote;
#SerializedName("genre_ids")
List<Integer> genresId;
#SerializedName("release_date")
String releaseDate;
public itemList_Results(String title, Double vote, String releaseDate) {
this.title = title;
this.vote = vote;
this.releaseDate = releaseDate;
}
public String getTitle() {
return title;
}
public Double getVote() {
return vote;
}
public List<Integer> getGenresId() {
return genresId;
}
public String getReleaseDate() {
return releaseDate;
}
}
public class itemList_genresId{
#SerializedName("genre_ids")
int id;
public int getId() {
return id;
}
}
}
adapter class
public class genres_adapter extends ArrayAdapter<itemList_model.itemList_Results> {
RetrofitCalls calls;
public genres_adapter(#NonNull Context context, ArrayList<itemList_model.itemList_Results> list) {
super(context, 0, list);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View itemView = convertView;
if (itemView == null){
itemView = LayoutInflater.from(getContext()).inflate(R.layout.genres_item_view, parent, false);
}
itemList_model.itemList_Results model = getItem(position);
TextView title = itemView.findViewById(R.id.title);
title.setText(model.getTitle());
TextView vote = itemView.findViewById(R.id.vote);
vote.setText(String.valueOf(model.getVote()));
TextView release = itemView.findViewById(R.id.release);
release.setText(model.getReleaseDate());
return itemView;
}
}
activity java class
ArrayList<itemList_model.itemList_Results> arrayList;
genres_adapter adapter;
String title = "";
Double average_votes = 0.0;
String date = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_genres_list_view);
arrayList = new ArrayList<itemList_model.itemList_Results>();
adapter = new genres_adapter(this, arrayList);
ListView listView = (ListView) findViewById(R.id.genres_listView);
listView.setAdapter(adapter);
getListViewItems();
}
public void getListViewItems(){
String url = "https://api.themoviedb.org/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
apiCall api = retrofit.create(apiCall.class);
Call<itemList_model> call = api.getHorror();
call.enqueue(new Callback<itemList_model>() {
#Override
public void onResponse(Call<itemList_model> call, Response<itemList_model> response) {
if (!response.isSuccessful()) {
Log.i(TAG, "onResponse: " + response.code());
}
List<itemList_model.itemList_Results> list = response.body().getResults();
for (itemList_model.itemList_Results model : list){
title =model.getTitle();
average_votes = Double.valueOf(model.getVote());
date =model.getReleaseDate();
}
list.add(new itemList_model.itemList_Results(title,average_votes,date));
}
#Override
public void onFailure(Call<itemList_model> call, Throwable t) {
Log.i(TAG, "onFailure: "+t.getMessage());
}
});
}
activity, that contains ListView, is called activity_genres_list_view
activity which will be used by adapter, is called genres_item_view
I'm guess, it's about data from list, maybe it's not being added?
Apparently, you should add your list to adapter after you received it:
adapter.addAll(list);
I cannot see any notifyDataSetChanged() call in your code. When the response is found from the Retrofit call, you need to update the list which was passed to the adapter and call notifyDataSetChanged on your adapter to see the effect.
Hence you might consider doing something like the following.
public void getListViewItems(){
String url = "https://api.themoviedb.org/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
apiCall api = retrofit.create(apiCall.class);
Call<itemList_model> call = api.getHorror();
call.enqueue(new Callback<itemList_model>() {
#Override
public void onResponse(Call<itemList_model> call, Response<itemList_model> response) {
if (!response.isSuccessful()) {
Log.i(TAG, "onResponse: " + response.code());
}
// Do not create a new list here. Use the arrayList which was declared before and passed to the adapter
// List<itemList_model.itemList_Results> list = response.body().getResults();
// Clear the arrayList before pushing new data
arrayList.clear();
for (itemList_model.itemList_Results model : list){
title =model.getTitle();
average_votes = Double.valueOf(model.getVote());
date = model.getReleaseDate();
// Add the data into the arrayList instead of the list
arrayList.add(new itemList_model.itemList_Results(title,average_votes,date));
}
// Now call notifyDataSetChanged on your adapter to see the changes in the ListView
adapter.notifyDataSetChanged();
}
#Override
public void onFailure(Call<itemList_model> call, Throwable t) {
Log.i(TAG, "onFailure: "+t.getMessage());
}
});
}
Hope that fixes your problem.
Nothing appear because you are setting adapter before getting information :
adapter = new genres_adapter(this, arrayList);//array list is still empty here
listView.setAdapter(adapter);
getListViewItems();
what yo should do is setting adapter once you get the list ! or call notifydatasetChanged on your adapter after you get your informations !
{
.....
arrayList.add(new itemList_model.itemList_Results(title,average_votes,date));
.....
}
adapter.notifyDataSetChanged();

Clearing my adapter

I populated a GridView with a Custom Adapter of images. I added a button to sort the images. I'm trying to figure out how to do this properly. As of right now, I'm trying to delete all the images by using the clear() method and repopulating it with the sorted images. However, I can't seem to delete the images. It just adds to the sorted images to the original. Please help
public class MainActivity extends AppCompatActivity {
private ImageView mImageView;
private TextView mTextView;
String githubSearchResults;
String default_sort = "http://api.themoviedb.org/3/movie/top_rated?api_key=(MY OWN API KEY)";
ArrayList<String> listdata = new ArrayList<String>();
List<String> posterData = new ArrayList<String>();
JSONObject results;
private MovieAdapter movieAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.movie_image);
getURL(default_sort);
}
JSONArray getResults(String JSONString) throws JSONException {
JSONObject movieResults = new JSONObject(JSONString);
JSONArray jArray = (JSONArray)movieResults.getJSONArray("results");
if (jArray != null) {
for (int i=0;i<jArray.length();i++){
listdata.add(jArray.getString(i));
}
}
return jArray;
}
void getPosterPath() throws JSONException {
for(int i = 0;i<listdata.size();i++) {
results = new JSONObject(listdata.get(i));
String poster_path = results.getString("poster_path");
posterData.add(poster_path);
Log.d("myTag", poster_path);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.sort_popular, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.popular:
movieAdapter.clear();
movieAdapter.notifyDataSetChanged();
String sort_popular = "http://api.themoviedb.org/3/movie/popular?api_key=(MY OWN API KEY)";
getURL(sort_popular);
return true;
default:
super.onOptionsItemSelected(item);
}
return true;
}
private void getURL(String string){
URL getURL = NetworkUtils.buildUrl(string);
new GithubQueryTask().execute(getURL);
}
public class GithubQueryTask extends AsyncTask<URL, Void, String> {
// COMPLETED (2) Override the doInBackground method to perform the query. Return the results. (Hint: You've already written the code to perform the query)
#Override
protected String doInBackground(URL... params) {
URL searchUrl = params[0];
String githubSearchResults = null;
try {
githubSearchResults = NetworkUtils.getResponseFromHttpUrl(searchUrl);
getResults(githubSearchResults);
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return githubSearchResults;
}
// COMPLETED (3) Override onPostExecute to display the results in the TextView
#Override
protected void onPostExecute(String githubSearchResults) {
try {
getPosterPath();
} catch (JSONException e) {
e.printStackTrace();
}
movieAdapter = new MovieAdapter(MainActivity.this, posterData);
GridView listView = (GridView) findViewById(R.id.listview_flavor);
listView.setAdapter(movieAdapter);
}
}
}
Here's my custom adapter:
public class MovieAdapter extends ArrayAdapter<String> {
private ImageView mImageView;
public MovieAdapter(Activity context, List<String> movieArray) {
super(context, 0, movieArray);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String androidFlavor = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.activity_detail, parent, false);
}
mImageView= (ImageView) convertView.findViewById(R.id.movie_image);
Picasso.with(getContext()).load("http://image.tmdb.org/t/p/w185/"+androidFlavor).into(mImageView);
return convertView;
}
}
If I'm not mistaking, the problem is you never clear posterData list (what about listdata as well). You just keep adding to it. I would suggest to change the getPosterPath method like this:
void getPosterPath() throws JSONException {
posterData.Clear();
for(int i = 0;i<listdata.size();i++) {
results = new JSONObject(listdata.get(i));
String poster_path = results.getString("poster_path");
posterData.add(poster_path);
Log.d("myTag", poster_path);
}
movieAdapter.notifyDataSetChanged();
}
And in public boolean onOptionsItemSelected you don't need to call
movieAdapter.clear();
movieAdapter.notifyDataSetChanged();
since any change in the list the adapter is bound to should reflect in the adapter after getPosterPath() gets executed.
EDIT:
The reason you get this error is because
movieAdapter = new MovieAdapter(MainActivity.this, posterData);
happens after the call to getPosterPath().
Just alter the onPostExectute() a bit:
protected void onPostExecute(String githubSearchResults) {
movieAdapter = new MovieAdapter(MainActivity.this, posterData);
try {
getPosterPath();
} catch (JSONException e) {
e.printStackTrace();
}
GridView listView = (GridView) findViewById(R.id.listview_flavor);
listView.setAdapter(movieAdapter);
}

I am using a git repo called LikeButton, but the state of my button keeps jumping around in my recyclerview?

I am using a git repo called LikeButton, but the state of my button keeps jumping around in my recyclerview? Here is the repo https://github.com/jd-alexander/LikeButton. Basically when I click on a recyclerview item, it sets a textview to the word true or false based on if the user liked the post or not, and this works. However, the state of my button is doing some weird stuff, it jumps around...
Here is my Adapter, is their anything wrong with it?
public class ViewpagerAdapter extends RecyclerView.Adapter<ViewpagerAdapter.ViewDashboard>{
private LayoutInflater mLayoutInflater;
private ArrayList<QuestionData> data = new ArrayList<>();
public ViewpagerAdapter(Context context) {
mLayoutInflater=LayoutInflater.from(context);
}
public void setBloglist(ArrayList<QuestionData> listBlogs) {
this.data = listBlogs;
notifyItemRangeChanged(0,listBlogs.size());
}
#Override
public ViewDashboard onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mLayoutInflater.inflate(R.layout.customizejson, parent, false);
ViewDashboard viewholder = new ViewDashboard(view);
return viewholder;
}
#Override
public void onBindViewHolder(ViewDashboard holder, int position) {
QuestionData questionHolder = data.get(position);
holder.questionText.setText(questionHolder.getMtext());
//This sets the text, to a true or a false String
holder.mStudentVoted.setText(questionHolder.getVoters());
holder.mLikeButton.setTag(holder);
}
#Override
public int getItemCount() {
return data.size();
}
class ViewDashboard extends RecyclerView.ViewHolder {
private TextView questionText;
private LikeButton mLikeButton;
private TextView mStudentVoted;
public ViewDashboard(View itemView) {
super(itemView);
questionText = (TextView)itemView.findViewById(R.id.questionText);
mStudentVoted = (TextView)itemView.findViewById(R.id.studentVoted);
mLikeButton = (LikeButton)itemView.findViewById(R.id.like_button_viewpager);
mLikeButton.setOnLikeListener(new OnLikeListener() {
#Override
public void liked(LikeButton likeButton) {
Voting voting = new Voting(getAdapterPosition(),ViewpagerAdapter.this, questionId);
voting.onUpVote();
}
#Override
public void unLiked(LikeButton likeButton) {
Voting voting = new Voting(getAdapterPosition(),ViewpagerAdapter.this, questionId);
voting.onDownVote();
}
});
}
}
}
Voting Class
public class Voting {
private int adapterPosition;
private RecyclerView.Adapter adapter;
private String stringId;
private TextView studentVoted;
//TODO Trim Constructor
public Voting(int adapterPosition,final RecyclerView.Adapter adapter, TextView questionId, TextView studentVoted) {
stringId = questionId.getText().toString();
this.adapter = adapter;
this.studentVoted=studentVoted;
}
public void onUpVote() {
final RequestQueue mRequestQueue = VolleySingleton.getInstance().getRequestQueue();
StringRequest postVoteUp = new StringRequest(Request.Method.PUT, PUT_VOTE_UP, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
System.out.println("Succesful Upvote The Students Value is " + studentVoted);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("failed Upvote");
}
});
mRequestQueue.add(postVoteUp);
}
public void onDownVote() {
final RequestQueue mrequestQueue = VolleySingleton.getInstance().getRequestQueue();
//TODO Delete Token(inserted for student 3 for testing purposes)
StringRequest postVoteDown = new StringRequest(Request.Method.PUT, PUT_VOTE_DOWN, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//TODO OnResponse, must setLiked(False)
//Succesful downVote The Students Value is true
//studentVoted.setText("false");
System.out.println("Succesful downVote The Students Value is "+studentVoted);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("failed downVote");
}
});
mrequestQueue.add(postVoteDown);
}
public void realTimeUpVoting(TextView textView){
String voteString= textView.getText().toString();
int voteNumber=Integer.parseInt(voteString)+1;
textView.setText("" + voteNumber);
}
public void realTimeDownVoting(TextView textView){
String voteString= textView.getText().toString();
int voteNumber=Integer.parseInt(voteString)-1;
textView.setText("" + voteNumber);
}
}
Json Request and Parsing Methods
public void JsonRequestMethod() {
mVolleySingleton = VolleySingleton.getInstance();
mRequestQueue = mVolleySingleton.getRequestQueue();
JsonArrayRequest request = new JsonArrayRequest(Request.Method.GET, URL_HOME, (String) null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
mListblogs.clear();
mListblogs = new YourTask().execute(response).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
mRequestQueue.add(request);
}
private ArrayList<QuestionData> parseJSONResponse(JSONArray response) {
if (!response.equals("")) {
try {
StringBuilder data = new StringBuilder();
for (int x = 0; x < response.length(); x++) {
JSONObject currentQuestions = response.getJSONObject(x);
JSONArray arrSubcategory = currentQuestions.optJSONArray("questions");
for (int y = 0; y < arrSubcategory.length(); y++) {
JSONObject objectSubcategory = arrSubcategory.getJSONObject(y);
String text = objectSubcategory.optString("text");
String studentId = objectSubcategory.optString("studentId");
String votes=objectSubcategory.optString("votes");
/*JSONArray cycles through the array of voters, when a user votes
their ID is added to the array.When they downvote, it is removed
*/
JSONArray voters= objectSubcategory.optJSONArray("voters");
QuestionData questionData = new QuestionData();
questionData.setMstudentId(studentId);
questionData.setMtext(text);
questionData.setVotes(votes);
questionData.setVoters(checkIfVoted(voters));
mQuestionDataArrayList.add(questionData);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return mQuestionDataArrayList;
}
private static String checkIfVoted(JSONArray jsonArray ) {
/*pass in a json Array, copy the array into ints, and if
the students Id is contained in the array return the string true
*/
int[] voteIds = new int[jsonArray.length()];
for(int i=0;i<voteIds.length;i++){
voteIds[i] = jsonArray.optInt(i);
}
for(int i=0;i<voteIds.length;i++){
if(voteIds[i]== Integer.parseInt(Login.getUserId())){
//TODO String was only used for Testing purposes, Convert to Boolean later
return "true";
}
}
return "false";
}
you are currently only updating the textview which is why your recycleview changes state when scrolling.
Should change your voting class and pass the question Data rather textview
public Voting(int adapterPosition,final RecyclerView.Adapter adapter, TextView questionId, TextView studentVoted) {
change to
public Voting(int adapterPosition,final RecyclerView.Adapter adapter, QuestionData questionData, TextView studentVoted) {
// make other changes for the data
and then in
public void realTimeUpVoting(QuestionData questionData){
data.votes++ //something like that. idont know your model
// now call back using interface the recyleview data changed method so it updates the count in recycleview automatically.
Edit
passing the question Data in click button
class ViewDashboard extends RecyclerView.ViewHolder {
public int position
public void onBindViewHolder(ViewDashboard holder, int position) {
holder.position = position
}
public void liked(LikeButton likeButton) {
QuestionData questionHolder = data.get(position);

Synchronizing of threading

I have a fragment class that describes RecyclerView. It takes array for creating elements. The array is forming by parsing JSON. When I use good internet connection everything is OK, and I can see desirable list of items. But using low-speed connection my UI is empty.
I realize that there are some problems with threads, but I haven't enough knowledge to fix my problem.
Here is a code:
public class ListVideo extends Fragment {
private int loadLimit = 9;
private RecyclerView recyclerView;
private RecyclerAdapter adapter;
private LinearLayoutManager linearLayoutManager;
final OkHttpClient client = new OkHttpClient();
List<VideoData> videoList;
List<String> videoDataList;
JSONArray json_array_list_of_videos;
int counter = 0;
int offset;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.listvideofragment, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
videoList = new ArrayList<>();
videoDataList = new ArrayList<>();
recyclerView = (RecyclerView) view.findViewById(R.id.list);
loadData(offset);
createRecycleView();
recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(
linearLayoutManager) {
#Override
public void onLoadMore(int offset) {
// do somthing...
loadMoreData(offset);
}
});
}
private void loadMoreData(int offset) {
loadLimit += 10;
loadData(offset);
adapter.notifyDataSetChanged();
}
private void loadData(final int offset) {
try {
Request request = new Request.Builder()
.url("http://video.motti.be/api/video.getVideoList?offset=" +
offset
+ "&limit=20")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException throwable) {
throwable.printStackTrace();
}
#Override
public void onResponse(Response response) throws IOException {
try {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
String json_string_obj = response.body().string();
JSONObject url = new JSONObject(json_string_obj);
json_array_list_of_videos = url.getJSONArray("data");
System.out.println(json_array_list_of_videos.toString());
for (int y = 0; y <= 9; y++) {
if (json_array_list_of_videos.get(y).toString().equals("A9knX0GXrg")) {
videoDataList.add("6kS9Tt1e47g");
} else {
videoDataList.add(json_array_list_of_videos.get(y).toString());
System.out.println("++++++" + json_array_list_of_videos.get(y).toString());
}
}
for (int i = counter; i <= loadLimit; i++) {
if (videoDataList == null) {
return;
} else {
VideoData next_queue_id = new VideoData(videoDataList.get(i));
videoList.add(next_queue_id);
counter++;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
public void createRecycleView() {
adapter = new RecyclerAdapter(videoList, getContext());
linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
}
I understand, that I get Response later then new adapter creates.For the lack of knowledge, as I sad, I have no idea how to make thread with onResponse method to wait.
Hope that you won't find this question as dull or stupid and will help me.
Thank you in advance!
You need to notify the adapter after you modify its list (videoList).
Currently the loadMoreData(int offset) method doesn't guaranty that because the loadData(offset); method can return before the list is modified (the request is processed asynchronously).
What you could do is this:
Remove the adapter.notifyDataSetChanged(); statement from the loadMoreData(int offset) method and add it to the onResponse(Response response) method.
Example:
#Override
public void onResponse(Response response) throws IOException {
try {
...
for (int i = counter; i <= loadLimit; i++) {
if (videoDataList == null) {
return;
} else {
VideoData next_queue_id = new VideoData(videoDataList.get(i));
videoList.add(next_queue_id);
counter++;
}
}
ListVideo.this.runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
This approach can create other problems because the videoList can be modified by multiple threads at the same time. You need to find a way to synchronize the access to this list or use a thread safe list.

Categories