2

解決しようとしているアプリがあります。アプリの 1 つのレイアウトには、複数の ListView コンポーネントがあります。アウトラインは次のようになります。

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

3 つの個別の ListView コンポーネントは、3 つの異なるタイプのアイテムをリストしています。私の問題は、ScrollView でカプセル化すると、ListViews がそれぞれ 1 つのアイテムのみを表示することを決定し、スクロール動作に陥ることです。私が好むのは、すべてのアイテムを表示し、ScrollView にスクロールさせることです。これは可能ですか?

ここで他のいくつかの質問を読みましたが、レイアウトごとに複数の ListView を使用しないことが慣習になっているようです。リストされている 3 つの個別のアイテムは関連しており、一緒に表示するのが理にかなっているからです。

4

2 に答える 2

1

ListViews は、主にスクロール用に設計されています。ListView はリサイクル メカニズムを使用して、項目数が多い場合 (画面サイズに対して相対的に) スクロールをスムーズにします。ListView にすべての項目を一度に表示させたい場合は、実際には ListView は必要ありません。代わりに LinearLayout を使用してください。リストを埋めるためにリストアダプターを使用していると思います。そのため、カスタムの setAdapter() メソッドを使用して LinearLayout を拡張し、アダプタを利用できます。これは、スクロールできないリスト用に作成したクラスです。

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

リストビューを使用するのと同じように使用できます。アイテム数は比較的少ないと予想されます。そうしないと、パフォーマンスの問題が発生します。

于 2012-11-30T05:42:33.020 に答える
0

私が最終的に使用したソリューションは、レイアウトに単一の ListView コントロールを設定するために使用される Sectioned List Adapter でした。Sectioned Adapter により、ListView はプリファレンス リストに非常に似ています。ただし、Sectioned Adapter は、セクション区切り項目をカスタマイズして、複数のリスト項目レイアウトを含めることができるため、より用途が広いです。Mono for Android の基本を既に理解していることを前提として、これを実現する方法の内訳を次に示します。

まず、個々のリスト セクションを記述するセクション オブジェクトが必要です。

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>

お気づきでしょう、私が style="?android:attr/listSeparatorTextViewStyle" タグを付けた Inner LinearLayout コントロール。これは、そのビューに下の境界線を与えるように Android に指示します。単純な TextView セパレーターが必要な場合は、これを実行して、同じスタイル タグを付けるだけです。

現在、私の ListAdapter はすべて基本的に同じで、異なるデータ オブジェクトを拡張するだけです。3 つすべてが 3 つの列にデータを入力し、そのすべてが同じサイズの列にデータを収めます。それらは単に異なる論理オブジェクトです。各アダプターは、ビューに 3 列のデータを設定する BaseAdapter 拡張機能です。ここでは、BaseAdapter の標準拡張機能を作成する方法を知っていることを前提としています。ここで説明するのは、ListSectionAdapter の作成方法です。

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

これで、ListView を含むアクティビティまたはレイアウトにデータを入力するときにコードを作成し、個別のアダプターを作成してから、セクション化されたアダプターを作成し、必要な個別のリスト タイプごとにセクションを追加するだけです。

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 を比較します。ベース リスト アダプタ オブジェクト タイプ。

private void MyList_ItemClick(オブジェクト送信者、AdapterView.ItemClickEventArgs e) { ListSectionAdapter アダプタ = (ListView としての送信者).Adapter として ListSectionAdapter; if(adapter.GetItem(e.Position).ToString() == typeof(ObjectA).ToString()) { // クリックされたオブジェクト タイプ A に応じて応答する } // 含まれる異なるオブジェクト タイプごとに同様の処理を行うセクション化されたリストで }

私のクリックイベントは、クリックされたアイテムを説明する新しいレイアウトを作成して開きます。レイアウト上の静的情報はオブジェクト タイプごとに異なるため、クリックされたオブジェクト タイプに基づいて異なるレイアウトを使用するため、オブジェクト タイプの区別が必要です。

お役に立てれば。この例は、Wrox の著書『Professional Android Programming with Mono C#/.Net』から収集し、私のニーズに合わせて変更しました。うまくいけば、どのように機能するかを確認して、自分のニーズに合わせて変更できることを願っています。

于 2012-12-11T14:52:25.287 に答える