寻求一些洞察力,了解simpleCursorAdapter的工作原理

时间:2013-02-28 00:28:18

标签: java android xml database sqlite

嘿伙计我只是希望有人可以了解这段代码是如何工作的,更具体地说是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);
  }

} 

3 个答案:

答案 0 :(得分:2)

  

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

方法:

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

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

  1. this,一个上下文。适配器需要一个Context来膨胀每一行的布局。
  2. R.layout.todo_row,行的布局。光标中的每条记录都将以此布局显示。 (确切地 如何显示Cursor取决于fromto。)
  3. null,一个光标。这包含将在ListView中显示的所有数据。
  4. from,行布局中基本视图的数组。
  5. to,来自Cursor的基本列数组。
  6. 0,标记何时以及为何应该刷新数据。
  7. 每件事背后的诀窍是:第四个(from)中的ID必须与第二个参数(R.layout.todo_row)中的View匹配。第五个参数中的字符串必须分别与Cursor中的列名匹配。第四个(from)和第五个参数(to)必须一对一匹配,因为每个列都显示在一个视图中。就是这样。


    你现在可能已经意识到,这个说明:

      

    R.layout.todo_row是一个近乎空白的xml,当没有没有列表视图的“待办事项”时使用。

    错了,抱歉。如果要在Cursor为空时显示注释,请添加:

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

    todo_list.xml,如ListActivity's documenation中所述。通过在TextView中使用此“魔术ID”,应在适当时自动显示或隐藏注释。


    所有这些只是交互视图第一个ListView(id:`android:id =“@ android:id / list”),你需要创建新的游标和适配器来使用其他ListViews。希望有所帮助!

答案 1 :(得分:0)

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

  1. 根据您在fillData中提供的参数查询数据。
  2. 循环显示结果并使用模板填充列表。
  3. 在我的调试器中,我注意到填充列表非常有效 - 它只分配显示所需的行数。当你滚动时,它会回收它们而不是免费重新分配它们。

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

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

答案 2 :(得分:0)

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

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

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

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