0

I'm populating RecyclerView with AppWidgetHostViews. They are sorted one below the other and each one of them has delete button next to it. This is how I'm setting the RecyclerView and Adapter (event.object is a AppWidgetHostView):

        private List<View> viewList = new ArrayList<>();

        mAdapter = new AppWidgetAdapter(this);
        mRecycler.setLayoutManager(new LinearLayoutManager(getActivity()));
        mRecycler.setItemAnimator(new DefaultItemAnimator());
        mRecycler.setAdapter(mAdapter);
        viewList.add((View) event.eventObject);
        mAdapter.setData(viewList);

This is my Adapter that holds AppWidgetHostViews:

public class AppWidgetAdapter extends RecyclerView.Adapter<AppWidgetAdapter.AppWidgetHolder> {

private List<View> viewList = new ArrayList<>();
private AdapterListener listener;

public AppWidgetAdapter(AdapterListener listener) {
    this.listener = listener;
}

public void setData(List<View> list) {
    viewList.clear();
    viewList.addAll(list);
    notifyDataSetChanged();
}

@Override
public AppWidgetHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.app_widget_layout, parent, false);
    return new AppWidgetHolder(view);
}

@Override
public void onBindViewHolder(final AppWidgetHolder holder, int position) {
    final View view = getItem(position);
    holder.setView(view);
    holder.setNum(String.valueOf(position));

    holder.button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int pos = holder.getAdapterPosition();
            viewList.remove(pos);
            notifyItemRemoved(pos);
            notifyItemRangeRemoved(pos, viewList.size());
            listener.onItemRemoved(pos);
        }
    });
}

private View getItem(int position) {
    return !viewList.isEmpty() ? viewList.get(position) : null;
}

@Override
public int getItemCount() {
    return viewList.size();
}

class AppWidgetHolder extends RecyclerView.ViewHolder {

    private FrameLayout view;
    private Button button;

    AppWidgetHolder(View itemView) {
        super(itemView);
        view = (FrameLayout) itemView.findViewById(R.id.app_widget);
        button = (Button) itemView.findViewById(R.id.delete);

    }

    private void setView (View view) {
        if(view.getParent() != null) {
            ((ViewGroup)view.getParent()).removeView(view);
        }
        this.view.addView(view);
    }

    private void setNum (String num) {
        this.button.setText(num);
    }

}

}

XML of my ViewHolder looks like this:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="20dp">

<Button
    android:layout_centerInParent="true"
    android:id="@+id/delete"
    android:layout_width="50dp"
    android:layout_alignParentEnd="true"
    android:layout_height="wrap_content"
    android:layout_marginLeft="5dp"
    tools:text="Del" />

<FrameLayout
    android:layout_toStartOf="@id/delete"
    android:layout_centerHorizontal="true"
    android:layout_centerInParent="true"
    android:id="@+id/app_widget"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentStart="true" />

So inside onBindViewHolder method, I'm setting onClick listener which should remove AppWidgetHostView in that position.. So after notifying item removed, I'm calling listener.onItemRemoved(pos), and it's implementation in fragment looks like this:

   @Override
public void onItemRemoved(int position) {
    viewList.remove(position);
    mAdapter.setData(viewList);
} 

So it again sets the data for adapter and notifies data set changed.

But the problem is, items aren't properly removed when I click delete button. ViewList resizes ok, but items behave strangely, if I add 3 items and delete second one for example, it stays on the screen but third goes on top of it (second still visible), then, if I delete first one, it stays, previous second one is now deleted and new second one goes on top of first one. But if I click delete button for every item, it will delete all the views, but they will behave strangely in that process... Any advice, hint?

joe
  • 1,341
  • 4
  • 21
  • 32
  • You are removing the item in `onClick()` and then again removing it in `onItemRemoved()`.remove call to `onItemRemoved()` in `onClick()`. – Adithya Jul 11 '17 at 08:45
  • First I'm removing it inside adapter and then I'm calling onItemRemoved, to remove the item in fragment's view list that I'm passing to adapter in setData method. For example if I remove item in adapter and Not call remove in fragment and then I add new item, it will pass the list with deleted item back to adapter. – joe Jul 11 '17 at 08:53
  • no need to do that.both the lists will have same reference.[See this](https://stackoverflow.com/questions/11397008/does-a-list-object-get-passed-by-reference). – Adithya Jul 11 '17 at 09:01
  • so should I just call onItemRemoved() inside onClick method? And then in it's implementation remove item in view list in that position and call adapter.notifyItemRemoved and notifyItemRangeRemoved? – joe Jul 11 '17 at 09:17

3 Answers3

4

try only adding follow code in onBindViewHolder:

holder.button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        viewList.remove(position);
        notifyItemRemoved(position);
        //notifyDataSetChanged();
    }
});
Arpit J.
  • 1,108
  • 12
  • 20
Shweta Chauhan
  • 6,739
  • 6
  • 37
  • 57
  • 1
    Unfortunately didn't help :/ I've read that you should always use notifyItemRemoved after removing it from list and also notifyItemRangeRemoved after that.. But every time, if I have 3 items and delete second one, it stays on the screen, adapter updates, but item that was third, goes up and cover previous second one that is still visible... – joe Jul 11 '17 at 08:55
2

Using both @Adithya and @Stanislav Bondar's advices, I came up to solution:

This is the onBindViewHolder method:

  @Override
public void onBindViewHolder(final AppWidgetHolder holder, final int position) {
    final View view = getItem(position);
    holder.setView(view);
    holder.setNum(String.valueOf(position));

    holder.button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            int pos = holder.getAdapterPosition();
            viewList.remove(pos);
            listener.onItemRemoved(pos);
        }
    });
}

And inside onItemRemoved:

@Override
public void onItemRemoved(int position) {
    viewList.remove(position);
    mAdapter.notifyItemRemoved(position);
}

I had to remove the item at that position both inside adapter and fragment, because if I had 5 items in fragment which i pass to adapter and if I remove it only in adapter, once I pass new item again, it will send 6 items instead 5.. Maybe there is a better way, but this did solution resolved the problem

joe
  • 1,341
  • 4
  • 21
  • 32
  • I am struggling to understand why `final int position` in `onBindViewHolder` is different from `holder.getAdapterPosition();`. While I was using `final int position`, items were been removed in a wrong way. `holder.getAdapterPosition();` always gets the correct item position. – Aliton Oliveira Nov 05 '19 at 00:21
1

You don't have to call setData(List<View> list) when removing item. You called twice viewList.remove(pos); first time in onBindViewHolder and second in onItemRemoved(int position)

Stanislav Bondar
  • 6,056
  • 2
  • 34
  • 46
  • First I'm removing it inside adapter and then I'm calling onItemRemoved, to remove the item in fragment's view list that I'm passing to adapter in setData method. For example if I remove item in adapter and Not call remove in fragment and then I add new item, it will pass the list with deleted item back to adapter. – joe Jul 11 '17 at 08:53
  • @joe You don't need to clear `viewList` when item removed. Use `mAdapter.notifyItemRemoved(pos);` instead `mAdapter.setData(viewList);` – Stanislav Bondar Jul 11 '17 at 09:01
  • So can I just call onItemRemoved(int position) from onClick() and inside it's implementation, call mAdapter.notifyItemRemoved(pos);? Or should I also call viewList.remove(position); in onClick() ? – joe Jul 11 '17 at 09:14