2

My stack is really overflow, I mean currently my brain can't think of a way to solve the below problem (Although I'm through trial-and-errors many days). All I want to do is a sreen for deletion purpose like (http://asset0.cbsistatic.com/cnwk.1d/i/tim/2010/11/18/Foreman_11654166_1073_com.probosoft.masscontactsdelete0_257x386.png)

I have a layout of a list-view-item like this (log_view.xml):

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

    <TextView
        android:id="@+id/date_view"
        android:gravity="top|left"
        android:textStyle="bold"

        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true" />

    <TextView
        android:id="@+id/value_view"
        android:gravity="top|left"
        android:layout_width="match_parent"
        android:layout_height="match_parent"

        android:layout_below="@+id/date_view"
        android:layout_alignParentLeft="true" />

     <CheckBox 
        android:id="@+id/del_box"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="5dp"
        android:visibility="gone"

        android:focusable="false"
    android:focusableInTouchMode="false" />

</RelativeLayout>

And this is my custom Adapter:

class LogViewAdapter extends ArrayAdapter<Log>  {   
    public ArrayList<Boolean> checkList;
    private int layoutResourceId;
    private List<Log> logList;

    Context context;

    public LogViewAdapter(Context context, int layoutResourceId, List<Log> data) {
        super(context, R.layout.log_view, data);

        this.layoutResourceId = layoutResourceId;
        this.logList = data;
        this.context = context;

        checkList = new ArrayList<Boolean>();
        for (int i = 0; i < logList.size(); i ++)   {
            checkList.add(false);
        }
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        LogHolder logholder = null;

        // Unidentified view            
        if(convertView == null) {
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            convertView = inflater.inflate(layoutResourceId, parent, false);

            // Create a new holder
            logholder = new LogHolder();

            logholder.del_box = (CheckBox)convertView.findViewById(R.id.del_box);
            logholder.del_box.setChecked(false);

            final int iFinal = position;
            logholder.del_box.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    checkList.set(iFinal, buttonView.isChecked());
                }
            });

            logholder.date_view = (TextView)convertView.findViewById(R.id.date_view);
            logholder.value_view = (TextView)convertView.findViewById(R.id.value_view);

            convertView.setTag(logholder);
        }

        // Identified view
        else    {
            logholder = (LogHolder) convertView.getTag();
        }

        if (null != logList)    {
            if (null == checkList.get(position))    {
                android.util.Log.e("getView", "Checklist null at " + position);
            }

            //logholder.del_box.setChecked((boolean)checkList.get(position));
            logholder.date_view.setText(logList.get(position).date);
            logholder.value_view.setText(logList.get(position).value);
        }

        android.util.Log.e("getView", " at " + position + ", value = " + logholder.value_view.getText().toString());
        return convertView;
    }

    static class LogHolder  {
        TextView date_view;
        TextView value_view;
        CheckBox del_box;
    }
}

Here 's my Activity:

public class LoggingActivity extends Activity   {

    //private EditText edt_name;
    //private EditText edt_value;
    private List<Log> logList;
    private LogViewAdapter lw_adapter;
    private ListView listView;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.log_list);
        //setContentView(android.R.layout.simple_list_item_multiple_choice);

        logList = loadLog();
        listView = (ListView)findViewById(R.id.log_list);

        lw_adapter = new LogViewAdapter(this, R.layout.log_view, logList);
        //lw_adapter = new LogViewAdapter(this, android.R.layout.simple_list_item_multiple_choice, logList);

        listView.setAdapter(lw_adapter);
    }

    /** implements for the MENU soft-key. Android 2.3.x */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.layout.log_menu, menu);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // IMPORTANT: listView.getChildAt(index) will only return the
        // VISIBLE views in our current screen
        if (null == listView)   {
            android.util.Log.e("onOptionsItemSelected", "list view null");
            return true;
        }

        for (int i = 0; i < listView.getChildCount(); i++) {
            if (null == listView.getChildAt(i)) continue;

            CheckBox cb = (CheckBox) listView.getChildAt(i).findViewById(R.id.del_box);
            if (null == cb) {
                android.util.Log.e("CB", "CheckBox at i = " + i + " null");
                return true;
            }

            final int iFinal = i + listView.getFirstVisiblePosition();
            cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    listView.setItemChecked(iFinal, buttonView.isChecked());
                    lw_adapter.checkList.set(iFinal, buttonView.isChecked());
                }
            });

            SparseBooleanArray selectedItems = listView.getCheckedItemPositions();

            for (int j = 0; j < selectedItems.size(); j++) {
                if (selectedItems.get(j)) {
                    android.util.Log.e("CHECKED", "Row "+ j + ", value = " + logList.get(j).value);
                }

                else    {
                    android.util.Log.e("CHECKED", "Row "+ j + " not checked");
                }
            }

            cb.setVisibility(View.VISIBLE);
            cb.setEnabled(true);
        }

        File folder = new File(Environment.getExternalStorageDirectory(), "50802566/logs");

        // Handle item selection
        switch (item.getItemId()) {
            case R.id.log_sort:
                Collections.sort(logList, Log.COMPARE_BY_VALUE);
                lw_adapter.notifyDataSetChanged();

                return true;
            case R.id.log_del:
                for (int i = 0; i < logList.size(); i++) {
                    if (null == lw_adapter.checkList.get(i))    {
                        android.util.Log.e("DEL", "checklist["+ i + "] = null");
                        continue;
                    }

                    if (true == lw_adapter.checkList.get(i)) {
                        // Delete correlative xml file
                        Date date = null;
                        try {
                            date = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss").parse(logList.get(i).date);
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }

                        String filename = new SimpleDateFormat("dd_MM_yyyy - HH_mm_ss").format(date) + ".xml";
                        File toDelete = new File(folder, filename);
                        if (!toDelete.delete()) {
                            android.util.Log.e("DEL", "File " + filename + ": not deleted or not existed");
                        }

                        // Update the UI
                        //lw_adapter.remove(logList.get(i));
                        //lw_adapter.checkList.remove(i);

                        logList.set(i, null);
                        lw_adapter.checkList.set(i, null);
                        android.util.Log.e("DEL", "Deleted " + i + ", size now = " + logList.size());
                    }  
                }

                for (int i = 0; i < logList.size(); i ++)   {
                    if (null == logList.get(i)) {
                        logList.remove(i);
                        lw_adapter.checkList.remove(i);
                    }
                }

                lw_adapter.notifyDataSetChanged();

                // Refresh the checklist
                for (int i = 0; i < lw_adapter.checkList.size(); i ++)  {
                    lw_adapter.checkList.set(i, false);
                }

                for (int i = 0; i < listView.getChildCount(); i++) {
                    if (null == listView.getChildAt(i)) continue;
                    CheckBox cb = (CheckBox) listView.getChildAt(i).findViewById(R.id.del_box);
                    cb.setChecked(false);
                }

                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private ArrayList<Log> loadLog()    {
        /** Access folder "my_logs" in external memory */
        File baseDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
                                File.separator +
                                "50802566/logs");
        if (!baseDir.exists())  {   baseDir.mkdirs();   }
        File[] list_file = baseDir.listFiles();

        /** For internal memory */
        //File[] list_file = this.getFilesDir().listFiles();

        for (int i = 0; i < list_file.length; i ++) {
            //android.util.Log.e("PRINTLN", list_file[i].getPath());
        }

        int l = list_file.length;

        ArrayList<Log> list = new ArrayList<Log>();

        while (l-- > 0) {
            File file = list_file[l];

            FileInputStream fin = null;
            LogReader logr = new LogReader();

            try {
                fin = new FileInputStream(file);
                list.add(list_file.length - l - 1, logr.read(fin));
                fin.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return list;
    }
}

The problems are:

  1. I can't keep track of the boolean values of each CheckBox. For example if I sort the logList (my database) then "notifyDataSetChanged" to the listView, but my checkList (which contains booleans of CheckBoxes) is still the same, and strange things happen. How can I apply EXACTLY all the sorting-operation of one list to another one? (IMO, "synchronized sorting" or something like that...)

  2. As you can see, I've try to solve #1 by getting the del_box (my CheckBox id) from its row, then set its "OnCheckedChangeListener" to do the assigning (once checked, it will update relative value in the boolean List "checkList"). But strange things happen again:

    • When checked a row and scroll down, I see another one is (automatically) checked.
    • When scrolling back, the previous checked one... is unchecked (automatically, of course).

Maybe the Adapter is re-using the wrong Views/values but I have no clue of how to make it do things right... >"<

Your helps and guides are great. Sample codes of yours will be more than perfect to me... (I'm really exhausted right now >"<)

UPDATE:

I've find a way to (somehow) solve it, but maybe it's against API behavior of Android... If you have safer solution, plz let me know ^^

As mentioned in my second comment, here's the code:

@Override
    public void onCheckedChanged(CompoundButton cb, boolean isChecked) {
        int graphicalPos = -1;

        // This is top view on the screen for sure
        if (((View)cb.getParent()).getTop() < 0)    {
            graphicalPos = 0;
        }

        else if (((View)cb.getParent()).getTop() == 0)  {
            graphicalPos = (int) (((View)cb.getParent()).getTop() / ((View)cb.getParent()).getHeight());
        }

        else if (((View)cb.getParent()).getTop() > 0)   {
            graphicalPos = (int) (((View)cb.getParent()).getTop() / ((View)cb.getParent()).getHeight());
            if (listView.getChildAt(0).getTop() < 0)    {
                graphicalPos = graphicalPos + 1;
            }

            else    {
                android.util.Log.e("NOT PLUS", "First Top = " + listView.getChildAt(0).getTop());
            }
        }

        int dataPos = graphicalPos + listView.getFirstVisiblePosition();

        lw_adapter.checkList.set(dataPos, isChecked);
        android.util.Log.e("onCheckedChanged", "Checked row " + dataPos + ", graphicalPos = " + graphicalPos + ", top = " + ((View)cb.getParent()).getTop() + ", firstVisible = " + listView.getFirstVisiblePosition());
    }
Obelisk
  • 23
  • 5
  • My post for [Unable to check/uncheck CheckedTextView inside getView](http://stackoverflow.com/questions/12641529/unable-to-check-uncheck-checkedtextview-inside-getview/12642050#12642050) might be helpful. – Akhilesh Mani Mar 22 '13 at 09:01
  • Thank you! I'm digging it ^^ – Obelisk Mar 22 '13 at 10:04
  • tell me if you found trouble in digging it :-D – Akhilesh Mani Mar 22 '13 at 11:02
  • Your answer is quite useful, indeed. It gave me good understanding of how tagging work. But, still, doesn't solve the trick. This http://stackoverflow.com/questions/14638717/dynamic-change-on-listview-row-layout-affects-other-rows-too explained my question in a clearer way. According to comment of Nathan, it's a trade-off between performance and tracking. But I've come up with a new way: once a CheckBox is checked, I'll calculate the REAL position of its row in screen, and since that position is the same in my database (give or take some offsets), I can set the relative Boolean accurately. – Obelisk Mar 22 '13 at 21:30

0 Answers0