Reputation: 9067
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;
}
}
}
Upvotes: 1
Views: 347
Reputation: 24195
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.
Upvotes: 1
Reputation: 2578
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));
}
Upvotes: 1
Reputation: 8508
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
Upvotes: -1