Android - ArrayAdapter:将 TextView 设置为 SQLiteDatabase 中的值

Posted

技术标签:

【中文标题】Android - ArrayAdapter:将 TextView 设置为 SQLiteDatabase 中的值【英文标题】:Android - ArrayAdapter: set TextView to value from SQLiteDatabase 【发布时间】:2014-09-13 02:36:42 【问题描述】:

我想要完成的事情:

我希望将 ListView 的每行一个 TextView 动态设置(其显示文本)为 SQLite 数据库中的值。

我尝试了什么:

getView() 中,我通过findViewById() 将上述TextView 分配给了一个全局变量。然后我将值position(来自getView() 的参数)分配给我的全局变量mListRowPosition。之后,我通过new AttemptGetCustomerInfo().execute() 执行我的 AsyncTask 子类。

我的 AsyncTask 子类从我的 DatabaseHelper 获取 SQLiteDatabase。使用mListRowPosition,它从ArrayList<Service> 数据集中的Service 对象的getCustomerID() 方法接收customer_id

customer_id 一起,它构建了一个SQL 查询来获取客户的简称。查询后,从 Cursor 中获取字符串shortname。然后我 setText(shortname) 之前的全局 TextView(在 getView() 内部)。

这样做的问题:

这种“工作”在某些时候,但它似乎太慢了,以至于只有最后一个(几乎每次)都有一个值设置为它的文本。有时它也会出错。

在调试日志记录之后,我看到调用 getView() 的速度比 AsyncTask 完成的速度快得多(这是有道理的,但它破坏了我对问题的解决方案)。

也很有趣:我的调试日志告诉我,getView() 被调用的频率高于 ArrayList 中的数据条目。如果有 5 个条目,它将调用 getView() 大约 15 到 20 次。为什么?

背后的代码:

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);
            
        
    

其他信息:

我没有粘贴所有代码,并且在上面的代码中发生了很多代码引用,也没有代码。我希望通过方法名称可以理解我未显示的方法的功能。

【问题讨论】:

【参考方案1】:

那么,让我们开始吧。

如果您想在Listview 中显示数据库中的信息,我强烈建议您使用CursorAdapter 而不是ArrayAdapter,它的运行速度可能比现在快得多。

关于getView() 调用,这是由于android 绘制列表视图的方式而发生的,Android 第一次会调用getView() 多次以正确显示事物,例如,如果您将ListView 的高度从@ 更改为987654328@ 到 wrap_content 或反之亦然,您会注意到您的 getView() 方法将被调用不同的次数。


现在关于您的代码,您没有正确编程您的getView() 方法。在第一部分if (convertView == null) 中,您应该使用ViewHolder pattern 定义您的视图,这将提高您的性能。

我在这里发现的另一个问题是,每次调用 getView() 方法时,您都会启动您的 AsyncTask,这将导致您的 ListView 滚动出现问题,因为它会阻止顺利进行(在平板电脑中例如,您将一个接一个地运行 40 或 50 个异步任务,这是一个繁重的工作量)。

如果您想保留当前代码(我强烈建议您不要这样做),您将需要一种方法来控制当前行是否已执行您的 AsyncTask 代码,以免重复该工作。

希望对你有帮助

【讨论】:

那么您如何看待整体问题的解决方案?如果我使用 CursorAdapter 会解决我的问题吗? 你会有相当的进步 感谢您的补充。我应用了 ViewHolder 模式并保存了 LayoutInflater 参考以供将来访问。这似乎是一些更好的性能,但它并不能解决一些 TextViews 只是空的而另一个被填充(使用错误的文本)的问题。我很高兴更改我的代码,但除了您告诉我的更改之外,我想不出有什么不同。我需要为每一行运行 AsyncTask。我认为这将是最好的方法,但似乎并不完全如此。您在代码形式上是否有进一步的改进(如果可能)? 如果您事先为您需要的每一行运行该查询并将其与您的Service 对象一起发送怎么办?那会更快 还有一个改进:运行查询后总是关闭游标!否则,会产生内存泄漏。

以上是关于Android - ArrayAdapter:将 TextView 设置为 SQLiteDatabase 中的值的主要内容,如果未能解决你的问题,请参考以下文章

Android之ArrayAdapter使用

Android View 不会通过滑动手势滚动,也不会滚动。将 ListView 与 ArrayAdapter 一起使用

Android:使用 ArrayAdapter 在 ListView 中替换颜色 [重复]

为 Android 排序 ArrayAdapter

Android:如何从 listView 和 arrayAdapter 中删除项目

Android 的 ArrayAdapter - 添加字符串列表视图而不是替换