2013-02-28 87 views
1

嘿,我只是希望有人能夠揭示這個代碼是如何工作的,更具體地說是simpleCursorAdapter。完整的程序是一個應用程序,它是一個待辦事項列表,它是一個非常簡單的教程,用戶可以使用遊標和加載器輸入數據或「筆記」並保存到sqlite數據庫。尋求一些洞察力,瞭解simpleCursorAdapter是如何工作的

所以我的問題是,有一個具體的方法,我很難掌握它是如何工作的,因此我無法操縱數據的顯示方式。我認爲問題在於我不明白適配器如何採用與顯示內容不同的佈局,並將其全部顯示在列表視圖中。

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

} 
+0

什麼是「near blank xml」? – 2013-02-28 01:44:14

回答

2

所以我的問題是,有一個具體的方法,我無法理解它是如何工作的,因此我不能操縱數據的顯示方式。

的方法:

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

好吧,讓我們打破這種構造減少了每個參數:

  1. this,上下文。適配器需要一個上下文來擴充每一行的佈局。
  2. R.layout.todo_row,行的佈局。光標中的每條記錄都將以此佈局顯示。 (具體是如何顯示光標取決於fromto。)
  3. null,光標。這包含將在ListView中顯示的所有數據。
  4. from,行佈局中基本視圖的數組。
  5. to,從您的光標重要列的數組。
  6. 0,標記何時以及爲什麼應該刷新數據。

每一件事背後的竅門是:第四個(from)中的ID必須與第二個參數(R.layout.todo_row)中的View匹配。第五個參數中的字符串必須與光標中的列名相匹配。第四個(from)和第五個參數(to)必須一對一匹配,因爲每個列都顯示在一個視圖中。就是這樣。


正如你可能現在已經意識到,這樣一個字條:

R.layout.todo_row是一個幾乎空白的XML,使用時有沒有「待辦事項」沒有列表視圖。

是錯誤的,對不起。如果你想顯示一個音符當光標位於空的附加:

<TextView android:id="@android:id/empty" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      android:text="No data"/> 
ListActivity's documenation描述

todo_list.xml。通過在TextView中使用這個「magic id」,便可在適當的時候自動顯示或隱藏註釋。


這一切都只是相互作用的查看第一的ListView(ID爲:'機器人:ID =「@機器人:ID /列表」),您需要創建新的遊標和適配器使用其他列表視圖。希望有所幫助!

+0

非常感謝先生,問題在於往返。現在我的下一個問題:)他們究竟在做什麼?他們指向的TextView的ID,並以某種方式將它們放入列表視圖? 而且,當設置爲R.id.list程序崩潰,我覺得很奇怪,我想我只是不明白那些真的在做什麼,澄清將非常感謝哈哈謝謝。 – Rayne 2013-03-01 19:01:57

+0

也許看看源代碼會有幫助:['SimpleAdapter#getView()'](http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1。 2_r1 /機器人/插件/ SimpleAdapter.java#SimpleAdapter.getView%28int%2Candroid.view.View%2Candroid.view.ViewGroup%29)。 'getView()'膨脹行佈局並移入'bindView()'。 'bindView()'爲''''中的每個值調用'findViewById()',每個'from'調用'get()'。接下來,'bindView()'試圖通過查看當前的View:ImageView,TextView等來確定它當前有什麼類型的數據以及如何處理它。這是否有幫助? – Sam 2013-03-01 19:19:25

+0

這是一個很好的例子。但是,如何用這種機制將id設置爲視圖?謝謝 – Ahmed 2014-01-06 17:33:25

0

我沒有看過的SimpleCursorAdapter的源代碼。然而,它似乎主要做兩件事:

  1. 查詢您的數據,基於您在fillData中提供的參數。
  2. 循環播放結果並使用您的模板填充列表。

在我的調試中,我確實注意到它對於填充列表非常有效 - 它只分配儘可能多的行來顯示。在您滾動時,它將回收它們,而不是自由地重新分配它們。

它看起來像你的fillData代碼是好的。你不會說什麼不起作用,所以也許它在別處。我從來沒有使用onCreateLoader(但可能應該),所以不能評論。

我看見一個小問題:在你的R.layout.todo_row,你忘了定向屬性。

0

我假設的代碼編譯和運行很好,你只是想知道發生了什麼事情。那麼,你需要注意一些事情。第一個是ListView不包含佈局參數,您的活動在setContentView中執行。你R.layout.todo_list僅用於由TodosOverviewActivity打造的「屏幕」或在側「看」活動,即,3 ListView意見的一面。由於該活動是ListActivity它會自動尋找ListView型與@android:id/list的ID的條目自動掛鉤名單聽衆(只是保存你一點打字的),所以你的其他名單會非常簡單,只是坐在那裏,直到你將自己掛鉤(不要在同一個佈局上使用相同的ID)。如果您需要訪問其他列表,則需要在活動中使用findViewById方法並搜索所需列表的ID。例如,我們可以通過以下方式訪問中間列表:

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

現在我們已經有了列表,我們需要顯示一些東西。這些列表完全是空的,你需要從某處引入數據。在你的情況下,數據來自的Cursor對象。光標只包含一列對我們而言很重要的列,TodoTable.COLUMN_SUMMARY列有我們想要在列表中顯示的文本。問題是,一個列表並不知道如何處理Cursor,因爲它唯一的作用是在屏幕上放置一個視圖並上下滾動。另一方面,Cursor擁有您想要顯示的所有數據,但不知道View是什麼,更不用說如何將其包含的所有數據放在列表中顯示。現在你有SimpleCursorAdapter這就像名字所說的那樣是一個適配器。它被用來使不相容的東西一起工作。一方面你有一個需要視圖的列表,另一方面你有一個你想要顯示的數據的光標,所以現在你需要一個適配器將每個數據映射到視圖的一部分。 SimpleCursorAdapter會特別問你4件事情。首先是要在列表中顯示的視圖佈局,即列表項應該是什麼樣子。這是R.layout.todo_row,它告訴適配器應該創建哪些視圖。在這種情況下,我們只有一個圖標和一個編號爲R.id.labelTextView。其次,它會詢問您包含數據的光標,該數據在onLoadFinished方法(在創建適配器時爲null)內設置。第三,它想知道遊標上的哪些列是重要的。這是String[] from數組,它表示它應該查找TodoTable.COLUMN_SUMMARY中的數據。最後,它需要知道放置該數據的視圖中的哪個位置,並且這是,其中包含TextView的ID,您將使用它顯示文本R.id.label

總之,適配器就像來自光標的數據和視圖的佈局之間的映射。現在,當列表需要在屏幕上顯示的視圖時,它會要求適配器給它一個。然後,適配器會從您提供的佈局中回收或創建一個視圖,從光標中爲每個佈局獲取數據,併爲它準備好進入列表以將其放置在屏幕上。