前言

  接近半年的时间没有写博客了,今年公司的项目有点多,比较忙,没时间写,这是其一。其次是,这半年来,有时间的时候,
我都会看看自己以前写的博客,也许是以前刚刚写博客,经验不足,感觉写出来的博客质量很不好,而最近,也经常在网上看别人的博客
学到到了很多,趁最近项目都差不多收尾了,就写写今年下半年的第一篇博客...

  从事Android开发工作有几年时间了,我发现做手机应用的话,基本都离不开ListView,GridView,这些控件,尤其是ListView
我曾开发过一个项目,70%的都是列表,光写adapter,就接近上百个,写到最后,都感觉已经麻木了...比如下面这个例子

MainActivity页面

 package com.example.huangjialin.listviewadapter;

 import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView; import java.util.ArrayList;
import java.util.List; public class MainActivity extends Activity {
private ListView listview;
private List<String> stringList;
private TestAdapter adapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); stringList = new ArrayList<String>();
for (int i = 0; i < 50; i++) {
stringList.add("测试" + i);
} listview = (ListView) findViewById(R.id.listview);
adapter = new TestAdapter(this, stringList);
listview.setAdapter(adapter);
}
}

主页面布局

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
> <ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>

TestAdapter页面

 package com.example.huangjialin.listviewadapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List; /**
* Created by huangjialin on 2017/9/12.
*/
public class TestAdapter extends BaseAdapter {
private List<String> stringList;
private Context mContext; public TestAdapter(Context context, List<String> stringList) {
this.stringList = stringList;
this.mContext = context;
} @Override
public int getCount() {
return stringList.size();
} @Override
public Object getItem(int position) {
return null;
} @Override
public long getItemId(int position) {
return 0;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_single_str,null);
viewHolder = new ViewHolder();
viewHolder.mTextView = (TextView) convertView
.findViewById(R.id.id_tv_title);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.mTextView.setText(stringList.get(position));
return convertView;
} private final class ViewHolder {
TextView mTextView;
}
}

item_single_str  Item布局

 <?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"> <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/id_tv_title"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#aa111111"
android:gravity="center_vertical"
android:paddingLeft="15dp"
android:text="hello"
android:textColor="#ffffff"
android:textSize="20sp"
android:textStyle="bold"/>
</LinearLayout>

是不是觉得上面的代码很熟悉,写了很多,甚至都写到吐了,但是如果仔细观察的话,就会发现adapter里面的代码很多都是固定的
那么我们能不能把那些重复的代码封装起来呢...

ViewHolder

先简单的说一下ViewHolder的作用,可以理解为是一个容器,每一个convertView通过setTag来绑定一个ViewHolder对象,convertView中的控件就保存到ViewHolder中,当convertView复用的时候,直接通过getTag从ViewHolder中
取出来即可,不在需要在到布局文件中通过findViewById去找。通常,Android中写一个列表,无论是ListView还是GridView,我们都是一个activity对应一个adapter,但是如果我们能写出一个通用的adapter的话,是不是会省事很多呢??
先不说其他的,至少我们的代码会少很多。好,come on ,接着往下。。。

万能ViewHolder

从ViewHolder下手,先上代码

 package com.example.administrator.listviewtest;

 import android.content.Context;
import android.view.LayoutInflater;
import android.view.View; import java.util.HashMap;
import java.util.Map; /**
* Created by Administrator on 2017/10/10 0010.
*/ public class ViewHolder {
private View mConvertView;
private Map<Integer,View> viewMap; //保存控件 public ViewHolder(Context context,int layoutId) {
viewMap = new HashMap<Integer,View>();
mConvertView = LayoutInflater.from(context).inflate(layoutId,null);
mConvertView.setTag(this);
} /**
* 获取ViewHolder对象
* @param convertView
* @return
*/
public static ViewHolder getViewHolder(View convertView,Context context,int layoutId) {
//先判断convertView组件是否存在,存在的话,说明ViewHolder已经创建
if (convertView == null) {
return new ViewHolder(context,layoutId);
}
return (ViewHolder) convertView.getTag();
} /**
* 获取控件
* 由于每一个item布局不一样,控件是未知的,但是,无论是哪一个控件,他的父类都是View
*/
public <T extends View > T getView(int viewId){
View view = viewMap.get(viewId); //从Map中去出控件
if(view == null){ //说明,没有有这个控件,到布局中找
view = mConvertView.findViewById(viewId);
viewMap.put(viewId,view);
}
return (T) view;
} public View getConvertView(){
return mConvertView;
}
}

ViewHolder中,我创建了一个Map,用来保存控件,当然你也可以使用其他,比如SparseArray,ArrayMap,相比效率或者性能
方面,后者会比HashMap好很多,只是我觉得使用HashMap,更多人会更容易理解,如果想使用后两种,那直接替换就可以了,
他们的区别我在这里就不说了,有兴趣的朋友可以看看这篇文章:http://blog.csdn.net/u010687392/article/details/47809295

现在再来看看适配器中的代码

TestAdapter类

 .
.省略若干方法
. @Override
public View getView(int position, View convertView, ViewGroup parent) {
/*
常规的写法
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_single_str, null);
viewHolder = new ViewHolder();
viewHolder.mTextView = (TextView) convertView .findViewById(R.id.id_tv_title);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.mTextView.setText(stringList.get(position));
return convertView;*/ //使用万能的ViewHoler协防
ViewHolder holder = ViewHolder.getViewHolder(convertView, mContext, R.layout.item_single_str); //获取ViewHolder对象
TextView textView = holder.getView(R.id.id_tv_title);//从ViewHolder中获取控件
textView.setText(stringList.get(position));
return holder.getConvertView(); } /* private final class ViewHolder {
TextView mTextView;
}*/

简单的说一下思路:
先看TestAdapter中的getView方法,先获取这个ViewHolder对象,而获取ViewHolder 的时候,先判断convertView是否存在,如果存在,则说明ViewHolder对象已经存在,直接通过getTag来获取,如果不存在,则需要将item的布局填充到
convertView中,再通过setTag进行绑定。其实和常规写法的思路差不多,唯独就是每一次创建新的一个ViewHolder的时候,会创建一个viewMap,这个viewMap是用来保存控件的。然后在通过viewholder对象从viewMap中取出相应的控件
从而进行赋值。这里需要注意一个就是,getView方法中return 的view,是在ViewHolder填充Item的mConvertView,而不是直接拿getView中的convertView ......哈哈是不是代码量少很多了啊,现在不在需要每次创建一个adapter都要弄一个ViewHolder了
省事很多,但是......这里只是刚刚开始而已,接着开干....

万能的adapter适配器:
前面我们弄了一个通用ViewHolder,但是,我们写出来的列表还是得创建一个adapter,类并没有少,代码量少了一点点,这并不能达到我想要的,我想要的是类也少,代码量也少,万能adapter出来吧...动画片看多了
在封装一个万成的适配器之前,我们先看一下这个adapter的代码:

 package com.example.administrator.listviewtest;

 import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView; import java.util.List; /**
* Created by huangjialin on 2017/9/12.
*/
public class TestAdapter extends BaseAdapter {
private List<String> stringList;
private Context mContext; public TestAdapter(Context context, List<String> stringList) {
this.stringList = stringList;
this.mContext = context;
} @Override
public int getCount() {
return stringList.size();
} @Override
public Object getItem(int position) {
return stringList.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = ViewHolder.getViewHolder(convertView, mContext, R.layout.item_single_str); //获取ViewHolder对象
TextView textView = holder.getView(R.id.id_tv_title);//从ViewHolder中获取控件
textView.setText(stringList.get(position));
return holder.getConvertView();
}
}

从代码中,我们可以看到,继承BaseAdapter,实现这4个方法,其中有三个方法写法基本是固定不变的最主要的就是getView方法了,既然我们要弄一个万能适配器,是不是只要把getView方法单独抽出来实现就好,对吧。还有既然是通用的adapter,
那么数据肯定是不固定的,数据,我们就得需要泛型了....come on ,先创建一个万能适配器的类 PowerfulAdapter

 package com.example.administrator.listviewtest;

 import android.content.Context;
import android.widget.BaseAdapter;
import java.util.List; /**
* Created by Administrator on 2017/10/11 0011.
* 万能适配器
*/ public abstract class PowerfulAdapter<T> extends BaseAdapter { private List<T> stringList;
private Context mContext; public PowerfulAdapter(List<T> stringList, Context mContext) {
this.stringList = stringList;
this.mContext = mContext;
} @Override
public int getCount() {
return stringList.size();
} @Override
public Object getItem(int position) {
return stringList.get(position);
} @Override
public long getItemId(int position) {
return position;
}
}

从代码中我们可以看到,除了这个getView方法我们没有实现之外,另外的三个方法,我们都实现了,并且把这个类弄成抽象的,由于这几个方法,基本写法都是固定的,所以后面我们写适配器的时候,只需要继承这个万成的适配器,并且实现getView一个方法就可以了
,另外几个方法我们就不在需要考虑了。为了和前面的TestAdapter区别开来,我们另外创建一个适配器,SecondAdapter这个适配器就继承我们的万能适配器PowerfulAdapter<T>,上代码

 package com.example.administrator.listviewtest;

 import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; import java.util.List; /**
* Created by Administrator on 2017/10/11 0011.
*/ public class SecondAdapter<T> extends PowerfulAdapter<T>{
private List<String> stringList;
private Context mContext; public SecondAdapter(List<T> stringList, Context mContext) {
super(stringList);
this.stringList = (List<String>) stringList;
this.mContext = mContext;
} @Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
ViewHolder holder = ViewHolder.getViewHolder(convertView, mContext, R.layout.item_single_str); //获取ViewHolder对象
TextView textView = holder.getView(R.id.id_tv_title);//从ViewHolder中获取控件
textView.setText(stringList.get(position));
return holder.getConvertView();
}
}

现在我们的适配器,继承这个PowerfulAdapter<T>这个万能适配器,最后面只需要实现一个getView方法就完全可以了,相当省了一般的代码量,有些兄弟就说了,毛线啊,我从头看到尾,我没发现代码少啊,我见你反而多写了几个类呢,
莫慌,现在我这边只是写了一个列表,,我写了ViewHolder,PowerfulAdapter<T> ,而且这两个类相当于工具类一样,以后所有的listview或者gridview都可以使用,这样效率就会高的多了....实际上封装到这,也差不多可以了,也满足了大部分人的需要了,
但是,我这么帅,得省点时间出来约会啊,还是觉得代码太多了,接着封装...

我们观察getView方法会发现

 TextView textView = holder.getView(R.id.id_tv_title);//从ViewHolder中获取控件
textView.setText(stringList.get(position));

这些TextView是Android常用的控件,那如果我们能不能先把一些常用的控件先封装起来,在这里就不需要获取了呢,我们试试。。。

我们在ViewHolder类中加一个这样的方法

 /**
* 给TextView设置值
*/
public ViewHolder setText(int viewId, String text) {
if (viewId > 0 && text != null) {
TextView tv_Text = getView(viewId);
tv_Text.setText(text);
}
return this;
}

然后在getView方法中只需要这样写...

   @Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
ViewHolder holder = ViewHolder.getViewHolder(convertView, mContext, R.layout.item_single_str); //获取ViewHolder对象 /**
* 旧的写法
*/
/*TextView textView = holder.getView(R.id.id_tv_title);//从ViewHolder中获取控件
textView.setText(stringList.get(position));*/ /**
* 在ViewHolder加了setText方法后的写法
*/
holder.setText(R.id.id_tv_title, stringList.get(position)); return holder.getConvertView();
}

哈哈哈,是不是代码又比原来少了一点,但是,在ViewHolder中加一些常用控件得自己手动添加,可能刚刚开始会觉得很不全面,当久而久之,加进去多了,就慢慢完善了。
到这里,基本上一种封装已经完成了。到时候我会将这一部分源码上传,需要的可以下载... 可以在文章后面下载,也可以点击这里 下载

But,有些人就会说,你前面不是说能够少创建很多Java文件吗,我没有看到在哪里少呢,莫慌,马上来了...

在回到 PowerfulAdapter<T>这个类,前面我们并没有在这个类中实现getView方法,但是现在我们在该类中实现getView方法,附上代码

 package com.example.administrator.listviewtest;

 import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.List; /**
* Created by Administrator on 2017/10/11 0011.
* 万能适配器
*/ public abstract class PowerfulAdapter<T> extends BaseAdapter {
private Context mContext;
private List<T> stringList; public PowerfulAdapter(Context mContext, List<T> stringList) {
this.mContext = mContext;
this.stringList = stringList;
} @Override
public int getCount() {
return stringList.size();
} @Override
public Object getItem(int position) {
return stringList.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
ViewHolder holder = ViewHolder.getViewHolder(convertView, mContext, R.layout.item_single_str); //获取ViewHolder对象
convert(holder,stringList.get(position));
return holder.getConvertView();
} /**
* 对外提供一个抽象方法
*/
public abstract void convert(ViewHolder helper, T item); }

在MainActivity的直接一个匿名内部类来实现convert方法,附上代码

 package com.example.administrator.listviewtest;

 import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast; import java.util.ArrayList;
import java.util.List; public class MainActivity extends Activity {
private ListView listview;
private List<String> stringList; private PowerfulAdapter adapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); stringList = new ArrayList<String>();
for (int i = 0; i < 50; i++) {
stringList.add("不写adapter,直接匿名内部类实现" + (i + 50));
} listview = (ListView) findViewById(R.id.listview); adapter = new PowerfulAdapter<String>(this, stringList, R.layout.item_single_str) {
@Override
public void convert(ViewHolder holder, String item) {
holder.setText(R.id.id_tv_title, item);
Button button = holder.getView(R.id.button); button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "我是按钮,我被点击了----->", Toast.LENGTH_LONG).show();
}
}); }
};
listview.setAdapter(adapter); listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(MainActivity.this, "我被点击了----->" + i, Toast.LENGTH_LONG).show();
}
}); } }

最后附上一张运行的效果图

ok,是不是连adapter都不需要创建了,如果项目有几十,几百个列表,就相当于少了几十几百个Java类,多爽啊,哈哈最后附上两种封装方式的源码链接,
有需要的朋友可以下载,同时,如果有朋友发现bug或者将已解决问题的方法,或者有更好的封装方式,欢迎留言,一块探讨学习。

源码链接

通用适配器封装源码:https://github.com/343661629/---ListView-GridView

另一种封装方式源码: https://github.com/343661629/ListView-

ListView ,GridView 通用适配器的更多相关文章

  1. ScrollView listView gridView 之间的冲突问题

    在ScrollView中的listView gridView添加适配器之后添加//设置gridView整体的高度 public void setListViewHeightBasedOnChildre ...

  2. Android 快速开发系列 打造万能的ListView GridView 适配器

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38902805 ,本文出自[张鸿洋的博客] 1.概述 相信做Android开发的写 ...

  3. Android 中万能的 BaseAdapter(Spinner,ListView,GridView) 的使用!

    大家好!今天给大家讲解一下BaseAdapter(基础适配器)的用法,适配器的作用主要是用来给诸如(Spinner,ListView,GridView)来填充数据的.而(Spinner,ListVie ...

  4. RecyclerView的通用适配器

    本来这一个主题应该早就写了,只是项目多,属于自己的时间不多,所以现在才开动!! 前一段时间写了一篇文章,是关于ListView,GriView万能适配器,没有看过的同学,可以先看看那篇文章,然后在来学 ...

  5. ScrollView嵌套ListView,GridView数据加载不全问题的解决

    我们大家都知道ListView,GridView加载数据项,如果数据项过多时,就会显示滚动条.ScrollView组件里面只能包含一个组件,当ScrollView里面嵌套listView,GridVi ...

  6. 浅谈RecyclerView(完美替代ListView,GridView)

    Android RecyclerView 是Android5.0推出来的,导入support-v7包即可使用. 个人体验来说,RecyclerView绝对是一款功能强大的控件. 首先总结下Recycl ...

  7. 细解ListView之自定义适配器

    下面我们将以一个例子来讲述ListView之自定义适配器 首先我们看一下效果图: [分析] 首先:需要创建一个ListView控件,自定义适配器是为了实现自定义ListView的ListView_It ...

  8. listview-android:打造万能通用适配器(转)

    转载:https://blog.csdn.net/q649381130/article/details/51781921: 1.前言 listview作为安卓项目中一个的明星控件,它的适配器的写法是广 ...

  9. RecyclerView的通用适配器,和滚动时不加载图片的封装

    对于RecyclerView我们需要使用RecyclerAdapter,使用方式与ListViewAdapter类似,具体代码大家可以在网上搜索,这里就只教大家使用封装后的简洁RecyclerAdap ...

随机推荐

  1. std::mutex 引起的 C2280 尝试引用已删除的函数

    起因是把之前写的类中的 mutex 使用了(之前注释掉了没用到这个变量); 或者说添加了一个 mutex 变量, 然后 这个类有嵌套在了 其类的 map 中使用, 然后 编译 就报错 ` C2280 ...

  2. Vertex and fragment programs

    [Vertex and fragment programs] When you use vertex and fragment programs (the so called "progra ...

  3. Oracle中针对中文进行排序

    在oracle 9i之前,对中文的排序,是默认按2进制编码来进行排序的. 9i时增加了几种新的选择: 按中文拼音进行排序:SCHINESE_PINYIN_M 按中文部首进行排序:SCHINESE_RA ...

  4. Android开发实战之ViewPager实现向导界面

    当我们更新应用,或者第一次进入应用时都会有一个向导界面,介绍这个app的内容和使用方式. 如果你细心你会发现其实这就是个viewpager,本篇博文将介绍应用的向导界面是如何制作的.希 望本篇博文对你 ...

  5. 解剖Nginx·自动脚本篇(3)源码相关变量脚本 auto/sources

    在configure脚本中,运行完auto/options和auto/init脚本后,接下来就运行auto/soures脚本.这个脚本是为编译做准备的. 目录 核心模块 事件模块 OpenSSL 模块 ...

  6. Renderer.materials 和sharedMaterials一些用法上的区别

    Not allowed to access Renderer.materials on prefab object. Use Renderer.sharedMaterials insteadUnity ...

  7. 微信小程序文档里看不到的小Tips

    前几天折腾了下.然后列出一些实验结果,供大家参考. 0. 使用开发工具模拟的和真机差异还是比较大的.也建议大家还是真机调试比较靠谱. 1. WXML(HTML) 1.1 小程序的WXML没有HTML的 ...

  8. linux系统中的进程

    一.fork 在类unix系统中,我们所执行的任何程序,都是由父进程(parent process)所产生出来的一个子进程(child process),子进程在结束后,将返回到父进程去.此一现象被称 ...

  9. Docker学习记录常用命令

    1. docker ps  -a 查看运行中的容器 2. docker images 查看docker镜像 3. docker rm id(容器id)  删除容器(容器id可以通过docker ps查 ...

  10. LinuxSystemProgramming-vi

    Basic VI