Android предоставляет серию различных макетов для удовлетворения потребностей ваших приложений. Один из самых быстрых и простых способов отображения информации для пользователей — через компонент ListView
. Этот компонент создает простую прокручиваемую область, которая может отображать уникальные наборы информации. Отличительной особенностью ListView
Например, для отображения изображения слева и заголовка справа для каждой записи. Это возможно путем создания и использования пользовательских ArrayAdapter
В этом руководстве вы узнаете, как получить ListView
адаптера ArrayAdapter . Разобравшись с этими темами, вы сможете создавать удивительно выглядящие прокручиваемые списки информации.
Компоненты Android ArrayAdapter
Есть несколько взаимосвязанных небольших частей для создания вашего окончательного настроенного ListView
Вам понадобится следующее:
- Источник данных, как правило, в виде списка объектов
- Настраиваемый объект
ArrayAdapter
-
ListView
ArrayAdapter
Об этом процессе проще думать визуально. Вам нужен источник данных, адаптер для управления макетом и компонент для визуального отображения результатов
Индивидуальная аренда ListView Showcase
В качестве примера вы создадите персонализированный ListView
Каждое свойство будет отображать такую информацию, как адрес, рекомендуемое изображение, цена и краткое описание. Вот что вы можете увидеть в конце.
Идея состоит в том, чтобы научиться настраивать макет, а не копировать его точно. Как только вы поймете, как работают принципы, вы можете создать макет, который работает для вас.
Окончательный код этого проекта находится на GitHub .
Создание вашего проекта
Создайте новый проект в Android Studio с поддержкой Android 4.0.3 (API 15 — Ice Cream Sandwich) и выше, который охватит большую часть рынка Android. Добавьте пустое действие и назовите проект Rental Properties .
Как только Android Studio завершит настройку вашего проекта, вы должны увидеть простой класс MainActivity
activity_main.xml . Это все, что вам нужно для начала.
Определение исходных данных
Прежде чем вы сможете создать ListView
Поскольку каждое свойство имеет набор информации, начните с создания класса для установки и получения информации. Создайте новый класс Property
//Base class to hold information about the property
public class Property {
//property basics
private int streetNumber;
private String streetName;
private String suburb;
private String state;
private String description;
private String image;
private Double price;
private int bedrooms;
private int bathrooms;
private int carspots;
//constructor
public Property(int streetNumber, String streetName, String suburb, String state, String description, Double price, String image, int bedrooms, int bathrooms, int carspots){
this.streetNumber = streetNumber;
this.streetName = streetName;
this.suburb = suburb;
this.state = state;
this.description = description;
this.price = price;
this.image = image;
this.bedrooms = bedrooms;
this.bathrooms = bathrooms;
this.carspots = carspots;
}
//getters
public int getStreetNumber() { return streetNumber; }
public String getStreetName() {return streetName; }
public String getSuburb() {return suburb; }
public String getState() {return state; }
public String getDescription() {return description; }
public Double getPrice() {return price; }
public String getImage() { return image; }
public int getBedrooms(){ return bedrooms; }
public int getBathrooms(){ return bathrooms; }
public int getCarspots(){ return carspots; }
}
Это создает структуру для класса путем добавления личных полей для хранения информации, такой как адрес улицы, штат, описание и т. Д.
Продолжите, создав метод конструктора Property
Наконец, определите функции get
Создание файла макета XML
Теперь, когда у вас есть класс, вам нужно определить файл макета XML, используемый настраиваемым ArrayAdapter
Откройте папку проектов верхнего уровня res и найдите подкаталог макета . Создайте новый файл макета с именем ‘property_layout’ и добавьте RelativeLayout в качестве корневого тега (это характерно для моего дизайна).
Дизайн макета разделен на три раздела:
- Левый раздел, содержащий изображение.
- Правильный раздел, который содержит адрес и описание.
- Нижний раздел, который содержит информацию о свойствах, таких как количество спален, ванных комнат, места для автомобилей и цену.
При визуализации он должен выглядеть следующим образом.
Скопируйте следующее в ваш новый макет. Здесь вы создали вложенный макет, где каждый из разделов / элементов имеет идентификаторы, чтобы вы могли размещать и заполнять их. Не стесняйтесь настраивать поля, позиции и размеры элементов по своему усмотрению.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="10dp"
android:paddingBottom="10dp">
<ImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginRight="10dp"
android:contentDescription="Property Image" />
<LinearLayout
android:id="@+id/infoSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/image"
android:orientation="vertical">
<TextView
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:text="Street Address"
android:textSize="18sp" />
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:text="Description"
android:textSize="15sp" />
</LinearLayout>
<RelativeLayout
android:id="@+id/pricingSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/infoSection"
android:orientation="vertical">
<TextView
android:id="@+id/price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:text="Price" />
<TextView
android:id="@+id/bedroom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/price"
android:layout_marginRight="15dp"
android:text="Bed:" />
<TextView
android:id="@+id/bathroom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/price"
android:layout_marginRight="15dp"
android:layout_toRightOf="@id/bedroom"
android:text="Bath:" />
<TextView
android:id="@+id/carspot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/price"
android:layout_marginRight="15dp"
android:layout_toRightOf="@id/bathroom"
android:text="Car:" />
</RelativeLayout>
</RelativeLayout>
И замените TextView
activity_main.xml на:
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/customListView"></ListView>
Добавление изображений
Большинство компонентов пользовательского интерфейса — это просто элементы TextView
ImageView
Каждое из свойств имеет соответствующее изображение, нарисованное при визуализации представления. В этом примере я использовал имя property_image_X
Поскольку вы добавите 4 свойства, вам понадобятся 4 изображения, вы можете найти изображения, которые я использовал здесь .
Если вы ранее не добавляли изображения в проект Android Studio, скопируйте все изображения, которые вы хотите добавить в свой проект, и выберите « Вставить» в папку для рисования .
Выделение изображений и перетаскивание их в папку для рисования не будет работать.
Заполнение данных для использования
Поскольку у вас настроен базовый класс Property
После открытия класса MainActivity
ArrayList
private ArrayList<Property> rentalProperties = new ArrayList<>();
Теперь onCreate
MainActivity
//create property elements
rentalProperties.add(
new Property(10, "Smith Street", "Sydney", "NSW", "A large 3 bedroom apartment right in the heart of Sydney! A rare find, with 3 bedrooms and a secured car park.", 450.00, "property_image_1", 3, 1, 1));
rentalProperties.add(
new Property(66, "King Street", "Sydney", "NSW", "A fully furnished studio apartment overlooking the harbour. Minutes from the CBD and next to transport, this is a perfect set-up for city living.", 320.00, "property_image_2", 1, 1, 1));
rentalProperties.add(
new Property(1, "Liverpool Road", "Liverpool", "NSW", "A standard 3 bedroom house in the suburbs. With room for several cars and right next to shops this is perfect for new families.", 360.00, "property_image_3", 3, 2, 2));
rentalProperties.add(
new Property(567, "Sunny Street", "Gold Coast", "QLD", "Come and see this amazing studio appartment in the heart of the gold coast, featuring stunning waterfront views.", 360.00, "property_image_4" , 1, 1, 1));
Настройте это, если хотите, но обратите внимание, что все эти поля являются обязательными и должны быть определены в соответствии с ожиданиями конструктора (т.е. сначала номер улицы, затем название улицы, затем пригород и т. Д.).
ArrayAdapter
В реальном приложении вы извлекаете эти данные из веб-источника и создаете объекты на лету. Для этого урока проще определить значения в коде.
Создание пользовательского класса ArrayAdapter
Стандартный Android ArrayAdapter
Хотя это может быть хорошо для простых списков, вам нужна возможность предоставить свои собственные макеты и заполнить их соответствующим образом. Вы делаете это, создавая свой собственный класс MainActivty
Для простоты я создаю новый класс внутри класса //custom ArrayAdapter
class propertyArrayAdapter extends ArrayAdapter<Property>{
private Context context;
private List<Property> rentalProperties;
//constructor, call on creation
public propertyArrayAdapter(Context context, int resource, ArrayList<Property> objects) {
super(context, resource, objects);
this.context = context;
this.rentalProperties = objects;
}
//called when rendering the list
public View getView(int position, View convertView, ViewGroup parent) {
//get the property we are displaying
Property property = rentalProperties.get(position);
//get the inflater and inflate the XML layout for each item
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.property_layout, null);
TextView description = (TextView) view.findViewById(R.id.description);
TextView address = (TextView) view.findViewById(R.id.address);
TextView bedroom = (TextView) view.findViewById(R.id.bedroom);
TextView bathroom = (TextView) view.findViewById(R.id.bathroom);
TextView carspot = (TextView) view.findViewById(R.id.carspot);
TextView price = (TextView) view.findViewById(R.id.price);
ImageView image = (ImageView) view.findViewById(R.id.image);
//set address and description
String completeAddress = property.getStreetNumber() + " " + property.getStreetName() + ", " + property.getSuburb() + ", " + property.getState();
address.setText(completeAddress);
//display trimmed excerpt for description
int descriptionLength = property.getDescription().length();
if(descriptionLength >= 100){
String descriptionTrim = property.getDescription().substring(0, 100) + "...";
description.setText(descriptionTrim);
}else{
description.setText(property.getDescription());
}
//set price and rental attributes
price.setText("$" + String.valueOf(property.getPrice()));
bedroom.setText("Bed: " + String.valueOf(property.getBedrooms()));
bathroom.setText("Bath: " + String.valueOf(property.getBathrooms()));
carspot.setText("Car: " + String.valueOf(property.getCarspots()));
//get the image associated with this property
int imageID = context.getResources().getIdentifier(property.getImage(), "drawable", context.getPackageName());
image.setImageResource(imageID);
return view;
}
}
PropertyArrayAdapter
Выше вы создали новый адаптер с именем ArrayAdapter
private Context context;
private List<Property> rentalProperties;
Вы добавили два приватных поля вверху, потому что вам нужны методы внутри класса для доступа к контексту и списку свойств.
ArrayAdapter
ArrayAdapter Constructor
При расширении context
resource
objects
//constructor, call on creation
public propertyArrayAdapter(Context context, int resource, ArrayList<Property> objects) {
super(context, resource, objects);
this.context = context;
this.rentalProperties = objects;
}
ListView
Этот конструктор передает весь список свойств массива, а также контекст. Оба важны и присваиваются классам приватных переменных для использования.
Визуализация представления с помощью метода getView
Каждый раз, когда getView
//called when rendering the list
public View getView(int position, View convertView, ViewGroup parent) {
//get the property we are displaying
Property property = rentalProperties.get(position);
//get the inflater and inflate the XML layout for each item
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.property_layout, null);
}
Поскольку вы хотите отобразить пользовательский макет, вам нужно определить этот метод.
position
Поскольку у вас есть доступ к context
Теперь у вас есть этот объект, вы можете получить адрес, описание и все другие поля, используя методы getter класса.
Загрузка нашего XML-файла макета
Следующим шагом будет использование ранее сохраненного LAYOUT_INFLATER_SERVICE
LayoutInflater
Создайте новый объект detail_layout
//get the inflater and inflate the XML layout for each item
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.property_layout, null);
streetNumber
Заполнение файла макета и его возврат
Поскольку теперь у вас есть доступ к XML-файлу, вы можете найти элементы по их идентификатору.
Получить все элементы, а затем заполнить их содержимое. Вы создаете поле адреса путем объединения streetName
state
description
Другие элементы, такие как property_image_1
Актив изображения обрабатывается по-разному. При создании каждому из свойств присваивалось соответствующее имя изображения, например, getResources
Вы используете метод getIdentifier
ImageView
drawable . Это даст идентификатор ресурса для заполнения TextView description = (TextView) view.findViewById(R.id.description);
TextView address = (TextView) view.findViewById(R.id.address);
TextView bedroom = (TextView) view.findViewById(R.id.bedroom);
TextView bathroom = (TextView) view.findViewById(R.id.bathroom);
TextView carspot = (TextView) view.findViewById(R.id.carspot);
TextView price = (TextView) view.findViewById(R.id.price);
ImageView image = (ImageView) view.findViewById(R.id.image);
//set address and description
String completeAddress = property.getStreetNumber() + " " + property.getStreetName() + ", " + property.getSuburb() + ", " + property.getState();
address.setText(completeAddress);
//display trimmed excerpt for description
int descriptionLength = property.getDescription().length();
if(descriptionLength >= 100){
String descriptionTrim = property.getDescription().substring(0, 100) + "...";
description.setText(descriptionTrim);
}else{
description.setText(property.getDescription());
}
//set price and rental attributes
price.setText("$" + String.valueOf(property.getPrice()));
bedroom.setText("Bed: " + String.valueOf(property.getBedrooms()));
bathroom.setText("Bath: " + String.valueOf(property.getBathrooms()));
carspot.setText("Car: " + String.valueOf(property.getCarspots()));
//get the image associated with this property
int imageID = context.getResources().getIdentifier(property.getImage(), "drawable", context.getPackageName());
image.setImageResource(imageID);
//return the view, very important
return view;
ArrayAdapter
Этот процесс может быть настолько простым или сложным, насколько вам нужно. В нашем примере мы показываем довольно много информации и гарантируем, что она хороша для конечного пользователя. Вы можете сократить это, чтобы отобразить несколько предложений или расширить его, чтобы обеспечить больше функциональности.
Использование Custom ArrayAdapter
Теперь, когда у вас есть новый настроенный класс onCreate
MainActivity
//create our new array adapter
ArrayAdapter<Property> adapter = new propertyArrayAdapter(this, 0, rentalProperties);
Ниже, где вы добавили данные арендной собственности, создайте новый адаптер:
context
Три свойства, передаваемые в этот адаптер:
- Текущий
this
MainActivity
0
- Значение
0
ArrayList
-
ListView
Следующим шагом является подключение этого нового адаптера к ListView
Сделайте это, найдя setAdapter
//Find list view and bind it with the custom adapter
ListView listView = (ListView) findViewById(R.id.customListView);
listView.setAdapter(adapter);
ArrayAdapter
Вот и все!
Когда вы запускаете приложение, вы должны увидеть новый настроенный макет, определенный в XML-файле и настроенный внутри ListView
Бонус — динамическая загрузка макетов
В этом примере вы отобразили все свойства, используя property_layout.xml . Это было для простоты, так как обычно вы хотите, чтобы все элементы списка выглядели одинаково.
Когда каждое представление отображается внутри Property
Для простоты вы можете загрузить новый макет для выбранного элемента (например, рекомендуемого элемента) или измененную версию.
Вернитесь к примеру и измените его, чтобы выбранные свойства имели разный макет.
Измените класс featured
private Boolean featured;
Добавьте функцию получения:
public Boolean getFeatured(){return featured; }
Вам также необходимо добавить его в конструктор, чтобы убедиться, что переданные данные содержат эту дополнительную информацию.
this.featured = featured;
Например, установите свойство id 1, чтобы оно было добавлено, а остальные оставьте как есть.
Скопируйте файл property_layout.xml и переименуйте его в property_layout_alt.xml . Внесите любые изменения, которые вы хотите, я выделил адрес и цену на красном фоне.
Внутри класса MainActivity
getView
//conditionally inflate either standard or special template
View view;
if(property.getFeatured() == true){
view = inflater.inflate(R.layout.property_layout_alt, null);
}else{
view = inflater.inflate(R.layout.property_layout, null);
}
Вы проверяете, есть ли он, и загружаете ли он новый файл property_layout_alt.xml (который содержит новые визуальные изменения)
При запуске вы должны увидеть что-то вроде этого, с выделенными свойствами, используя альтернативный макет
Бонус — обработка событий кликов для вашего ArrayAdapter
Теперь, когда ваш контент отображается в великолепно выглядящем списке, вы можете отслеживать события кликов, чтобы запускать такие действия, как переход к новому действию.
Вернитесь к методу onCreate
MainActivity
//add event listener so we can handle clicks
AdapterView.OnItemClickListener adapterViewListener = new AdapterView.OnItemClickListener() {
//on click
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Property property = rentalProperties.get(position);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("streetNumber", property.getStreetNumber());
intent.putExtra("streetName", property.getStreetName());
intent.putExtra("suburb", property.getSuburb());
intent.putExtra("state", property.getState());
intent.putExtra("image", property.getImage());
intent.putExtra("bedrooms", property.getBedrooms());
intent.putExtra("bathrooms", property.getBathrooms());
intent.putExtra("carspots", property.getCarspots());
intent.putExtra("description", property.getDescription());
startActivity(intent);
}
};
//set the listener to the list view
listView.setOnItemClickListener(adapterViewListener);
Вы создаете новый объект onItemClickListener
onItemClick
В этом примере вы получаете свойство из списка арендуемых свойств, а затем устанавливаете дополнительные свойства Intent через объект Intent
После того, как вы настроили дополнительные функции, вы можете начать следующее действие, Детальное задание.
Когда пользователь щелкает элемент, он переходит в детальное действие. Чтобы добавить действие в файл манифеста проекта:
<activity
android:name=".DetailActivity"
android:name=".DetailActivity"
android:parentActivityName=".MainActivity" android:label="Rental Detail">
</activity>
В следующем упражнении вы можете получить дополнительные функции, переданные на этапе намерения, создавая ваше детальное действие.
Вот класс DetailActivity
public class DetailActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detail_layout);
//set the back (up) button
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//find all our view components
ImageView imageView = (ImageView) findViewById(R.id.image);
TextView addressTV = (TextView) findViewById(R.id.address);
TextView descriptionTV = (TextView) findViewById(R.id.description);
TextView priceTV = (TextView) findViewById(R.id.price);
TextView bedroomsTV = (TextView) findViewById(R.id.bedrooms);
TextView bathroomsTV = (TextView) findViewById(R.id.bathrooms);
TextView carspotsTV = (TextView) findViewById(R.id.carspots);
//collect our intent and populate our layout
Intent intent = getIntent();
Integer streetNumber = intent.getIntExtra("streetNumber", 0);
String streetName = intent.getStringExtra("streetName");
String suburb = intent.getStringExtra("suburb");
String state = intent.getStringExtra("state");
String description = intent.getStringExtra("description");
Double price = intent.getDoubleExtra("price", 0.0);
Integer bedrooms = intent.getIntExtra("bedrooms", 0);
Integer bathrooms = intent.getIntExtra("bathrooms", 0);
Integer carspots = intent.getIntExtra("carspots", 0);
String image = intent.getStringExtra("image");
Integer imageID = this.getResources().getIdentifier(image, "drawable", this.getPackageName());
String address = streetNumber + " " + streetName + ", " + suburb + ", " + state;
//set elements
imageView.setImageResource(imageID);
addressTV.setText(address);
descriptionTV.setText(description);
priceTV.setText('$' + Double.toString(price));
bathroomsTV.setText("Bathrooms: " + bathrooms);
bedroomsTV.setText("Bedrooms: " + bedrooms);
carspotsTV.setText("Car Spots: " + carspots);
//set the title of this activity to be the street name
getSupportActionBar().setTitle(address);
}
}
И макет связанной деятельности:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:contentDescription="Property Image"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_marginBottom="10dp"
android:background="#cc0033"/>
<TextView
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Address goes here"
android:layout_marginBottom="10dp"
android:textSize="18sp"/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="Description here"
android:layout_marginBottom="15dp"/>
<TextView
android:id="@+id/price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:text="price"/>
<TextView
android:id="@+id/bedrooms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bedrooms"
android:layout_marginBottom="5dp"/>
<TextView
android:id="@+id/bathrooms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bathrooms"
android:layout_marginBottom="5dp"/>
<TextView
android:id="@+id/carspots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Car Spots"
android:layout_marginBottom="5dp"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
Куда отсюда?
Теперь, когда вы знаете основы настройки класса ArrayAdapter, вы можете создавать отличные интерфейсы для своих списков. ArrayAdapter не ограничивается компонентом ListView, его также можно использовать с компонентами GridView или Spinner.
Создание богатых, визуально впечатляющих списков — отличный способ заинтересовать ваших пользователей. Вы могли бы расширить эти идеи, добавив анимацию прокрутки или выполнив различные функции для каждого элемента (например, зарегистрировав одно действие, которое срабатывает при нажатии, а другое — при пролистывании).
Любые комментарии или вопросы, пожалуйста, дайте мне знать в комментариях ниже.