Android ListView,每行有不同的布局

我正在试图确定有一个ListView包含每行不同的布局的最佳方法。 我知道如何创build一个自定义的行+自定义数组适配器来支持整个列表视图的自定义行,但我怎样才能在ListView中实现许多不同的行样式?

既然你知道有多lesstypes的布局,可以使用这些方法。

getViewTypeCount() – 此方法返回信息在列表中有多less种types的行

getItemViewType(int position) – 根据位置返回您应该使用哪种布局types的信息

然后,只有当它为null并使用getItemViewType确定types时,才会使布局膨胀。

查看本教程以获取更多信息。

为了达到你在评论中描述的结构的一些优化,我会build议:

  • 将视图存储在名为ViewHolder对象中。 它会提高速度,因为您不必每次都在getView方法中调用findViewById() 。 请参阅API演示中的List14 。
  • 创build一个符合所有属性组合的通用布局,如果当前位置没有,则隐藏一些元素。

我希望这会帮助你。 如果您可以提供一些XML存根与您的数据结构和信息如何将其映射到行,我将能够给你更精确的build议。 按像素。

我知道如何创build一个自定义的行+自定义数组适配器来支持整个列表视图的自定义行。 但是,一个listview如何支持许多不同的行风格呢?

你已经知道基础知识。 您只需要让您的自定义适配器根据提供的行/光标信息返回不同的布局/视图。

ListView可以支持多行样式,因为它来自AdapterView :

AdapterView是一个视图,子视图由适配器决定。

如果您查看适配器 ,您将看到使用特定于行的视图的方法:

 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 ... 

后两种方法提供的位置,所以你可以用它来确定你应该使用该行 的视图的types


当然,您通常不直接使用AdapterView和Adapter,而是使用或派生自其中一个子类。 Adapter的子类可能会添加其他function,这些function会更改如何获取不同行的自定义布局。 由于用于给定行的视图是由适配器驱动的,因此技巧是让适配器返回给定行所需的视图。 如何做到这一点取决于具体的适配器。

例如,要使用ArrayAdapter

  • 重写getView()来膨胀,填充并返回给定位置的所需视图。 getView()方法通过convertView参数包含一个机会重用视图。

但是要使用CursorAdapter的衍生物,

  • 重载newView()来膨胀,填充并返回当前游标状态的所需视图(即当前的“行”)[您还需要重写bindView以便小部件可以重用视图]

但是,要使用SimpleCursorAdapter

  • 使用setViewValue()方法定义一个SimpleCursorAdapter.ViewBinder来给给定的行(当前的游标状态)和数据“列”填充,填充和返回所需的视图。 该方法只能定义“特殊”视图,并遵循SimpleCursorAdapter的“普通”绑定的标准行为。

查找最终使用的适配器的具体示例/教程。

看看下面的代码。

首先,我们创build自定义布局。 在这种情况下,有四种types。

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> 

然后,我们创build列表视图项目。 在我们的例子中,用一个string和一个types。

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

之后,我们创build一个视图持有者。 强烈build议这样做,因为Android操作系统会保留布局参考,以便在物品消失时再次使用并显示在屏幕上。 如果你不使用这种方法,每一次你的项目出现在屏幕上的Android操作系统将创build一个新的,并导致你的应用程序泄漏内存。

 public class ViewHolder { TextView text; public ViewHolder(TextView text) { this.text = text; } public TextView getText() { return text; } public void setText(TextView text) { this.text = text; } } 

最后,我们创build我们的自定义适配器,覆盖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里面像这样创build一个listview

 <?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> 

在您的自定义数组适配器中,您可以重写getView()方法,正如您大概熟悉的那样。 然后,您只需使用switch语句或if语句,根据传递给getView方法的位置参数返回特定的自定义View。 Android是聪明的,它只会给你一个适合你的位置/行types的convertView; 你不需要检查它是正确的types。 您可以通过适当地重写getItemViewType()和getViewTypeCount()方法来帮助Android。

如果我们需要在列表视图中显示不同types的视图,那么在适配器中使用getViewTypeCount()和getItemViewType(),而不是切换视图VIEW.GONE和VIEW.VISIBLE在getView()影响列表滚动。

请检查这个在Adapter中使用getViewTypeCount()和getItemViewType()。

链接: 使用getviewtypecount

ListView适用于所有行项目的相同静态视图的简单用例。
由于您必须创buildViewHolders并大量使用getItemViewType() ,并dynamic显示不同的行项目布局xml,您应该尝试使用Android API 22中提供的RecyclerView 。它为多视图提供了更好的支持和结构types。

看看这个教程如何使用RecyclerView来做你正在寻找的东西。