Xamarin Android ListView를 데이터로 채우기Populating a Xamarin.Android ListView With Data

ListView에 행을 추가 하려면 레이아웃에 행을 추가 하 고 ListView에서 자신을 채우기 위해 호출 하는 메서드를 사용 하 여 IListAdapter를 구현 해야 합니다.To add rows to a ListView you need to add it to your layout and implement an IListAdapter with methods that the ListView calls to populate itself. Android에는 사용자 지정 레이아웃 XML 또는 코드를 정의 하지 않고 사용할 수 있는 기본 제공 ListActivityArrayAdapter 클래스가 포함 되어 있습니다.Android includes built-in ListActivity and ArrayAdapter classes that you can use without defining any custom layout XML or code. ListActivity 클래스는 자동으로 ListView을 만들고 ListAdapter 속성을 노출 하 여 어댑터를 통해 표시할 행 뷰를 제공 합니다.The ListActivity class automatically creates a ListView and exposes a ListAdapter property to supply the row views to display via an adapter.

기본 제공 어댑터는 각 행에 사용 되는 매개 변수로 뷰 리소스 ID를 사용 합니다.The built-in adapters take a view resource ID as a parameter that gets used for each row. Android.Resource.Layout와 같은 기본 제공 리소스를 사용 하 여 사용자가 직접 작성할 필요가 없도록 할 수 있습니다.You can use built-in resources such as those in Android.Resource.Layout so you don't need to write your own.

ListActivity 및 ArrayAdapter를 사용 하 여 문자열><Using ListActivity and ArrayAdapter<String>

HomeScreen 예에서는 이러한 클래스를 사용 하 여 몇 줄의 코드만으로 ListView를 표시 하는 방법을 보여 줍니다.The example BasicTable/HomeScreen.cs demonstrates how to use these classes to display a ListView in only a few lines of code:

[Activity(Label = "BasicTable", MainLauncher = true, Icon = "@drawable/icon")]
public class HomeScreen : ListActivity {
   string[] items;
   protected override void OnCreate(Bundle bundle)
   {
       base.OnCreate(bundle);
       items = new string[] { "Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers" };
       ListAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleListItem1, items);
   }
   protected override void OnListItemClick(ListView l, View v, int position, long id)
}

행 클릭 처리Handling Row Clicks

일반적으로 ListView는 사용자가 특정 작업 (예: 노래 재생, 연락처 호출 또는 다른 화면 표시)을 수행할 수 있습니다.Usually a ListView will also allow the user to touch a row to perform some action (such as playing a song, or calling a contact, or showing another screen). 사용자에 게 응답 하려면 ListActivityOnListItemClick –에서 구현 된 메서드를 하나 이상 사용 해야 합니다. 예를 들면 다음과 같습니다.To respond to user touches there needs to be one more method implemented in the ListActivityOnListItemClick – like this:

SimpleListItem의스크린샷Screenshot of a SimpleListItem

protected override void OnListItemClick(ListView l, View v, int position, long id)
{
   var t = items[position];
   Android.Widget.Toast.MakeText(this, t, Android.Widget.ToastLength.Short).Show();
}

이제 사용자가 행을 터치 하 고 Toast 경고가 표시 됩니다.Now the user can touch a row and a Toast alert will appear:

행이 작업 될 때 표시 되는 알림 스크린샷Screenshot of Toast that appears when a row is touched

ListAdapter 구현Implementing a ListAdapter

ArrayAdapter<string>은 간단 하기 때문에 좋지만 매우 제한적입니다.ArrayAdapter<string> is great because of its simplicity, but it's extremely limited. 그러나 바인딩하려는 문자열이 아닌 비즈니스 엔터티 컬렉션이 있는 경우가 종종 있습니다.However, often times you have a collection of business entities, rather than just strings that you want to bind. 예를 들어 데이터가 Employee 클래스 컬렉션으로 구성 된 경우 목록에서 각 직원의 이름만 표시 하는 것이 좋습니다.For example, if your data consists of a collection of Employee classes, then you might want the list to just display the names of each employee. 표시 되는 데이터를 제어 하는 ListView의 동작을 사용자 지정 하려면 다음 네 가지 항목을 재정의 하는 BaseAdapter의 서브 클래스를 구현 해야 합니다.To customize the behavior of a ListView to control what data is displayed you must implement a subclass of BaseAdapter overriding the following four items:

  • Count –를 통해 데이터에 있는 행 수를 컨트롤에 알립니다.Count – To tell the control how many rows are in the data.

  • Getview 를 – 하 여 데이터로 채워진 각 행에 대 한 뷰를 반환 합니다.GetView – To return a View for each row, populated with data. 이 메서드에는 다시 사용 하기 위해 사용 되지 않는 기존 행을 전달 하는 ListView에 대 한 매개 변수가 있습니다.This method has a parameter for the ListView to pass in an existing, unused row for re-use.

  • Getitemid –은 행 식별자 (일반적으로 원하는 long 값이 될 수 있지만 행 번호)를 반환 합니다.GetItemId – Return a row identifier (typically the row number, although it can be any long value that you like).

  • 이 [int] 인덱서는 특정 행 번호와 연결 된 데이터를 반환 – 합니다.this[int] indexer – To return the data associated with a particular row number.

Basictableadapter/HomeScreenAdapter 의 예제 코드는 BaseAdapter서브 클래스 하는 방법을 보여 줍니다.The example code in BasicTableAdapter/HomeScreenAdapter.cs demonstrates how to subclass BaseAdapter:

public class HomeScreenAdapter : BaseAdapter<string> {
   string[] items;
   Activity context;
   public HomeScreenAdapter(Activity context, string[] items) : base() {
       this.context = context;
       this.items = items;
   }
   public override long GetItemId(int position)
  {
       return position;
   }
   public override string this[int position] {  
       get { return items[position]; }
   }
   public override int Count {
       get { return items.Length; }
   }
   public override View GetView(int position, View convertView, ViewGroup parent)
   {
       View view = convertView; // re-use an existing view, if one is available
      if (view == null) // otherwise create a new one
           view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
       view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = items[position];
       return view;
   }
}

사용자 지정 어댑터 사용Using a Custom Adapter

사용자 지정 어댑터를 사용 하는 것은 기본 제공 ArrayAdapter와 비슷하며 context를 전달 하 고 표시할 값의 string[]를 전달 합니다.Using the custom adapter is similar to the built-in ArrayAdapter, passing in a context and the string[] of values to display:

ListAdapter = new HomeScreenAdapter(this, items);

이 예제에서는 동일한 행 레이아웃 (SimpleListItem1)을 사용 하기 때문에 결과 응용 프로그램은 이전 예제와 동일 하 게 보입니다.Because this example uses the same row layout (SimpleListItem1) the resulting application will look identical to the previous example.

행 뷰 다시 사용Row View Re-Use

이 예제에는 6 개의 항목만 있습니다.In this example there are only six items. 화면은 8에 맞출 수 있으므로 행을 다시 사용 하지 않아도 됩니다.Since the screen can fit eight, no row re-use required. 그러나 수백 또는 수천 개의 행을 표시 하는 경우에는 한 번에 8 개만 화면에 맞출 때 수백 또는 수천 개의 View 개체를 만들기 위한 메모리가 낭비 될 수 있습니다.When displaying hundreds or thousands of rows, however, it would be a waste of memory to create hundreds or thousands of View objects when only eight fit on the screen at a time. 이러한 상황을 방지 하기 위해 행이 화면에서 사라질 때 해당 뷰는 다시 사용 하기 위해 큐에 배치 됩니다.To avoid this situation, when a row disappears from the screen its view is placed in a queue for re-use. 사용자가 스크롤하면 ListView에서 GetView를 호출 하 여 사용 가능한 경우 convertView 매개 변수에 사용 되지 않는 뷰를 전달 하는 새 – 보기를 요청 합니다.As the user scrolls, the ListView calls GetView to request new views to display – if available it passes an unused view in the convertView parameter. 이 값이 null 이면 코드에서 새 뷰 인스턴스를 만들어야 합니다. 그렇지 않으면 해당 개체의 속성을 다시 설정 하 고 다시 사용할 수 있습니다.If this value is null then your code should create a new view instance, otherwise you can re-set the properties of that object and re-use it.

GetView 메서드는 다음 패턴에 따라 행 뷰를 다시 사용 해야 합니다.The GetView method should follow this pattern to re-use row views:

public override View GetView(int position, View convertView, ViewGroup parent)
{
   View view = convertView; // re-use an existing view, if one is supplied
   if (view == null) // otherwise create a new one
       view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
   // set view properties to reflect data for the given row
   view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = items[position];
   // return the view, populated with data, for display
   return view;
}

사용자 지정 어댑터 구현은 긴 목록을 표시할 때 메모리 부족을 방지 하기 위해 새 뷰를 만들기 전에 항상 convertView 개체를 다시 사용 해야 합니다.Custom adapter implementations should always re-use the convertView object before creating new views to ensure they do not run out of memory when displaying long lists.

일부 어댑터 구현 (예: CursorAdapter)에는 GetView 메서드가 없으므로 GetView의 책임을 두 가지 방법으로 구분 하 여 행 다시 사용을 강제 적용 하는 두 가지 방법 NewViewBindView 필요 합니다.Some adapter implementations (such as the CursorAdapter) don't have a GetView method, rather they require two different methods NewView and BindView which enforce row re-use by separating the responsibilities of GetView into two methods. 문서의 뒷부분에 CursorAdapter 예제가 있습니다.There is a CursorAdapter example later in the document.

빠른 스크롤 사용Enabling Fast Scrolling

빠른 스크롤을 사용 하면 목록의 일부에 직접 액세스 하는 스크롤 막대 역할을 하는 추가 ' 핸들 '을 제공 하 여 긴 목록을 스크롤할 수 있습니다.Fast Scrolling helps the user to scroll through long lists by providing an additional 'handle' that acts as a scroll bar to directly access a part of the list. 이 스크린샷은 빠른 스크롤 핸들을 보여 줍니다.This screenshot shows the fast scroll handle:

스크롤 핸들을 사용 하 여 빠른 스크롤의스크린샷Screenshot of fast-scrolling with a scroll handle

빠른 스크롤 핸들을 표시 하는 것은 FastScrollEnabled 속성을 true로 설정 하는 것 처럼 간단 합니다.Causing the fast scrolling handle to appear is as simple as setting the FastScrollEnabled property to true:

ListView.FastScrollEnabled = true;

섹션 인덱스 추가Adding a Section Index

섹션 인덱스는 긴 목록을 통해 빠르게 스크롤할 때 사용자에 대 한 추가 피드백을 제공 하며,이는 스크롤 한 ' 섹션 '을 보여 – 합니다.A section index provides additional feedback for users when they are fast-scrolling through a long list – it shows which 'section' they have scrolled to. 섹션 인덱스를 표시 하려면 어댑터 하위 클래스가 표시 되는 행에 따라 인덱스 텍스트를 제공 하기 위해 ISectionIndexer 인터페이스를 구현 해야 합니다.To cause the section index to appear the Adapter subclass must implement the ISectionIndexer interface to supply the index text depending on the rows being displayed:

H (H)로 시작 하는 앞의 섹션에 표시 된 H의스크린샷Screenshot of H appearing above section that starts with H

ISectionIndexer를 구현 하려면 어댑터에 다음 세 가지 메서드를 추가 해야 합니다.To implement ISectionIndexer you need to add three methods to an adapter:

  • Getsections –는 표시할 수 있는 섹션 인덱스 타이틀의 전체 목록을 제공 합니다.GetSections – Provides the complete list of section index titles that could be displayed. 이 메서드는 Java 개체 배열을 필요로 하므로 코드가 .NET 컬렉션에서 Java.Lang.Object[]을 만들어야 합니다.This method requires an array of Java Objects so the code needs to create a Java.Lang.Object[] from a .NET collection. 이 예제에서는 목록에서 Java.Lang.String의 초기 문자 목록을 반환 합니다.In our example it returns a list of the initial characters in the list as Java.Lang.String .

  • Getpositionforsection – 지정 된 섹션 인덱스에 대 한 첫 번째 행 위치를 반환 합니다.GetPositionForSection – Returns the first row position for a given section index.

  • Getsection Forposition – 지정 된 행에 대해 표시할 섹션 인덱스를 반환 합니다.GetSectionForPosition – Returns the section index to be displayed for a given row.

예제 SectionIndex/HomeScreenAdapter.cs 파일은 이러한 메서드를 구현 하 고 생성자에 몇 가지 추가 코드를 구현 합니다.The example SectionIndex/HomeScreenAdapter.cs file implements those methods, and some additional code in the constructor. 생성자는 모든 행을 반복 하 고 제목의 첫 번째 문자를 추출 하 여 섹션 인덱스를 작성 합니다 .이 작업을 수행 하려면 항목이 이미 정렬 되어 있어야 합니다.The constructor builds the section index by looping through every row and extracting the first character of the title (the items must already be sorted for this to work).

alphaIndex = new Dictionary<string, int>();
for (int i = 0; i < items.Length; i++) { // loop through items
   var key = items[i][0].ToString();
   if (!alphaIndex.ContainsKey(key))
       alphaIndex.Add(key, i); // add each 'new' letter to the index
}
sections = new string[alphaIndex.Keys.Count];
alphaIndex.Keys.CopyTo(sections, 0); // convert letters list to string[]

// Interface requires a Java.Lang.Object[], so we create one here
sectionsObjects = new Java.Lang.Object[sections.Length];
for (int i = 0; i < sections.Length; i++) {
   sectionsObjects[i] = new Java.Lang.String(sections[i]);
}

데이터 구조가 생성 되 면 ISectionIndexer 방법은 매우 간단 합니다.With the data structures created, the ISectionIndexer methods are very simple:

public Java.Lang.Object[] GetSections()
{
   return sectionsObjects;
}
public int GetPositionForSection(int section)
{
   return alphaIndexer[sections[section]];
}
public int GetSectionForPosition(int position)
{   // this method isn't called in this example, but code is provided for completeness
    int prevSection = 0;
    for (int i = 0; i < sections.Length; i++)
    {
        if (GetPositionForSection(i) > position)
        {
            break;
        }
        prevSection = i;
    }
    return prevSection;
}

섹션 인덱스 제목은 실제 섹션에 1:1을 매핑할 필요가 없습니다.Your section index titles don't need to map 1:1 to your actual sections. 이것은 GetPositionForSection 메서드가 존재 하는 이유입니다.This is why the GetPositionForSection method exists. GetPositionForSection를 사용 하면 인덱스 목록에 있는 모든 인덱스를 목록 뷰에 있는 섹션에 매핑할 수 있습니다.GetPositionForSection gives you an opportunity to map whatever indices are in your index list to whatever sections are in your list view. 예를 들어 인덱스에 "z"가 있지만 모든 문자에 대 한 테이블 섹션이 없을 수 있으므로 26에 매핑되는 "z" 대신 25 또는 24로 매핑하거나 "z"가 매핑될 모든 섹션 인덱스를 매핑할 수 있습니다.For example, you may have a "z" in your index, but you may not have a table section for every letter, so instead of "z" mapping to 26, it may map to 25 or 24, or whatever section index "z" should map to.