介绍

在上一篇“SimpleAdapter“章节中,我们看到了把:ListView和Listview内部详细页面进行分离的Adapter的设计手法。

可是,这个SimpleAdapter的构造函数不够录活、苦涩难懂。很难满足我们实际大多生产场景的开发。

因此,今天我们就要来看一个更人性化的“自定义BaseAdapter“。实际生产应用场景开发中充斥着自定义BaseAdapter,因此必须要提及它并且围绕着这个extends BaseAdapter我们要持续说不少高级特性。

先来看一下课程最终要实现的目标

有喵、有汪、有金钱。还多了表头和表尾。

我们这次就要使用真正的面向业务逻辑、面向对象的手法来实现这个界面。

设计

上述界面其实和上一篇例子相仿,使用到了:1个ImageView、两个TextView。

只不过这次我们用的是标准MVC模式的自定义Adapter。

项目结构

是不是很详尽?保姆式教程,不详尽不称为“保姆”。

先来看UI端代码

UI端代码

这一块和上一篇几乎相似,没有什么太多变化

activity_main.xml文件

很简单,没有任何神密可言,就一个“光杆”Listview的存在。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><ListViewandroid:id="@+id/listView"android:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>

customized_layout.xml文件

内容也是very easy,属于“常规损人和”,和上一篇无异。

<?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"><!-- 定义一个用于显示头像的ImageView --><ImageViewandroid:id="@+id/touxiang"android:layout_width="64dp"android:layout_height="64dp"android:baselineAlignBottom="true"android:paddingLeft="8dp" /><!-- 定义一个竖直方向的LinearLayout,把QQ呢称与说说的文本框设置出来 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:id="@+id/name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:paddingLeft="8dp"android:textColor="#1D1D1C"android:textSize="20sp" /><TextViewandroid:id="@+id/description"android:layout_width="wrap_content"android:layout_height="wrap_content"android:paddingLeft="8px"android:textColor="#B4B4B9"android:textSize="14sp" /></LinearLayout>
</LinearLayout>

后端代码

PetBean.java

package org.mk.android.demo.democustomizedadapter;import java.io.Serializable;public class PetBean implements Serializable {private String name = "";private int imgId;private String description = "";public int getImgId() {return imgId;}public void setImgId(int imgId) {this.imgId = imgId;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getName() {return name;}public void setName(String name) {this.name = name;}public PetBean(int touxiang, String name, String description) {this.imgId = touxiang;this.name = name;this.description = description;}
}

这个Java Bean里分别就对应着一个ImageView,两个TextView。

始终记得,把Image传递给到Adapter用的是一个int值,它来源于:R.drawable.图片名称(不带.即postfix)。

PetAdapter.java

package org.mk.android.demo.democustomizedadapter;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;import java.util.List;public class PetAdapter extends BaseAdapter {private List<PetBean> data;private Context ctx;public PetAdapter(List<PetBean> data, Context ctx) {this.data = data;this.ctx = ctx;}@Overridepublic int getCount() {if (data != null) {return data.size();}return 0;}@Overridepublic Object getItem(int i) {return null;}@Overridepublic long getItemId(int i) {return i;}@Overridepublic View getView(int i, View view, ViewGroup viewGroup) {MyViewHolder viewHolder=null;if (view == null) {view = LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false);viewHolder=new MyViewHolder();viewHolder.touxiang = (ImageView) view.findViewById(R.id.touxiang);viewHolder.name = (TextView) view.findViewById(R.id.name);viewHolder.description = (TextView) view.findViewById(R.id.description);view.setTag(viewHolder);}else{viewHolder=(MyViewHolder)view.getTag();}if (data != null) {viewHolder.touxiang.setBackgroundResource(data.get(i).getImgId());viewHolder.name.setText(data.get(i).getName());viewHolder.description.setText(data.get(i).getDescription());return view;}return null;}static class MyViewHolder {public ImageView touxiang;public TextView name;public TextView description;}
}

代码导读

整个自定义的Adapter是extends自BaseAdapter,这个BaseAdapter在extends后有几个方法需要进行覆盖:

  • 构造函数,构造函数里需要两个参数:

    • 第一个参数,构造函数里把自定义的数据源在上一例里我们用的是List<Map<String,Object>>(不够面向对象),而这边就是List<我们的ViewBean>传进去;
    • Context,如果在MainActivity.java里,我们就可以用这样的形式来传这个参数:Context ctx = MainActivity.this;
  • public int getCount() ,它返回的就是你的ListView里有多少行的这个size即我们在构造方法里传入的这个List<ViewBean>的size;
  • public Object getItem(int i),这个方法我们在后一步,高级定制化Adapter里会进一步用到,目前在此我们直接return null就完事了,不用作纠结;
  • public long getItemId(int i),这边的int i其实是position,我们可以这么干:直接return i即可,它其实是一种“一行行从List<ViewBean>取出数据做渲染”用的;
  • public View getView(int i, View view, ViewGroup viewGroup) ,这个函数是核心,它的故事长了,来看一步步导读:
    • 这个方法的作用就是一条条把List<ViewBean>数据取出来作渲染用的,它依赖于这一句话:LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false);这个语句被调用的次数=List.size(),每调用一次这条语句,Android界面会渲染一次(一次开销);
    • 每一个ListView内的行显示的内容根据List<ViewBean>里每一行不同的内容会有不同的显示,在这边的一行指的就是:一个ImageView+两个TextView的渲染。因此你要做的就是一个个“控制件名.set属性(List里取出相应的该行的这个数组的属性件)”,因此才有了如此的写法:name.setText(data.get(i).getName());如:description.setText(data.get(i).getDescription());如:touxiang.setBackgroundResource(data.get(i).getImgId());这样的东西。随便说一句:此处的i带的正是getView里的(int i...)里的这个i,这个i对应着你的List<ViewBean>里当前的“游标”;
    • 全部一个个set完了后,把这个view return出去;
    • 接着我们来说,这块代码看似没逻辑那为什么会有:View Holder这是一个什么鬼?前面我们提到了一句:LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false);这个语句被调用的次数=List.size(),每调用一次这条语句,Android界面会渲染一次,这个动作其实是很开销资源的。比如说我的List<ViewBean>里有100条数据,Android会界面渲染100次。其实这个渲染只是一个“一次性”的事,在这边只要渲染一次就够了,其余99次是多余重复的。渲染太多会造成这个Android极其吃手机的“运存”。所以我们使用了一个小技巧:只在这个View为空时做一次渲染。渲染过后就不要再渲染了,直接填充界面控件内的属性值就行了。因此才有了第一个if (view == null) {的判断。
    • 那么ViewHolder呢?还是没有解释ViewHolder的作用。我们前面解决了这个LayoutInflater.from(ctx).inflate的重复调用问题,但是读者们知道吗?你在getView方法里的findViewById(R.id.description)这样的东西也是会每次被重复调用一次的,举例来说:你有3个控件,在List<ViewBean>里有3行数据,你以为你只调用了3次findViewById?其实是调用了总计3*3=9次,即调用第二个控件的findViewById时它依旧会重复调用第一个控件的findViewById。这个动作也是开销Android的运存和cpu的。那么我们同样为了减少findViewById的重复调用,因此我们使用一个MyViewHolder,让其和我们的ViewBean(此处就是PetBean)一样的结构,它专门是用于保存已经被调用过findViewById的状态(TAG)。然后使用view.setTag和view.getTag来做状态保留。如果这个Tag存在那么不用再findViewById一次了。如果不存在再findViewById一次

接着我们就来看交程序交互后端代码MainActivity.java

MainActivity.java

package org.mk.android.demo.democustomizedadapter;import androidx.appcompat.app.AppCompatActivity;import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ListView;import java.util.ArrayList;
import java.util.List;public class MainActivity extends AppCompatActivity {private List<PetBean> data = null;private Context ctx;private PetAdapter adapter = null;private ListView listView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ctx = MainActivity.this;listView = (ListView) findViewById(R.id.listView);data = new ArrayList<PetBean>();data.add(new PetBean(R.drawable.cat,"猫","这是一只猫"));data.add(new PetBean(R.drawable.dog,"狗","这是一只狗"));data.add(new PetBean(R.drawable.jingqianbao,"金钱豹","这是金钱豹"));adapter = new PetAdapter((List<PetBean>) data, ctx);final LayoutInflater inflater = LayoutInflater.from(this);View headView = inflater.inflate(R.layout.view_header, null, false);View footView = inflater.inflate(R.layout.view_footer, null, false);listView.addHeaderView(headView);listView.addFooterView(footView);listView.setAdapter(adapter);}
}

我们这次为我们的ListView增加了一个表头,一个表尾。表头表尾分别对应着两个layout xml文件,它们位于我们项目的res\layout目录下。

注:

记得在调用addHeaderView和addFootView的动作必须位于setAdapter(adapter)语句前;

表头样式-view_header.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="vertical"android:gravity="center"><TextViewandroid:layout_width="match_parent"android:layout_height="48dp"android:textSize="18sp"android:text="表头"android:gravity="center"android:background="#43BBEB"android:textColor="#FFFFFF"/>
</LinearLayout>

表尾样式-view_footer.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="vertical"android:gravity="center"><TextViewandroid:layout_width="match_parent"android:layout_height="48dp"android:textSize="18sp"android:text="表尾"android:gravity="center"android:background="#ECE9E6"android:textColor="#0C0C0C"/>
</LinearLayout>

运行效果

自己动一下手试试就能找到自定义Adapter的感觉。自定义Adapter的作用很大、使用场景也很多。我们后面会继续强化自定义Adapter的业务场景的使用。

Android入门第26天-在Android里自定义Adapter相关推荐

  1. Android入门第3天-在Android Studio里配置虚拟器

    配置 点击右上角这个地方,选Device Manager 然后你会看到以下这个界面: 然后你这边按下[Next]按钮后会来到下面这个界面 在左边有一个"下载"箭头的代表,这个虚拟机 ...

  2. Android零基础入门第26节:layout_gravity和gravity大不同

    Android零基础入门第26节:layout_gravity和gravity大不同 原文:Android零基础入门第26节:layout_gravity和gravity大不同 上一期我们一起学习了L ...

  3. Android零基础入门第33节:Android事件处理概述

    原文:Android零基础入门第33节:Android事件处理概述 通过对Android基本组件的学习,也有接触少部分Android的事件处理,比如按钮的点击事件.选框的状态切换事件. 一.Andro ...

  4. Android零基础入门第1节:Android的前世今生

    原文:Android零基础入门第1节:Android的前世今生 现在网上有很多各色Android资料了,但相对来说还是比较零散,Android覆盖的范围极广,最近刚好有机会全部拉通整理一遍,也保存起来 ...

  5. Android入门第七篇之ListView (二)

    Android入门第六篇之ListView (一) ,讲的是如何制作一个具有两行文本的 自定义控件 ,作为ListView的Item的使用方法.这篇接下来也是围绕ListView和Item,更加深入地 ...

  6. Android 入门第四讲03-列表RecyclerView(RecyclerView使用步骤(详),RecyclerView指定一行item的数目+指定一行item的数量,并且设置列表方向)

    Android 入门第四讲03-列表RecyclerViewRecyclerView使用步骤(详),RecyclerView指定一行item的数目+指定一行item的数量,并且设置列表方向) 1.Re ...

  7. Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)

    Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)

  8. Android入门第31天-Android里的ViewFlipper翻转视图的使用

    介绍 本篇给大家带了的是ViewFlipper,它是Android自带的一个多页面管理控件,且可以自动播放! 和ViewPager不同,ViewPager是一页页的,而ViewFlipper则是一层层 ...

  9. Android入门第20天-Android里的ScrollView的使用

    介绍 ScrollView(滚动条),它有两种"滚动条": 竖直滚动条: 水平方向上的滚动条:HorizontalScrollView: 我们经常可以看到在手机里正在垂直加载一堆的 ...

最新文章

  1. 史上最易懂的Kubernetes儿童插图指南
  2. python输出数据到excel-python实现数据导出到excel的示例--普通格式
  3. excel批量更改超链接_Excel如何批量建立超链接,搭建工作台
  4. 【整理】ABAP 7.40新特性介绍(下)
  5. P2787 语文1(chin1)- 理理思维
  6. matlab中计算不等式的解,matlab解不等式
  7. Visual C# 诠释常用排序算法
  8. Python | threading02 - 互斥锁解决多个线程之间随机调度,造成“线程不安全”的问题。
  9. Qt工作笔记-Windows上界面滑动效果
  10. 想到Exchanger N parties的一种用法
  11. JavaScript:设置网站title
  12. java中如何表示圆周率
  13. 作为一个程序猿你必须会的技能----自学框架!
  14. 学习笔记:Github(1)站在巨人的肩膀上
  15. 3.17 inappropriateintimacy (狎昵关系)
  16. fluent udf dpm 捕集效率
  17. 黄金票据的制作与使用
  18. 操作系统笔记 清华大学陈渝
  19. WPS添加下划线,文字尾部不显示下划线问题解决(一个So stupid问题)
  20. 数据分析6-数据可视化

热门文章

  1. SpringBoot2.x 集成 七牛云对象存储Kodo
  2. Linux之C++获取系统用户名
  3. cat 链接并显示文件
  4. wpf入门第七篇 使用Squirrel自动更新应用
  5. 猫和老鼠服务器未响应是怎么回事,猫和老鼠手游怎么玩不了?游戏设置及异常解决方法[多图]...
  6. html代码 如何使用方法,如何使用html代码
  7. 微信墙如何搭建服务器地址,如何制作微信墙
  8. 使用Weka进行数据可视化
  9. (十)Intellij 远程调试 a bug
  10. oracle查询练习2(解析+答案)