自学Android之UI组件:(一)ListView功能详解And实战

转载的老板请注明出处:http://blog.csdn.net/cc_xz/article/details/61422291 万分感谢!

前言:

从本篇开始讲解关于UI方面的内容,不过数量不会很多,这是因为,比较基础的UI例如Button等,网上的相关教程非常多,而且使用起来也比较简单。关于UI的大部分精力计划将放在RecyclerView等高级UI上,以及自定义View中。
而且从本篇开始,将以代码为主,再给予非常详细的注释来进行说明。话不多说,本篇开始。

通过本篇,你将了解到:
1.初步实现一个ListView。
2.对ListView进行优化。

实现使用ListView显示数据:

首先来看本篇文章实现的效果:
这里写图片描述
代码思路如下:


/** 代码思路:
 * 1.首先初始化所需组件,以及定义MainActivity的布局文件。
 * 2.然后新建一个类定义ListView显示的数据格式。
 * 3.通过ArrayList添加用于显示数据。
 * 4.继承自BaseAdapter并定义Adapter的构造方法。
 * 5.定义ListView布局页面。
 * 6.定义Adapter的getView方法。
 * 7.定义Adapter的其他方法。
 * 8.应用Adapter。*/

以下代码在activity_main中写入:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

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

</LinearLayout>

以下代码在MainActivity中写入:


    private String TAG = "来自于MainActivity";
    private Context mContext;
    private SpursAdapter mAdapter;
    private ListView mListView;
    private ArrayList<GetSpurs> mListData;

以下代码在MainActivity的onCreate()中写入:


        /* 代码执行1:
        * 1.将MainActivity的上下文环境作为参数传递给Context中,用于Adapter使用。
        * 2.初始化ListView组件。值得注意的是,ListView是写在activity_main中的。
        * 3.然后创建一个ArrayList对象,用于放置数据。*/
        mContext = MainActivity.this;
        mListView = (ListView) findViewById(R.id.ListView);
        mListData = new ArrayList<>();

以下代码在新创建的GetSpurs类中写入:


/* 代码执行2:
* 1.首先确定ListView都需要显示那些内容,然后根据显示的内容的格式来创建变量。
* 2.1.这里分别创建用于显示名称的String变量,显示说明的String变量以及显示头像的int类型变量。
* 2.2.值得注意的是,图片资源路径在Android是由R文件进行存储,而R文件存储的格式就是int类型。3
* 3.接着对应创建的变量,分别创建构造方法及get方法。
* 4.值得注意的是,很多文章中都创建是set方法,但是本篇中不会用到set方法,所以无需设置。*/
public class GetSpurs {
    private String TAG = "来自于GetSpurs";
    private String mName;
    private String mExplain;
    private int mIcon;

    public GetSpurs(String name, String explain, int icon) {
        mName = name;
        mExplain = explain;
        mIcon = icon;
    }

    public String getName() {
        return mName;
    }

    public String getExplain() {
        return mExplain;
    }

    public int getIcon() {
        return mIcon;
    }
}

以下代码在MainActivity中写入:


        /* 代码执行3:
        * 1.通过创建好的ArrayList对象来将数据填充进去。
        * 2.首先使用已经创建好的数据类型类(在前面创建ArrayList的时候已经指定了泛型)在.add()中新建一个内部匿名对象。
        * 3.接着通过GetSpurs中的构造方法的的参数顺序来初始化希望在ListView中显示的数据的内容。
        * 4.你可以按照我给出的数据来进行填充,也可以根据你自己的想法进行改造。*/
        mListData.add(new GetSpurs("科怀·莱昂纳德","比赛终结者/窒息兄弟/马刺新领袖",R.drawable.im_spurs_1));
        mListData.add(new GetSpurs("拉马库斯·阿尔德里奇","原中投小王子/现大里锋/马刺二当家/不是关键·篮板·不抢星人",R.drawable.im_spurs_2));
        mListData.add(new GetSpurs("保罗·加索尔","进攻轴/速度巨慢/站撸抓冒",R.drawable.im_spurs_3));
        mListData.add(new GetSpurs("托尼·帕克","法国自行车/小陀螺转/马刺掌舵者",R.drawable.im_spurs_4));
        mListData.add(new GetSpurs("丹尼·格林","窒息兄弟/皇阿玛/不是关键·三分·不进星人",R.drawable.im_spurs_5));
        mListData.add(new GetSpurs("帕蒂·米尔斯","米神/三分神经刀/XJBD小分队成员",R.drawable.im_spurs_6));
        mListData.add(new GetSpurs("大卫·李","大腿李/篮下终结者/颜值帝",R.drawable.im_spurs_7));
        mListData.add(new GetSpurs("马努·吉诺比利","妖刀/男神没有之一/XJBD小分队队长",R.drawable.im_spurs_8));
        mListData.add(new GetSpurs("乔纳森·西蒙斯","身体爆劲/扣篮不进.....",R.drawable.im_spurs_9));
        mListData.add(new GetSpurs("德维恩·戴德蒙","身体爆劲/真正蓝领/穷人小乔丹",R.drawable.im_spurs_10));
        mListData.add(new GetSpurs("德章泰·穆雷","新秀一枚/潜力股",R.drawable.im_spurs_11));

以下代码在新创建的SpursAdapter类中写入:


/* 代码执行4:
* 1.首先创建一个类型,用于继承自BaseAdapter。
* 2.继承完后,根据编译器的提示,重写出getCount()等方法。
* 3.接着定义Adapter中所需的变量。
* 4.1.然后创建构造方法,用于在MainActivity调用Adapter时传输参数。
* 4.2.第一个参数是在MainActivity中定义好的数据源,Adapter就是用于将数据源进行处理,然后显示到ListView中。
* 4.3.由于我们需要定义ListView的布局,所以需要获取布局中的ID,这需要一个Activity的上下文环境,所以将MainActivity的上下文环境传输至此。*/
public class SpursAdapter extends BaseAdapter{
    private String TAG = "来自于SpursAdapter";
    private ArrayList<GetSpurs> mListData;
    private Context mContext;

    public SpursAdapter(ArrayList<GetSpurs> listData, Context context){
        mListData = listData;
        mContext = context;
    }
    @Override
    public int getCount() {
        return 0;
    }

    @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) {
        return null;
    }
}

以下代码在新创建的list_item Layout中写入:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/ImageView"
        android:layout_width="160dp"
        android:layout_height="140dp"
        android:paddingLeft="10dp" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/TextViewName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:paddingTop="8dp"
            android:textColor="#AAAAAA"
            android:textSize="24sp" />

        <TextView
            android:id="@+id/TextViewSays"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:paddingTop="5dp"
            android:textColor="#72b6e3"
            android:textSize="17sp" />
    </LinearLayout>
</LinearLayout>

值得注意的是,当你在上面的布局文件中写出什么样子的布局,ListView就将以什么界面去显示布局。而且可能让你困惑的是,如果此时通过Android Studio的布局查看器查看当前布局,你会发现两个TextView只占了很少的部分,如下图:

这里写图片描述

这是因为,布局页面的垂直方向设置成了”horizontal”(水平方向),而最上层的LinearLayout中只有“两个”布局文件,分别是显示图片的ImageView和放置TextView的LinearLayout。
而我在放置TextView的LinearLayout中设置了”vertical”(垂直方向),又考虑到不同球员的名称长度、说明长度不同,所以并没有强行设置TextView的宽高,等到数据填充进去的时候,自动会根据数据源的大小而改变两个组件的大小。就像前面所展示的效果图一样。

以下代码在SpursAdapter的getView()中写入:
注意敲黑板,下面的代码是本篇的重中之重!!!


    /* 代码执行6:
    * 1.1.getView()是Adapter中最重要的接口回调,它用于将需要在视图中显示的数据同显示数据的组件进行绑定。
    * 1.2.每增加一个Icon,则需要调用一次getView,用于创建Icon。
    * 2.1.getView提供了三个参数,第一个参数为当前显示的Icon的位置,即:第一个Icon对应的值为0,第10个Icon对应的值为9。
    * 2.2.converView则是一个用于获取View布局文件的对象,我们通过mContext对象中存储的MainActivity的上下文交给它,从而获得用于ListView显示的布局文件。
    * 2.3.第三个参数是ViewGroup,即是否要定义这个ListView的父View。此处暂且不表。*/
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        /* 代码执行6:
        * 1.1.首先通过LayoutInflater的.from()获取到一个Activity的上下文环境,即需要添加的Layout是依附哪个Activity上显示。
        * 1.2.接着通过inflate()来获取定义好的Layout,即当前ListView显示的布局文件。
        * 1.3.后面的null,就是添加的ListView的父View,可以添加一个ViewGroup类型的父布局,如果不需要也可以填写为null。
        * 2.1.然后分别创建ListView布局文件中的三个组件的对象,但是值得注意的是,在获取ID时同在Activity中直接获取不同。
        * 2.2.是通过定义好的convertView来调用findViewById()。
        * 2.3.这是因为当前类并不是Activity,所以并不具有获取ID的方法,但是通过LayoutInflater可以获取到一个布局,且返回格式为View。
        * 2.4.这就表示通过了一个定义好的View视图来获取ID,即获取这个定义好的View中你的组件ID。
        * 3.1.之后分别给三个组件进行赋值。
        * 3.2.所赋的值全部来自于在MainActivity中定义好的ArrayList,通过get(),这时就使用到了position参数。即:当前要显示哪个Icon,就从对应的ArrayList中取出对应的数据。
        * 3.3.由于定义的ArrayList是通过GetSpurs定义的数据格式,当取出数据时,也需要通过GetSpurs的getXXX()来获取数据。(暂时还用不到set方法)。
        * 4.1.再次强调一遍,每显示一个Icon,就调用一次getView(),所以每次都把新的View视图(convertView)返回给ListView(这里是Adapter)。*/
        convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item, null);
        ImageView imageView = (ImageView) convertView.findViewById(R.id.ImageView);
        TextView textViewName = (TextView) convertView.findViewById(R.id.TextViewName);
        TextView textViewSays = (TextView) convertView.findViewById(R.id.TextViewSays);

        imageView.setBackgroundResource(mListData.get(position).getIcon());
        textViewName.setText(mListData.get(position).getName());
        textViewSays.setText(mListData.get(position).getExplain());

        return convertView;
    }

以下代码在SpursAdapter中写入:


    /* 代码执行7:
    * 1.1.首先定义getCount(),该方法是在ListView开始创建前,获取ListView所显示的长度的。
    * 1.2.所以返回数据源的长度,告知ListView本次需要显示多少条数据。
    * 2.1.getItem()是用于定义ListView中显示的组件类型,暂时设置null即可,下篇文章设置此处。
    * 3.1.getItemId即返回与getItem()对应的ListView显示位置。*/
    @Override
    public int getCount() {
        return mListData.size();
    }

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

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

以下代码在MainActivity的onCreate()中写入:


        /* 代码执行8:
        * 1.首先创建SpursAdapter为对象,并且根据构造方法,将当前的数据源和上下文环境作为参数。(回想一下这两个参数的作用?)
        * 2.接着,将定义好的Adapter应用到ListView中,此时ListView开始工作,显示内容。*/
        mAdapter = new SpursAdapter(mListData, mContext);
        mListView.setAdapter(mAdapter);

以上,你就可以试着运行程序了,这样实现的效果同前面展示的效果相同,再来看一遍,如下图所示:
这里写图片描述

分析现有的ListView:

好了,现在你的程序已经可以使用了,但是现在存在一个问题,那就是每当创建一个Icon,都会调用一次getView(),现在我们展示的内容还很少,只有10来条,而ListView中的组件也不多,只有3个(组件越多创建的对象越多)。但是现在毕竟只是测试环境,真正的APP上要显示的内容和组件数量要比现在多的多,虽然不会内存溢出,但是如果以现在的状态去运行,那么程序可能会被卡死。
那么这时候如果使用ViewHolder会提供程序运行效率,在不用ViewHolder的时候,每次调用getView方法,都会通过findViewById查找布局里的控件,这个是比较耗费时间的,使用ViewHolder可以把控件存储到ViewHolder里面,在使用时候直接从ViewHolder取出来就可以,这样能有效的提高程序效率。
现在来尝试优化一下。

优化ListView:

废话不多说,直接上代码:

以下代码在SpursAdapter中写入:


    /* 代码执行9:
    * 1.首先创建一个ViewHolder子类,在这个类中创建我们所需要的组件静态变量。
    * 2.要知道,ViewHolder并不是Java或Android提供给我们的一种API,而是一种编程方式。
    * 3.这是因为,原来每次新增一个Icon,都需要调用一次getView(),而每调用一次getView(),里面的所有对象、变量都会被重写创建。
    * 4.于是采用这种方式,用来保存对象,配合if判断,就可以实现只有在第一次调用getView()创建对象,后面的使用原有对象即可。*/
    public class ViewHolder {
        ImageView imageView;
        TextView textViewName;
        TextView textViewSays;
    }

以下代码在SpursAdapter的getView()中重写入(原有代码注释掉):


        /* 代码执行9:
        * 1.首先创建并初始化ViewHolder对象,用于获取其中与组件所对应的对象。
        * 2.1.接着判断当前getView()接口传递来的convertView若为不空,则表示已经使用过ListView,无需创建对象,直接调用viewHolder即可。
        * 2.2.反之,如果convertView如果为空,则表示是第一次使用ListView,则需要创建各个对象。
        * 3.如果已经使用过ListView,则只需通过getTag()将打入的TAG(标记)取出,便可以获得已经完成的初始化操作(对象中包含的组件对象的赋值)。
        * 4.1.如果没有使用过ListView,除了获取布局和通过ViewHolder获得组件ID外,还需要打标签。
        * 4.2.打标签是指,将ViewHolder现有的信息,即对象初始化数据,通过标签的形式打入converView中,再通过第三步取出,则无需再次初始化组件。*/
        ViewHolder viewHolder = new ViewHolder();
        if (convertView != null) {
            viewHolder = (ViewHolder) convertView.getTag();
        } else {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item, null);
            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.ImageView);
            viewHolder.textViewName = (TextView) convertView.findViewById(R.id.TextViewName);
            viewHolder.textViewSays = (TextView) convertView.findViewById(R.id.TextViewSays);
            convertView.setTag(viewHolder);
        }

        /* 代码执行9:
        * 1.设置数据的方式同原来的方式大致形同,但是通过ViewHolder获取组件对象来获取ID。*/
        viewHolder.imageView.setBackgroundResource(mListData.get(position).getIcon());
        viewHolder.textViewName.setText(mListData.get(position).getName());
        viewHolder.textViewSays.setText(mListData.get(position).getExplain());

后记:

本篇通过一个比较简单的ListView应用讲解了ListView的使用方式,主要是每个方法的功能、每个步骤的作用。后续的文章也会使用这种方式来书写。
另外,刚刚看了一下效果,发现代码中的注释看着效果还是比较晕,所以推荐把源码下载下载,直接使用Android Studio来观看,个人感觉效果还是比较好的。代码中的注释也比较全。

源码传送门:http://download.csdn.net/detail/cc_xz/9778113

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值