Search code examples
androidonclicklistenercustom-adapter

Android OnClickListener not working with custom adapter


I've made a custom adapter for a ListView in order to display a String and an ImageButton within the same row. A row is generated every time the add button is clicked. The string is taken from the EditText field, while the ImageButton bin_button is allocated dinamically. Everything works fine except I can't make the bin_buttons fire an OnClick event.

I've tried several solutions. Here bin_button_listener is generated outside the add_button OnClickListener and then set. I've also tried to set listeners with a new OnClickListener every time a new bin_button was created.

Here some code:

Row layout list_string_button_layout.xml:

<TextView
        android:id="@+id/list_item_string"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingStart="8dp"
        android:paddingEnd="0dp"
        android:paddingTop="8dp"
        android:textSize="18sp"
        android:textStyle="bold"/>

<ImageButton
        android:id="@+id/delete_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingTop="10dp"
        android:paddingEnd="5dp"
        android:background="#00ffffff"
        android:src="@drawable/ic_delete_black_18dp"
        android:layout_alignBottom="@+id/list_item_string"
        android:layout_alignParentEnd="true"
        android:focusableInTouchMode="true"
    /> 
</RelativeLayout>

Activity layout activity_search.xml:

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/ingredient_text_input"
    android:hint="@string/start_typing_ingredient"
    android:layout_marginTop="52dp"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    />

<Button
    style="@style/Button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/add_button"
    android:backgroundTint="@color/colorPrimaryLight"
    android:id="@+id/add_button"
    android:layout_alignTop="@+id/ingredient_text_input"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"/>

<ListView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/ingredients_view"
    android:layout_above="@+id/search_button"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_below="@+id/add_button" />

 </RelativeLayout>

Custom Adapter IngredientBinAdapter:

public class IngredientBinAdapter extends BaseAdapter {

private Context context = null;
private List<HashMap<String,ImageButton>> items = new ArrayList<>();



public IngredientBinAdapter(Context context, ArrayList<HashMap<String,ImageButton>> items){
    this.context=context;
    this.items=items;
}

@Override
public int getCount() {
    return items.size();
}

@Override
public Object getItem(int position) {
    return items.get(position);
}

@Override
public long getItemId(int position) {
    return getItem(position).hashCode();
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    if(convertView == null)
        convertView = LayoutInflater.from(context).inflate(R.layout.list_string_button_layout,null);

    HashMap<String,ImageButton> item = (HashMap<String,ImageButton>) getItem(position);
    TextView string = (TextView) convertView.findViewById(R.id.list_item_string);

    String key=(String) item.keySet().toArray()[0];
    string.setText(key);
    item.get(key).setImageResource(R.drawable.ic_delete_black_18dp);


    return convertView;
}
}

Activity:

public class SearchActivity extends AppCompatActivity{

//getting resources and allocating variables
Button add_button = null;
Button search_button = null;
EditText insert_ingredient = null;
ListView ingredient_list_view = null;
ArrayList<HashMap<String,ImageButton>> ingredient_list = new ArrayList<>();
IngredientBinAdapter list_adapter = new IngredientBinAdapter(this,ingredient_list);
ArrayList<String> ingredients_utils = new ArrayList<>();

View.OnClickListener bin_button_listener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        System.out.println("BIN_BUTTON CLICKED");
        ImageButton clicked_button = (ImageButton) v;
        for(Iterator<HashMap<String,ImageButton>> i = ingredient_list.iterator(); i.hasNext(); ){
            HashMap<String,ImageButton> temp= i.next();
            if(temp.values().toArray()[0].equals(clicked_button))
                ingredient_list.remove(temp);
        }
        list_adapter.notifyDataSetChanged();

    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_search);
}

@Override
protected void onStart(){
    super.onStart();

    add_button = (Button) findViewById(R.id.add_button);
    search_button = (Button) findViewById(R.id.search_button2);
    insert_ingredient = (EditText) findViewById(R.id.ingredient_text_input);
    ingredient_list_view = (ListView) findViewById(R.id.ingredients_view);



    ingredient_list_view.setAdapter(list_adapter);

    //add_button listener
    add_button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String current_ingredient_string = insert_ingredient.getText().toString().trim();
            if (!current_ingredient_string.isEmpty() && !ingredients_utils.contains(current_ingredient_string)) {
                HashMap<String, ImageButton> temp = new HashMap<String, ImageButton>();
                ImageButton temp_bin_button = new ImageButton(SearchActivity.this);
                temp_bin_button.setOnClickListener(bin_button_listener);

                temp.put(current_ingredient_string, temp_bin_button);
                ingredient_list.add(temp);

                list_adapter.notifyDataSetChanged();

            }
            ingredients_utils.add(insert_ingredient.getText().toString().trim());
            insert_ingredient.getText().clear();
        }

    });
}
}

Solution

  • first of all

    second

    • u should in your method in adapter :

      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
      
          // 1. inflate view 
          // 2. find button 
          // 3. assign here listener to bin button 
      
          //  i advice to use convert view pattern 
      
      } 
      

    some hints :

    • create data object describing row item

      public class BinRow {
      
           private String _someString;
           private Drawable _imageDrawable; //  (or resource id name)
      
           public String getString() {
                return _someString;
           }
      
           public Drawable getImage() {
                return _imageDrawable;
          }
      
           public BinRow(String someText, Drawable imageDrawable) {
              _someText = someText; 
              _imageDrawable = imageDrawable;
           }
      
      
      }
      

    here u have adapter example ( u need adjust Album - to your BinRow & layout for row & convert view items in view holder ) - this should show you idea how adapter works :

    public class AlbumAdapter extends BaseAdapter implements IListAdapter<Album> {
    
        private List<Album> _albums;
        private Context _context;
        private LayoutInflater _layoutInflater;
    
        static class ViewHolder {
            ImageView imageView;
            TextView albumName;
    
             public ViewHolder(View v) {
                imageView = (ImageView) v.findViewById(R.id.image);
                albumName = (TextView) v.findViewById(R.id.tvAlbumName);
            }
        }
    
        public AlbumAdapter(List<Album> albums) {
            if(albums == null) { 
               _albums = new ArrasyList<>();
            } else {
               _albums = albums;
            }
        }
    
        @Override
        public int getCount() {
             return _albums.size();
        }
    
        @Override
        public Album getItem(int position) {
              return _albums.get(position);
        }
    
        @Override
        public long getItemId(int position) {
             return position;
        }
    
        @Override
        public View getView(int position, View convertView, final ViewGroup parent) {
    
            final Album album = getItem(position);
    
            if (_context == null) {
                _context = parent.getContext();
            }
    
            if (_layoutInflater == null) {
                _layoutInflater = LayoutInflater.from(_context);
            }
    
            View _view = convertView;
    
            ViewHolder viewHolder;
    
            if (_view == null) {
               _view = _layoutInflater.inflate(R.layout.album_item_layout, parent, false);
                viewHolder = new ViewHolder(_view);
                _view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) _view.getTag();
            }
    
            viewHolder.albumName.setText(album.getAlbumDescription());
    
            // here u can set:  image to image view & listener to image 
            // image view could be a button 
            viewHolder.imageView.setOnClickListener(View.OnClickListener);
    
            return _view;
         }
    
    
    
        @Override
        public void add(Album listElement) {
            _albums.add(listElement);
        }
    
        @Override
        public void addAll(List<Album> listOfElements) {
            _albums.addAll(listOfElements);
        }
    
        @Override
        public void clear() {
            _albums.clear();
        }
    
        @Override
        public void remove(Album listElement) {
            _albums.remove(listElement);
        }
    }
    
    public interface IListAdapter<T> {
        void add(T listElement);
        void addAll(List<T> listOfElements);
        void clear();
        void remove(T listElement);
    }
    
    • above interface serves as helper for adapter list methods