文章目录


在开发中,我们经常使用到ListView这个控件。 Android 的API也提供了许多创建ListView适配器的快捷方式。例如 ArrayAdapter、SimpleAdapter和SimpleCursorAdapter 等。但你是否发现,如果采用这些系统自带的适配器,对于事件的响应只能局限在一个行单位。假设一行里面有一个按钮和一个图片控件,它们之间的响应操作是不一样的。若采用系统自带的适配器,就 不能精确到每个控件的响应事件 。这时,我们一般采取自定义适配器来实现这个比较精确地请求。


案例:商品列表

一、创建列表单元模板

  • 基本布局
  • 【Android】ListView自定义Adapter_android

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".listview.ListView2ItemActivity">

<TextView
android:id="@+id/comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="118dp"
android:layout_marginTop="97dp"
android:text="好吃!"
android:textColor="@android:color/holo_orange_dark"
android:textSize="20dp"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteX="68dp"
tools:layout_editor_absoluteY="64dp" />

<TextView
android:id="@+id/good_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="200dp"
android:layout_marginTop="32dp"
android:text="杨桃"
android:textColor="@android:color/holo_purple"
android:textSize="20dp"
tools:layout_editor_absoluteX="68dp"
tools:layout_editor_absoluteY="101dp" />

<TextView
android:id="@+id/price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="179dp"
android:layout_marginTop="65dp"
android:text="15.00/kg"
android:textColor="@android:color/holo_green_dark"
android:textSize="20dp"
tools:layout_editor_absoluteX="69dp"
tools:layout_editor_absoluteY="136dp" />

<Button
android:id="@+id/purchase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="56dp"
android:layout_marginEnd="33dp"
android:text="购买"
android:textSize="20dp"
tools:layout_editor_absoluteX="244dp"
tools:layout_editor_absoluteY="90dp" />

<ImageView
android:id="@+id/good_img"
android:layout_width="71dp"
android:layout_height="81dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="25dp"
android:layout_marginTop="37dp"
app:srcCompat="@android:drawable/btn_star_big_on" />

<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="119dp"
android:layout_marginTop="65dp"
android:text="价格:"
android:textSize="20dp" />

<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="118dp"
android:layout_marginTop="32dp"
android:text="商品名:"
android:textSize="20dp" />
</RelativeLayout>

返回顶部


二、使用bean封装单个商品

  • 就是ListView中每一条对应的商品规范,这里也可以看做是单个商品的信息模板。
package com.example.jyandroid.listview;

public class Product {

// 封裝模板的数据
private String title;
private Double price;
private Integer imgId;

// setter、getter方法集
public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public Double getPrice() {
return price;
}

public void setPrice(Double price) {
this.price = price;
}

public Integer getImgId() {
return imgId;
}

public void setImgId(Integer imgId) {
this.imgId = imgId;
}
}

返回顶部


三、自定义Adapter适配器

  • 继承自BaseAdapter类
  • BaseAdapter的四个基础方法
  • getCount : 要绑定的条目的数目,比如格子的数量(ListView中item的数目)
  • getItem : 根据一个索引(位置)获得该位置的对象
  • getItemId : 获取条目的id
  • getView : 获取该条目要显示的界面
package com.example.jyandroid.listview;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.jyandroid.R;
import java.util.List;

/* 把数据组装到页面控件中,生成所有的行 */
public class MyAdapter extends BaseAdapter {

// 数据bean
private List<Product> productions; // 商品集合
private Context context; // 上下文对象

/**
* 含参构造
* @param productions 商品集合
* @param context 上下文对象
*/
public MyAdapter(List<Product> productions, Context context) {
super();
this.productions = productions;
this.context = context;
}

/**
* @return 获取总加载数量,返回总数据集合的大小
*/
@Override
public int getCount() {
return productions.size();
}

/**
* @param position 获取具体行的控件item
* @return 返回获取具体行的控件item
*/
@Override
public Object getItem(int position) {
return productions.get(position);
}

/**
* @param position 当前位置的行号
* @return 返回当前位置的行号
*/
@Override
public long getItemId(int position) {
return position;
}

/**
* @param position 当前所处的位置 第一行0
* @param convertView 整个行被当做容器管理,包含一行中的组件 --- 完整的当前位置的视图
* @param parent 父级容器
* @return 返回view对象,用于安卓界面显示
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {

// 1.convertView是一个作为缓存的view,通过使用这个缓存可以替换掉用Inflater加载组件这一步。
convertView = LayoutInflater.from(context).inflate(R.layout.activity_list_view2_item,null);

// 2.获取组装 item 控件,一个图片两个文本一个按钮 --- 模板
ImageView tv_img = convertView.findViewById(R.id.good_img);
TextView tv_title = convertView.findViewById(R.id.good_name);
TextView tv_price = convertView.findViewById(R.id.price);
Button purchase = convertView.findViewById(R.id.purchase);

// 3.待组装的每一个item对应的bean,从对应位置中获取对应bean的数据
Product product = productions.get(position);
Integer imgId = product.getImgId();
String title = product.getTitle();
Double price = product.getPrice();

// 4.将数据配置到模板组件中
tv_img.setImageResource(imgId);
tv_title.setText(title);
tv_price.setText(String.valueOf(price));

// 5.按钮事件监听
purchase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,"点击的是第"+(position+1)+"行",Toast.LENGTH_LONG).show();
}
});
return convertView;
}
}

解释一下LayoutInflater方法,它是用来加载布局的,用LayoutInflater的inflate方法就可以将你的item布局绘制出来。getView方法中的三个参数,第一position是指现在是第几个条目;第二convertView是已经绘制好了的视图;parent是ListView之类的View视图。用inflate方法绘制好后的view最后return返回给getView方法就可以了。

返回顶部


四、启用Activity

简单来说,药片大家都见过,就像生产药品一样,外面装药的模板(activity_list_view2_item.xml)、药品种类(Product)、组装器((MyAdapter)都已经准备好了,最后只需要开启生产线投入原料运作就可以了,最后这个过程就类似Activity的功效,这里我们模拟数据来实现。

package com.example.jyandroid.listview;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import com.example.jyandroid.R;
import java.util.ArrayList;
import java.util.List;

public class ListView2Activity extends AppCompatActivity {
// 声明组件
private ListView listview2;
private List<Product> productList = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view2);
// 获取组件对象
listview2 = findViewById(R.id.listview2);
// 模拟数据
String[] datas = {"商品1","商品2","商品3","商品4"};
Double[] prices = {11.11,22.22,33.33,44.44};
int[] imgIds = {R.mipmap.dou,R.mipmap.heihei,R.mipmap.huahua,R.mipmap.tiantian};
// 将数据封装至模板
productList = new ArrayList<>();
for (int i = 0; i < datas.length; i++) {
Product product = new Product();
product.setTitle(datas[i]);
product.setPrice(prices[i]);
product.setImgId(imgIds[i]);
productList.add(product);
}
// 利用适配器进行组装
MyAdapter myAdapter = new MyAdapter(productList,this);
// 接通视图
listview2.setAdapter(myAdapter);
}
}

返回顶部


五、修改主程序

【Android】ListView自定义Adapter_数据_02


返回顶部


六、运行

【Android】ListView自定义Adapter_控件_03


返回顶部