Android 工人、手机、手机包装盒——解析适配器 BaseAdapter

这一篇博文是建立在慕课网大牛Eclipse xu的基础上的,笔者听了他的课,在此表示感谢。

【如何描述BaseAdapter?】
软件世界中其实我们关注的其实是某个类的作用,而不是它是一个什么。

所谓BaseAdapter,按照笔者目前的理解,其实就是数据源和控件之间的桥梁。

我们首先考虑一个问题。
一个手机包装盒,一个手机,是否可以完成包装的任务呢。

答案是不可以,因为完成包装的既不是手机,也不是手机包装盒自身。而是人。包装工人要有一套包装的规则,手机怎么放,充电线放哪里,这都是规则指定的。
如果一个View是手机包装盒,那么你所想要展示的数据源就是手机,而BaseAdapter所扮演的就是人的角色。

那么ListView呢?ListView可以看作装有很多个手机包装盒的包装箱。

【BaseAdapter使用】
我们用一个小Demo来解析一下。
首先我们来定制一下手机包装盒。

<!--item.xml 手机包装盒的样式-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".MainActivity"
    android:id="@+id/rl_item">
    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/ic_launcher"
        />
    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_toEndOf="@+id/iv_image"
        android:text="Title"
        android:gravity="center"
        android:textSize="20sp"
        />
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_toEndOf="@+id/iv_image"
        android:layout_below="@+id/tv_title"
        android:text="Content"
        android:gravity="center_vertical"
        android:textSize="15sp"
        />
</RelativeLayout>

接下来是activity_main.xml

<!--在这里我们可以清楚地看到一个手机包装盒系列ListView-->
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
    android:id="@+id/mainLayout">

    <ListView
        android:id="@+id/lv_main"

        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentEnd="true"
       />

</RelativeLayout>

下面是手机的代码。

package com.practice.fridge.fridgetest;

import android.widget.ImageView;

/**
 * ItemBean就是我们所要的手机
 * Created by Fridge on 2015/5/7.
 */
public class ItemBean {
    public int ItemImageResId;
    public String ItemTitle;
    public String ItemContent;

    public ItemBean(int itemImageResId, String itemTitle, String itemContent) {
        ItemImageResId = itemImageResId;
        ItemTitle = itemTitle;
        ItemContent = itemContent;
    }
}

现在,这个世界看起来并不圆满。
手机ItemBean还没有一个实例。
在手机包装盒item中空有手机的模子,但是并没有实物。
而包装箱ListView中什么都没有。

我们需要一个工人!他可以精准地将100个1000个手机精准地放入到手机包装盒之中。

//MyBaseAdapter.java
package com.practice.fridge.fridgetest;

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;
import java.util.zip.Inflater;

/**
 * Created by Fridge on 2015/5/7.
 */
 //我们的工人,底下的四个函数是必须的
public class MyBaseAdapter extends BaseAdapter {
    //等待着工人去处理的一系列手机
    private List<ItemBean> mItemBeanList;
    //通过Inflater,工人可以将一个手机包装盒拿到手
    private LayoutInflater mInflater;
    public MyBaseAdapter(Context context,List<ItemBean> itemBeans) {
        mItemBeanList=itemBeans;
        mInflater=LayoutInflater.from(context);
    }

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

    @Override
    public Object getItem(int i) {
        return mItemBeanList.get(i);
    }


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


//最重要的组装过程
    @Override
    public View getView(int i, View convertView, ViewGroup viewGroup) {
    //根据包装盒的id找到手机包装盒
        View view=mInflater.inflate(R.layout.item,null);
    //看准了包装盒中手机、手机充电器的槽
            ImageView imageView = (ImageView) view.findViewById(R.id.iv_image);
            TextView  title=(TextView) view.findViewById(R.id.tv_title);
            TextView  content=(TextView) view.findViewById(R.id.tv_content);
         //将一个手机、手机充电器拿在手中,一一套进去
        ItemBean itemBean=mItemBeanList.get(i);
        imageView.setImageResource(itemBean.ItemImageResId);
        title.setText(itemBean.ItemTitle);
        content.setText(itemBean.ItemContent);
    //将手机返回给手机包装箱
        return view;

    }
}

大功告成!
这时候不要忘记了,我们只是定义了这几个类的指责罢了。
还需要在MainActivity中实例化他们,并且将关系联结好。

package com.practice.fridge.fridgetest;

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 List<ItemBean> mItemBeanList;
    private MyBaseAdapter mMyBaseAdapter;
    protected ListView mListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mItemBeanList=new ArrayList<>();
        for(int i=0;i<20;i++){
            ItemBean itemBean=new ItemBean(R.mipmap.ic_launcher,"标题"+i,"内容");
            mItemBeanList.add(itemBean);
        }
        mMyBaseAdapter=new MyBaseAdapter(this,mItemBeanList);
       //这个包装箱的负责人就交给这一名工人了
        mListView=(ListView) findViewById(R.id.lv_main);
        mListView.setAdapter(mMyBaseAdapter);
    }





}

【更好的工人】
没有错,我们现在已经拥有了一名可以稳定工作的工人BaseAdapter,但是!不想做程序员的厨师不是好工人。所以我们也许需要对他进行一下调教

且看这一句

 //根据包装盒的id找到手机包装盒
 View view=mInflater.inflate(R.layout.item,null);

这里就好像工人分明一直包装的iPhone手机,但是他每一次得根据包装盒的规格去找这个包装盒。其实这个包装盒对他来说只有一种嘛~干嘛这么费劲!
我们也看到getView的三个参数int position ,view convertView 和viewGroup我们一个也没有用到,这让我们从直觉上觉得不太对。
其实Android中拥有缓存机制,之前的View会缓存。之后会通过convertView这个参数传递进来。

所以,我们应该把上面这行代码这样写。

        View view;
        if(convertView==null){
            view=mInflater.inflate(R.layout.item,null);
        }
        else{
            view=convertView;
        }

这样,就可以不用每一次调用getView()这个函数时都去inflate了。

那么,是不是还可以做得更好?
还可以。你也许已经注意到了既然View的Inflate可以只做一次就足够了,那么是不是findViewById也可以只做一次呢?

//新的代码
//新建一个内部类
class ViewHolder{
        public ImageView imageView;
        public TextView title;
        public TextView content;

    }
//
 public View getView(int i, View convertView, ViewGroup viewGroup) {
        View view;
        ViewHolder viewHolder;
        if(convertView==null){
            view=mInflater.inflate(R.layout.item,null);
            viewHolder=new ViewHolder();
            viewHolder.imageView=(ImageView) view.findViewById(R.id.iv_image);
            viewHolder.title=(TextView)view.findViewById(R.id.tv_title);
            viewHolder.content=(TextView) view.findViewById(R.id.tv_content);
            convertView.setTag(viewHolder);
        }
        else{
            view=convertView;
            viewHolder=(ViewHolder) convertView.getTag();
            ItemBean itemBean=mItemBeanList.get(i);
            viewHolder.imageView.setImageResource(itemBean.ItemImageResId);
            viewHolder.title.setText(itemBean.ItemTitle);
            viewHolder.content.setText(itemBean.ItemContent);
        }




        return view;

    }

通过convertView.setTag()和getTag()这两个方法,我们可以方便地在第一次inflate这个View的时候将这个View中的子View传递进去。在需要的时候取出。这样的效率要比findViewById()有效得多。尤其是在View很大的时候。

版权声明:本文为博主原创文章,转载请标明出处。

posted @ 2015-05-08 00:49  Fridge  阅读(157)  评论(0编辑  收藏  举报