Android入门之实现自定义Adapter

作者:TGITCIC 时间:2021-09-30 17:34:10 

介绍

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

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

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

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

Android入门之实现自定义Adapter

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

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

设计

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

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

项目结构

Android入门之实现自定义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">

<ListView
       android:id="@+id/listView"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
</LinearLayout>

customized_layout.xml文件

内容也是very easy,属于&ldquo;常规损人和&rdquo;,和上一篇无异。

<?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 -->
   <ImageView
       android:id="@+id/touxiang"
       android:layout_width="64dp"
       android:layout_height="64dp"
       android:baselineAlignBottom="true"
       android:paddingLeft="8dp" />

<!-- 定义一个竖直方向的LinearLayout,把QQ呢称与说说的文本框设置出来 -->
   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">

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

<TextView
           android: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;
   }

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

@Override
   public Object getItem(int i) {
       return null;
   }

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

@Override
   public 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后有几个方法需要进行覆盖:

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

  • 第一个参数,构造函数里把自定义的数据源在上一例里我们用的是List<Map<String,Object>>(不够面向对象),而这边就是List<我们的ViewBean>传进去;

  • Context,如果在MainActivity.java里,我们就可以用这样的形式来传这个参数:Context ctx = MainActivity.this;

2.public int getCount() ,它返回的就是你的ListView里有多少行的这个size即我们在构造方法里传入的这个List<ViewBean>的size;

3.public Object getItem(int i),这个方法我们在后一步,高级定制化Adapter里会进一步用到,目前在此我们直接return null就完事了,不用作纠结;

4.public long getItemId(int i),这边的int i其实是position,我们可以这么干:直接return i即可,它其实是一种&ldquo;一行行从List<ViewBean>取出数据做渲染&rdquo;用的;

5.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的渲染。因此你要做的就是一个个&ldquo;控制件名.set属性(List里取出相应的该行的这个数组的属性件)&rdquo;,因此才有了如此的写法: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>里当前的&ldquo;游标&rdquo;;

全部一个个set完了后,把这个view return出去;

接着我们来说,这块代码看似没逻辑那为什么会有:View Holder?这是一个什么鬼?前面我们提到了一句:

LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false);这个语句被调用的次数=List.size(),每调用一次这条语句,Android界面会渲染一次,这个动作其实是很开销资源的。比如说我的List<ViewBean>里有100条数据,Android会界面渲染100次。其实这个渲染只是一个&ldquo;一次性&rdquo;的事,在这边只要渲染一次就够了,其余99次是多余重复的。渲染太多会造成这个Android极其吃手机的&ldquo;运存&rdquo;。所以我们使用了一个小技巧:只在这个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;
   @Override
   protected 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">
   <TextView
       android: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">
   <TextView
       android:layout_width="match_parent"
       android:layout_height="48dp"
       android:textSize="18sp"
       android:text="表尾"
       android:gravity="center"
       android:background="#ECE9E6"
       android:textColor="#0C0C0C"/>
</LinearLayout>

运行效果

Android入门之实现自定义Adapter

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

来源:https://blog.csdn.net/lifetragedy/article/details/127851339

标签:Android,自定义,Adapter
0
投稿

猜你喜欢

  • Android横竖屏幕切换小结

    2023-04-15 08:03:20
  • Unity 如何通过反射给gameObject添加组件

    2022-06-14 20:58:34
  • 基于mybatis逆向工程的使用步骤详解

    2022-10-28 09:27:26
  • java基础-数组扩容详解

    2022-05-24 00:34:58
  • 解决java 查看JDK中底层源码的实现方法

    2022-03-13 20:40:14
  • C#6.0新语法示例详解

    2023-11-16 03:43:42
  • 详解C# WebApi 接口测试工具:WebApiTestClient

    2022-05-15 07:58:44
  • android实现程序自动升级到安装示例分享(下载android程序安装包)

    2023-08-12 09:37:19
  • 详解JAVA高质量代码之数组与集合

    2022-03-31 16:42:07
  • Android 音乐播放器的开发实例详解

    2023-12-07 17:53:10
  • Spring Security基于json登录实现过程详解

    2023-12-07 07:15:18
  • Java实现动态模拟时钟

    2022-07-25 17:35:25
  • Android 实现九宫格抽奖功能

    2021-10-02 21:42:27
  • 在Spring Boot2中使用CompletableFuture的方法教程

    2022-09-27 14:18:51
  • C#中DataSet、DataTable、DataRow数据的复制方法

    2021-10-26 20:10:31
  • Java编程实现打印螺旋矩阵实例代码

    2021-10-16 19:15:14
  • android开发之蜂鸣提示音和震动提示的实现原理与参考代码

    2022-11-22 21:47:38
  • java线程池参数位置导致的夺命故障宿主机打不开

    2021-09-29 23:27:34
  • Java SpringMVC异步处理详解

    2021-08-10 15:03:58
  • Java+MySQL实现学生信息管理系统源码

    2023-11-28 04:29:31
  • asp之家 软件编程 m.aspxhome.com