2011-01-23 104 views
318

我想確定一個ListView包含每行不同佈局的最佳方法。我知道如何創建一個自定義行+自定義數組適配器來支持整個列表視圖的自定義行,但是如何在ListView中實現多種不同的行樣式?Android ListView每行不同的佈局

+1

更新:使用演示了多行佈局Android的RecyclerView HTTP ://code2concept.blogspot.in/2015/10/android-multiple-row-layout-using.html – nitesh 2015-10-17 06:00:45

回答

399

既然你知道有多少類型的佈局,你可以使用這些方法。

getViewTypeCount() - 這個方法返回的信息很多類型的行你怎麼在你的列表

getItemViewType(int position) - 返回你應該使用基於

然後你誇大佈局位置,其佈局類型信息,只有當它的空並使用getItemViewType確定類型。

看看this tutorial瞭解更多信息。

爲了實現結構優化,一些你已經在註釋中所描述我建議:

  • 在對象存儲的觀點稱爲ViewHolder。它會提高速度,因爲您不必每次都在getView方法中調用findViewById()。見List14 in API demos
  • 創建一個通用佈局,該佈局將符合所有屬性組合,並在當前位置沒有它時隱藏某些元素。

我希望能幫到你。如果你可以提供一些XML存根與你的數據結構和信息,你想如何將它映射到行中,我將能夠給你更精確的建議。按像素。

+0

非常感謝博客,但我添加了複選框。我遇到了一個問題,它會檢查第一個項目並滾動列表。奇怪的匿名項目被檢查。你能爲此提供解決方案嗎?謝謝 – 2011-10-08 13:53:35

+2

對於再次挖掘這個問題感到抱歉,但是您實際上會建議擁有一個大的佈局文件並控制其中某些部分的可見性,而不是使用單獨的佈局文件,這些佈局文件分別使用getItemViewType進行膨脹? – Makibo 2013-05-09 03:08:36

+2

你也可以這樣做。雖然我仍然喜歡這裏暴露的方式。它使你想要達到的更清晰。 – Cristian 2013-05-09 21:24:48

12

在您的自定義數組適配器中,您可以重寫getView()方法,正如您大概熟悉的一樣。然後,您只需使用switch語句或if語句返回特定的自定義視圖,具體取決於傳遞給getView方法的位置參數。 Android很聰明,因爲它只會爲您提供適合您的位置/行的適當類型的convertView;你不需要檢查它是正確的類型。您可以通過適當地重寫getItemViewType()和getViewTypeCount()方法來幫助Android。

60

我知道如何創建自定義行+自定義數組適配器來支持整個列表視圖的自定義行。但是,一個listview如何支持許多不同的行風格?

您已經瞭解基礎知識。您只需讓自定義適配器根據提供的行/光標信息返回不同的佈局/視圖。

ListView可以支持多個列的樣式,因爲它從AdapterView導出:

一個AdapterView是一個視圖其孩子由一個適配器確定。

如果你看一下Adapter,你會看到該帳戶使用特定行觀點方法:

abstract int getViewTypeCount() 
// Returns the number of types of Views that will be created ... 

abstract int getItemViewType(int position) 
// Get the type of View that will be created ... 

abstract View getView(int position, View convertView, ViewGroup parent) 
// Get a View that displays the data ... 

後兩種方法提供位置這樣你就可以使用它來確定視圖的類型您應該使用對於該行


當然,您通常不直接使用AdapterView和Adapter,而是使用或派生自其某個子類。 Adapter的子類可能會添加更多功能,這些功能會更改如何爲不同行獲取自定義佈局。 由於用於給定行的視圖是由適配器驅動的,因此技巧是讓適配器返回給定行所需的視圖。如何做到這一點取決於特定的適配器。

例如,使用ArrayAdapter

  • 倍率getView()膨脹,填充,並返回所需的視圖對於給定的位置。 getView()方法通過參數convertView包含機會重用視圖。

但使用的CursorAdapter衍生物,

  • 倍率newView()膨脹,填充,並返回所需的視圖對於當前光標狀態(即,當前的「行」。)[你還需要覆蓋bindView以便Widget可以重新使用視圖]

但是,要使用SimpleCursorAdapter

  • 限定SimpleCursorAdapter.ViewBindersetViewValue()方法膨脹,填充,並返回所需的視圖對於給定的行(當前光標狀態)和數據「列」。該方法只能定義「特殊」視圖,並遵循SimpleCursorAdapter對「常規」綁定的標準行爲。

查找最終使用的適配器種類的具體示例/教程。

3

如果我們需要在列表視圖中顯示不同類型的視圖,那麼在適配器中使用getViewTypeCount()和getItemViewType()很好,而不是切換視圖VIEW.GONE和VIEW.VISIBLE在getView中可能是非常昂貴的任務()會影響列表滾動。

請在適配器中選中getViewTypeCount()和getItemViewType()。

鏈接:the-use-of-getviewtypecount

1

ListView控件是打算簡單的用例像所有的行項目相同的靜態視圖。
由於您必須創建ViewHolders並大量使用getItemViewType()並動態顯示不同的行項目佈局xml,因此您應該嘗試使用Android API 22中提供的RecyclerView來完成此操作。它爲多視圖提供了更好的支持和結構類型。

看看這個tutorial關於如何使用RecyclerView來做你正在尋找的東西。

35

看看下面的代碼。

首先,我們創建自定義佈局。在這種情況下,有四種類型。

even.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:background="#ff500000" 
    android:layout_height="match_parent"> 

    <TextView 
     android:id="@+id/text" 
     android:textColor="@android:color/white" 
     android:layout_width="match_parent" 
     android:layout_gravity="center" 
     android:textSize="24sp" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 

odd.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:background="#ff001f50" 
    android:gravity="right" 
    android:layout_height="match_parent"> 

    <TextView 
     android:id="@+id/text" 
     android:textColor="@android:color/white" 
     android:layout_width="wrap_content" 
     android:layout_gravity="center" 
     android:textSize="28sp" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 

white.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:background="#ffffffff" 
    android:gravity="right" 
    android:layout_height="match_parent"> 

    <TextView 
     android:id="@+id/text" 
     android:textColor="@android:color/black" 
     android:layout_width="wrap_content" 
     android:layout_gravity="center" 
     android:textSize="28sp" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 

black.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:background="#ff000000" 
    android:layout_height="match_parent"> 

    <TextView 
     android:id="@+id/text" 
     android:textColor="@android:color/white" 
     android:layout_width="wrap_content" 
     android:layout_gravity="center" 
     android:textSize="33sp" 
     android:layout_height="wrap_content" /> 

</LinearLayout> 

然後,我們創建listview項目。在我們的例子中,用一個字符串和一個類型。

public class ListViewItem { 
     private String text; 
     private int type; 

     public ListViewItem(String text, int type) { 
      this.text = text; 
      this.type = type; 
     } 

     public String getText() { 
      return text; 
     } 

     public void setText(String text) { 
      this.text = text; 
     } 

     public int getType() { 
      return type; 
     } 

     public void setType(int type) { 
      this.type = type; 
     } 

    } 

之後,我們創建一個視圖持有者。強烈建議這樣做,因爲Android操作系統會保留佈局參考,以便在物品消失並重新出現在屏幕上時重複使用。如果你不使用這種方法,你的項目每次出現在屏幕上的Android操作系統將創建一個新的並導致你的應用程序泄漏內存。最後,我們創建了我們的自定義適配器重寫getViewTypeCount()和getItemViewType(int position)。

public class CustomAdapter extends ArrayAdapter { 

     public static final int TYPE_ODD = 0; 
     public static final int TYPE_EVEN = 1; 
     public static final int TYPE_WHITE = 2; 
     public static final int TYPE_BLACK = 3; 

     private ListViewItem[] objects; 

     @Override 
     public int getViewTypeCount() { 
      return 4; 
     } 

     @Override 
     public int getItemViewType(int position) { 
      return objects[position].getType(); 
     } 

     public CustomAdapter(Context context, int resource, ListViewItem[] objects) { 
      super(context, resource, objects); 
      this.objects = objects; 
     } 

     @Override 
     public View getView(int position, View convertView, ViewGroup parent) { 

      ViewHolder viewHolder = null; 
      ListViewItem listViewItem = objects[position]; 
      int listViewItemType = getItemViewType(position); 


      if (convertView == null) { 

       if (listViewItemType == TYPE_EVEN) { 
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_even, null); 
       } else if (listViewItemType == TYPE_ODD) { 
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_odd, null); 
       } else if (listViewItemType == TYPE_WHITE) { 
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_white, null); 
       } else { 
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_black, null); 
       } 

       TextView textView = (TextView) convertView.findViewById(R.id.text); 
       viewHolder = new ViewHolder(textView); 

       convertView.setTag(viewHolder); 

      } else { 
       viewHolder = (ViewHolder) convertView.getTag(); 
      } 

      viewHolder.getText().setText(listViewItem.getText()); 

      return convertView; 
     } 

    } 

而且我們的活動是這樣的:

private ListView listView; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(R.layout.activity_main); // here, you can create a single layout with a listview 

     listView = (ListView) findViewById(R.id.listview); 

     final ListViewItem[] items = new ListViewItem[40]; 

     for (int i = 0; i < items.length; i++) { 
      if (i == 4) { 
       items[i] = new ListViewItem("White " + i, CustomAdapter.TYPE_WHITE); 
      } else if (i == 9) { 
       items[i] = new ListViewItem("Black " + i, CustomAdapter.TYPE_BLACK); 
      } else if (i % 2 == 0) { 
       items[i] = new ListViewItem("EVEN " + i, CustomAdapter.TYPE_EVEN); 
      } else { 
       items[i] = new ListViewItem("ODD " + i, CustomAdapter.TYPE_ODD); 
      } 
     } 

     CustomAdapter customAdapter = new CustomAdapter(this, R.id.text, items); 
     listView.setAdapter(customAdapter); 
     listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
      @Override 
      public void onItemClick(AdapterView adapterView, View view, int i, long l) { 
       Toast.makeText(getBaseContext(), items[i].getText(), Toast.LENGTH_SHORT).show(); 
      } 
     }); 

    } 
} 

現在裏面mainactivity.xml 創建一個列表視圖這樣

<?xml version="1.0" encoding="utf-8"?> 
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true" 
    tools:context="com.example.shivnandan.gygy.MainActivity"> 

    <android.support.design.widget.AppBarLayout 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:theme="@style/AppTheme.AppBarOverlay"> 

     <android.support.v7.widget.Toolbar 
      android:id="@+id/toolbar" 
      android:layout_width="match_parent" 
      android:layout_height="?attr/actionBarSize" 
      android:background="?attr/colorPrimary" 
      app:popupTheme="@style/AppTheme.PopupOverlay" /> 

    </android.support.design.widget.AppBarLayout> 

    <include layout="@layout/content_main" /> 

    <ListView 
     android:layout_width="match_parent" 

     android:layout_height="match_parent" 

     android:id="@+id/listView" 
     android:layout_alignParentRight="true" 
     android:layout_alignParentEnd="true" 


     android:layout_marginTop="100dp" /> 

</android.support.design.widget.CoordinatorLayout>