创建您自己的自定义适配器时,getView() 方法如何工作?
Posted
技术标签:
【中文标题】创建您自己的自定义适配器时,getView() 方法如何工作?【英文标题】:How does the getView() method work when creating your own custom adapter? 【发布时间】:2012-04-24 14:16:55 【问题描述】:我的问题是:
-
LayoutInflater的具体作用是什么?
为什么我读过的所有文章都先检查 convertview 是否为空?为 null 时是什么意思,不是时又是什么意思?
此方法接受的父参数是什么?
【问题讨论】:
【参考方案1】:1:LayoutInflater
获取您的布局 XML 文件并根据其内容创建不同的视图对象。
2:适配器是为重用视图而构建的,当滚动视图以使其不再可见时,它可以用于出现的新视图之一。这个重用的视图是convertView
。如果为null,则表示没有回收的View,我们必须创建一个新的,否则我们应该使用它来避免创建一个新的。
3:提供了parent
,因此您可以将视图扩展为适当的布局参数。
所有这些都可以用来有效地创建将出现在列表中的视图(或其他需要适配器的视图):
public View getView(int position, @Nullable View convertView, ViewGroup parent)
if (convertView == null)
//We must create a View:
convertView = inflater.inflate(R.layout.my_list_item, parent, false);
//Here we can do changes to the convertView, such as set a text on a TextView
//or an image on an ImageView.
return convertView;
注意LayoutInflater
的使用,parent
可以用作它的参数,以及如何重用convertView
。
【讨论】:
Convertview == null 当您的所有项目都遵循相同的布局时很有用。例如,当您需要检查单选按钮或选中按钮并根据每个项目更改布局时,您需要重新充气,否则它会获取缓存视图。 不需要太充气。只需要在 getview 中编写 switch 或 if-else 阶梯并根据您的情况扩展视图,覆盖 public int getItemViewType(int position) 和 public int getViewTypeCount()。 @sagits If 语句通常可以工作,但是当使用单选按钮、编辑文本和这些我在使用缓存视图时遇到问题的东西时,关于堆栈溢出的这些东西存在一些问题。【参考方案2】:Adapter 中的getView()
方法用于生成ListView
、Gallery
、...的项目视图。
LayoutInflater
用于获取您在布局 xml 中定义的 View 对象(根对象,通常为 LinearLayout
,
FrameLayout
,或RelativeLayout
)
convertView
用于回收。假设您有一个列表视图,它一次只能显示 10 个项目,目前它是
显示项目 1 -> 项目 10。当您向下滚动一项时,
项目 1 将不在屏幕上,将显示项目 11。到
为项目 11 生成视图,将调用 getView() 方法,并且
convertView
这里是第 1 项的视图(不是必须的
不再)。所以改为为第 11 项创建一个新的 View 对象(即
昂贵),为什么不重复使用convertView
? => 我们只检查convertView
是
是否为null,如果为null,则创建新视图,否则重复使用convertView
。
parentView
是 ListView 或 Gallery...,其中包含 getView()
生成的项目视图。
注意:你不直接调用这个方法,只需要实现它告诉父视图如何生成item的视图。
【讨论】:
对 parentView 的优秀解释,找不到比这个更好的解释,+1 很好的解释+1【参考方案3】:您可以观看有关列表视图的视频。它来自去年的 Google IO,并且仍然是我心目中关于列表视图的最佳演练。
http://www.youtube.com/watch?v=wDBM6wVEO70
它将布局(res/layout/ 文件夹中的 xml 文件)扩展为 java 对象,例如 LinearLayout 和其他视图。
观看视频,让您了解转换视图的最新用途,基本上它是一个等待您重用的回收视图,以避免创建新对象并减慢滚动你的清单。
允许您从适配器引用您的列表视图。
【讨论】:
【参考方案4】:LayoutInflater的具体作用是什么?
当您使用 XML 进行设计时,您的所有 UI 元素都只是标签和参数。在您可以使用这些 UI 元素(例如 TextView 或 LinearLayout)之前,您需要创建与这些 xml 元素相对应的实际对象。这就是充气机的用途。充气机使用这些标签及其相应的参数来创建实际对象并设置所有参数。在此之后,您可以使用 findViewById() 获取对 UI 元素的引用。
为什么我读过的所有文章都先检查 convertview 是否为空?为 null 时是什么意思,不是时是什么意思?
这是一个有趣的问题。您会看到,每次绘制列表中的项目时都会调用 getView()。现在,在绘制项目之前,必须先创建它。现在 convertView 基本上是最后使用的视图来绘制一个项目。在 getView() 中,您首先膨胀 xml,然后使用 findByViewID() 来获取列表项的各种 UI 元素。当我们检查 (convertView == null) 时,我们所做的是检查一个视图是否为 null(对于第一项)然后创建它,否则,如果它已经存在,则重用它,无需再次进行膨胀过程.效率更高。
你一定也遇到过 getView() 中的 ViewHolder 的概念。这使列表更有效率。我们所做的是创建一个 viewholder 并存储对我们在膨胀后获得的所有 UI 元素的引用。这样,我们可以避免调用大量的 findByViewId() 并节省大量时间。此 ViewHolder 在 (convertView == null) 条件下创建,并使用 setTag() 存储在 convertView 中。在 else 循环中,我们只需使用 getView() 将其取回并重用它。
这个方法接受的父参数是什么?
父级是一个 ViewGroup,由 getView() 创建的视图最终附加到该视图组。现在,在您的情况下,这将是 ListView。
希望这会有所帮助:)
【讨论】:
【参考方案5】:Layout inflator 将外部 XML 膨胀/添加到您当前的视图中。
getView() 被多次调用,包括在滚动时。因此,如果它已经膨胀了视图,我们不想再做一次,因为膨胀是一个代价高昂的过程。这就是为什么我们检查它是否为空,然后再膨胀它。
父视图是列表的单个单元格..
【讨论】:
父视图在这里解释不正确。它将是 ListView 而不是 ListItem【参考方案6】:LayoutInflater
用于为ListView
项或片段的onCreateView
生成XML 的动态视图。
ConvertView
主要用于回收当前不在视图中的视图。假设您有一个可滚动的ListView
。在向下或向上滚动时,convertView
给出滚动的视图。这种重用可以节省内存。
getView()
方法的 parent 参数给出了对具有 listView 的父布局的引用。假设您想获取您可以使用的父 XML 中任何项目的 Id:
ViewParent nv = parent.getParent();
while (nv != null)
if (View.class.isInstance(nv))
final View button = ((View) nv).findViewById(R.id.remove);
if (button != null)
// FOUND IT!
// do something, then break;
button.setOnClickListener(new OnClickListener()
@Override
public void onClick(View v)
// TODO Auto-generated method stub
Log.d("Remove", "Remove clicked");
((Button) button).setText("Hi");
);
break;
【讨论】:
【参考方案7】:getView()
方法为 Listview
或 Spinner 的每一行创建新的 View
或 ViewGroup
。您可以在res/layout
文件夹中的Layout XML
文件中定义此View
或ViewGroup
,并可以将其引用到Adapter
类对象。
如果您在传递给适配器的数组中有 4 个项目。 getView()
方法将为 4 行 Adaper 创建 4 个 View。
LayoutInflater 类有一个方法 inflate() 从 XML 资源布局创建视图对象。
【讨论】:
【参考方案8】:您还可以在 Adapter.java 文件中的 Adapter 接口中找到有关 getView 的有用信息。 它说;
/**
* Get a View that displays the data at the specified position in the data set. You can either
* create a View manually or inflate it from an XML layout file. When the View is inflated, the
* parent View (GridView, ListView...) will apply default layout parameters unless you use
* @link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)
* to specify a root view and to prevent attachment to the root.
*
* @param position The position of the item within the adapter's data set of the item whose view
* we want.
* @param convertView The old view to reuse, if possible. Note: You should check that this view
* is non-null and of an appropriate type before using. If it is not possible to convert
* this view to display the correct data, this method can create a new view.
* Heterogeneous lists can specify their number of view types, so that this View is
* always of the right type (see @link #getViewTypeCount() and
* @link #getItemViewType(int)).
* @param parent The parent that this view will eventually be attached to
* @return A View corresponding to the data at the specified position.
*/
View getView(int position, View convertView, ViewGroup parent);
【讨论】:
【参考方案9】:如果您想将视图传递给函数(作为参数),您可以先定义一个元素,例如框架布局:
FrameLayout frameLayout = findViewById(R.id.frame_layout);
然后获取元素的视图:
frameLayout.getRootView();
【讨论】:
以上是关于创建您自己的自定义适配器时,getView() 方法如何工作?的主要内容,如果未能解决你的问题,请参考以下文章
在扩展数组适配器时自定义适配器的getView()中的空指针异常
android-继承BaseAdapter--自己定义适配器,getView运行多次的解决方法
NullPointerException 自定义列表视图适配器