cleanrun
cleanrun

Reputation: 746

How can i show Material Dialog in a RecyclerView Adapter?

I have a problem with showing a MaterialDialog i've created in my RecyclerView Adapter. I want to show the dialog in the activity the recycler view is in and i have passed the activity context but it always gives me this exception :

com.afollestad.materialdialogs.MaterialDialog$DialogException: Bad window token, you cannot show a dialog before an Activity is created or after it's hidden.

Here is my RecyclerView Adapter :

public class AssetsAdapter extends RecyclerView.Adapter<AssetsAdapter.ItemHolder>{
    private static final String TAG = "AssetsAdapter";

    private ArrayList<Asset> listData;
    private Context activityContext;

    private MaterialDialog dialog;

    public AssetsAdapter(ArrayList<Asset> listData, Context activityContext, UserService userService) {
        this.listData = listData;
        this.activityContext = activityContext;
    }

    public AssetsAdapter(Context activityContext) {
        this.activityContext = activityContext;
    }

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

    @Override
    public void onBindViewHolder(@NonNull ItemHolder holder, final int position) {

        holder.tv_name.setText(listData.get(position).getName());
        holder.tv_manufacturer.setText(listData.get(position).getAsset_id());
        holder.tv_quantity.setText(listData.get(position).getPurchase_cost());


        holder.btn_delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showCreateDialog(position);
            }
        });

        holder.btn_edit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(activityContext, "Edit", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        if(listData.isEmpty()) return 0;
        else return listData.size();
    }

    public class ItemHolder extends RecyclerView.ViewHolder{
        private ImageView iv_asset;

        private TextView tv_name;
        private TextView tv_manufacturer;
        private TextView tv_quantity;
        private TextView tv_status;

        private ImageButton btn_edit;
        private ImageButton btn_delete;

        public ItemHolder(View itemView) {
            super(itemView);

            iv_asset = itemView.findViewById(R.id.iv_asset);

            tv_name = itemView.findViewById(R.id.tv_name);
            tv_manufacturer = itemView.findViewById(R.id.tv_manufacturer);
            tv_quantity = itemView.findViewById(R.id.tv_quantity);
            tv_status = itemView.findViewById(R.id.tv_status);

            btn_edit = itemView.findViewById(R.id.btn_edit);
            btn_delete = itemView.findViewById(R.id.btn_delete);
        }
    }

    private void showCreateDialog(final int id){
        Log.i(TAG, "showCreateDialog: called");

        MaterialDialog.Builder builder = new MaterialDialog.Builder(activityContext)
                .content("Are you sure you want to delete this data?")
                .contentGravity(GravityEnum.CENTER)
                .autoDismiss(true)
                .positiveText("Yes")
                .negativeText("No")
                .onPositive(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                        //deleteAsset(id);
                    }
                })
                .onNegative(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                        // do nothing
                    }
                })
                .canceledOnTouchOutside(true);

        dialog = builder.build();
        dialog.show();
    }
}

I think i have passed the context correctly since i have no problem of showing toasts. So i'm not sure what i did wrong. Any answer would be appreciated, thank you.

Upvotes: 1

Views: 1064

Answers (3)

Madi
Madi

Reputation: 1855

Because you're using dialog view inside your adapter. Don't made actions inside adapter like clickListeners. Use interface to do click actions.

Inside adapter add click listener

  var mItemClickListener: MyCallback? = null
  fun setOnClickListener(click: MyCallback) {
    mItemClickListener = click
  }

MyCallback

public interface MyCallback {
    void onListClick(int position, Object _list);
}

Click actions inside adapter

holder.btn_delete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
             mItemClickListener.onListClick(getAdapterPosition(), yourItemModel)
        }
    });

Activity/Fragment

MyAdapter myAdapter = new MyAdapter()
recyclerView.setAdapter(myAdapter)

myAdapter.setOnClickListener(new MyCallback(){

    @Override
    public void onListClick(Int position, Item yourItem) {
        //here you can show dialog
        showDialog()
    }
})

Hope this help you :)

Upvotes: 1

prashant17
prashant17

Reputation: 1550

You can use EventBus

In your gradle file

dependencies {
      implementation 'org.greenrobot:eventbus:3.1.1'
}

In onBindViewHolder

        holder.btn_delete.setTag(holder);
        holder.btn_delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ItemHolder myItemHolder = (ItemHolder) v.getTag();
                EventBus.getDefault().post(listData.get(myItemHolder.getAdapterPosition()));
            }
        });

In Activity

    @Override
    protected void onStart () {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop () {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void deleteAssetEvent (Asset asset){
        //Call your dialog here
    }

Upvotes: 1

Surbhi Aggarwal
Surbhi Aggarwal

Reputation: 907

Use the interface like this in your adapter

public class AssetsAdapter extends RecyclerView.Adapter<AssetsAdapter.ItemHolder>{
    private static final String TAG = "AssetsAdapter";
private OnButtonClickListener listener;
...........
..........
.........
public AssetsAdapter(ArrayList<Asset> listData, Context activityContext, UserService userService,OnButtonClickListener  listener) {
        this.listData = listData;
        this.activityContext = activityContext;
        this.listener = listener;
    }
.......
......
...
..

 @Override
    public void onBindViewHolder(@NonNull ItemHolder holder, final int position) {

        holder.tv_name.setText(listData.get(position).getName());
        holder.tv_manufacturer.setText(listData.get(position).getAsset_id());
        holder.tv_quantity.setText(listData.get(position).getPurchase_cost());


        holder.btn_delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                listener.showCreateDialog();
            }
        });

        holder.btn_edit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(activityContext, "Edit", Toast.LENGTH_SHORT).show();
            }
        });
    }

Create the interface like

public interface OnButtonClickListener {
    void showCreateDialog();
}

Implement the interface in your activity and create the material dialog in the implementation of that method.

Upvotes: 2

Related Questions