I'm building a simple IMDB app and I'm almost done save for one tiny detail. The API(http://www.omdbapi.com/) supplies only 10 movies at a time, and the user can specify which "page" do they want. I would like to retrieve all entries. My code looks something like this:
//This populates the list
private void populateList(String title) {
myAPI.getSearchResults(title, page).enqueue(new Callback<Movies>() {
#Override
public void onResponse(Call<Movies> call, Response<Movies> response) {
movies = response.body().getSearch();
recyclerView.setAdapter(new ItemAdapter(movies));
recyclerView.addOnItemTouchListener(
new ItemClickableListener(getActivity(), new ItemClickableListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
String id = movies.get(position).getImdbID();
showDetails(id, view);
}
}));
}
#Override
public void onFailure(Call<Movies> call, Throwable t) {
Log.d(TAG, "Error: " + t);
}
});
}
And in my interface:
//For populating the list
#GET("?")
Call<Movies> getSearchResults(#Query("s") String title, #Query("page") int pages);
There is a way to know how many entries there are in total but the query must run at least once to retrieve that info. I tried fixing it with a "do...while" loop and adding each consecutive batch of movies to a list and only then populating the RecyclerView but it just wouldn't work (it would leave the loop without displaying a thing). Maybe I overlooked something and that is the correct answer, but even then - Isn't there a more elegant approach?
I think you need EndlessRecyclerView to retrieve pages ten by ten. with following code:
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mAdapter = new MyAdapter(getActivity(), this);
scrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) mRecyclerView.getLayoutManager()) {
#Override
public void onLoadMore(int page) {
callWebservice(page);
}
};
mRecyclerView.addOnScrollListener(scrollListener);
mRecyclerView.setAdapter(mAdapter);
When callWebservice is done add Items to your list:
#Override
public void onResponse(Call<List<ShortVideoModel>> call, Response<List<ShortVideoModel>> response) {
mAdapter.addItems(response.body());
}
I ended up checking out EndlessRecyclerView and it works almost perfectly, but I've run into a few issues so I'm posting the code here. It kept stacking listeners and adapters so I swap them. It also kept scrolling up each time data is inserted so I forced it to stay but it's little jittery.
public class SearchFragment extends Fragment {
final String TAG = "LOG.SearchFragment";
final String baseUrl = "http://www.omdbapi.com/";
Button searchButton;
EditText searchField;
RecyclerView recyclerView;
LinearLayoutManager llm;
String title = "";
int page = 1;
List<Search> movies;
Gson gson;
Retrofit retrofit;
MyAPI myAPI;
ItemClickableListener listener;
EndlessRecyclerOnScrollListener scrollListener;
int firstItem;
float topOffset;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "Starting SearchFragment...");
return inflater.inflate(R.layout.search_fragment, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//Preparing RecyclerView
recyclerView = (RecyclerView) getActivity().findViewById(R.id.recycler_view);
llm = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(llm);
setOnScrollManager();
//List for the movies
movies = new ArrayList<>();
//UI
searchField = (EditText) getActivity().findViewById(R.id.search_field);
searchButton = (Button) getActivity().findViewById(R.id.search_button);
searchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!searchField.getText().toString().equals("")) {
gson = new GsonBuilder().create();
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
myAPI = retrofit.create(MyAPI.class);
title = searchField.getText().toString();
movies.clear();
page=1;
setOnScrollManager();
fetchMovies(title, page);
}
}
});
}
private void setOnScrollManager() {
if (scrollListener!=null) recyclerView.removeOnScrollListener(scrollListener);
scrollListener = new EndlessRecyclerOnScrollListener((LinearLayoutManager) recyclerView.getLayoutManager()) {
//This happens when user scrolls to bottom
#Override
public void onLoadMore(int newPage) {
Log.d(TAG, "OnLoadMore "+newPage);
//Preparing the scroll
firstItem = llm.findFirstVisibleItemPosition();
View firstItemView = llm.findViewByPosition(firstItem);
topOffset = firstItemView.getTop();
//Getting new page
page=newPage;
fetchMovies(title, page);
}
};
recyclerView.addOnScrollListener(scrollListener);
}
//This populates the list
private void fetchMovies(String title, int page) {
Log.d(TAG, "Getting "+title+", page "+page);
myAPI.getSearchResults(title, page).enqueue(new Callback<Movies>() {
#Override
public void onResponse(Call<Movies> call, Response<Movies> response) {
if (movies.size()==0) Toast.makeText(getActivity(), "No movies found", Toast.LENGTH_SHORT).show();
movies.addAll(response.body().getSearch());
//We swap the adatper's content when user scrolls down and loads more data
recyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool());
recyclerView.swapAdapter(new ItemAdapter(movies), true);
//Scrolling
Log.d(TAG, "Scrolling to "+firstItem);
llm.scrollToPositionWithOffset(firstItem, (int) topOffset);
//We avoid stacking up listeners
if (listener!=null) recyclerView.removeOnItemTouchListener(listener);
listener = new ItemClickableListener(getActivity(), new ItemClickableListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
String id = movies.get(position).getImdbID();
showDetails(id, view);
}
});
recyclerView.addOnItemTouchListener(listener);
}
#Override
public void onFailure(Call<Movies> call, Throwable t) {
Log.d(TAG, "Error: " + t);
}
});
}
//This gets the movie details
private void showDetails(String id, final View view){
myAPI.getDetails(id).enqueue(new Callback<MovieDetails>() {
#Override
public void onResponse(Call<MovieDetails> call, Response<MovieDetails> response) {
showPopup(response.body(), view);
}
#Override
public void onFailure(Call<MovieDetails> call, Throwable t) {
Log.d(TAG, "Error: " + t);
}
});
}
//This displays the movie details
private void showPopup(MovieDetails details, View anchorView) {
View popupView = getActivity().getLayoutInflater().inflate(R.layout.popup_layout, null);
PopupWindow popupWindow = new PopupWindow(popupView,
RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT);
TextView title = (TextView) popupView.findViewById(R.id.movie_detail_title);
TextView year = (TextView) popupView.findViewById(R.id.movie_detail_year);
TextView rating = (TextView) popupView.findViewById(R.id.movie_detail_rating);
TextView director = (TextView) popupView.findViewById(R.id.movie_detail_director);
TextView stars = (TextView) popupView.findViewById(R.id.movie_detail_stars);
TextView desc = (TextView) popupView.findViewById(R.id.movie_detail_desc);
title.setText(details.getTitle());
title.setTextColor(Color.parseColor("#ffffff"));
year.setText(details.getYear());
year.setTextColor(Color.parseColor("#ffffff"));
rating.setText(details.getImdbRating()+"/10");
rating.setTextColor(Color.parseColor("#ffffff"));
director.setText("Dir: "+details.getDirector());
director.setTextColor(Color.parseColor("#ffffff"));
stars.setText("Stars: "+details.getActors());
stars.setTextColor(Color.parseColor("#ffffff"));
desc.setText(details.getPlot());
desc.setTextColor(Color.parseColor("#ffffff"));
UrlValidator urlValidator = new UrlValidator();
if (urlValidator.isValid(details.getPoster())) {
ImageView poster = (ImageView) popupView.findViewById(R.id.movie_detail_poster);
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(details.getPoster(), poster);
}
// If the PopupWindow should be focusable
popupWindow.setFocusable(true);
// If you need the PopupWindow to dismiss when when touched outside
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#CC000000")));
int location[] = new int[2];
// Get the View's(the one that was clicked in the Fragment) location
anchorView.getLocationOnScreen(location);
// Using location, the PopupWindow will be displayed right under anchorView
popupWindow.showAtLocation(anchorView, Gravity.NO_GRAVITY,
location[0], location[1] + anchorView.getHeight());
}
}
Related
This is my code so far.
Fragment to which recylerview is attached.
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRestaurantLandingPage = getActivity().findViewById(R.id.restaurant_activity);
// mRestaurantMenuRecyclerView = view.findViewById(R.id.restaurant_dish_item_recyler_view);
isAvailable = foodItemViewModel.getRestaurantId();
mRestaurantMenuRecyclerView = view.findViewById(R.id.restaurant_dish_recyler_view);
mRestaurantMenuRecyclerView.setHasFixedSize(true);
mRestaurantMenuRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(),
LinearLayoutManager.VERTICAL, false));
if (!(isAvailable == mRestaurantId)) {
//InsertFoodIAsyncTask insertFoodIAsyncTask = new InsertFoodIAsyncTask();
//insertFoodIAsyncTask.execute(mRestaurantId);
insertFoodItems(mRestaurantId);
} else {
progressDialog.show();
mCategory = foodItemViewModel.getFoodCategories();
for (String category : mCategory) {
foodItems = new ArrayList<>();
foodItems.addAll(foodItemViewModel.getFoodByCategory(category));
RestaurantMenuCategoryModel menuCategoryModel = new RestaurantMenuCategoryModel(category, foodItems);
menuCategoryModels.add(menuCategoryModel);
mRestaurantMenuRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
restaurantMenuCategoryAdapter = new RestaurantMenuCategoryAdapter(getContext(), menuCategoryModels,
mRestaurantLandingPage, foodItemViewModel);
mRestaurantMenuRecyclerView.setAdapter(restaurantMenuCategoryAdapter);
restaurantMenuCategoryAdapter.notifyDataSetChanged();
}
progressDialog.dismiss();
}
foodItemViewModel.getAllLiveFood().observeForever(new Observer<List<FoodItem>>() {
#Override
public void onChanged(#Nullable List<FoodItem> foodItemList) {
}
});
}
private void insertFoodItems(final int restaurantid) {
String mUrl = mApiUrl + "getRestaurantMenu?restaurantId=" + restaurantid;
Log.d("Inside Database ", "from Async Hope it works");
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(
mUrl,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d(TAG, "response length" + response.length());
for (int i = 0; i < response.length(); i++) {
try {
updateRoom Database
} catch (JSONException e) {
e.printStackTrace();
}
}upDateUI();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("JSON Response", error.toString());
}
});
RequestController.getInstance().addToRequestQueue(jsonArrayRequest);
}
private void upDateUI() {
mCategory = foodItemViewModel.getFoodCategories();
for (String category : mCategory) {
foodItems = new ArrayList<>();
foodItems.addAll(foodItemViewModel.getFoodByCategory(category));
RestaurantMenuCategoryModel menuCategoryModel = new RestaurantMenuCategoryModel(category, foodItems);
menuCategoryModels.add(menuCategoryModel);
mRestaurantMenuRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
restaurantMenuCategoryAdapter = new RestaurantMenuCategoryAdapter(getContext(), menuCategoryModels,
mRestaurantLandingPage, foodItemViewModel);
mRestaurantMenuRecyclerView.setAdapter(restaurantMenuCategoryAdapter);
restaurantMenuCategoryAdapter.notifyDataSetChanged();
progressDialog.dismiss();
}
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState) {
RestaurantMainActivity mRestaurantMainActivity = (RestaurantMainActivity) getActivity();
mQueryData = new ArrayList();
mQueryData = mRestaurantMainActivity.getIntentDataForFragments();
mRestaurantId = Integer.parseInt(mQueryData.get(0).toString());
foodItemViewModel = ViewModelProviders.of(this).get(FoodItemViewModel.class);
progressDialog = new ProgressDialog(getActivity());
view = inflater.inflate(R.layout.restaurant_menu_fragment, container, false);
return view;
}
Recyclerview where user can increase or decrease the food count.
holder.mAddToCart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentItem.setQuantityincart(1);
currentItem.setFoodincart(1);
foodItemViewModel.addFoodToCart(currentItem);
holder.mAddToCart.setVisibility(View.GONE);
holder.mAddButtonQuantityLinearLayout.setVisibility(View.VISIBLE);
holder.mFoodQuantityTextView.setText(String.valueOf(currentItem.getQuantityincart()));
showViewCart();
}
});
holder.mDecreaseQuantityButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentItem.setQuantityincart(currentItem.getQuantityincart() - 1);
if (currentItem.getQuantityincart() < 1) {
holder.mAddToCart.setVisibility(View.VISIBLE);
holder.mAddButtonQuantityLinearLayout.setVisibility(View.GONE);
currentItem.setFoodincart(0);
foodItemViewModel.decreaseFoodQuantity(currentItem);
}
holder.mFoodQuantityTextView.setText(String.valueOf(currentItem.getQuantityincart()));
foodItemViewModel.decreaseFoodQuantity(currentItem);
showViewCart();
}
});
holder.mIncreaseQuantityButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(mContext, "Before Decrease Click Current Food Item Count " + currentItem.getQuantityincart(), Toast.LENGTH_SHORT).show();
currentItem.setQuantityincart(currentItem.getQuantityincart() + 1);
foodItemViewModel.increaseFoodQuantity(currentItem);
holder.mFoodQuantityTextView.setText(String.valueOf(currentItem.getQuantityincart()));
Toast.makeText(mContext, "Before Decrease Click Current Food Item Count " + currentItem.getQuantityincart(), Toast.LENGTH_LONG).show();
showViewCart();
}
});
private void showViewCart() {
mSnackbar = Snackbar.make(mRestaurantLandingPage, "Total Cost ₹ " + foodItemViewModel.getTotalPrice(), Snackbar.LENGTH_INDEFINITE);
mSnackbar.getView().setBackgroundColor(ContextCompat.getColor(mContext, R.color.colorPrimary));
mSnackbar.setActionTextColor(mContext.getResources().getColor(R.color.colorBackground));
mSnackbar.setAction("View Cart", new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent mCartIntent = new Intent(mContext, CartItemsActivity.class);
mContext.startActivity(mCartIntent);
}
});
mSnackbar.show();
}`enter code here`
When User Clicks on snackbar user is routed to CartActivity where user can remove the item or increase or decrease the item which is passed.
Currently when User updates the value like increasing or decreasing or removing the food item from cart at that point in time I update the Room dB.
Next if user clicks on back button User is routed to Activity which holds the Fragment and the related recylerview but the recyler view still shows the old data like the quantity is not reset though in Db it is updated (Room DB).
I am not sure how exactly use Room DB live data and mutablelivedata in this scenario. Thanks in Advance a solution or guidance will be helpful
I am trying to do this..
.
I am consuming two different requests resources from the same API and in MainActivity doing two different calls. But, I can't show the content I want from both JSON on one RecyclerView view.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Retrofit retrofit;
private static final String TAG = "Football";
private RecyclerView recyclerView;
private ListaPartidosAdapter listaPartidosAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
listaPartidosAdapter = new ListaPartidosAdapter(this);
recyclerView.setAdapter(listaPartidosAdapter);
recyclerView.setHasFixedSize(true);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this, VERTICAL, true);
recyclerView.setLayoutManager(layoutManager);
retrofit = new Retrofit.Builder()
.baseUrl("http://api.football-data.org/v2/")
.addConverterFactory(GsonConverterFactory.create())
.build();
obtenerDatos();
}
private void obtenerDatos() {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String todayDate=df.format(calendar.getTime());
calendar.add(Calendar.DATE,3);
String endDate = df.format(calendar.getTime());
Log.i(TAG, "todayDate : " + todayDate);
Log.i(TAG, "endDate : " + endDate);
footballdataService service = retrofit.create(footballdataService.class);
Call<PartidosRespuesta> partidosRespuestaCall = service.obtenerlistaPartidos(todayDate,endDate);
Call<StandingsRespuesta> standingsRespuestaCall = service.obtenerStandings();
partidosRespuestaCall.enqueue(new Callback<PartidosRespuesta>() {
#Override
public void onResponse(Call<PartidosRespuesta> call, Response<PartidosRespuesta> response) {
if(response.isSuccessful()) {
PartidosRespuesta partidosRespuesta = response.body();
List<Partido> listaPartidos = partidosRespuesta.getMatches();
listaPartidosAdapter.adicionarListaPartidos((ArrayList<Partido>) listaPartidos);
}
else {
Log.e(TAG, "onResponse: " + response.errorBody());
}
}
#Override
public void onFailure(Call<PartidosRespuesta> call, Throwable t) {
Log.e(TAG, "onFailure: " + t.getMessage());
}
});
standingsRespuestaCall.enqueue(new Callback<StandingsRespuesta>() {
#Override
public void onResponse(Call<StandingsRespuesta> call, Response<StandingsRespuesta> response) {
if(response.isSuccessful()) {
StandingsRespuesta standingsRespuesta = response.body();
List<Stand> listaStands = standingsRespuesta.getStandings();
listaPartidosAdapter.adicionarListaStands((ArrayList<Stand>) listaStands);
}
}
#Override
public void onFailure(Call<StandingsRespuesta> call, Throwable t) {
}
});
}
}
As I say before, each request has a different enqueue Call. I don't know if it is the right way of do it but think yes because each call has its own service.
ListaPartidosAdapter.java
public class ListaPartidosAdapter extends RecyclerView.Adapter<ListaPartidosAdapter.ViewHolder> {
private static final String TAG = "Football_Adapter";
private ArrayList<Partido> dataset;
private ArrayList<Stand> dataset_stand;
private Context context;
public ListaPartidosAdapter(Context context) {
this.context = context;
this.dataset = new ArrayList<Partido>();
this.dataset_stand = new ArrayList<Stand>();
}
#Override
public ListaPartidosAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_partidos, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ListaPartidosAdapter.ViewHolder holder, int position) {
Partido p = dataset.get(position);
String status = p.getStatus();
if (status.equals("SCHEDULED")){
String status_ = "SCH";
holder.status.setText(status_);
}
holder.utcDate.setText(p.getUtcDate());
Partido.EquipoCasa homeTeam = p.getHomeTeam();
String id_homeTeam = homeTeam.getId();
holder.homeTeam.setText(homeTeam.getName());
Partido.EquipoVisita awayTeam = p.getAwayTeam();
holder.awayTeam.setText(awayTeam.getName());
Stand s = dataset_stand.get(position);
Stand.Table table = (Stand.Table) s.getTable();
Stand.Table.Equipo team = (Stand.Table.Equipo) table.getEquipo();
String id_equipo = team.getId();
holder.homeTeam.setText(team.getName());
if(id_homeTeam.equals(id_equipo)){
Glide.with(context)
.load(team.getCrestUrl())
.centerCrop()
.crossFade()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(holder.team);
//holder.team.setImageDrawable(team.getCrestUrl());
}
}
#Override
public int getItemCount() {
return dataset.size()+dataset_stand.size();
}
public void adicionarListaPartidos(ArrayList<Partido> listaPartidos){
dataset.addAll(listaPartidos);
notifyDataSetChanged();
}
public void adicionarListaStands(ArrayList<Stand> listaStands){
dataset_stand.addAll(listaStands);
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView status;
private TextView utcDate;
private TextView homeTeam;
private TextView awayTeam;
public ImageView team;
public ViewHolder(View itemView) {
super(itemView);
status = (TextView) itemView.findViewById(R.id.status);
utcDate = (TextView) itemView.findViewById(R.id.utcDate);
homeTeam = (TextView) itemView.findViewById(R.id.homeTeam);
awayTeam = (TextView) itemView.findViewById(R.id.awayTeam);
team = (ImageView) itemView.findViewById(R.id.team);
}
}
}
The problem comes in this line. Stand s = dataset_stand.get(position);, if a comment it with code below, It works without using the second JSON or the second request but as I showed on image I want to merge two different requests on the same RecyclerView view.
The problem statement
The problem comes in this line. Stand s = dataset_stand.get(position);, if a comment it with code below, It works without using the second JSON or the second request.
Yes, it's expected. Why? Because you're calling RecyclerView.Adapter#notifyDataSetChanged() method inside adicionarListaPartidos(ArrayList<Partido> listaPartidos) after inserting data to ArrayList in RecyclerView array adapter class which informs ArrayAdapter to redraw/refresh components inside RecyclerView container.
Now RecycelrView starts binding component within the onBindViewHolder(ListaPartidosAdapter.ViewHolder holder, int position) method. So, what's happening here?
#Override
public void onBindViewHolder(ListaPartidosAdapter.ViewHolder holder, int position) {
Partido p = dataset.get(position);
String status = p.getStatus();
if (status.equals("SCHEDULED")){
String status_ = "SCH";
holder.status.setText(status_);
}
holder.utcDate.setText(p.getUtcDate());
Partido.EquipoCasa homeTeam = p.getHomeTeam();
// other code
Stand s = dataset_stand.get(position); // <====== the problem
// other code
}
Your dataset_stand list will be empty (size of the list is 0) until you get data/response from standingsRespuestaCall.enqueue() method in your activity.
Keep in mind that Retrofit's Call#enque() method is Asynchronous. Which means in your case obtenerDatos() method executes top to bottom in a single hit. You only get data when Retrofit returns success response with onResponse() method.
The easiest way to fix this issue is to comment out the notifyDataSetChanged() method inside adicionarListaPartidos(ArrayList<Partido> listaPartidos). Like below
public void adicionarListaPartidos(ArrayList<Partido> listaPartidos){
dataset.addAll(listaPartidos);
// notifyDataSetChanged(); // <====== just COMMENT OUT this line
}
This will prevent onBindViewHolder() being called. When the second request standingsRespuestaCall.enqueue() completes it's operation, as per your code notifies dataset changed. Like below.
public void adicionarListaStands(ArrayList<Stand> listaStands){
dataset_stand.addAll(listaStands);
notifyDataSetChanged(); // <==== DO NOT remove this
}
Side Note: Your code is problematic. You're using multiple request to fill a single RecyclerView container. Your RecyclerView fails to display record if standingsRespuestaCall.enqueue() fails to get response from server.
PS: Please check method names I mentioned in my answer properly and alter the code accordingly. Do not get confused with the method names.
My RetroFit call that gets a list of two strings and two objects succeed on the first time but the response every time after that gets an empty body for some reason.
This is the Adapter for it:
public class AssistsAdapter extends RecyclerView.Adapter<AssistsAdapter.AssistsViewHolder> {
private List<TopAssists> mTopAssistsList;
private int mRowLayout;
private Context mContext;
public class AssistsViewHolder extends RecyclerView.ViewHolder{
LinearLayout assistsLayout;
TextView playerRank, playerAssists, playerName, playerTeam;
public AssistsViewHolder(View itemView) {
super(itemView);
assistsLayout = itemView.findViewById(R.id.assists_layout);
playerRank = itemView.findViewById(R.id.assists_rank);
playerAssists = itemView.findViewById(R.id.assists_assists);
playerName = itemView.findViewById(R.id.assists_player);
playerTeam = itemView.findViewById(R.id.assists_team);
}
}
public AssistsAdapter(List<TopAssists> topAssistsList, int rowLayout, Context context){
mTopAssistsList = topAssistsList;
mRowLayout = rowLayout;
mContext = context;
}
#NonNull
#Override
public AssistsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(mRowLayout, parent, false);
AssistsViewHolder holder = new AssistsViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(AssistsViewHolder holder, int position) {
holder.playerRank.setText(String.valueOf(mTopAssistsList.get(position).getRank()));
holder.playerAssists.setText(String.valueOf(mTopAssistsList.get(position).getAssists()));
holder.playerName.setText(mTopAssistsList.get(position).getPlayer().getName());
holder.playerTeam.setText(mTopAssistsList.get(position).getTeam().getName());
}
#Override
public int getItemCount() {
return this.mTopAssistsList.size();
}
This is the fragment that initiate the RetroFit call:
public class TournamentsInfoFragment extends Fragment {
private final static String TAG = "Call Failed";
private StandingsAdapter mStandingsAdapter;
private RecyclerView mRecyclerView;
private ProgressBar mProgressBar;
private String mTournamentId;
private String mRegion;
private String mKey;
private GoalsAdapter mGoalsAdapter;
private AssistsAdapter mAssistsAdapter;
private TextView mFailedMessage;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_standings, container, false);
// Initializing the failure message in case of an error
mFailedMessage = view.findViewById(R.id.standings_failed);
// Getting the arguments from the previous fragment
Bundle bundle = getArguments();
mTournamentId = bundle.getString("tournament_id");
mRegion = bundle.getString("region");
mKey = bundle.getString("key");
// Showing the Progressbar
mProgressBar = view.findViewById(R.id.standings_progress_bar);
// Creating an instance of the ApiInterface
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
// Calling to get the standings from the API
Call<StandingsResponse> call = apiService.getStandings(mRegion, mTournamentId, mKey);
// Logging the URL Call
Log.wtf("URL Called", call.request().url() + "");
call.enqueue(new Callback<StandingsResponse>() {
#Override
public void onResponse(Call<StandingsResponse> call, Response<StandingsResponse> response) {
// Checking the response code to act accordingly
if(response.code() == 404){
mFailedMessage.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
else{
if (response.body() == null){
Toast.makeText(getActivity().getApplicationContext(),
R.string.standings_problem, Toast.LENGTH_LONG).show();
mProgressBar.setVisibility(View.GONE);
}
else {
generateStandings(response.body().getStandings());
mStandingsAdapter.notifyDataSetChanged();
mProgressBar.setVisibility(View.GONE);
}
}
}
#Override
public void onFailure(Call<StandingsResponse> call, Throwable t) {
Log.e(TAG, t.toString());
Toast.makeText(getActivity(), R.string.enqueue_failure, Toast.LENGTH_LONG).show();
mProgressBar.setVisibility(View.GONE);
}
});
// Calling to get the Goals and assists leaders from the API
Call<LeadersResponse> leadersCall = apiService.getLeaders(mRegion,mTournamentId,mKey);
// Logging the URL Call
Log.wtf("Leaders URL Called", leadersCall.request().url() + "");
leadersCall.enqueue(new Callback<LeadersResponse>() {
#Override
public void onResponse(Call<LeadersResponse> call, Response<LeadersResponse> response) {
// Checking the response code and acting accordingly
if(response.code() == 404){
mFailedMessage.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
else{
if (response.body() == null){
Toast.makeText(getActivity().getApplicationContext(),
R.string.leaders_problem, Toast.LENGTH_LONG).show();
mProgressBar.setVisibility(View.GONE);
}
else {
generateGoals(response.body().getTopGoalsList());
generateAssists(response.body().getTopAssists());
mGoalsAdapter.notifyDataSetChanged();
mAssistsAdapter.notifyDataSetChanged();
mProgressBar.setVisibility(View.GONE);
}
}
}
#Override
public void onFailure(Call<LeadersResponse> call, Throwable t) {
Log.e(TAG, t.toString());
Toast.makeText(getActivity(), R.string.enqueue_failure, Toast.LENGTH_LONG).show();
mProgressBar.setVisibility(View.GONE);
}
});
return view;
}
/**
* Method to generate List of standings using RecyclerView with custom adapter
*/
private void generateStandings(final List<Standings> empDataList) {
mRecyclerView = getView().findViewById(R.id.standings_rv);
mStandingsAdapter = new StandingsAdapter(empDataList, R.layout.item_standings, getActivity());
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.setAdapter(mStandingsAdapter);
}
/**
* Method to generate List of Goal leaders using RecyclerView with Custom adapter
*/
private void generateGoals(List<TopGoals> topGoals) {
mRecyclerView = getView().findViewById(R.id.goals_rv);
mGoalsAdapter = new GoalsAdapter(topGoals, R.layout.item_goals, getActivity());
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.setAdapter(mGoalsAdapter);
}
/**
* Method to generate List of assists leaders using RecyclerView with Custom adapter
*/
private void generateAssists(List<TopAssists> topAssists) {
mRecyclerView = getView().findViewById(R.id.assists_rv);
mAssistsAdapter = new AssistsAdapter(topAssists, R.layout.item_assists, getActivity());
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.setAdapter(mAssistsAdapter);
}
It shows the RecyclerView for the first time but every time after that the response body is empty and causes a NullPointerException, I had to create an if statement and Toast just to prevent the application from crashing.
Error Log:
6-03 21:55:51.241 6034-6034/com.mad.footstats E/URL Called: https://api.sportradar.us/soccer-t3/eu/en/tournaments/sr:tournament:17/standings.json?api_key=w7c74newrykj8m57rda6xwrk 06-03 21:55:51.253 6034-6034/com.mad.footstats E/Leaders URL Called: https://api.sportradar.us/soccer-t3/eu/en/tournaments/sr:tournament:17/leaders.json?api_key=w7c74newrykj8m57rda6xwrk 06-03 21:55:51.278 6034-6034/com.mad.footstats E/RecyclerView: No adapter attached; skipping layout 06-03 21:55:51.279 6034-6034/com.mad.footstats I/chatty: uid=10085(com.mad.footstats) identical 1 line 06-03 21:55:51.279 6034-6034/com.mad.footstats E/RecyclerView: No adapter attached; skipping layout 06-03 21:55:51.507 1393-3495/? W/audio_hw_generic: Not supplying enough data to HAL, expected position 10934976 , only wrote 10934640 06-03 21:55:51.557 1393-3495/? W/audio_hw_generic: Hardware backing HAL too slow, could only write 0 of 720 frames
I have successfully to get next page of post using Blogger's API, but i can't achieve the smooth scrolling of recyclerview, it look lagging.
I have tried using recyclerViewData.setNestedScrollingEnabled(false) and adapter.notifyDataSetChanged() but still doesn't work.
My point is How to achieve smooth of recyclerview. Any suggestion will be appreciate. thank is advance.
Here is my code
private void getData() {
showLoading(true);
final Call<ResponseBlogPost> postList = apiService.getListPost(GlobalVariable.APP_KEY);
postList.enqueue(new Callback<ResponseBlogPost>() {
#Override
public void onResponse(Call<ResponseBlogPost> call, Response<ResponseBlogPost> response) {
showLoading(false);
ResponseBlogPost responseBlogPost = response.body();
initDataView(responseBlogPost);
}
#Override
public void onFailure(Call<ResponseBlogPost> call, Throwable t) {
showLoading(false);
Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
private void initDataView(ResponseBlogPost listpost){
GlobalFunction.saveString(this,GlobalVariable.TOKEN_PAGINATION, listpost.getNextPageToken());
final String nextPageToken = GlobalFunction.getStrings(this, GlobalVariable.TOKEN_PAGINATION);
itemsList.addAll(listpost.getItems());
adapter = new MainAdapter(itemsList) {
#Override
public void load() {
if(nextPageToken==null){
return;
}
getNextListPost();
}
};
recyclerViewData.setAdapter(adapter);
recyclerViewData.setHasFixedSize(true);
staggeredGridLayoutManager = new StaggeredGridLayoutManager(2,1);
staggeredGridLayoutManager.setGapStrategy(0);
recyclerViewData.setLayoutManager(staggeredGridLayoutManager);
}
private void getNextListPost(){
showLoading(true);
final String nextPageToken = GlobalFunction.getStrings(this, GlobalVariable.TOKEN_PAGINATION);
Call<ResponseBlogPost> call = apiService.getNexPageListPost(GlobalVariable.APP_KEY,nextPageToken);
call.enqueue(new Callback<ResponseBlogPost>() {
#Override
public void onResponse(Call<ResponseBlogPost> call, Response<ResponseBlogPost> response) {
showLoading(false);
ResponseBlogPost responseModel = response.body();
if(nextPageToken!=null){
initDataView2(responseModel);
}else{
Toast.makeText(MainActivity.this, "tidak ada data lagi ya", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseBlogPost> call, Throwable t) {
showLoading(false);
Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
private void initDataView2(ResponseBlogPost listpost){
GlobalFunction.saveString(this,GlobalVariable.TOKEN_PAGINATION, listpost.getNextPageToken());
final String nextPageToken = GlobalFunction.getStrings(this, GlobalVariable.TOKEN_PAGINATION);
itemsList.addAll(listpost.getItems());
adapter = new MainAdapter(itemsList) {
#Override
public void load() {
if(nextPageToken!=null){
getNextListPost();
}
}
};
recyclerViewData.setAdapter(adapter);
recyclerViewData.setHasFixedSize(true);
staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, 1);
staggeredGridLayoutManager.setGapStrategy(0);
recyclerViewData.setLayoutManager(staggeredGridLayoutManager);
}
My Adapter Code :
public abstract class MainAdapter extends RecyclerView.Adapter<MainAdapter.MainViewHolder>{
private List<BlogPostModel> responseBlogPost;
public MainAdapter(List<BlogPostModel> responseBlogPost) {
this.responseBlogPost = responseBlogPost;
}
public abstract void load();
#Override
public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_main,parent,false);
return new MainViewHolder(view);
}
#Override
public void onBindViewHolder(final MainViewHolder holder, int position) {
final BlogPostModel model = responseBlogPost.get(position);
ArrayList<String> urlImage = pullLinks(model.getContent());
String firstImage = "";
for (int i = 0; i < urlImage.size(); i++) {
firstImage = urlImage.get(1);
GlideCustomLoading.setImageFromURL(holder.itemView.getContext(), urlImage.get(0)
,holder.avLoadingIndicatorView, holder.ivItemPost,holder.tvFailedLoadImage);
}
holder.tvTitleItemPost.setText(model.getTitle());
final String finalFirstImage = firstImage;
holder.llItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String id = String.valueOf(model.getId());
DetailPostActivity.start(holder.itemView.getContext(), id, finalFirstImage, model.getTitle(), model.getUrl());
}
});
if(position>=getItemCount()-1){
load();
}
}
#Override
public int getItemCount() {
return responseBlogPost.size();
}
public class MainViewHolder extends RecyclerView.ViewHolder{
CardView cardViewItemPost;
ImageView ivItemPost;
TextView tvTitleItemPost;
AVLoadingIndicatorView avLoadingIndicatorView;
TextView tvFailedLoadImage;
LinearLayout llItem;
public MainViewHolder(View itemView) {
super(itemView);
cardViewItemPost = itemView.findViewById(R.id.cardview_item_post);
ivItemPost = itemView.findViewById(R.id.iv_image_post);
tvTitleItemPost = itemView.findViewById(R.id.tv_title_post);
avLoadingIndicatorView = itemView.findViewById(R.id.avi_load);
tvFailedLoadImage = itemView.findViewById(R.id.tv_gagal_menampilkan_gambar);
llItem = itemView.findViewById(R.id.ll_item_post);
}
}
private ArrayList pullLinks(String html) {
ArrayList links = new ArrayList();
Elements srcs = Jsoup.parse(html).select("[src]"); //get All tags containing "src"
for (int i = 0; i < srcs.size(); i++) {
links.add(srcs.get(i).attr("abs:src")); // get links of selected tags
}
return links;
}
}
While I'm not familiar with Jsoup I'm reasonably confident that it's the source of your scroll lag.
You are calling the method pullLinks in your onBindViewHolder method which is called every time a new view is added to your RecyclerView. I'm assuming pullLinks does some pretty intensive work that takes some time to complete.
What you would be better off doing is to add all of the data to each item in your responseBlogPost List before loading it in to the adapter.
I have a ListView in which there several rows containing two buttons and a ProgressBar (Visibility:GONE) each.
My purpose is to display the ProgressBar upon click on the buttons and after completing a certain set of background operations remove that row entirely.
The problem here is that after removing the item from the ArrayList which the ListView is created upon and calling notifyDataSetChanged the row is removed successfully but the ProgressBar remains visible.
Shouldn't it be removed along with it's parent view?
Checkout the following record to see the problem in action.
Here is the source of my entire fragment:
public class FriendRequestFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "FriendRequestFragment";
ArrayList<FriendRequest> friendRequests;
#InjectView(R.id.friendRequestList)
ListView mListView;
#InjectView(R.id.noRequestsText)
TextView noRequestsText;
#InjectView(R.id.swipe)
SwipeRefreshLayout swipeRefreshLayout;
// NotificationHandler nh;
/**
* The Adapter which will be used to populate the ListView/GridView with
* Views.
*/
private FriendRequestAdapter mAdapter;
private Context c;
private boolean isProcessing = false;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public FriendRequestFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Util.trackFragment(this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_friendrequest_list, container, false);
ButterKnife.inject(this, view);
c = getActivity();
friendRequests = new ArrayList<>();
swipeRefreshLayout.setOnRefreshListener(this);
mAdapter = new FriendRequestAdapter(getActivity(), friendRequests);
mListView.setAdapter(mAdapter);
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int topRowVerticalPosition =
(view == null || view.getChildCount() == 0) ?
0 : view.getChildAt(0).getTop();
swipeRefreshLayout.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
Log.d(TAG, "SwipeRefresh: " + String.valueOf(firstVisibleItem == 0 && topRowVerticalPosition >= 0));
}
});
loadRequests();
return view;
}
private void loadRequests() {
// nh = new NotificationHandler(getActivity());
swipeRefreshLayout.setRefreshing(true);
Log.d(TAG, "loading requests init");
HashMap<String, Integer> params = new HashMap<>();
params.put("profile_id", Util.getCurrentProfileID(c));
final String uniqueID = Util.getCurrentProfileID(c) + String.valueOf(System.currentTimeMillis() / 1000 / 1200);
new ApiRequest(Util.URL_GET_FRIEND_REQUESTS, params, new AjaxCallback<String>() {
#Override
public void callback(String url, String result, AjaxStatus status) {
super.callback(url, result, status);
ApiResponse apiResponse = new ApiResponse(url, result, uniqueID);
Log.d(TAG, "Friend Requests Response: " + result);
if (apiResponse.isSuccessful()) {
JSONArray jsonArray = apiResponse.getDataJSONArray();
try {
for (int i = 0; i < jsonArray.length(); i++) {
friendRequests.add(new FriendRequest(jsonArray.getJSONObject(i)));
}
mAdapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
mListView.setVisibility(View.VISIBLE);
} else if (apiResponse.getErrorMessage().equals("request_not_found")) {
noRequestsText.setVisibility(View.VISIBLE);
}
swipeRefreshLayout.setRefreshing(true);
}
}).setUniqueID(uniqueID).execute();
}
#Override
public void onRefresh() {
loadRequests();
}
private void acceptRequest(final int position, final View rootView) {
if (isProcessing) {
CustomToast.makeToast(getActivity(), CustomToast.TYPE_ALERT, getString(R.string.please_wait), CustomToast.LENGTH_SHORT);
return;
}
rootView.findViewById(R.id.loading).setVisibility(View.VISIBLE);
rootView.findViewById(R.id.acceptBtn).setVisibility(View.GONE);
rootView.findViewById(R.id.denyBtn).setVisibility(View.GONE);
isProcessing = true;
Log.d("FriendRequest", "accepting:" + position);
FriendRequest request = friendRequests.get(position);
HashMap<String, Integer> params = new HashMap<>();
params.put("request_id", request.getRequestID());
params.put("profile_id", ProfilesSingleton.getInstance().getCurrentProfile().getProfileID());
new ApiRequest(Util.URL_ACCEPT_REQUEST, params, new AjaxCallback<String>() {
#Override
public void callback(String url, String object, AjaxStatus status) {
super.callback(url, object, status);
ApiResponse apiResponse = new ApiResponse(object);
if (apiResponse.isSuccessful()) {
friendRequests.remove(position);
CustomToast.makeToast(getActivity(), CustomToast.TYPE_DEFAULT,
getString(R.string.you_are_now_friends_with) + " " + friendRequests.get(position).getFullName(),
CustomToast.LENGTH_SHORT);
mAdapter.notifyDataSetChanged();
}else {
rootView.findViewById(R.id.loading).setVisibility(View.GONE);
rootView.findViewById(R.id.acceptBtn).setVisibility(View.VISIBLE);
rootView.findViewById(R.id.denyBtn).setVisibility(View.VISIBLE);
}
isProcessing = false;
}
}).execute();
}
private void denyRequest(final int position, final View rootView) {
if (isProcessing) {
CustomToast.makeToast(getActivity(), CustomToast.TYPE_ALERT, getString(R.string.please_wait), CustomToast.LENGTH_SHORT);
return;
}
rootView.findViewById(R.id.loading).setVisibility(View.VISIBLE);
rootView.findViewById(R.id.acceptBtn).setVisibility(View.GONE);
rootView.findViewById(R.id.denyBtn).setVisibility(View.GONE);
Log.d("FriendRequest", "denying:" + position);
FriendRequest request = friendRequests.get(position);
HashMap<String, Integer> params = new HashMap<>();
params.put("request_id", request.getRequestID());
params.put("profile_id", ProfilesSingleton.getInstance().getCurrentProfile().getProfileID());
new ApiRequest(Util.URL_DENY_REQUEST, params, new AjaxCallback<String>() {
#Override
public void callback(String url, String object, AjaxStatus status) {
super.callback(url, object, status);
ApiResponse apiResponse = new ApiResponse(object);
if (apiResponse.isSuccessful()) {
friendRequests.remove(position);
mAdapter.notifyDataSetChanged();
}else {
rootView.findViewById(R.id.loading).setVisibility(View.GONE);
rootView.findViewById(R.id.acceptBtn).setVisibility(View.VISIBLE);
rootView.findViewById(R.id.denyBtn).setVisibility(View.VISIBLE);
}
}
}).execute();
}
public class FriendRequestAdapter extends ArrayAdapter<FriendRequest> {
public FriendRequestAdapter(Context context, ArrayList<FriendRequest> objects) {
super(context, 0, objects);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View rootView = convertView;
final ViewHolder holder;
final FriendRequest friendRequest = getItem(position);
if (rootView == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rootView = inflater.inflate(R.layout.friend_request_item, parent, false);
holder = new ViewHolder();
holder.profilePhoto = (RoundedImageView) rootView.findViewById(R.id.profilePhoto);
holder.fullName = (TextView) rootView.findViewById(R.id.fullName);
holder.acceptBtn = (ImageView) rootView.findViewById(R.id.acceptBtn);
holder.denyBtn = (ImageView) rootView.findViewById(R.id.denyBtn);
holder.loading = (ProgressBar) rootView.findViewById(R.id.loading);
rootView.setTag(holder);
} else {
holder = (ViewHolder) rootView.getTag();
}
holder.fullName.setText(friendRequest.getFullName());
if (friendRequest.getFullPhotoPath().equals("")) {
ImageUtil.replaceWithInitialsView(getContext(), holder.profilePhoto, friendRequest.getInitials());
} else {
Util.aQuery.id(holder.profilePhoto).image(friendRequest.getFullPhotoPath(), false, true, 50, R.drawable.avatar_profile, null, AQuery.FADE_IN);
}
final View finalRootView = rootView;
holder.acceptBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
acceptRequest(position, finalRootView);
}
});
holder.denyBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
denyRequest(position, finalRootView);
}
});
return rootView;
}
public class ViewHolder {
RoundedImageView profilePhoto;
TextView fullName;
ImageView acceptBtn, denyBtn;
ProgressBar loading;
}
}
}
Add a field in your FriendRequest class that saves the current state of the progress bar. based on it set the visibility of the progress bar.
The same view row has been sent to another row. in your getView method you must always set the progress bar visibility based on its status.
Code Sample:
final View finalRootView = rootView;
if (friendRequest.acceptingRequestInProgress())
holder.loading.setVisibility(View.Visibile);
else
holder.loading.setVisibility(View.Gone);
holder.acceptBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
friendRequest.setAcceptingInProgress(true);
acceptRequest(position, finalRootView);
}
});
holder.denyBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
denyRequest(position, finalRootView);
}
});
Another place to modify:
if (apiResponse.isSuccessful()) {
friendRequest.setAcceptingInProgress(false);
friendRequests.remove(position);
mAdapter.notifyDataSetChanged();
}
Note: this is also handles the case when the user scrolls the list
view and the row view in progress is no longer visible. this will
hands the view to another row. But since we check the row state the
progress bar will be stopped. and when user scrolls back to the row
view in progress and hands it a reusable view the progress bar will be
visible again if accepting is still in progress.
Views are getting reused by the ListView and in the getView() method you are not cleaning up the reused view, that's why the progress bar will become visible for an item that shouldn't display it.
Similarly if an item would be removed some items with progress bars visible would loose their progress bar, handing them over to an item that didn't need it.
In getView(), after initializing the holder, you should check if progress bar is necessary.
Start with storing progress bar values at the beginning:
private ArrayList<Integer> progresses = new ArrayList<Integer>();
Update these values every time the list changes (when list changes in loadRequests and when value changes not sure where).
And in getView()
if (progresses.get(position) == 100) {
holder.loading.setVisibility(View.GONE);
} else {
holder.loading.setVisibility(View.VISIBLE);
holder.loading.setProgress(progresses.get(position));
}
The problem is due to visibility of progressbar is VISIBLE default so in getView() after you call notifyDataSetChanged(), the progressbar becomes visible to row position (i - 1).
holder.loading = (ProgressBar) rootView.findViewById(R.id.loading);
holder.loading.setVisibility(View.GONE);
Set progressbar visibility to GONE in getView() and this problem will not come