Lesson 54. Customizing the List. We create our adapter

Lesson 54. Customizing the List. We create our adapter


In this lesson:

– we create our adapter based on BaseAdapter

The adapters we provide are versatile and useful, but sometimes their capabilities are not enough to realize what they intended. Then you need to write your adapter. Let’s try it too. We will not create from scratch, but using BaseAdapter.

Let’s make a likeness of an online store. We will display a list of goods. Each item in the list will contain the product name, price and image. It will also be possible to tick the item with a pebble, thereby placing it in the basket.

At the bottom of the list, we’ll make a button that will display the contents of the Recycle Bin. In this online store we would hang on to it, for example, the transition to order creation.

Let’s create a project:

Project name: P0541_CustomAdapter
Build Target: Android 2.3.3
Application name: CustomAdapter
Package name: ru.startandroid.develop.p0541customadapter
Create Activity: MainActivity

To file strings.xml we will add a text parameter to the button name.

Корзина

screen main.xml:



    
    
    

layout for list item – item.xml:



    
    
    
        
        
        
        
    
    
    

Checkbox, pair of text boxes and picture.

Now we write the code. You can write everything in MainActivity.java, but then it will turn out to be quite large and inconvenient to read. I’ll give all the code in three classes.

Product.java – a class describing the product:

package ru.startandroid.develop.p0541customadapter;

public class Product {
  
  String name;
  int price;
  int image;
  boolean box;
  

  Product(String _describe, int _price, int _image, boolean _box) {
    name = _describe;
    price = _price;
    image = _image;
    box = _box;
  }
}

Everything is simple here – only the constructor and elements of the class. Do not bother with Set / Get access and methods so as not to complicate the code.

BoxAdapter.java – The adapter we created will be created

package ru.startandroid.develop.p0541customadapter;

import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.TextView;

public class BoxAdapter extends BaseAdapter {
  Context ctx;
  LayoutInflater lInflater;
  ArrayList objects;

  BoxAdapter(Context context, ArrayList products) {
    ctx = context;
    objects = products;
    lInflater = (LayoutInflater) ctx
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  }

  // кол-во элементов
  @Override
  public int getCount() {
    return objects.size();
  }

  // элемент по позиции
  @Override
  public Object getItem(int position) {
    return objects.get(position);
  }

  // id по позиции
  @Override
  public long getItemId(int position) {
    return position;
  }

  // пункт списка
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    // используем созданные, но не используемые view
    View view = convertView;
    if (view == null) {
      view = lInflater.inflate(R.layout.item, parent, false);
    }

    Product p = getProduct(position);

    // заполняем View в пункте списка данными из товаров: наименование, цена
    // и картинка
    ((TextView) view.findViewById(R.id.tvDescr)).setText(p.name);
    ((TextView) view.findViewById(R.id.tvPrice)).setText(p.price + "");
    ((ImageView) view.findViewById(R.id.ivImage)).setImageResource(p.image);

    CheckBox cbBuy = (CheckBox) view.findViewById(R.id.cbBox);
    // присваиваем чекбоксу обработчик
    cbBuy.setOnCheckedChangeListener(myCheckChangeList);
    // пишем позицию
    cbBuy.setTag(position);
    // заполняем данными из товаров: в корзине или нет
    cbBuy.setChecked(p.box);
    return view;
  }

  // товар по позиции
  Product getProduct(int position) {
    return ((Product) getItem(position));
  }

  // содержимое корзины
  ArrayList getBox() {
    ArrayList box = new ArrayList();
    for (Product p : objects) {
      // если в корзине
      if (p.box)
        box.add(p);
    }
    return box;
  }

  // обработчик для чекбоксов
  OnCheckedChangeListener myCheckChangeList = new OnCheckedChangeListener() {
    public void onCheckedChanged(CompoundButton buttonView,
        boolean isChecked) {
      // меняем данные товара (в корзине или нет)
      getProduct((Integer) buttonView.getTag()).box = isChecked;
    }
  };
}

Just in case I will remind the general principle of action adapter: he gets it data and issues View to display a list item.

We look at the code. IN designers we fill in our internal variables and receive LayoutInflater to work with layout resources. IN objects we now have a list of products that need to be listed.

Methods marked with annotation @Override, We are obliged to realize in inheritance BaseAdapter. These methods are used by the list and should work correctly.

method getCount must return the number of items. We return quantity of goods.

method getItem must return the item at the specified position. Using a position, we get a specific element from objects.

method getItemId must return the element id. Do not bother and return the position. By the way, it is also made in some adapters. That is why we have seen in handlers that id = position.

method getView must return View item list. To do this, we created a layout resource R.layout.item. In this method we have to create a View from R.layout.item, fill it with data and submit it to the list. But before we create, we try to use convertViewWhich goes to the input method. This is a previously created View, but not currently used. For example, when scrolling through a list, some of the items go behind the screen and no longer need to be drawn. The view from these “invisible” items is used for new items. All we have to do is fill in their data. This greatly speeds up the work of the program, because you do not need to drive the inflate one more time.

If convertView this time did not give us (null), then we create the view itself. Then fill in the name, price and picture of the goods. For the checkbox, we assign a handler, store the item’s Tag in the Tag and put a pebble if the item is already in the basket.

Tag is some kind of Object repository for everyone ViewWhere you can put the data you need. In our case, for each checkbox I place in his Tag the position number of the list item. Later in the checkbox handler I will be able to extract this position number and determine at which point in the list the checkbox was pressed.

As a result, the method getView returns the list completely filled view, And the list will display it as the next item.

The following is a couple of methods that were not necessarily created when inheriting BaseAdapter. I created them for convenience.

method getProduct is an analog getItem, but it immediately converts Object to Product. It is only used a couple of times. And in principle, one could do without it.

method getBox checks which goods are marked with pebbles and forms a basket collection from them.

myCheckChangeList – checkbox handler. When we click on a checkbox in the list, it triggers, reads from Tag the position of the list item and marks the corresponding item as put in the basket.

Here it is important to understand that without this handler would not work putting the goods in the basket. And on the screen – the values ​​of the checkboxes in the list would be lost when scrolling. Because the list items are converted if they go “behind the screen” and reappear. This recreation provides the getView method, and it takes data from products to fill the View. So when you click on the checkbox, it is necessary to save in the product data that it is now in the basket.

remains MainActivity.java:

package ru.startandroid.develop.p0541customadapter;

import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

  ArrayList products = new ArrayList();
  BoxAdapter boxAdapter;

  /** Called when the activity is first created. */
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // создаем адаптер
    fillData();
    boxAdapter = new BoxAdapter(this, products);

    // настраиваем список
    ListView lvMain = (ListView) findViewById(R.id.lvMain);
    lvMain.setAdapter(boxAdapter);
  }

  // генерируем данные для адаптера
  void fillData() {
    for (int i = 1; i <= 20; i++) {
      products.add(new Product("Product " + i, i * 1000,
          R.drawable.ic_launcher, false));
    }
  }

  // выводим информацию о корзине
  public void showResult(View v) {
    String result = "Товары в корзине:";
    for (Product p : boxAdapter.getBox()) {
      if (p.box)
        result += "n" + p.name;
    }
    Toast.makeText(this, result, Toast.LENGTH_LONG).show();
  }
}

There is very little code here.

IN onCreate create an adapter and a list.

In the method fillData we generate data for the adapter. As a picture we use the standard for all items. Ideally, each product has its own picture.

method showResult receives from the adapter a list of basket products and displays their name. This method is called after pressing a button on the screen because it is written in its properties onClick.

We save and run everything. We mark the goods and press the button to view the contents of the basket.

A rather difficult example came out through a checkbox.

It may well be that there is another way to implement this example. But the point was to show the creation of your adapter. For further fixation, look at this Google example.

In the next lesson:

- we use Header and Footer in the lists
- Understand how and where HeaderViewListAdapter is used




Discuss in the forum [269 replies]

Leave a Comment