Search code examples
androidviewcursor

android Cursor in View must be final?


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

Solution

  • 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