1

so I'm quite new on android development and I'm here to learn. I have been doing some tutorials online for listviews.

I'm on this one now: http://www.survivingwithandroid.com/2013/02/android-listview-adapter-checkbox-item_7.html

Now, I have followed the tutorial, but I have added a little twist of my own. Instead of doing everything in MainActivity as the tutorial says, I have used a fragment to inflate my views, register for context menu etc. The fragment is an inner class within my MainActivity.class.

I'm at the point of the tutorial where I need to add in a CheckBox in the listview. This seems fairly easy, so I go and implement the OnCheckedChangeListener inside my fragmentclass.

public class MainActivity extends ActionBarActivity  {  
//PlaceholderFragment is an inner class of MainActivity
    public static class PlaceholderFragment extends Fragment implements android.widget.CompoundButton.OnCheckedChangeListener {

            ListView lv;
            public PlaceholderFragment() {
            }

            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // TODO Auto-generated method stub
                 int pos = lv.getPositionForView(buttonView);
                 System.out.println("Pos ["+pos+"]");


         if (pos != ListView.INVALID_POSITION) {
                 Planet p = planetsList.get(pos);         
                 p.setChecked(isChecked);
        }
        }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_main, container,
            false);
    ListView lv = (ListView) rootView.findViewById(R.id.listView);
    aAdpt = new PlanetAdapter(planetsList, getActivity());
    lv.setAdapter(aAdpt);
    registerForContextMenu(lv);
    lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

         public void onItemClick(AdapterView<?> parentAdapter, View view, int position,
                                 long id) {


             Toast.makeText(getActivity(), "Item with id ["+id+"] - Position ["+position+"] - Genre ["+aAdpt.getItem(position).getDescr()+"]", Toast.LENGTH_SHORT).show();
         }
    });
    return rootView;
}

In my PlanetAdapter.class in getView method I have the following code (as per the tutorial):

public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;

    PlanetHolder holder = new PlanetHolder();


    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = inflater.inflate(R.layout.checkbox_layout, parent, false);

        TextView tv = (TextView) v.findViewById(R.id.name);
        TextView distView = (TextView) v.findViewById(R.id.dist);
        CheckBox chk = (CheckBox) v.findViewById(R.id.chk);

        holder.chk = chk;
        holder.planetNameView = tv;
        holder.distView = distView;
        **chk.setOnCheckedChangeListener((MainActivity) context);** 
        v.setTag(holder);
    }
    else 
        holder = (PlanetHolder) v.getTag();

    Planet p = planetList.get(position);
    holder.planetNameView.setText(p.getName());
    holder.distView.setText("" + p.getDescr());
    holder.chk.setChecked(p.getChecked());


    return v;
} 

}

Now, when I try to run the code on Eclipse, there is an error because the listener is no longer in the MainActivity chk.setOnCheckedChangeListener((MainActivity) context); - I had move it to the fragment inner class.

Now, I change the code to chk.setOnCheckedChangeListener((MainActivity.PlaceholderFragment) context); and it says I cannot Cast from Context to MainActivity.PlaceholderFragment.

Does anyone know how I can solve this problem?

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
Simon
  • 19,658
  • 27
  • 149
  • 217

1 Answers1

3

Change this

 chk.setOnCheckedChangeListener((MainActivity) context)

to

 chk.setOnCheckedChangeListener(PlaceholderFragment.this)

Your Fragment implements the interface CompoundButton.OnCheckedChangeListener not your Activity

 public static class PlaceholderFragment extends Fragment implements android.widget.CompoundButton.OnCheckedChangeListener {

Edit:

Seems you have checkbox in Adapter class

So you need your PlanetAdapter to implement CompoundButton.OnCheckedChangeListener

Change to

chk.setOnCheckedChangeListener(PlanetAdapter.this);

And there is no need for Fragment to implement the interface. So change to

PlaceholderFragment extends Fragment

Example:

public class MainActivity extends Activity {

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

        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction()
                    .replace(R.id.container, new PlaceholderFragment()).commit();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }


        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container,
                    false);

               ApplicationInfo applicationInfo = getActivity().getApplicationInfo();
                PackageManager pm = getActivity().getPackageManager();
                List<PackageInfo> pInfo = new ArrayList<PackageInfo>();
                pInfo.addAll(pm.getInstalledPackages(0));
                final AppInfo[] app_info = new AppInfo[pInfo.size()];

                int counter = 0;
                for(PackageInfo item: pInfo){
                    try{

                        applicationInfo = pm.getApplicationInfo(item.packageName, 1);

                        app_info[counter] = new AppInfo(pm.getApplicationIcon(applicationInfo), 
                                String.valueOf(pm.getApplicationLabel(applicationInfo)));

                        System.out.println(counter);

                    }
                    catch(Exception e){
                         e.printStackTrace();
                    }

                    counter++;
                }

            ListView listApplication = (ListView)rootView.findViewById(R.id.listView1);
            final AppInfoAdapter adapter = new AppInfoAdapter(getActivity(), app_info);
            listApplication.setAdapter(adapter);
            Button b= (Button)rootView.findViewById(R.id.button1);
            b.setOnClickListener(new OnClickListener()
            {

                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub

                    StringBuilder result = new StringBuilder();
                    for(int i=0;i<app_info.length;i++)
                    {
                        if(adapter.mCheckStates.get(i)==true)
                        {

                                           result.append(app_info[i].applicationName);
                            result.append("\n");
                        }

                    }
                    Toast.makeText(getActivity(), result, 1000).show();
                }

            });


            return rootView;
        }
    }

}

AppInfo.java

public class AppInfo {
    public Drawable icon;
    public String applicationName;

    public AppInfo(){
        super();
    }

    public AppInfo(Drawable icon, String applicationName){
        super();
        this.icon = icon;
        this.applicationName = applicationName;
    }


}

AppInfoAdapter.java

public class AppInfoAdapter extends ArrayAdapter<AppInfo> implements CompoundButton.OnCheckedChangeListener
{  SparseBooleanArray mCheckStates; 

    Context context;
    AppInfo  data[] = null;
    LayoutInflater mInflater;
    public AppInfoAdapter(Context context, AppInfo[] data){
        super(context, 0,data);
        this.context = context;
        this.data = data;
        mInflater = LayoutInflater.from(context);
        mCheckStates = new SparseBooleanArray(data.length);
    }

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

        View row = convertView;
        AppInfoHolder holder= null;

        if (row == null){
            row = mInflater.inflate(R.layout.list_item, parent, false);

            holder = new AppInfoHolder();

            holder.imgIcon = (ImageView) row.findViewById(R.id.imageView1);
            holder.txtTitle = (TextView) row.findViewById(R.id.textView1);
            holder.chkSelect = (CheckBox) row.findViewById(R.id.checkBox1);

            row.setTag(holder);

        }
        else{
            holder = (AppInfoHolder)row.getTag();
        }


        AppInfo appinfo = data[position];
        holder.txtTitle.setText(appinfo.applicationName);
        holder.imgIcon.setImageDrawable(appinfo.icon);
       // holder.chkSelect.setChecked(true);
        holder.chkSelect.setTag(position);
        holder.chkSelect.setChecked(mCheckStates.get(position, false));
        holder.chkSelect.setOnCheckedChangeListener(this);
        return row;

    }
    public boolean isChecked(int position) {
        return mCheckStates.get(position, false);
    }

    public void setChecked(int position, boolean isChecked) {
        mCheckStates.put(position, isChecked);

    }

    public void toggle(int position) {
        setChecked(position, !isChecked(position));

    }
@Override
public void onCheckedChanged(CompoundButton buttonView,
        boolean isChecked) {

     mCheckStates.put((Integer) buttonView.getTag(), isChecked);    

}
static class AppInfoHolder
{
    ImageView imgIcon;
    TextView txtTitle;
    CheckBox chkSelect;

}
}

fragment_main.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.listviewcheckbox.MainActivity$PlaceholderFragment" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Button" />

    <ListView
        android:id="@+id/listView1"
        android:layout_above="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="16dp" >
    </ListView>

</RelativeLayout>

list_item.xml

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

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="34dp"
        android:layout_marginTop="21dp"
        android:text="TextView" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="32dp"
        android:src="@drawable/ic_launcher" />

    <CheckBox
        android:id="@+id/checkBox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/textView1"
        android:layout_marginRight="14dp"
        android:text="CheckBox" />

</RelativeLayout>

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.listviewcheckbox.MainActivity"
    tools:ignore="MergeRootFrame" />

Snap

enter image description here

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • I tried that... but now the error says: No enclosing instance of the type MainActivity.PlaceholderFragment is accessible in scope – Simon Mar 30 '14 at 09:38
  • Hi Raghunandan, do you mean you want to see the updated code for the setOnCheckedChangeListener? I did as you wrote above:chk.setOnCheckedChangeListener((PlaceholderFragment.this); – Simon Mar 30 '14 at 09:40
  • @Simon your adapter class should implement the interface not fragment. – Raghunandan Mar 30 '14 at 09:41
  • Is there no simpler way to get the context from the Fragment like how the initial code got the context from MainActivity? If I move the implements android.widget.CompoundButton.OnCheckedChangeListener to the Adapter, it would mean that the adapter needs to implement the onCheckedChanged method and since the listview (lv) is within the Fragment view, that would also mean code within the onCreateView needs to move as well as that generates the listview (I have updated the code above for you to see). – Simon Mar 30 '14 at 09:47
  • Nope I did not try to edit your post... must have been someone else. – Simon Mar 30 '14 at 09:48
  • @Simon yes you need to move all those code to your adapter class. It is not something else. I am sure of it – Raghunandan Mar 30 '14 at 09:48
  • @Simon And if you are not sure of it do check my answer to similar question i answered long time back with snap shots. http://stackoverflow.com/questions/18162931/android-get-selected-item-using-checkbox-in-listview-when-i-click-a-button. Even in the linked post adapter implements the interface – Raghunandan Mar 30 '14 at 09:50
  • but is it possible to move all the code from the fragment to the adapter, because if you look above, the lv depends on the rootView that was created through the method onCreateView which is only called from the fragment... – Simon Mar 30 '14 at 09:56
  • @Simon did you even check my post that i linked to. Its possible and the link i posted has a working example too. Given some time i can post an example with fragment too – Raghunandan Mar 30 '14 at 09:56
  • @Simon same example that i linked to with fragments and snap shots with toast. Rest is upto you to fix. My suggestion is right. – Raghunandan Mar 30 '14 at 10:22