Android之Adapter(适配器)

继承关系

Adapter是一个接口:
在这里插入图片描述
BaseAdapter是一个抽象类,BaseAdapter也实现了ListAdapter接口,通常会继承BaseAdapter类来实现更复杂的需求:
在这里插入图片描述

常用的Adapter

适配器对象充当视图(AdapterView)与该视图的基础数据之间的桥梁,ListView ,GridView等均为AdapterView的子类,所以通常配合适配器使用。适配器提供对数据项的访问, 适配器还负责为数据集中的每个项目生成一个View 。

ArrayAdapter

常用的构造函数:ArrayAdapter(Context context, int resource, T[] objects),依次传入的三个参数为:①当前上下文;②ListView等组件子项布局的id;③需要适配的数据。

示例

运行效果:
在这里插入图片描述
代码:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView listView = findViewById(R.id.listView);

        List<String> list = new ArrayList<>();
        for(int i = 0; i < 20; i++){
            list.add(String.valueOf(i));
        }
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, list);
        listView.setAdapter(arrayAdapter);
    }
}

R.layout.simple_list_item_1

注:上面ArrayAdapter构造函数的第二个参数R.layout.simple_list_item_1是Android提供的列表item,下面列出的是其它自带的item,也可以使用自己自定义的xml文件。常用的几个:

  • android.R.layout.simple_list_item_1 //一行text
  • android.R.layout.simple_list_item_2 //一行title,一行text
  • android.R.layout.simple_list_item_single_choice //单选按钮
  • android.R.layout.simple_list_item_multiple_choice //多选按钮
  • android.R.layout.simple_list_item_checked //勾选框
    在这里插入图片描述

SimpleAdapter

虽然字面上看是“简单适配器”,但是SimpleAdapter功能强大,可以实现复杂的效果,已能满足许多需求。

构造函数: SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to),传入的五个参数分别是:①当前上下文;②数据列表,列表中的每个条目对应于列表中的一行,映射包含每一行的数据,并且应该包含“from”中指定的所有条目;③显示列表项数据的视图资源符,此布局中需包含“to”参数中定义的视图;④与每个列表项关联的映射中的列名;⑤在“from”参数中显示的列表项视图。

示例

运行效果:
在这里插入图片描述
代码:

  • 自定义每个列表项布局:list_item.xml
<?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="horizontal">
    
    <ImageView
        android:id="@+id/ivChatHead"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:baselineAlignBottom="true"
        android:paddingLeft="8dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="8dp"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/tvContent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="8px"
            android:textColor="#808080"
            android:textSize="14sp" />

    </LinearLayout>
</LinearLayout>

  • MainActivity.java
public class MainActivity extends AppCompatActivity {
    private int[] chatHead = new int[]{R.mipmap.chathead1, R.mipmap.chathead2, R.mipmap.chathead3};
    private String[] name = new String[]{"张三", "李四", "王五"};
    private String[] content = new String[]{"我是张三,大家好", "今天天气不错", "好嗨哟"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView listView = findViewById(R.id.listView);

        List<Map<String, Object>> list = new ArrayList<>();
        for (int i = 0; i < name.length; i++) {
            Map<String, Object> map = new HashMap<>();
            map.put("chatHead", chatHead[i]);
            map.put("name", name[i]);
            map.put("content", content[i]);
            list.add(map);
        }
        
        SimpleAdapter myAdapter = new SimpleAdapter(this, list, R.layout.list_item,
                new String[]{"chatHead", "name", "content"}, new int[]{R.id.ivChatHead, R.id.tvName, R.id.tvContent});
        listView.setAdapter(myAdapter);
    }
}

调整为类似微信列表布局

微信的列表布局与上面的差别在分隔线,上面使用的是默认的分隔线。这里做了两个调整:①将组件调整对齐;②更改分隔线为自定义。

运行效果:
在这里插入图片描述
代码:

  • 自定义分隔线:list_item_driver.xml
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@color/lineColorGray"
    android:insetLeft="70dp">
</inset>

注: "inset"标签,可以将其它的Drawable内嵌到自己当中(上面引入了颜色资源来设置分隔线的颜色),并可以在四周预留出一定的间距。

  • 列表项布局:list_item.xml
<?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="horizontal">

    <LinearLayout
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:gravity="center">
        <ImageView
            android:id="@+id/ivChatHead"
            android:layout_width="50dp"
            android:layout_height="50dp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:orientation="vertical"
        android:gravity="center_vertical">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/tvContent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#808080"
            android:textSize="15sp" />

    </LinearLayout>
</LinearLayout>

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@drawable/list_item_driver"
        android:dividerHeight="1dp">

    </ListView>

</android.support.constraint.ConstraintLayout>

注:给ListView设置自定义分隔线时,必须重新设置线高(android:dividerHeight),否则分隔线将不显示。具体可以查看ListView的源码,如果不设置高度,Android会将高度设置为-1。

参数from是如何与参数to绑定的

使用SimpleAdapter之后可能会产生疑问

为什么to参数中的View会显示from参数的值为自己的text,如果form参数不是String/int类型的,而是Bitmap类型的,那么to参数中的View还会显示吗?

答案是:不会。

这种情况就需要自定义Adapter了,SimpleAdapter参数的绑定是有限制的,查看SimpleAdapter的源码后这一点会更加清晰。

下面是从SimpleAdapter源码中提取出来的方法,可以回答上面的问题。

/**
 * Called by bindView() to set the image for an ImageView but only if
 * there is no existing ViewBinder or if the existing ViewBinder cannot
 * handle binding to an ImageView.
 * <p>
 * This method is called instead of {@link #setViewImage(ImageView, String)}
 * if the supplied data is an int or Integer.
 *
 * @param v     ImageView to receive an image
 * @param value the value retrieved from the data set
 * @see #setViewImage(ImageView, String)
 */
public void setViewImage(ImageView v, int value) {
    v.setImageResource(value);
}

/**
 * Called by bindView() to set the image for an ImageView but only if
 * there is no existing ViewBinder or if the existing ViewBinder cannot
 * handle binding to an ImageView.
 * <p>
 * By default, the value will be treated as an image resource. If the
 * value cannot be used as an image resource, the value is used as an
 * image Uri.
 * <p>
 * This method is called instead of {@link #setViewImage(ImageView, int)}
 * if the supplied data is not an int or Integer.
 *
 * @param v     ImageView to receive an image
 * @param value the value retrieved from the data set
 * @see #setViewImage(ImageView, int)
 */
public void setViewImage(ImageView v, String value) {
    try {
        v.setImageResource(Integer.parseInt(value));
    } catch (NumberFormatException nfe) {
        v.setImageURI(Uri.parse(value));
    }
}

/**
 * Called by bindView() to set the text for a TextView but only if
 * there is no existing ViewBinder or if the existing ViewBinder cannot
 * handle binding to a TextView.
 *
 * @param v    TextView to receive text
 * @param text the text to be set for the TextView
 */
public void setViewText(TextView v, String text) {
    v.setText(text);
}

如果to参数中的View是TextView的话,则会调用上面的setViewText()方法,将from参数设置为TextView的text,此时如果from参数不是String类型的而是int类型的,则会调用SimpleAdapter源码中的bindView(int position, View view)方法将int转换为String。

如果to参数中的View是ImageView 的话,则会调用上面的setViewImage()方法来设置ImageView 的背景图片,不过这里只接受int类型和uri类型的参数,所以传进来Bitmap类型的参数的话将不会正常显示,只好通过自定义Adapter的方式来弥补了。

自定义Adapter

这里用自定义的Adapter实现上面同样的效果,与上面不同的是:这里传进来的头像图片不是项目目录下的资源,而是某个文件目录下的图片。

示例

代码:

  • 列表项数据类:MyListItemInfo.java
public class MyListItemInfo {
    private Bitmap chatHead;
    private String name;
    private String content;

    public Bitmap getChatHead() {
        return chatHead;
    }

    public void setChatHead(Bitmap chatHead) {
        this.chatHead = chatHead;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}
  • 自定义Adapter:MyListAdapter .java
public class MyListAdapter extends BaseAdapter {
    private Context context = null;
    private List<MyListItemInfo> list = null;

    public MyListAdapter(Context context, List<MyListItemInfo> list){
        this.context = context;
        this.list = list;
    }

    @Override
    public int getCount() {
        int count = 0;
        if (list != null) {
            count = list.size();
        }
        return count;
    }

    @Override
    public MyListItemInfo getItem(int position) {
        MyListItemInfo item = null;
        if (list != null) {
            item = list.get(position);
        }
        return item;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    //convertView参数用于将之前加载好的布局进行缓存,以便旧的View可以重用
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        //如果convertView为null,则创建一个ViewHolder对象,并将控件的实例放在ViewHolder中
        if (convertView == null) {
            viewHolder = new ViewHolder();
            LayoutInflater mInflater = LayoutInflater.from(context);
            convertView = mInflater.inflate(R.layout.list_item, parent);

            viewHolder.ivChatHead = (ImageView) convertView.findViewById(R.id.ivChatHead);
            viewHolder.tvName = (TextView) convertView.findViewById(R.id.tvName);
            viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tvContent);
            //将Holder存储到convertView中
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        //为viewHolder设置项目数据
        MyListItemInfo myListItemInfo = getItem(position);
        if (myListItemInfo != null) {
            viewHolder.ivChatHead.setImageBitmap(myListItemInfo.getChatHead());
            viewHolder.tvName.setText(myListItemInfo.getName());
            viewHolder.tvContent.setText(myListItemInfo.getContent());
        }

        return convertView;
    }

    //使用ViewHolder对控件的实例进行缓存
    private static class ViewHolder{
        private ImageView ivChatHead;
        private TextView tvName;
        private TextView tvContent;
    }
}

  • MainActivity.java
public class MainActivity extends AppCompatActivity {
    private List<Bitmap> chatHeadList = null;
    private String[] name = new String[]{"张三", "李四", "王五"};
    private String[] content = new String[]{"我是张三,大家好", "今天天气不错", "好嗨哟"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String filePath= Environment.getExternalStorageDirectory().toString() + "/AAAMyImage";
        //运行时权限申请
        if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
        }else {
            try {
                chatHeadList = getBitmapFromFiles(filePath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        ListView listView = findViewById(R.id.listView);

        List<MyListItemInfo> list = new ArrayList<>();
        int nameLength = name.length;
        for (int i = 0; i < nameLength; i++) {
            MyListItemInfo myListItemInfo = new MyListItemInfo();
            if (chatHeadList != null) {
                myListItemInfo.setChatHead(chatHeadList.get(i));
            }
            myListItemInfo.setName(name[i]);
            myListItemInfo.setContent(content[i]);
            list.add(myListItemInfo);
        }

        MyListAdapter myAdapter = new MyListAdapter(this, list);
        listView.setAdapter(myAdapter);
    }
    
    public static List<Bitmap> getBitmapFromFiles(String path) throws FileNotFoundException {
        File file = new File(path);
        //listFiles是获取该目录下所有文件和目录的绝对路径
        File[] files = file.listFiles();
        if (files == null){
            Log.e("error","空目录");
            //return null;
        }

        List<Bitmap> bitmapList = new ArrayList<>();
        int filesLength = files.length;
        for(int i =0; i < filesLength; i++){
            FileInputStream fis = new FileInputStream(files[i]);

            //把流转化为Bitmap图片
            bitmapList.add(BitmapFactory.decodeStream(fis));
        }
        return bitmapList;
    }
}
  • 程序中需要读取本地文件,需添加权限:AndroidManifest.xml
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

☝ ★★★ — 返回 《Android开发笔记汇总》总目录 — ★★★ ☝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

画茧自缚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值