1

嘿伙计们,我只是希望有人能阐明这段代码是如何工作的,更具体地说是 simpleCursorAdapter。完整的程序是一个待办事项列表的应用程序,它是一个非常简单的教程,用户可以输入数据或“注释”并使用光标和加载器保存到 sqlite 数据库。

所以我的问题是,有一种特定的方法我无法掌握它的工作原理,因此我无法操纵数据的显示方式。我认为问题在于我只是不明白适配器如何采用与显示的布局不同的布局并将其全部显示在列表视图中。

  private void fillData() {

    // Fields from the database (projection)
    // Must include the _id column for the adapter to work
    String[] from = new String[] { TodoTable.COLUMN_SUMMARY };


    // Fields on the UI to which we map
    int[] to = new int[] { R.id.label }; //I don't quite understand but I know it's just a value for the adapter

    getLoaderManager().initLoader(0, null, this);

    adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from,  
        to, 0); /*This line specifically I don't understand how it is working. 
            R.layout.todo_row is a near blank xml, used when there are no "todos"
            with no listviews.  R.layout.todo_list has the listview's but when 
            assigned in the adapter it doesn't work.


    setListAdapter(adapter);

  }

总体而言,我正在尝试并排制作 3 个列表视图以从数据库中读取数据并随意使用。如果有人可以帮助我,将非常感激,谢谢。

R.layout.todo_row

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="30dp"
        android:layout_height="24dp"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:src="@drawable/reminder" >
    </ImageView>

    <TextView
        android:id="@+id/label"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:lines="1"
        android:text="@+id/TextView01"
        android:textSize="24dp" 
        >
    </TextView>


</LinearLayout> 

和 R.layout.todo_list

<?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:orientation="vertical" >

        <ListView
           android:id="@android:id/list"
            android:layout_width="110dp"
            android:layout_height="200dp" >

        </ListView>

        <ListView
            android:id="@+id/listMiddle"
            android:layout_width="110dp"
            android:layout_height="200dp"
            android:layout_toRightOf="@android:id/list" >
        </ListView>

       <ListView
            android:id="@+id/listRight"
            android:layout_width="110dp"
            android:layout_height="200dp"
            android:layout_toRightOf="@id/listMiddle" >
        </ListView>

    <TextView
        android:id="@android:id/empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/no_todos" />

</RelativeLayout> 

整个班级如下

package de.vogella.android.todos;

import android.app.ListActivity;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import de.vogella.android.todos.contentprovider.MyTodoContentProvider;
import de.vogella.android.todos.database.TodoTable;

/*
 * TodosOverviewActivity displays the existing todo items
 * in a list
 * 
 * You can create new ones via the ActionBar entry "Insert"
 * You can delete existing ones via a long press on the item
 */

public class TodosOverviewActivity extends ListActivity implements
    LoaderManager.LoaderCallbacks<Cursor> {
  private static final int ACTIVITY_CREATE = 0;
  private static final int ACTIVITY_EDIT = 1;
  private static final int DELETE_ID = Menu.FIRST + 1;
  // private Cursor cursor;
  private SimpleCursorAdapter adapter;
  private SimpleCursorAdapter middleAdapter;
  private SimpleCursorAdapter rightAdapter;



/** Called when the activity is first created. */

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.todo_list);
    this.getListView().setDividerHeight(2);
    fillData();
    registerForContextMenu(getListView());
  }

  // Create the menu based on the XML defintion
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.listmenu, menu);
    return true;
  }

  // Reaction to the menu selection
  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.insert:
      createTodo();
      return true;
    }
    return super.onOptionsItemSelected(item);
  }

  @Override
  public boolean onContextItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case DELETE_ID:
      AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
          .getMenuInfo();
      Uri uri = Uri.parse(MyTodoContentProvider.CONTENT_URI + "/"
          + info.id);
      getContentResolver().delete(uri, null, null);
      fillData();
      return true;
    }
    return super.onContextItemSelected(item);
  }

  private void createTodo() {
    Intent i = new Intent(this, TodoDetailActivity.class);
    startActivity(i);
  }

  // Opens the second activity if an entry is clicked
  @Override
  protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    Intent i = new Intent(this, TodoDetailActivity.class);
    Uri todoUri = Uri.parse(MyTodoContentProvider.CONTENT_URI + "/" + id);
    i.putExtra(MyTodoContentProvider.CONTENT_ITEM_TYPE, todoUri);

    startActivity(i);
  }



  private void fillData() {

    // Fields from the database (projection)
    // Must include the _id column for the adapter to work
    String[] from = new String[] { TodoTable.COLUMN_SUMMARY };
    String[] middleId = new String[] { TodoTable.COLUMN_ID };

    // Fields on the UI to which we map
    int[] to = new int[] { R.id.label };
    int[] two = new int[] { R.id.label };

    getLoaderManager().initLoader(0, null, this);
    adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from,
        to, 0);

    middleAdapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, middleId,
            two, 0);


    setListAdapter(adapter);
   // setListAdapter(middleAdapter);

  }

  @Override
  public void onCreateContextMenu(ContextMenu menu, View v,
      ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    menu.add(0, DELETE_ID, 0, R.string.menu_delete);
  }

  // Creates a new loader after the initLoader () call
  @Override
  public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    String[] projection = { TodoTable.COLUMN_ID, TodoTable.COLUMN_SUMMARY };
    CursorLoader cursorLoader = new CursorLoader(this,
        MyTodoContentProvider.CONTENT_URI, projection, null, null, null);
    return cursorLoader;
  }

  @Override
  public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    adapter.swapCursor(data);
  }

  @Override
  public void onLoaderReset(Loader<Cursor> loader) {
    // data is not available anymore, delete reference
    adapter.swapCursor(null);
  }

} 
4

3 回答 3

2

所以我的问题是,有一种特定的方法我无法掌握它的工作原理,因此我无法操纵数据的显示方式。

方法:

adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from, to, 0);

好吧,让我们按每个参数分解这个构造函数:

  1. this,一个上下文。Adapter 需要一个 Context 来扩充每一行的布局。
  2. R.layout.todo_row, 行的布局。光标中的每条记录都将显示在此布局中。(光标的具体显示方式取决于fromto。)
  3. null, 一个光标。这包含将在您的 ListView 中显示的所有数据。
  4. from,行布局中基本视图的数组。
  5. to,一组来自光标的基本列。
  6. 0, 标志何时以及为何刷新数据。

每件事背后的诀窍是:第四个(from)中的 id 必须每个都匹配第二个参数(R.layout.todo_row)中的视图。第五个参数中的字符串必须每个都与光标中的列名匹配。第四个 ( from) 和第五个参数 ( to) 必须一对一匹配,因为每一列都显示在一个视图中。就是这样。


您现在可能已经意识到,这个注释:

R.layout.todo_row 是一个近乎空白的 xml,在没有列表视图的“todos”时使用。

错了,对不起。如果要在光标为空时显示注释,请添加:

 <TextView android:id="@android:id/empty"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:text="No data"/>

文档todo_list.xml中所述。通过在 TextView 中使用这个“神奇的 id”,注释应该会在适当的时候自动显示或隐藏。ListActivity


所有这一切都只与第一个 ListView 交互(使用 id:`android:id="@android:id/list"),您需要创建新的 Cursors 和 Adapters 才能使用其他 ListView。希望有帮助!

于 2013-02-28T01:38:36.273 回答
0

我假设代码编译并运行良好,您只想知道发生了什么。好吧,您需要注意一些事项。第一个是ListView不带布局参数,您的活动在setContentView. YourR.layout.todo_list仅用于TodosOverviewActivity创建活动的“屏幕”或“外观”,即ListView并排的 3 个视图。由于活动是一个ListActivity,它会自动查找一个类型ListView为 id的条目@android:id/list以自动连接列表侦听器(只是为您节省一点打字时间),因此您的其他列表几乎只会坐在那里,直到您连接它们自己(不要对同一布局上的项目使用相同的 id)。如果您需要访问这些其他列表,您需要使用findViewById在您的活动中使用方法并搜索您想要的列表的 ID。例如,我们可以使用以下方法访问中间列表:

ListView middleList = (ListView)this.findById(R.id.listMiddle);

现在我们有了列表,我们需要展示一些东西。列表完全是空的,您需要从某个地方引入数据。在您的情况下,数据来自Cursor您从ContentProvider. 光标只包含对我们很重要的一列,即包含TodoTable.COLUMN_SUMMARY我们要在列表中显示的文本的列。问题是列表不知道如何处理 a,Cursor因为它唯一要做的就是在屏幕上放置一个视图并上下滚动它。Cursor另一方面, 具有您要显示的所有数据,但不知道 a是什么View,更不用说如何将它包含的所有数据放入其中以供列表显示。现在你有了SimpleCursorAdapter顾名思义,这是一个适配器。它用于使不兼容的事物协同工作。一方面,您有一个需要视图的列表,另一方面,您有一个带有要显示的数据的光标,因此现在您需要一个适配器,它将每条数据映射到视图的一部分。他们SimpleCursorAdapter会特别要求你做 4 件事。首先是要在列表中显示的视图的布局,即列表项应该是什么样子。这是R.layout.todo_row告诉适配器应该创建哪些视图。在这种情况下,我们只有一个 icon 和一个TextViewid R.id.label。其次,它会询问您包含数据的游标,该游标是在onLoadFinished方法内部设置的(即null在创建适配器时)。第三,它想知道光标上的哪些列很重要。这是String[] from表示它应该在TodoTable.COLUMN_SUMMARY. 最后,它需要知道在视图中放置这些数据的位置,这是int[] to包含TextView您将用于显示文本的 id 的R.id.label.

总之,适配器就像来自光标的数据和视图布局之间的映射。现在,当列表需要在屏幕上显示视图时,它会要求适配器给它一个。然后,适配器从您提供的布局中回收或创建视图,从光标中获取每个布局的数据,并准备好进入列表以将其放在屏幕上。

于 2013-02-28T02:07:45.757 回答
0

我还没有查看 SimpleCursorAdapter 的源代码。但是,它似乎主要做两件事:

  1. 根据您在 fillData 中提供的参数查询您的数据。
  2. 循环遍历结果并使用您的模板填充列表。

在我的调试中,我确实注意到填充列表非常有效 - 它只分配显示所需的行数。当您滚动时,它会回收它们而不是免费重新分配它们。

看起来您的 fillData 代码很好。你没有说什么不起作用,所以也许它在其他地方。我从未使用过 onCreateLoader (但可能应该),因此无法对此发表评论。

我看到了一个小问题:在您的 R.layout.todo_row 中,您忘记了方向属性。

于 2013-02-28T01:42:13.430 回答