简体   繁体   中英

Android - ArrayAdapter: set TextView to value from SQLiteDatabase

What I want to accomplish:

I want one TextView per row of a ListView to be dynamically set (its display text) to the value from a SQLite database.

What I tried:

Inside getView() , I assigned the said TextView via findViewById() to a global variable. Then I assigned the value position (from the parameter of getView() ) to my global variable mListRowPosition . After this, I execute my AsyncTask sub-class via new AttemptGetCustomerInfo().execute() .

My AsyncTask sub-class gets the SQLiteDatabase from my DatabaseHelper . With the mListRowPosition it receives the customer_id from the method getCustomerID() of the Service object inside the ArrayList<Service> dataset.

Together with the customer_id , it builds a SQL query to get the shortname of the customer. After querying, it gets the String shortname from the Cursor. I then setText(shortname) the global TextView from before (inside getView() ).

The problems with that:

This 'kind of works' at some point, but it seems to be so slow that only the last (almost every time) has a value set as its text. Sometimes it also gets it wrong.

After debug-logging I saw that getView() gets called a lot faster than the AsyncTask is even finished (this makes sense, but it destroys my solution for my problem).

Also interesting: My debug log tells me that getView() gets called more often then there are data entries inside the ArrayList. If there are 5 entries, it will call getView() about 15 to 20 times. Why?

The code behind that:

public class ServiceAdapter extends ArrayAdapter<Service> {

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Get the data item for this position
        Service service = getItem(position);
        // Check if an existing view is being reused, otherwise inflate the view
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(
                    R.layout.listview_row, parent, false);
        }
        // Lookup view for data population
        TextView quantity = (TextView) convertView
                .findViewById(R.id.QUANTITY_CELL);
        TextView description = (TextView) convertView
                .findViewById(R.id.DESCRIPTION_CELL);
        Button delete = (Button) convertView.findViewById(R.id.BUTTON_DELETE);
        customerView = (TextView) convertView.findViewById(R.id.CUSTOMER_VIEW);
        mListRowPosition = position;
        Log.d("ServiceAdapter", "getView changed mListRowPositon to be "
                + String.valueOf(mListRowPosition));
        new AttemptGetCustomerInfo().execute();

        // Populate the data into the template view using the data object
        quantity.setText(String.valueOf(service.getQuantity()));
        description.setText(service.getDescription());

        // Set up the listener for the delete button.

        final int pos = position;
        delete.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                showDialog("deleteButton", pos);
            }
        });

        customerView.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                showDialog("customerView", pos);
            }
        });

        // Return the completed view to render on screen
        return convertView;
    }


class AttemptGetCustomerInfo extends AsyncTask<String, String, String> {
        String shortname = null;

        @Override
        protected String doInBackground(String... params) {

            DatabaseHelper db_helper = new DatabaseHelper(context);
            SQLiteDatabase db = db_helper.getReadableDatabase();

            Log.d("AttemptGetCustomerInfo",
                    "ListRowPos: " + String.valueOf(mListRowPosition));
            Log.d("AttemptGetCustomerInfo", "description of ListRowPos: "
                    + services.get(mListRowPosition).getDescription());
            int customer_id = services.get(mListRowPosition).getCustomerID();
            Log.d("AttemptGetCustomerInfo",
                    "customer id: " + String.valueOf(customer_id));
            Cursor c = db.rawQuery(
                    "SELECT " + CustomerEntry.COLUMN_NAME_SHORTNAME + " FROM "
                            + CustomerEntry.TABLE_NAME + " WHERE "
                            + CustomerEntry.COLUMN_NAME_CUSTOMER_ID + "="
                            + customer_id, null);
            Log.d("AttemptGetCustomerInfo",
                    "available cursor size" + String.valueOf(c.getCount()));
            if (c.getCount() == 0) {
                Log.d("AttemptGetCustomerInfo", "There are no Cursor entries!");
                return null;
            }
            c.moveToFirst();
            shortname = c
                    .getString(c
                            .getColumnIndexOrThrow(CustomerEntry.COLUMN_NAME_SHORTNAME));

            db.close();

            return null;
        }

        @Override
        protected void onPostExecute(String s) {
            if (shortname != null) {
                customerView.setText(shortname);
            }
        }
    }

Additional info:

I didn't paste all of the code and a lot of code refering happens inside the above code to which no code is there also. I hope the function of my methods not shown are understandable via the method name.

So, lets begin.

If you want to display information from your database in your Listview I strongly recommend you using CursorAdapter instead of ArrayAdapter it will work far faster than it is probably now.

About the getView() calling, that happens because of the way Android paint listviews, the first time Android will call getView() several times in order to display properly things, if you change for example your ListView height from match_parent to wrap_content or viceversa you will notice that your getView() method will get called a different number of times.


Now about your code, you are not properly programming your getView() method. Inside the first part if (convertView == null) you should define your views using the ViewHolder pattern which will improve your performance.

Another problem I find here is that you are launching your AsyncTask everytime you your getView() method is called, that will produce a problem with your ListView scrolling as it will prevent of going smoothly (In a tablet for example you will run like 40 or 50 asynctask one after another, and that is a heavy workload).

If you want to keep your current code which I would strongly discourage you to do it, you will need a way of controlling if the current row have been executed your AsyncTask code in order to not repeat that work.

Hope it helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM