如何操作 ListView 项目内的视图?

How to manipulate view inside ListView items?

我有一个 ListView,其中有几行包含两个按钮和一个 ProgressBar (Visibility:GONE) 每行。

我的目的是在单击按钮时显示 ProgressBar,并在完成一组特定的后台操作后完全删除该行。
这里的问题是,在从创建 ListViewArrayList 中删除项目并调用 notifyDataSetChanged 后,该行已成功删除,但 ProgressBar 仍然可见。

不应该将其与其父视图一起删除吗?

检查以下记录以查看实际问题。

这是我整个片段的来源:

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;
        }
    }
}

FriendRequest class 中添加一个用于保存进度条当前状态的字段。基于它设置进度条的可见性。

同一视图行已发送到另一行。在您的 getView 方法中,您必须始终根据其状态设置进度条可见性。

代码示例:

        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);
            }
        });

另外修改的地方:

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.

问题是由于 progressbar 的可见性是 VISIBLE 默认值,所以在 getView() 调用 notifyDataSetChanged() 后,进度条对行位置可见(我 - 1).

holder.loading = (ProgressBar) rootView.findViewById(R.id.loading);
holder.loading.setVisibility(View.GONE);

在getView()中设置progressbar visibility为GONE就不会出现这个问题了

视图正在被 ListView 重复使用,而在 getView() 方法中您没有清理重复使用的视图,这就是进度条对于不应显示它的项目变得可见的原因。

类似地,如果一个项目将被删除,一些进度条可见的项目将失去它们的进度条,将它们移交给不需要它的项目。

在getView()中,初始化holder后,需要检查是否需要进度条。

从开始存储进度条值开始:

private ArrayList<Integer> progresses = new ArrayList<Integer>();

每次列表更改时更新这些值(当列表在 loadRequests 中更改时以及值更改时不确定在哪里)。

并在 getView()

if (progresses.get(position) == 100) {
  holder.loading.setVisibility(View.GONE);
} else {
  holder.loading.setVisibility(View.VISIBLE);
  holder.loading.setProgress(progresses.get(position));
}