0

I want to show a list of titled checkboxes (planets in this example), and have something happen when I click on one of these checkboxes (a Toast and a Log in this example). To show the checkboxes however, I need to use a custom adapter (everything worked fine when I was using a SimpleAdapter), but the OnItemClickListener no longer works. The checkboxes check/uncheck fine, but I can't get the DialogFragment to acknowledge that anything has been clicked.

There's also a search bar, which dynamically filters the list as you type, but I don't think that has an impact as it was working fine with the SimpleAdapter.

Here's some fun code:

MainActivity.java

import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DialogFragment fragment = SearchDialogFragment.newInstance();
        fragment.show(getSupportFragmentManager(), "SearchDialog");
    }
}

SearchDialogFragment.java

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SearchDialogFragment extends DialogFragment
{
    private List<Map<String, Boolean>> planetsList;
    private ListView listView;
    private List<String> selectedPlanets;

    public static SearchDialogFragment newInstance()
    {
        return new SearchDialogFragment();
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.fragment_search_dialog, null);

        builder.setTitle("List of planets");
        builder.setView(view);

        listView = (ListView) view.findViewById(R.id.fragment_list_view);

        selectedPlanets = new ArrayList<>();
        createPlanetsList("");
        resetAdapter();

        /**
         * NOT WORKING
         */
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
        {
            public void onItemClick(AdapterView<?> parentAdapter, View view, int position, long id)
            {
                Toast.makeText(getActivity(), "CLICK!!!!", Toast.LENGTH_SHORT).show();
                Log.e("SDF", "CLICK!!!!");
            }
        });
        /**
         * /NOT WORKING
         */

        SearchView search = (SearchView) view.findViewById(R.id.fragment_search_view);
        search.setQueryHint("Search list");

        // Reacts to the search bar
        search.setOnQueryTextListener(new SearchView.OnQueryTextListener()
        {
            @Override
            public boolean onQueryTextSubmit(String query)
            {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String searchText)
            {
                updateDisplayList(searchText);
                return false;
            }
        });

        return builder.create();
    }

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
    }

    @Override
    public void onDetach()
    {
        super.onDetach();
    }

    private void createPlanetsList(String searchText)
    {
        planetsList = new ArrayList<>();

        addPlanet("Mercury", searchText);
        addPlanet("Venus", searchText);
        addPlanet("Earth", searchText);
        addPlanet("Mars", searchText);
        addPlanet("Jupiter", searchText);
        addPlanet("Saturn", searchText);
        addPlanet("Uranus", searchText);
        addPlanet("Neptune", searchText);
    }

    private void addPlanet(String planetName, String searchText)
    {
        if (planetName.toLowerCase().contains(searchText.toLowerCase()))
        {
            HashMap<String, Boolean> planet = new HashMap<>(1);
            boolean isSelected = selectedPlanets.contains(planetName);
            planet.put(planetName, isSelected);

            planetsList.add(planet);
        }
    }

    private void resetAdapter()
    {
        // It worked when I used this instead of my custom adapter
        /*
        SimpleAdapter simpleAdapter = new SimpleAdapter(
                getActivity(), // Context
                planetsList, // Data list
                android.R.layout.select_dialog_multichoice, // Row layout
                new String[] {"planet"}, // Keys for list
                new int[] {android.R.id.text1}); // View ID used to show the data
        listView.setAdapter(simpleAdapter);
        */

        MultiSelectAdapter adapter = MultiSelectAdapter.getAdapter(getActivity(), planetsList, selectedPlanets);
        listView.setAdapter(adapter);
    }

    private void updateDisplayList(String searchText)
    {
        createPlanetsList(searchText);
        resetAdapter();
    }
}

MultiSelectAdapter.java

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;

import java.util.List;
import java.util.Map;

public class MultiSelectAdapter extends ArrayAdapter
{
    private Context context;
    private List<Map<String, Boolean>> planetsList;

    private static List<String> selectedPlanets;

    public static MultiSelectAdapter getAdapter(Context context, List<Map<String, Boolean>> objects, List<String> dialogSelectedPlanets)
    {
        selectedPlanets = dialogSelectedPlanets;

        return new MultiSelectAdapter(context, android.R.id.text1, objects);
    }

    private MultiSelectAdapter(Context context, int resource, List<Map<String, Boolean>> objects)
    {
        super(context, resource, objects);

        this.context = context;
        this.planetsList = objects;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowView = inflater.inflate(R.layout.fragment_row_layout, parent, false);

        CheckBox checkBox = (CheckBox) rowView.findViewById(R.id.row_check_box);
        String planetName = (String) planetsList.get(position).keySet().toArray()[0];
        checkBox.setText(planetName);

        if (selectedPlanets.contains(planetName))
        {
            checkBox.setChecked(true);
        }

        return rowView;
    }
}

fragment_search_dialog.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="lomax.mysearchinterface.SearchDialogFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <SearchView
        android:id="@+id/fragment_search_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="30dp"
        android:layout_marginRight="30dp"
        android:iconifiedByDefault="false"/>

    <ListView
        android:id="@+id/fragment_list_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>

</LinearLayout>

fragment_row_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <CheckBox
        android:id="@+id/row_check_box"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="true"
        android:text="Planet name"
        android:focusable="false"/>

</LinearLayout>
LomaxOnTheRun
  • 592
  • 1
  • 8
  • 21
  • You said: "The checkboxes check/uncheck fine". I doubt that. Why? Because `if (selectedPlanets.contains(planetName)) { checkBox.setChecked(true); }` has no else. So, when the row views get reused, you may see some row that should not be checked is checked and vice versa. Solution? Supply an else statement calling `checkBox.setChecked(false);`. – Ahmed Khalaf Aug 04 '15 at 16:00
  • For the main problem you're facing, check [this question](http://stackoverflow.com/q/9900913/715593). – Ahmed Khalaf Aug 04 '15 at 16:04
  • @khalafnt Your first comment, while completely correct, is moot, as I need some form of clickListener to work before I can populate selectedPlanets. I've also had a look at the link you posted, and creating a setOnClickListener() on the adapter itself doesn't work either. Neither has setting focusable="false" and focusableInTouchMode="false" in the XML file. – LomaxOnTheRun Aug 04 '15 at 16:21
  • I've changed the checkboxes for textview (with almost no other code changes), and it all works fine. What is it about checkboxes that stops the setOnClickListener() from working on the adapter?!? – LomaxOnTheRun Aug 04 '15 at 16:33
  • `android:layout_width="fill_parent"` try making it `wrap_content`. I mean the checkbox of course. – Ahmed Khalaf Aug 04 '15 at 16:41

1 Answers1

0

After some more rambling through the internet for ideas, I've stumbled across a fix for this problem. In the getView(...) method of my adapter class:

checkBox.setOnCheckedChangeListener(this);

and having my adapter class implement:

implement CompoundButton.OnCheckedChangeListener

This seems to be working, without changing the XML of the row layout or any other clevernesses.

Hope this helps the next poor sod who comes across this problem...

LomaxOnTheRun
  • 592
  • 1
  • 8
  • 21