Android学习之路(17) Android Adapter详解

Adapter基础讲解

本节引言

从本节开始我们要讲的UI控件都是跟Adapter(适配器)打交道的,了解并学会使用这个Adapter很重要, Adapter是用来帮助填充数据的中间桥梁,简单点说就是:将各种数据以合适的形式显示到view上,提供 给用户看!

1.MVC模式的简单理解

在开始学习Adapter之前我们要来了解下这个MVC模式概念: 举个例子:大型的商业程序通常由多人一同开发完成,比如有人负责操作接口的规划与设计, 有人负责程序代码的编写如果要能够做到程序项目的分工就必须在程序的结构上做适合的安排 ,如果,接口设计与修改都涉及到程序代码的改变的话,那么两者的分工就会造成执行上的困难 良好的程序架构师将整个程序项目划分为如图的三个部分:

关系图解析:

  • Model:通常可以理解为数据,负责执行程序的核心运算与判断逻辑,通过view获得用户 输入的数据,然后根据从数据库查询相关的信息,最后进行运算和判断,再将得到的结果交给view来显示
  • view:用户的操作接口,说白了就是GUI,应该使用哪种接口组件,组件间的排列位置与顺序都需要设计
  • Controller:控制器,作为model与view之间的枢纽,负责控制程序的执行流程以及对象之间的一个互动
    而这个Adapter则是中间的这个Controller的部分: Model(数据) —> Controller(以什么方式显示到)—> View(用户界面) 这就是简单MVC组件的简单理解!

2.Adapter概念解析

首先我们来看看他的继承结构图:

上面就是Adapter以及继承结构图了,接着我们介绍一下实际开发中还用到的几个Adapter吧!

  • BaseAdapter:抽象类,实际开发中我们会继承这个类并且重写相关方法,用得最多的一个Adapter!
  • ArrayAdapter:支持泛型操作,最简单的一个Adapter,只能展现一行文字~
  • SimpleAdapter:同样具有良好扩展性的一个Adapter,可以自定义多种效果!
  • SimpleCursorAdapter:用于显示简单文本类型的listView,一般在数据库那里会用到,不过有点过时, 不推荐使用!

其实一个BaseAdapter就够玩的了,至于其他的,实际开发中用得不多,后面用到在讲解~

3.代码示例:

好的,多说无益,写代码最实际,接下来我们来写几个简单的Adapter实例, 帮助我们了解Adapter给我们带来的便利,另外,因为Adapter需要结合ListView, GridView等等控件讲解,一些高级一点的用法我们都放在ListView那里讲! 这里就简单演示下效果,另外这里用到的控件是ListView,下一节就会讲解, 现在看不懂也没关系!

1)ArrayAdapter使用示例:

运行效果图:

代码实现:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //要显示的数据
        String[] strs = {"基神","B神","翔神","曹神","J神"};
        //创建ArrayAdapter
        ArrayAdapter<String> adapter = new ArrayAdapter<String>
                (this,android.R.layout.simple_expandable_list_item_1,strs);
        //获取ListView对象,通过调用setAdapter方法为ListView设置Adapter设置适配器
        ListView list_test = (ListView) findViewById(R.id.list_test);
        list_test.setAdapter(adapter);
    }
}

一些相关的东西:

1.除了通过数组外,我们还可以写到一个数组资源文件中:

比如:在res\values下创建一个数组资源的xml文件:arrays.xml:

<?xml version="1.0" encoding="utf-8"?>  
<resources>  
    <string-array name="myarray">  
    <item>语文</item>  
    <item>数学</item>  
    <item>英语</item>  
    </string-array>      
</resources>

接着布局的listview属性设置下这个列表项:

<ListView  
        android:id="@id/list_test"  
        android:layout_height="match_parent"  
        android:layout_width="match_parent"   
        android:entries="@array/myarray"/>

就可以了~

当然我们也可以在Java代码中这样写:

ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
        R.array.myarray,android.R.layout.simple_list_item_multiple_choice );

同样也是可以的!

2.一开始也说了这个ArrayAdapter支持泛型,那么集合必不可少啦,比如,这样写:

List<String> data = new ArrayList<String>();
data.add("基神");
data.add("B神");
ArrayAdapter<String> adapter = new ArrayAdapter<String>
                (this,android.R.layout.simple_expandable_list_item_1,data);

就可以了~

3.我们看到了在实例化ArrayAdapter的第二个参数: android.R.layout.simple_expandable_list_item_1 其实这些是系统给我们提供好的一些ListView模板,有下面几种:

simple_list_item_1 : 单独一行的文本框

simple_list_item_2 : 两个文本框组成 simple_list_item_checked : 每项都是由一个已选中的列表项 simple_list_item_multiple_choice : 都带有一个复选框 simple_list_item_single_choice : 都带有一个单选钮

2)SimpleAdapter使用示例:

SimpleAdapter:简单的Adapter,看似简单,功能强大,下面我们来写个稍微复杂一点的列表 布局吧!

代码实现:

先来编写一个列表项目每一项的布局:

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 -->
    <ImageView
        android:id="@+id/imgtou"
        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/says"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="8px"
            android:textColor="#B4B4B9"
            android:textSize="14sp" />

    </LinearLayout>

</LinearLayout>

接下来是MainActivity.java:

public class MainActivity extends AppCompatActivity {

    private String[] names = new String[]{"B神", "基神", "曹神"};
    private String[] says = new String[]{"无形被黑,最为致命", "大神好厉害~", "我将带头日狗~"};
    private int[] imgIds = new int[]{R.mipmap.head_icon1, R.mipmap.head_icon2, R.mipmap.head_icon3};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<Map<String, Object>> listitem = new ArrayList<Map<String, Object>>();
        for (int i = 0; i < names.length; i++) {
            Map<String, Object> showitem = new HashMap<String, Object>();
            showitem.put("touxiang", imgIds[i]);
            showitem.put("name", names[i]);
            showitem.put("says", says[i]);
            listitem.add(showitem);
        }

        //创建一个simpleAdapter
        SimpleAdapter myAdapter = new SimpleAdapter(getApplicationContext(), listitem, R.layout.list_item, new String[]{"touxiang", "name", "says"}, new int[]{R.id.imgtou, R.id.name, R.id.says});
        ListView listView = (ListView) findViewById(R.id.list_test);
        listView.setAdapter(myAdapter);
    }
}

好的,上面就是SimpleAdapter的简单用法了,有点意思~

3)SimpleCursorAdapter使用示例:

虽然这东西过时了,不过对于不怎么会SQLite的初学者来说,用起来还是蛮方便的! 记得前面我们学ContentProivder写过的读取联系人的例子么?之前是通过打印Log的 方式显示出来,现在我们通过这个SimpleCursorAdapter把它显示到ListView上!

实现效果图:

代码实现:

先写下listView每个item的布局:

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">


    <TextView
        android:id="@+id/list_name"
        android:layout_width="0dp"
        android:layout_height="64dp"
        android:layout_weight="1"
        android:gravity="center"
        android:text="小猪"
        android:textColor="#0000FF"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/list_phone"
        android:layout_width="0dp"
        android:layout_height="64dp"
        android:layout_weight="1"
        android:gravity="center"
        android:text="13798989898"
        android:textColor="#EA5C4D"
        android:textSize="18sp" />


</LinearLayout>

接着activity_main布局和前面的一样,就是简单的ListView,然后是

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView list_test = (ListView) findViewById(R.id.list_test);
        //读取联系人
        Cursor cursor = getContentResolver()
                .query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
        SimpleCursorAdapter spcAdapter = new SimpleCursorAdapter(this,R.layout.list_item,cursor,
                new String[]{ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,ContactsContract.CommonDataKinds.Phone.NUMBER},
                new int[]{R.id.list_name,R.id.list_phone});
        list_test.setAdapter(spcAdapter);
    }
}

最后AndroidManifest.xml里加个读联系人的权限就可以了!

<uses-permission android:name="android.permission.READ_CONTACTS"/>

一问一答:

问:就这么简单?
——答:是的,直接获取到Cursor,然后绑定就好了,无需你自己再写什么SQL语句!
问:你说这东西过时了,那拿什么来代替?
——答:一般的做法是自己重写BaseAdapter,获取到数据集合后跟对应的控件进行绑定!
问:这个SimpleCursorAdapter还有没有要注意的地方?
——答:有,使用SimpleCursorAdapter的话,绑定的数据库表中一定要有id这个字段, 或者as id;而且在绑定时取出的数据必须包含这个id项,否则的话会报以下错误! java.lang.IllegalArgumentException: column ‘id’ does not exist**

BaseAdapter优化分类

本节引言:

上一节中我们学习了如何来使用一个ListView以及自定义一个简单的BaseAdapter,我们从代码 中可以看出比较重要的两个方法:getCount()和getView(),界面上有多少列就会调用多少次getView, 这个时候可能看出一些端倪,每次都是新inflate一个View,都要进行这个XML的解析,这样会 很浪费资源,当然,几十列或者几百列的列表并不能体现什么问题,但假如更多或者布局更加复杂? 所以学习ListView的优化很重要,而本节针对的是BaseAdapter的优化,优化的两点有,复用convertView 以及使用ViewHolder重用组件,不用每次都findViewById,我们具体通过代码来体会吧!

1.复用ConvertView:

上面也说了,界面上有多少个Item,那么getView方法就会被调用多少次! 我们来看看上一节我们写的getView()部分的代码:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
    ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
    TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
    TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);

    img_icon.setBackgroundResource(mData.get(position).getaIcon());
    txt_aName.setText(mData.get(position).getaName());
    txt_aSpeak.setText(mData.get(position).getaSpeak());
    return convertView;
}

是吧,inflate()每次都要加载一次xml,其实这个convertView是系统提供给我们的可供服用的View 的缓存对象,那就坐下判断咯,修改下,优化后的代码:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    if(convertView == null){
        convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
    }

    ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
    TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
    TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);

    img_icon.setBackgroundResource(mData.get(position).getaIcon());
    txt_aName.setText(mData.get(position).getaName());
    txt_aSpeak.setText(mData.get(position).getaSpeak());
    return convertView;
}

2.ViewHolder重用组件

嘿嘿,getView()会被调用多次,那么findViewById不一样得调用多次,而我们的ListView的Item 一般都是一样的布局,我们可以对这里在优化下,我们可以自己定义一个ViewHolder类来对这一部分 进行性能优化!修改后的代码如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    if(convertView == null){
        convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
        holder = new ViewHolder();
        holder.img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
        holder.txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
        holder.txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);
        convertView.setTag(holder);   //将Holder存储到convertView中
    }else{
        holder = (ViewHolder) convertView.getTag();
    }
    holder.img_icon.setBackgroundResource(mData.get(position).getaIcon());
    holder.txt_aName.setText(mData.get(position).getaName());
    holder.txt_aSpeak.setText(mData.get(position).getaSpeak());
    return convertView;
}

static class ViewHolder{
    ImageView img_icon;
    TextView txt_aName;
    TextView txt_aSpeak;
}

没错就是这么简单,你以后BaseAdapter照着这个模板写就对了,哈哈,另外这个修饰ViewHolder的 static,关于是否定义成静态,跟里面的对象数目是没有关系的,加静态是为了在多个地方使用这个 Holder的时候,类只需加载一次,如果只是使用了一次,加不加也没所谓。

构建一个可复用的自定义 BaseAdapter

本节引言:

如题,本节给大家带来的是构建一个可复用的自定义BaseAdapter,我们每每涉及到ListView GridView等其他的Adapter控件,都需要自己另外写一个BaseAdapter类,这样显得非常麻烦, 又比如,我们想在一个界面显示两个ListView的话,我们也是需要些两个BaseAdapter… 这,程序员都是喜欢偷懒的哈,这节我们就来写一个可复用的自定义BaseAdapter类~

1.我们一点点开始改:

首先我们把上节写的自定义BaseAdapter贴下,等下我们就要对他进行升级改造

/**
 * Created by Jay on 2015/9/21 0021.
 */
public class MyAdapter extends BaseAdapter {

    private Context mContext;
    private LinkedList<Data> mData;

    public MyAdapter() {
    }

    public MyAdapter(LinkedList<Data> mData, Context mContext) {
        this.mData = mData;
        this.mContext = mContext;
    }

    @Override
    public int getCount() {
        return mData.size();
    }

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list, parent, false);
            holder = new ViewHolder();
            holder.img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
            holder.txt_content = (TextView) convertView.findViewById(R.id.txt_content);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.img_icon.setImageResource(mData.get(position).getImgId());
        holder.txt_content.setText(mData.get(position).getContent());
        return convertView;
    }

    //添加一个元素
    public void add(Data data) {
        if (mData == null) {
            mData = new LinkedList<>();
        }
        mData.add(data);
        notifyDataSetChanged();
    }

    //往特定位置,添加一个元素
    public void add(int position,Data data){
        if (mData == null) {
            mData = new LinkedList<>();
        }
        mData.add(position, data);
        notifyDataSetChanged();
    }

    public void remove(Data data) {
        if(mData != null) {
            mData.remove(data);
        }
        notifyDataSetChanged();
    }

    public void remove(int position) {
        if(mData != null) {
            mData.remove(position);
        }
        notifyDataSetChanged();
    }

    public void clear() {
        if(mData != null) {
            mData.clear();
        }
        notifyDataSetChanged();
    }

    private class ViewHolder {
        ImageView img_icon;
        TextView txt_content;
    }

}

升级1:将Entity设置成泛型
好的,毕竟我们传递过来的Entitiy实体类可能千奇百怪,比如有Person,Book,Wether等,所以我们 将Entity设置成泛型,修改后的代码如下:

public class MyAdapter<T> extends BaseAdapter {

    private Context mContext;
    private LinkedList<T> mData;

    public MyAdapter() {
    }

    public MyAdapter(LinkedList<T> mData, Context mContext) {
        this.mData = mData;
        this.mContext = mContext;
    }

    @Override
    public int getCount() {
        return mData.size();
    }

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list, parent, false);
            holder = new ViewHolder();
            holder.img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
            holder.txt_content = (TextView) convertView.findViewById(R.id.txt_content);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.img_icon.setImageResource(mData.get(position).getImgId());
        holder.txt_content.setText(mData.get(position).getContent());
        return convertView;
    }

    //添加一个元素
    public void add(T data) {
        if (mData == null) {
            mData = new LinkedList<>();
        }
        mData.add(data);
        notifyDataSetChanged();
    }

    //往特定位置,添加一个元素
    public void add(int position,T data){
        if (mData == null) {
            mData = new LinkedList<>();
        }
        mData.add(position, data);
        notifyDataSetChanged();
    }

    public void remove(T data) {
        if(mData != null) {
            mData.remove(data);
        }
        notifyDataSetChanged();
    }

    public void remove(int position) {
        if(mData != null) {
            mData.remove(position);
        }
        notifyDataSetChanged();
    }

    public void clear() {
        if(mData != null) {
            mData.clear();
        }
        notifyDataSetChanged();
    }

    private class ViewHolder {
        ImageView img_icon;
        TextView txt_content;
    }

}

好的,上面我们做的事仅仅是将Data类型换成了泛型T!

升级2:ViewHolder类的升级改造:
我们先来看看前面我们的ViewHolder干了什么? 答:findViewById,设置控件状态; 下面我们想在完成这个基础上,将getView()方法大部分的逻辑写到ViewHolder类里, 这个ViewHolder要做的事:

  • 定义一个查找控件的方法,我们的思路是通过暴露公共的方法,调用方法时传递过来 控件id,以及设置的内容,比如TextView设置文本: public ViewHolder setText(int id, CharSequence text){文本设置}
  • 将convertView复用部分搬到这里,那就需要传递一个context对象了,我们把需要获取 的部分都写到构造方法中!
  • 写一堆设置方法(public),比如设置文字大小颜色,图片背景等!

好的,接下来我们就来一步步改造我们的ViewHolder类

1)相关参数与构造方法:

public static class ViewHolder {

    private SparseArray<View> mViews;   //存储ListView 的 item中的View
    private View item;                  //存放convertView
    private int position;               //游标
    private Context context;            //Context上下文

    //构造方法,完成相关初始化
    private ViewHolder(Context context, ViewGroup parent, int layoutRes) {
        mViews = new SparseArray<>();
        this.context = context;
        View convertView = LayoutInflater.from(context).inflate(layoutRes, parent,false);
        convertView.setTag(this);
        item = convertView;
    }

    ImageView img_icon;
    TextView txt_content;
}

2)绑定ViewHolder与Item
在上面的基础上我们再添加一个绑定的方法

//绑定ViewHolder与item
public static ViewHolder bind(Context context, View convertView, ViewGroup parent,
                              int layoutRes, int position) {
    ViewHolder holder;
    if(convertView == null) {
        holder = new ViewHolder(context, parent, layoutRes);
    } else {
        holder = (ViewHolder) convertView.getTag();
        holder.item = convertView;
    }
    holder.position = position;
    return holder;
}

3)根据id获取集合中保存的控件

public <T extends View> T getView(int id) {
    T t = (T) mViews.get(id);
    if(t == null) {
        t = (T) item.findViewById(id);
        mViews.put(id, t);
    }
    return t;
}
  1. 接着我们再定义一堆暴露出来的方法
/**
 * 获取当前条目
 */
public View getItemView() {
    return item;
}

/**
 * 获取条目位置
 */
public int getItemPosition() {
    return position;
}

/**
 * 设置文字
 */
public ViewHolder setText(int id, CharSequence text) {
    View view = getView(id);
    if(view instanceof TextView) {
        ((TextView) view).setText(text);
    }
    return this;
}

/**
 * 设置图片
 */
public ViewHolder setImageResource(int id, int drawableRes) {
    View view = getView(id);
    if(view instanceof ImageView) {
        ((ImageView) view).setImageResource(drawableRes);
    } else {
        view.setBackgroundResource(drawableRes);
    }
    return this;
}


/**
 * 设置点击监听
 */
public ViewHolder setOnClickListener(int id, View.OnClickListener listener) {
    getView(id).setOnClickListener(listener);
    return this;
}

/**
 * 设置可见
 */
public ViewHolder setVisibility(int id, int visible) {
    getView(id).setVisibility(visible);
    return this;
}

/**
 * 设置标签
 */
public ViewHolder setTag(int id, Object obj) {
    getView(id).setTag(obj);
    return this;
}

//其他方法可自行扩展

好的,ViewHolder的改造升级完成~

升级3:定义一个抽象方法,完成ViewHolder与Data数据集的绑定
public abstract void bindView(ViewHolder holder, T obj);
我们创建新的BaseAdapter的时候,实现这个方法就好,另外,别忘了把我们自定义 的BaseAdapter改成abstact抽象的!

升级4:修改getView()部分的内容
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = ViewHolder.bind(parent.getContext(), convertView, parent, mLayoutRes
, position);
bindView(holder,getItem(position));
return holder.getItemView();
}

2.升级完毕,我们写代码来体验下:

我们要实现的效果图:

就是上面有两个列表,布局不一样,但是我只使用一个BaseAdapter类来完成上述效果!

关键代码如下:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    private Context mContext;
    private ListView list_book;
    private ListView list_app;

    private MyAdapter<App> myAdapter1 = null;
    private MyAdapter<Book> myAdapter2 = null;
    private List<App> mData1 = null;
    private List<Book> mData2 = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = MainActivity.this;
        init();

    }

    private void init() {

        list_book = (ListView) findViewById(R.id.list_book);
        list_app = (ListView) findViewById(R.id.list_app);

        //数据初始化
        mData1 = new ArrayList<App>();
        mData1.add(new App(R.mipmap.iv_icon_baidu,"百度"));
        mData1.add(new App(R.mipmap.iv_icon_douban,"豆瓣"));
        mData1.add(new App(R.mipmap.iv_icon_zhifubao,"支付宝"));

        mData2 = new ArrayList<Book>();
        mData2.add(new Book("《第一行代码Android》","郭霖"));
        mData2.add(new Book("《Android群英传》","徐宜生"));
        mData2.add(new Book("《Android开发艺术探索》","任玉刚"));

        //Adapter初始化
        myAdapter1 = new MyAdapter<App>((ArrayList)mData1,R.layout.item_one) {
            @Override
            public void bindView(ViewHolder holder, App obj) {
                holder.setImageResource(R.id.img_icon,obj.getaIcon());
                holder.setText(R.id.txt_aname,obj.getaName());
            }
        };
        myAdapter2 = new MyAdapter<Book>((ArrayList)mData2,R.layout.item_two) {
            @Override
            public void bindView(ViewHolder holder, Book obj) {
                holder.setText(R.id.txt_bname,obj.getbName());
                holder.setText(R.id.txt_bauthor,obj.getbAuthor());
            }
        };

        //ListView设置下Adapter:
        list_book.setAdapter(myAdapter2);
        list_app.setAdapter(myAdapter1);

    }
}

我们写的可复用的BaseAdapter的使用就如上面所述~

最后写好的MyAdapter类吧,可根据自己的需求进行扩展:

MyAdapter.java:

/**
 * Created by Jay on 2015/9/22 0022.
 */
public abstract class MyAdapter<T> extends BaseAdapter {

    private ArrayList<T> mData;
    private int mLayoutRes;           //布局id


    public MyAdapter() {
    }

    public MyAdapter(ArrayList<T> mData, int mLayoutRes) {
        this.mData = mData;
        this.mLayoutRes = mLayoutRes;
    }

    @Override
    public int getCount() {
        return mData != null ? mData.size() : 0;
    }

    @Override
    public T getItem(int position) {
        return mData.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = ViewHolder.bind(parent.getContext(), convertView, parent, mLayoutRes
                , position);
        bindView(holder, getItem(position));
        return holder.getItemView();
    }

    public abstract void bindView(ViewHolder holder, T obj);

    //添加一个元素
    public void add(T data) {
        if (mData == null) {
            mData = new ArrayList<>();
        }
        mData.add(data);
        notifyDataSetChanged();
    }

    //往特定位置,添加一个元素
    public void add(int position, T data) {
        if (mData == null) {
            mData = new ArrayList<>();
        }
        mData.add(position, data);
        notifyDataSetChanged();
    }

    public void remove(T data) {
        if (mData != null) {
            mData.remove(data);
        }
        notifyDataSetChanged();
    }

    public void remove(int position) {
        if (mData != null) {
            mData.remove(position);
        }
        notifyDataSetChanged();
    }

    public void clear() {
        if (mData != null) {
            mData.clear();
        }
        notifyDataSetChanged();
    }


    public static class ViewHolder {

        private SparseArray<View> mViews;   //存储ListView 的 item中的View
        private View item;                  //存放convertView
        private int position;               //游标
        private Context context;            //Context上下文

        //构造方法,完成相关初始化
        private ViewHolder(Context context, ViewGroup parent, int layoutRes) {
            mViews = new SparseArray<>();
            this.context = context;
            View convertView = LayoutInflater.from(context).inflate(layoutRes, parent, false);
            convertView.setTag(this);
            item = convertView;
        }

        //绑定ViewHolder与item
        public static ViewHolder bind(Context context, View convertView, ViewGroup parent,
                                      int layoutRes, int position) {
            ViewHolder holder;
            if (convertView == null) {
                holder = new ViewHolder(context, parent, layoutRes);
            } else {
                holder = (ViewHolder) convertView.getTag();
                holder.item = convertView;
            }
            holder.position = position;
            return holder;
        }

        @SuppressWarnings("unchecked")
        public <T extends View> T getView(int id) {
            T t = (T) mViews.get(id);
            if (t == null) {
                t = (T) item.findViewById(id);
                mViews.put(id, t);
            }
            return t;
        }


        /**
         * 获取当前条目
         */
        public View getItemView() {
            return item;
        }

        /**
         * 获取条目位置
         */
        public int getItemPosition() {
            return position;
        }

        /**
         * 设置文字
         */
        public ViewHolder setText(int id, CharSequence text) {
            View view = getView(id);
            if (view instanceof TextView) {
                ((TextView) view).setText(text);
            }
            return this;
        }

        /**
         * 设置图片
         */
        public ViewHolder setImageResource(int id, int drawableRes) {
            View view = getView(id);
            if (view instanceof ImageView) {
                ((ImageView) view).setImageResource(drawableRes);
            } else {
                view.setBackgroundResource(drawableRes);
            }
            return this;
        }


        /**
         * 设置点击监听
         */
        public ViewHolder setOnClickListener(int id, View.OnClickListener listener) {
            getView(id).setOnClickListener(listener);
            return this;
        }

        /**
         * 设置可见
         */
        public ViewHolder setVisibility(int id, int visible) {
            getView(id).setVisibility(visible);
            return this;
        }

        /**
         * 设置标签
         */
        public ViewHolder setTag(int id, Object obj) {
            getView(id).setTag(obj);
            return this;
        }

        //其他方法可自行扩展

    }

}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android Spinner 是一个下拉列表控件,用于在列表中显示多个选项,并允许用户从中选择一个。为了将数据显示在 Spinner 中,需要使用一个适配器(Adapter)来为 Spinner 提供数据源。 下面是一个简单的 Spinner 适配器示例: 1. 首先,创建一个数据源,它可以是任何类型的集合(如数组、列表等): ``` List<String> dataList = new ArrayList<>(); dataList.add("Option 1"); dataList.add("Option 2"); dataList.add("Option 3"); ``` 2. 创建一个 ArrayAdapter 对象来将数据源与 Spinner 控件关联起来: ``` ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, dataList); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); ``` 这里,ArrayAdapter 的构造函数需要传入三个参数:上下文、Spinner 控件使用的布局以及数据源。setDropDownViewResource() 方法用于为 Spinner 中的每个选项指定下拉列表的布局。 以上代码中,数据源为字符串列表,所以 ArrayAdapter 的泛型类型为 String。如果数据源是其他类型的集合,则需要相应地修改 ArrayAdapter 的泛型类型。 除了 ArrayAdapter,还有其他类型的适配器可用于 Spinner,例如 CursorAdapter 和 SimpleCursorAdapter,它们可用于显示从数据库中检索的数据。 ### 回答2: Android中的Spinner是一个下拉列表控件,它允许用户从预定义的选项中选择一个值。Spinner的显示和选择功能由Adapter控制。 Adapter在Spinner中扮演着重要的角色,它负责管理Spinner的数据来源并将数据显示在Spinner中。通过SpinnerAdapter接口,我们可以自定义Spinner的样式和布局。 在使用Spinner时,我们需要创建一个适配器来将数据绑定到Spinner上。适配器可以是ArrayAdapter、CursorAdapter、SimpleAdapter等。适配器通过getView()方法来指定Spinner每个选项的视图,并将数据显示在Spinner中。 适配器的工作原理是通过getView()方法将每个选项的数据和布局进行绑定,并返回一个视图给Spinner显示。同时,适配器还可以为每个选项设置点击事件监听器。 使用Spinner和Adapter时,需要注意几个重要的方法。首先是setAdapter()方法,它用于为Spinner设置适配器。其次是getSelectedItem(),它可以获取用户选择的选项值。还有getCount()方法,用于获取Spinner中选项的数量。 另外,为了实现自定义的样式和布局,我们可以继承BaseAdapter类来创建自定义的适配器。在自定义适配器中,我们可以重写父类的getView()方法来实现不同的选项显示效果。 总而言之,Spinner和AdapterAndroid开发中非常常见和重要的控件和接口,它们能够方便地实现下拉列表的显示和选择功能。通过自定义适配器,我们可以轻松地实现Spinner的不同样式和布局。 ### 回答3: Android Spinner Adapter 是一个可以用来为 Spinner 组件提供数据源的适配器。在 Android 中,Spinner 是一个下拉列表的组件,用户可以通过点击下拉箭头选择其中的一项。为了提供下拉列表中的选项数据,我们需要使用 Spinner Adapter。 Spinner Adapter 是一个通用的接口,我们可以通过实现该接口来创建自定义的 Adapter 来为 Spinner 提供数据。我们可以通过重写 Adapter 的方法来定义列表的样式、数据结构和展示方式。 Spinner Adapter 中最重要的方法是 getCount()、getItem() 和 getView()。getCount() 用于返回列表的长度,即下拉列表中有多少个选项。getItem() 用于返回特定位置的数据项,而 getView() 用于获得列表中每个选项的视图。 在使用 Spinner Adapter 的时候,我们可以根据不同的数据类型选择不同的 Adapter。例如,如果数据是一个简单的字符串列表,我们可以使用 ArrayAdapter;如果数据是一个复杂的对象列表,我们可以使用 BaseAdapter 或者 RecyclerView.Adapter。 如果我们希望自定义下拉列表的样式,我们还可以使用自定义的布局资源文件来为 Adapter 设置列表项的布局。我们可以通过重写 getView() 方法来实现这个功能,可以将自定义的布局资源文件与 Adapter 关联,同时可以使用 findViewById() 方法来获取布局中的视图元素,并将数据填充到这些视图中。 总而言之,Android Spinner Adapter 是一个用于为 Spinner 提供数据源的接口。通过实现 Adapter 接口并重写其中的方法,我们可以创建自定义的 Adapter 来为 Spinner 提供数据,并可以自定义下拉列表的样式和布局。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值