创建自己的自定义适配器时,getView()方法是如何工作的?

我的问题是:

  1. 布局膨胀器的确切功能是什么?
  2. 为什么我读过的所有文章都首先检查 Convertview 是否为 null?当它为空时,它意味着什么? 当它不为空时,它意味着什么?
  3. 这个方法接受的父参数是什么?
132820 次浏览

You could have a look at this video about the list view. Its from last years Google IO and still the best walk-through on list views in my mind.

http://www.youtube.com/watch?v=wDBM6wVEO70

  1. It inflates layouts (the xml files on your res/layout/ folder) into java Objects such as LinearLayout and other views.

  2. Look at the video, will get you up to date with whats the use of the convert view, basically its a recycled view waiting to be reused by you, to avoid creating a new object and slowing down the scrolling of your list.

  3. Allows you to reference you list-view from the adapter.

  1. Layout inflator inflates/adds external XML to your current view.

  2. getView() is called numerous times including when scrolled. So if it already has view inflated we don't wanna do it again since inflating is a costly process.. thats why we check if its null and then inflate it.

  3. The parent view is single cell of your List..

LayoutInflater is used to generate dynamic views of the XML for the ListView item or in onCreateView of the fragment.

ConvertView is basically used to recycle the views which are not in the view currently. Say you have a scrollable ListView. On scrolling down or up, the convertView gives the view which was scrolled. This reusage saves memory.

The parent parameter of the getView() method gives a reference to the parent layout which has the listView. Say you want to get the Id of any item in the parent XML you can use:

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


}

getView() method create new View or ViewGroup for each row of Listview or Spinner . You can define this View or ViewGroup in a Layout XML file in res/layout folder and can give the reference it to Adapter class Object.

if you have 4 item in a Array passed to Adapter. getView() method will create 4 View for 4 rows of Adaper.

LayoutInflater class has a Method inflate() whic create View Object from XML resource layout.

1: The LayoutInflater takes your layout XML-files and creates different View-objects from its contents.

2: The adapters are built to reuse Views, when a View is scrolled so that is no longer visible, it can be used for one of the new Views appearing. This reused View is the convertView. If this is null it means that there is no recycled View and we have to create a new one, otherwise we should use it to avoid creating a new.

3: The parent is provided so you can inflate your view into that for proper layout parameters.

All these together can be used to effectively create the view that will appear in your list (or other view that takes an adapter):

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

Notice the use of the LayoutInflater, that parent can be used as an argument for it, and how convertView is reused.

getView() method in Adapter is for generating item's view of a ListView, Gallery,...

  1. LayoutInflater is used to get the View object which you define in a layout xml (the root object, normally a LinearLayout, FrameLayout, or RelativeLayout)

  2. convertView is for recycling. Let's say you have a listview which can only display 10 items at a time, and currently it is displaying item 1 -> item 10. When you scroll down one item, the item 1 will be out of screen, and item 11 will be displayed. To generate View for item 11, the getView() method will be called, and convertView here is the view of item 1 (which is not necessary anymore). So instead create a new View object for item 11 (which is costly), why not re-use convertView? => we just check convertView is null or not, if null create new view, else re-use convertView.

  3. parentView is the ListView or Gallery... which contains the item's view which getView() generates.

Note: you don't call this method directly, just need to implement it to tell the parent view how to generate the item's view.

What is exactly the function of the LayoutInflater?

When you design using XML, all your UI elements are just tags and parameters. Before you can use these UI elements, (eg a TextView or LinearLayout), you need to create the actual objects corresponding to these xml elements. That is what the inflater is for. The inflater, uses these tags and their corresponding parameters to create the actual objects and set all the parameters. After this you can get a reference to the UI element using findViewById().

Why do all the articles that I've read check if convertview is null or not first? What does it mean when it is null and what does it mean when it isn't?

This is an interesting one. You see, getView() is called everytime an item in the list is drawn. Now, before the item can be drawn, it has to be created. Now convertView basically is the last used view to draw an item. In getView() you inflate the xml first and then use findByViewID() to get the various UI elements of the listitem. When we check for (convertView == null) what we do is check that if a view is null(for the first item) then create it, else, if it already exists, reuse it, no need to go through the inflate process again. Makes it a lot more efficient.

You must also have come across a concept of ViewHolder in getView(). This makes the list more efficient. What we do is create a viewholder and store the reference to all the UI elements that we got after inflating. This way, we can avoid calling the numerous findByViewId() and save on a lot of time. This ViewHolder is created in the (convertView == null) condition and is stored in the convertView using setTag(). In the else loop we just obtain it back using getView() and reuse it.

What is the parent parameter that this method accepts?

The parent is a ViewGroup to which your view created by getView() is finally attached. Now in your case this would be the ListView.

Hope this helps :)

You can also find useful information about getView at the Adapter interface in Adapter.java file. It says;

/**
* 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);

If you want pass a view to a function (as argument) you can define an element first, for example a frame layout:

FrameLayout frameLayout = findViewById(R.id.frame_layout);

then get view of the element:

 frameLayout.getRootView();