2012-11-30 3 views
2

나는 운동하려고하는 앱이 있습니다. 응용 프로그램의 한 레이아웃에서 여러 ListView 구성 요소가 있습니다. 개요는 다음과 같습니다.동일한 RelativeLayout에서 여러 ListView

<ScrollView> 
    <RelativeLayout> 
     <TextView /> 
     <TextView /> 
     <ListView /> 
     <ListView /> 
     <ListView /> 
     <Button /> 
    </RelativeLayout> 
</ScrollView> 

3 개의 개별 ListView 구성 요소에는 3 가지 유형의 항목이 나열됩니다. 내 문제는 ScrollView와 함께 캡슐화 될 때 ListViews가 각각 1 개의 항목 만 표시 한 다음 스크롤 동작으로 넘어 가기로 결정한 것입니다. 내가 선호하는 것은 모든 항목을 표시하고 ScrollView가 스크롤을 수행하게하는 것입니다. 이것이 가능한가?

여기에 몇 가지 다른 질문을 읽었습니다. 아마도 레이아웃 당 여러 ListView를 사용하지 않는 것이 좋습니다. 목록에있는 3 개의 항목이 서로 관련되어 있으며 함께 표시되는 것이 합리적이므로 가능한 한이 작업을 가능하게 만듭니다.

답변

0

내 레이아웃으로 단일 ListView 컨트롤을 채우는 데 사용되는 Sectioned List Adapter가 나왔습니다. Sectioned Adapter는 ListView를 환경 설정 목록과 매우 유사하게 만듭니다. 그러나 Sectioned Adapter는 섹션 분리 자 항목을 사용자 정의하고 여러 목록 항목 레이아웃을 포함 할 수 있기 때문에 더욱 다양합니다. Android 용 Mono의 기본 사항을 이미 알고 있다고 가정하고이를 달성하는 방법에 대한 분석을 소개합니다.

먼저 각 개별 목록 섹션을 설명하는 섹션 개체가 필요합니다. 목록의 각 섹션에 대한

public class ListSection 
{ 
    private String _caption; 
    private String _columnHeader1, _columnHeader2, _columnHeader3; 
    private BaseAdapter _adapter; 
    public ListSection(String caption, String columnHeader1, String columnHeader2, String columnHeader3, BaseAdapter adapter) 
    { 
     _caption = caption; 
     _columnHeader1 = columnHeader1; 
     _columnHeader2 = columnHeader2; 
     _columnHeader3 = columnHeader3; 
     _adapter = adapter; 
    } 
    public String Caption { get { return _caption; } set { _caption = value; } } 
    public String ColumnHeader1 { get { return _columnHeader1; } set { _columnHeader1 = value; } } 
    public String ColumnHeader2 { get { return _columnHeader2; } set { _columnHeader2 = value; } } 
    public String ColumnHeader3 { get { return _columnHeader3; } set { _columnHeader3 = value; } } 
    public BaseAdapter Adapter { get { return _adapter; } set { _adapter = value; } } 
} 

이 객체를 저장하는 모든 정보, 내 목록을 갖고 싶어 섹션의 제목뿐만 아니라 3 열 각각에 대해 열 머리글 될 것 캡션. 또한 목록의이 섹션에 대한 뷰를 제공하는 고유 한 목록 어댑터를 저장합니다. 이렇게하면 각 섹션마다 다른 어댑터를 제공 할 수 있습니다. 원하는 경우 구분 섹션을 더 자세히 설명하기 위해이 섹션 객체를 확장하여 더 많은 유연성을 제공하고 각 섹션의 기본 구조가 어떻게 생겼는지를 변경할 수 있습니다.

다음으로 목록의 구분 기호를 설명하는 XML 템플릿이 필요합니다. 각 섹션은 기본 구조가 동일하므로 매번 동일한 템플릿을 재활용 할 수 있습니다.

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
       android:orientation="vertical" 
       android:layout_width="fill_parent" 
       android:layout_height="wrap_content"> 
       <TextView 
         android:id="@+id/caption" 
         android:layout_marginTop="10px" 
         android:layout_width="fill_parent" 
         android:layout_height="wrap_content" 
         android:textAppearance="?android:attr/textAppearanceSmall" /> 
       <LinearLayout 
         android:orientation="horizontal" 
         android:layout_width="fill_parent" 
         android:layout_height="wrap_content" 
         style="?android:attr/listSeparatorTextViewStyle"> 
         <TextView 
            android:id="@+id/columnHeader1" 
            android:layout_marginLeft="10px" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:width="100px" 
            android:textAppearance="?android:attr/textAppearanceSmall" /> 
         <TextView 
            android:id="@+id/columnHeader2" 
            android:layout_marginLeft="10px" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:width="100px" 
            android:textAppearance="?android:attr/textAppearanceSmall" /> 
         <TextView 
            android:id="@+id/columnHeader3" 
            android:layout_marginLeft="10px" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:width="100px" 
            android:textAppearance="?android:attr/textAppearanceSmall" /> 
         </LinearLayout> 
</LinearLayout> 

당신은 알 수 있습니다, 내가 스타일 = 준 내부의 LinearLayout 제어 "안드로이드 : ATTR/listSeparatorTextViewStyle"태그를. 이것은 안드로이드에게 그 경계선을 아래쪽 경계선에게 알려줍니다. 간단한 TextView 구분자를 원한다면이 작업을 수행하고 동일한 스타일 태그를 지정하면됩니다.

내 ListAdapter는 기본적으로 모두 동일하며 다른 데이터 객체를 확장합니다. 세 개의 모든 채우기 열은 같은 크기의 열에있는 데이터를 모두 포함하며 서로 다른 논리 개체입니다. 각 어댑터는 뷰의 3 열 데이터로 채우는 BaseAdapter 확장입니다.여기서는 BaseAdapter의 표준 확장을 만드는 방법을 알고 있다고 가정했습니다. ListSectionAdapter를 만드는 방법이 나와 있습니다. 당신이 활동을 채우는하거나 레이아웃, 당신의 단면 어댑터를 생성 한 후, 별도의 어댑터를 생성하고 각각 별도의 목록 유형에 대한 섹션을 추가 ListView에를 포함하면

public class ListSectionAdapter : BaseAdapter<ListSection> 
{ 
    private const int TYPE_SECTION_HEADER = 0; 
    private Context _context; 
    private List<ListSection> _sections; 
    private LayoutInflater _inflater; 
    public ListSectionAdapter(Context context) 
    { 
     _context = context; 
     _inflater = Inflater.From(_context); 
     _sections = new List<ListSection>(); 
    } 
    public List<ListSection> Sections { get { return _sections; } set { _sections = value; } } 
    public override int Count 
    { 
     get 
     { 
     int count = 0; 
     foreach(ListSection s in _sections) count += s.Adapter.Count + 1; 
     return count; 
     } 
    } 
    public override int ViewTypeCount 
    { 
     get 
     { 
     int viewTypeCount = 1; 
     foreach(ListSection s in _sections) viewTypeCount += s.Adapter.ViewTypeCount; 
     return viewTypeCount; 
     } 
    } 
    public override ListSection this[int index] { get { return _sections[index]; } } 
    public override bool AreAllItemsEnable() { return false; } 
    public override int GetItemViewType(int position) 
    { 
     int typeOffset = TYPE_SECTION_HEADER + 1; 
     foreach(ListSection s in _sections) 
     { 
     if(position == 0) return TYPE_SECTION_HEADER; 
     int size = s.Adapter.Count + 1; 
     if(position < size) return (typeOffset + s.Adapter.GetItemViewType(position - 1)); 
     position -= size; 
     typeOffset += s.Adapter.ViewTypeCount; 
     } 
     return -1; 
    } 
    public override long GetItemId(int position) { return position; } 
    public void AddSection(String caption, String columnHeader1, String columnHeader2, String columnHeader3, BaseAdapter adapter) 
    { 
     _sections.Add(new ListSection(caption, columnHeader1, columnHeader2, columnHeader3, adapter); 
    } 
    public override View GetView(int position, View convertView, ViewGroup parent) 
    { 
     View view = convertView; 
     foreach(ListSection s in _sections) 
     { 
     if(position == 0) 
     { 
      if(view == null || !(view is LinearLayout)) view = _inflater.Inflate(Resource.Layout.SectionSeparator, parent, false); 
      TextView caption = view.FindViewById<TextView>(Resource.Id.caption); 
      caption.Text = s.Caption; 
      TextView columnHeader1 = view.FindViewById<TextView>(Resource.Id.columnHeader1); 
      columnHeader1.Text = s.ColumnHeader1; 
      TextView columnHeader2 = view.FindViewById<TextView>(Resource.Id.columnHeader2); 
      columnHeader2.Text = s.ColumnHeader2; 
      TextView columnHeader3 = view.FindViewById<TextView>(Resource.Id.columnHeader3); 
      columnHeader3.Text = s.ColumnHeader3; 
      return view; 
      } 
      int size = s.Adapter.Count + 1; 
      if(position < size) return s.Adapter.GetView(position - 1, convertView, parent); 
      position -= size; 
     } 
     return null; 
    } 
    public override Java.Lang.Object GetItem(int position) 
    { 
     foreach(ListSection s in _sections) 
     { 
      if(position == 0) return null; 
      int size = s.Adapter.Count + 1; 
      if(position < size) return s.Adapter.GetItem(position); 
      position -= size; 
     } 
     return null; 
    } 
} 

지금 당신이해야 할 모든 코드에서 당신 그걸 원해. ,

ListAdapterType1 adapter1 = new ListAdapterType1(); 
ListAdapterType2 adapter2 = new ListAdapterType2(); 
ListAdapterType3 adapter3 = new ListAdapterType3(); 

ListSectionAdapter sectionAdapter = new ListSectionAdapter(this); 
sectionAdapter.AddSection("Section 1", "Column 1", "Column 2", "Column 3", adapter1); 
sectionAdapter.AddSection("Section 2", "Column 1", "Column 2", "Column 3", adapter2); 
sectionAdapter.AddSection("Section 3", "Column 1", "Column 2", "Column 3", adapter3); 

ListView myList = FindViewById<ListView>(Resource.Id.MyList); 
myList.SetAdapter(sectionAdapter); 

는 인 itemClick 이벤트의 경우,이 작업을 수행하는 더 나은 방법이있을 수 있습니다,하지만 난 구분 목록의 준 getItem (int) 메소드에서 반환 된 개체 유형의 있으며, toString를 비교하여 다음과 같은 방법을 사용 기본 목록 어댑터 객체 유형을 반환하도록 확장했습니다.

개인 무효 MyList_ItemClick (객체 송신자 AdapterView.ItemClickEventArgs E) {= (ListView를 송신자로서) ListSectionAdapter 어댑터 ListSectionAdapter 같은 .Adapter; if (object.Attribute) .ToString() == typeof (ObjectA) .ToString()) { // 클릭 한 객체 유형 A에 따라 응답하십시오. } // 기타 등등 섹션 목록에 포함 된 각기 다른 객체 유형 }

클릭 이벤트는 클릭 한 항목을 설명하는 새 레이아웃을 채우고 엽니 다. 클릭 한 객체 유형에 따라 다른 레이아웃을 사용하기 때문에 객체 유형별 차별화가 필요합니다. 레이아웃에 대한 정적 정보는 객체 유형마다 다릅니다.

희망이 도움이됩니다. Wrox의 책인 Mono C#/.Net을 사용한 Professional Android Programming에서이 예제를 수집하고 내 요구 사항을 충족하도록 수정했습니다. 어떻게 작동하는지 볼 수 있기 때문에 자신의 필요에 맞게 수정할 수 있습니다.

1

ListView는 주로 스크롤 용으로 설계되었습니다. ListView는 항목 수가 많을 때 (화면 크기에 비해) 화면 이동을 원활하게하기 위해 재활용 메커니즘을 사용합니다. ListView에 모든 항목을 한 번에 표시하려면 실제로 ListView가 필요하지 않습니다. 대신 LinearLayout을 사용하십시오. 목록을 채우기 위해 목록 어댑터를 사용하고있는 것 같습니다. 따라서 LinearLayout을 확장하고 사용자 정의 setAdapter() 메소드를 사용하여 어댑터를 활용할 수 있습니다. 다음은 스크롤 할 수없는 목록에 대해 생성 한 클래스입니다.

public class NonScrollableListView extends LinearLayout { 
    private BaseAdapter mAdapter; 
    private AdapterDataSetObserver mDataSetObserver; 

    public NonScrollableListView(Context context) { 
     super(context); 
    } 

    public NonScrollableListView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public NonScrollableListView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    @Override 
    protected void onAttachedToWindow() { 
     super.onAttachedToWindow(); 
     if(mAdapter != null && mDataSetObserver != null){ 
      mDataSetObserver = new AdapterDataSetObserver(); 
      mAdapter.registerDataSetObserver(mDataSetObserver); 
     } 
    } 

    @Override 
    protected void onDetachedFromWindow() { 
     super.onDetachedFromWindow(); 
     if(mAdapter != null && mDataSetObserver != null){ 
      mAdapter.unregisterDataSetObserver(mDataSetObserver); 
     } 
    } 

    public void setAdapter(BaseAdapter adapter) { 
     this.mAdapter = adapter; 

     if(mAdapter != null && mDataSetObserver != null){ 
      mAdapter.unregisterDataSetObserver(mDataSetObserver); 
     } 

     mDataSetObserver = new AdapterDataSetObserver(); 
     mAdapter.registerDataSetObserver(mDataSetObserver); 

     mDataSetObserver.onChanged(); 
    } 

    private void fillChildViews(){ 
     if(mAdapter != null){ 
      int requiredChilrenCount = mAdapter.getCount(); 
      int currentChildrenCount = getChildCount(); 

      for(int i = 0; i < requiredChilrenCount; i++){ 
       View nextChild = getChildAt(i); 
       View nextChildToAdd = mAdapter.getView(i, nextChild, this); 
       nextChildToAdd.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 

       if(nextChild == null){ 
        addView(nextChildToAdd); 
       } 
      } 

      //Remove remaining child views if any 
      for(int i = requiredChilrenCount; i < currentChildrenCount; i++){ 
       //The length of the children list changes so need to get it at each iteration 
       removeViewAt(getChildCount() - 1); 
      } 
     } 
     else{ 
      removeAllViews(); 
     } 
    } 

    private class AdapterDataSetObserver extends DataSetObserver{ 
     @Override 
     public void onChanged() { 
      fillChildViews(); 
     } 
    } 
} 

목록보기와 마찬가지로 사용할 수 있습니다. 항목 수는 상대적으로 적을 것으로 예상됩니다. 그렇지 않으면 성능 문제가 발생합니다.

+0

예 내 목록은 각각 10 개를 초과하지 않는 비교적 작습니다. 이것은 매우 간단합니다, 나는 오늘 밤 그것을 밖으로 시도하고 그것이 어떻게 작동하는지보십시오. 감사! – Wanabrutbeer

+0

몇 가지 문제가 있습니다. 먼저 AdapterDataSetObserver를 ScrollableListView 외부의 public 클래스로 만들고 Changed 이벤트를 추가하여 트리거 된 FillChildView를 가져와야합니다. 하지만 지금은 어떤 이유로 InflateException이 발생하고 있습니다. 또한 ItemClick 이벤트를 복제하는 데 더 많은 사용자 지정이 필요할 것이라고 가정합니다. – Wanabrutbeer

+0

목록을 업데이트하려면 어댑터에서 notifyDataSetChanged()를 호출하면됩니다. 따라서 AdapterDataSetObserrver를 public으로 설정할 필요가 없습니다. 또한 어댑터 내에서 OnClickListener를 설정할 수 있습니다. 어댑터 내에서 setItemClickListener()를 작성하고 getView()를 통해 작성한보기에 대해 onClickListener를 설정하십시오. 이 메소드는 다음과 같이 표시되어야합니다. @Override public View getView (int position, View convertView, ViewGroup parent) {... convertView.setOnClickListener (new OnClickListener() {public void OnClick (if (customListener! = null) {customListener! = null) {customListener() .onClick()}}}) ...} – Zzokk

관련 문제