1

I have an activity that displays user messages from the DB in a ListView. The ListView shows various info about the message correctly and each row in the listView has an onClickListener that launches another Activity to show further info about the message.

All this works fine. I have added a CheckBox to the listrow. The CheckBox is checked to mark that a message has been selected for deletion. Each message has a unique guid identifier that is stored in the DB an so is available through the cursor and adapter in the view. When a row is clicked then this quid is passed via the onclicklistener to the next Activity.

All this works fine. I've now added a onCheckedChanged listener to the checkbox. When i check the checkbox i want to get this unique guid identifier from the cursor in the getView.

When the checkbox has it's onCheckedChanged listener set, it uses an annonymous inner class and this is why the cursor nedds to be final.

Unfortunately this means if there are 5 messages in the DB and i check all to be deleted, then all have the same guid because once it is set to the first value it can't be changed(being final).

I understand why it has to be final as if the annonymous inner class lasts longer than the place where the cursor was created then the listener will have references to objects that don't exist.

link

How can i solve this? Basically how can i make the data in the cursor available inside the checkbox's onCheckedChanged method without making the cursor final?

Thanks in advance

public class ViewMessagesActivity extends Activity{

    private static final String TAG = ViewMessagesActivity.class.getSimpleName(); 
    final String               BACKPRESS_ACTION = "com.carefreegroup.rr3.BACKPRESS_ACTION";

    NfcScannerApplication nfcscannerapplication;
    Cursor cursorMessages;
    ListView listView;
    SimpleCursorAdapter adapter;
    MyAdapter myAdapter;
    TextView noMessages;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.viewmessages);

        nfcscannerapplication = (NfcScannerApplication) getApplication();





            //transactionCount = (TextView)findViewById(R.id.textviewtransactionsfordaycount);
            listView = (ListView) findViewById(R.id.listviewmessages);

            noMessages = (TextView)findViewById(R.id.textviewnomessageslabel);

            // get data
            cursorMessages = nfcscannerapplication.loginValidate.queryAllFromMessage();
            cursorMessages.moveToLast();
            startManagingCursor(cursorMessages);

            // setup adapter and show the data





            if(cursorMessages.getCount() == 0){


                noMessages.setVisibility(View.VISIBLE);
                listView.setVisibility(View.GONE);

            }else{

                listView.setVisibility(View.VISIBLE);
                noMessages.setVisibility(View.GONE);

            }




            String[] from = { 
                    LoginValidate.C_MESSAGE_CREATED_AT, LoginValidate.C_MESSAGE_TEXT, LoginValidate.C_MESSAGE_SENDER};
            int[] to = { R.id.messagecreatedat, R.id.messagetext, R.id.messagesender};

            myAdapter = new MyAdapter(nfcscannerapplication, R.layout.rowmessages, cursorMessages, from, to);
            listView.setAdapter(myAdapter);
            listView.setOnItemClickListener(myAdapter);



    }//end of onCreate







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


    }




private class MyAdapter extends SimpleCursorAdapter implements OnItemClickListener {

        public MyAdapter(Context context, int layout, Cursor c, String[] from,
                int[] to) {
            super(context, layout, c, from, to);


        }

        @Override
        public
        View getView(int position, View convertView, ViewGroup parent) {
            Log.e(TAG, "inside myadapter getview for messages");
            View v = super.getView(position, convertView, parent);
            if(v == null)
                return null;

            Cursor c = (Cursor)getItem(position);

            v.setTag(c);








            String messageSender = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_SENDER));
            String isRepliedTo = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_REPLIED));
            String isStandAlone = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_IS_STANDALONE));




            ((TextView)v.findViewById(R.id.messagecreatedat)).setText(formattedMessCreatedAt );
            ((TextView)v.findViewById(R.id.messagetext)).setText(messageText);
            ((TextView)v.findViewById(R.id.messagesender)).setText(messageSender);

            //#003F87 = blue

            ((TextView)v.findViewById(R.id.messagecreatedat)).setTextColor(Color.parseColor("#003F87"));
            ((TextView)v.findViewById(R.id.messagesender)).setTextColor(Color.parseColor("#003F87"));
            ((TextView)v.findViewById(R.id.messagetext)).setTextColor(Color.parseColor("#FF0000"));




            CheckBox cb = ((CheckBox)v.findViewById(R.id.list_checkbox));
            cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {

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


                    String messageGuid = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_GUID));

                    if(isChecked == true){

                        Log.e(TAG, "checkBox true and guid = " + messageGuid);
                    }else{
                        Log.e(TAG, "checkBox false and guid = " + messageGuid);
                    }

                }
            });


            return v;
        }

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int pos,
                long id) {

            Cursor itemCursor = (Cursor) view.getTag();

            String messageGuid = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_GUID));



            String messageText = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_TEXT));
            String messageCreatedAt = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_CREATED_AT));
            String messageSender = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_SENDER));
            String messageReplied = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_REPLIED));
            String messageSeen = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_SEEN));
            String isStandAlone = itemCursor.getString(itemCursor.getColumnIndex(LoginValidate.C_MESSAGE_IS_STANDALONE));

            Intent i = new Intent(ViewMessagesActivity.this, ReplyToMessageActivity.class);
            i.putExtra("guid", messageGuid);
            i.putExtra("message", messageText);
            i.putExtra("createdat", messageCreatedAt);
            i.putExtra("sender",  messageSender);
            i.putExtra("messagereplied", messageReplied);
            i.putExtra("messageseen", messageSeen);
            i.putExtra("isstandalone", isStandAlone);

            startActivity(i);



        }



    }// end of adapter




}

.

<?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"
    android:paddingTop="5dp"
    android:paddingBottom="5dp"



   >

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"

    android:background="@drawable/rounded_corners_white"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:layout_marginTop="5dp"
    android:layout_marginBottom="5dp"
    >


    <CheckBox 
    android:id="@+id/list_checkbox"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="false"
    android:focusable="false"
    android:focusableInTouchMode="false"
    android:background="@drawable/checkboxbg"
    android:layout_marginLeft="5px"
    android:layout_marginTop="5px"

    ></CheckBox>


    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:paddingBottom="10dp"
    android:paddingTop="10dp" >



        <TextView
        android:id="@+id/messagecreatedat"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.4"
        android:text="TextView"
         />

        <TextView
        android:id="@+id/messagesenderlabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0.5"
        android:gravity="right"
        android:text="From: "
        android:textColor="#003F87"
         />

        <TextView
        android:id="@+id/messagesender"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="left"
        android:text="TextView"
         />


        </LinearLayout>







    <TextView
        android:id="@+id/messagetext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="TextView"
        android:paddingBottom="10dp"
         />



</LinearLayout>

</RelativeLayout>

[edit1]

01-09 16:35:15.220: E/ViewMessagesActivity(1302): inside myadapter getview for messages
01-09 16:35:15.225: E/ViewMessagesActivity(1302): (Cursor)getItem(position) = android.database.sqlite.SQLiteCursor@4217c718
01-09 16:35:15.230: E/ViewMessagesActivity(1302): inside myadapter getview for messages
01-09 16:35:15.235: E/ViewMessagesActivity(1302): (Cursor)getItem(position) = android.database.sqlite.SQLiteCursor@4217c718
01-09 16:35:15.245: E/ViewMessagesActivity(1302): inside myadapter getview for messages
01-09 16:35:15.250: E/ViewMessagesActivity(1302): (Cursor)getItem(position) = android.database.sqlite.SQLiteCursor@4217c718
Community
  • 1
  • 1
turtleboy
  • 8,210
  • 27
  • 100
  • 199
  • made the cursor global and use boolean variables when needed to do different kind of thing in one cursor variable. – Ranjit Jan 09 '14 at 15:52

1 Answers1

1

You have this

Cursor c = (Cursor)getItem(position);

Then

CheckBox cb = ((CheckBox)v.findViewById(R.id.list_checkbox));
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String messageGuid = c.getString(c.getColumnIndex(LoginValidate.C_MESSAGE_GUID));

Since its annonymous inner class it requires that Cursor be final. Look at the docs

http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html#accessing

To avoid that declare it as a instance variable.

private class MyAdapter extends SimpleCursorAdapter implements OnItemClickListener {
Cursor c;

Then in getView

c = (Cursor)getItem(position);

Also you should consider using a ViewHolder Pattern

http://developer.android.com/training/improving-layouts/smooth-scrolling.html.

Also instead of 2 layouts LinearLayout and RelativeLayout you could use 1 in xml

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • @Ragunandan Thanks for your reply. I don't need the final modifier now but the variable messageGuid in onCheckedChanged is always the same value for each row. Any ideas why? – turtleboy Jan 09 '14 at 16:20
  • @turtleboy you need to consider listview view recycling also. check this if it helps http://stackoverflow.com/questions/18162931/android-get-selected-item-using-checkbox-in-listview-when-i-click-a-button – Raghunandan Jan 09 '14 at 16:27
  • @Ragunandan ok thanks i'll have a look at what you have suggested when i've got it working. I've logged out the cursor with Log.e(TAG, "(Cursor)getItem(position) = " + c); The results i've posted at [edit1]. – turtleboy Jan 09 '14 at 16:39
  • @turtleboy where do you inflate a layout in `getView` i don't see any – Raghunandan Jan 09 '14 at 16:40
  • @Ragunandan sorry what do you mean? All my views get inflated from the v variable, eg CheckBox cb = ((CheckBox)v.findViewById(R.id.list_checkbox)); Sorry if i've missed your point. – turtleboy Jan 09 '14 at 16:45
  • @turtleboy You have `View v = super.getView(position, convertView, parent);` where is the xml layout inflated? `View v = inflater.inflate(R.layout.mylayout,parent,false);` – Raghunandan Jan 09 '14 at 16:47
  • Do you mean the layout for the View(row)? The layout for each row is passed as an argument to the adapter constructor eg myAdapter = new MyAdapter(nfcscannerapplication, R.layout.rowmessages, cursorMessages, from, to); The 2nd arg. – turtleboy Jan 09 '14 at 16:51
  • R.layout.rowmessages is the layout for the row – turtleboy Jan 09 '14 at 16:52