初学安卓路之初识ListView

There’s a phrase in Buddhism, ‘Beginner’s mind.’ It’s wonderful to have a beginner’s mind.
佛教中有一句话:初学者的心态;拥有初学者的心态是件了不起的事情。——乔布斯

  小弟初学安卓,该文算是小弟的学习过程,课后笔记与一些自己的思考,希望在自己的自学路上留下印记,也许有一些自己想的不对的地方,希望各位前辈斧正。

一.ListView与Adapter

  1.说说Adapter
  
  Adapter,中文翻译为适配器,我们生活中肯定能见到一种适配器,就是电源适配器。例如笔记本的电源适配器,我国家庭标准电压为220V,而实际上笔记本电脑所需要的电压并不是220V,为了让笔记本正常工作,就需要将220V交流电通过变压、整流、滤波、稳压,输出为适合笔记本的电压。
  
  而在安卓中,为了使数据能以合适的方式在视图上显示,就得用到适配器。数据有许多种类型,就像是各种不同的”输入电压“,而经过适配器的处理,就能稳定输出视图能接受的”电压“。我们常用的适配器一共有:ArrayAdapter,SimpleAdapter等,他们都是继承于BaseAdapter 。而小弟这次主要初步了解了BaseAdapter,以后再详细研究ArrayAdapter,SimpleAdapter。
  
  2.初识ListView
  
  List为”列;列表“的意思,实际上ListView就是我们常见的列表视图,例如电话簿,QQ好友列表这些都是ListView展示的。许多图形界面编程里都有ListView这个控件。而在安卓中,ListView具有以下几个重要的属性:
  
   android:listSelector:设置列表项被点击时的背景色。
   android:scrollingCache:如果设为true,在滚动时会使用绘制缓存。
   android:divider:设置列表项的分割线(既可以是颜色分隔也可以是Drawable分隔)。
   android:dividerHeight:设置列表项的分割线的高度。
   android:headerDividerEnabled:如果设为false,则不在header View之后绘制分割线。
   android:footerDividerEnabled:如果设为false,则不在footer View之前绘制分割线。
  

  3.ListView与Adapter的关系
  
  说了这么多,为什么在讲ListView之前要提Adapter呢?因为要在ListView列表项显示数据,做出我们想看到的列表样式,就必须用到Adapter。引用一下别人的说法,就是把新建的ListView当成一个”柜子“的话,Adapter的任务就是将要展示的复杂数据,处理为一个个规格符合要求的”抽屉“,一层一层的塞入”柜子“,使这个”柜子“变得完整可用。这一层层”抽屉“就是列表视图的ITEM列表项。所以说,ListView与Adapter是密不可分的!它们是真心相爱的!!! 顺带一提ListView的父类的父类是AdapterView,未来小弟会做更深的了解!
  图片来自网络
  

二、制作一个简单的ListView

  以我自己模仿微信的关于界面做的一个样式为例吧(标题返回按钮没做),
  这里写图片描述
  这个页面只用了一个布局和一个ListView控件,小弟将慢慢讲述我的学习过程。
  
  1.布局的设计
  
  我想到了好几个方法,接下来小弟会一一的讲述:
  
  ⑴两个线性布局夹一个ListView?
  
  这个是我最初的想法,并且我还实践了,制作出来的效果很接近了,如图:
  
  这里写图片描述
  可是当我自己用手机进到了微信的关于微信界面看了之后,发现自己很蠢,有以下几个理由:
  ①最外面一层线性布局,里面还有两个线性布局?太浪费资源了吧!
  ②请看上图的最下边的字,那是我用来测试的,它的下面还有四个字“短短短短”未显示,上图的模拟器分辨率为768×1280,当我换为1440×2560的模拟器时,“短短短短”就显示了,显然分辨率为768×1280的手机要显示下面的文字,就得往上滑动一下,然而,布局本身是没有滑动功能的。说了这么多其实就是我发现微信的那个界面当内容没有完全展示时,它是可以滑动的,所以我的第一个设计肯定是错误的。
  
  ⑵最外面裹一层ScrollView试试?
  
  这个我稍微尝试了一下,发现很不好用,首先因为这个界面中间这块ListView因为ITEM较少,所以我们设置ListView的属性android:layout_height=”wrap_content”来使它展示所有选项,而不是令它有选项未展示而需要去滑动。而当在外面裹一层ScrollView时,这个属性就会使ListView只显示一行选项,必须去给它设置高度,而且ListView还不能滑动了,小弟度娘了一下ScrollView嵌套了ListView,发现其中有些复杂的原因,上面出现的问题也是可以解决的,小弟就不在这多说了,因为我们要制作的界面实际上比较简单,不需要做的那么复杂而且嵌套层次太多很浪费。
  
  ⑶就用一个ListView吧!
  
  既然需要用到滑动,干脆就直接使用ListView的滑动功能不就好了么?不过这样的话这个界面列表上下的灰色区域怎么做呢?小弟在上网络课程时听老师无意提起过ListView的addHeaderView()addFooterView()方法,一开始我不知道HeaderViewFooterView是啥玩意,在线翻译了一下似乎是页眉与页脚的意思。总之让我们先开始吧。
  
  首先是页面主布局:
  

<?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="match_parent"
    android:background="@color/gray"
    tools:context="com.qdf.wechatabout.MainActivity">


    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        />

</RelativeLayout>

  然后是MainAcitivity.java代码:
  

import android.support.v7.app.AppCompatActivity;
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 ListView mListView;
    private LayoutInflater mLayoutInflater;//声明布局解析器
    private List<String> mList;         //作为数据源
    private WeChatAboutAdapter mAdapter;//声明适配器

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mList = new ArrayList<>();//存放列表字符串的集合
        mList.add("去评分");
        mList.add("功能介绍");
        mList.add("系统通知");
        mList.add("帮助与反馈");
        mList.add("检查新版本");
        mAdapter = new WeChatAboutAdapter(MainActivity.this, mList);//实例化适配器
        /**令布局解析器获得系统布局解析服务*/
        mLayoutInflater = (LayoutInflater) MainActivity.this.getSystemService(LAYOUT_INFLATER_SERVICE);
        View headerView = mLayoutInflater.inflate(R.layout.headview_layout, null);
        View footerView = mLayoutInflater.inflate(R.layout.footerview_layout, null);
        mListView = (ListView) findViewById(R.id.listView);
        mListView.addHeaderView(headerView, null, false);//添加HeaderView,第三个参数为false表示不可被选择
        mListView.addFooterView(footerView, null, false);//添加FooterView,第三个参数为false表示不可被选择
        mListView.setAdapter(mAdapter);//设置适配器
    }
}

  来说说上面的代码,全局变量已经注释了。首先添加字符串到集合,作为待会分到每一选项的数据,然后解析自定义的headerview_layout.xml和footerview_layout.xml变为视图headerView与footerView并添加进mListView。这样列表视图的首尾项就能实现我们需要的灰色区域效果,之后给mListView设置适配器来将之前添加进mList的数据展示到每一个ITEM,要注意的是addHeaderView与addFooterView必须写在setAdapter之前,原因是当我们在调用setAdapter方法时会 android会判断当前listview是否已经添加header或者footer。在设置OnItemClickListener时,HeaderView的position值为0,依次往下排。值得一提的是,在我们自定义的adapter中,position值是不包含HeaderView和FooterView的,所以我们在适配器中为每项展示数据不会影响到HeaderView和FooterView。(P.s:positon就是一个选项在ListView的位置)
  
  然后是自定义适配器类WeChatAboutAdapter.java的代码:
  

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.ArrayList;
import java.util.List;

public class WeChatAboutAdapter extends BaseAdapter {

    private Context mContext;
    private List<String> mList = new ArrayList<>();
    private LayoutInflater mLayoutInflater;

    /**
     * 构造方法
     * @param context 上下文
     * @param list     需要展示的数据集合
     */
    public WeChatAboutAdapter(Context context, List<String> list) {
        mContext = context;
        mList = list;
        mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override

    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        return mList.get(position);
    }

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



    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        //convertView只有为null时才会进行一次引用,之后重用,控件相同
        if (convertView == null) {
            convertView = mLayoutInflater.inflate(R.layout.wexin_item_layout, null);
            viewHolder = new ViewHolder();
            viewHolder.textView = (TextView) convertView.findViewById(R.id.textView);
            convertView.setTag(viewHolder);//将viewHolder作为convertView的标签(额外的数据)
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.textView.setText(mList.get(position));
        return convertView;
    }

    /**
     * 该内部类ViewHolder用来进行控件的重用
     */
    class ViewHolder {
        TextView textView;
    }
}

  首先这个类的getItem方法得到的position是不包含HeaderView和FooterView的。在第一次调用getView方法时,convertView此时为null,我们为其引用解析好的ITEM_Layout视图,此时item1的视图就完成了,而因为我们要求“柜子”的“抽屉”样式相同,只是里面摆放的东西(数据)不一样,为了节省资源,convertView应该复用,我们让它只有在为空值时才进行引用,这样下一次使用无需再创建新的视图,只需要改变返回的数据即可,之后成为了item2。视图内控件也应当重用,利用ViewHolder,convertView为空时实例化ViewHolder然后获取控件,并将ViewHolder作为convertView的标签存放控件,复用时调用getTag()。

  最后附上为HeaderView和FooterView创建的布局文件:
  
  headerview_layout.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="wrap_content"
              android:background="@color/gray"
              android:gravity="center_horizontal"
              android:orientation="vertical">

    <ImageView
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:layout_marginTop="20dp"
        android:src="@drawable/weixin"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingBottom="20dp"
        android:text="@string/version_string"
        android:textSize="20sp"/>

</LinearLayout>

  
  footerview_layout.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="wrap_content"
              android:background="@color/gray"
              android:gravity="center_horizontal"
              android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:gravity="center"
        android:text="使用条款和隐私政策"
        android:textColor="@color/light_blue"
        android:textSize="12sp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="25dp"
        android:gravity="center_horizontal"
        android:text="腾讯公司 版权所有\nCopyright© 2011-2015 Tencent\nAll Rights Reserved"
        android:textColor="#A8A8A8"
        android:textSize="10sp"/>

</LinearLayout>

三、一些功能的实现

  1.监听ITEM点击事件
  
  因为ListView由多个选项组成一个整体,所以我们应该要监听每个ITEM的点击,这就需要使用setOnItemClickListener()而不是setOnClickListener(),且如果使用setOnClickListener()时会报错。
  这里写图片描述
  以下为setOnItemClickListener()代码:
  

        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                if(position==1){
                    mList.clear();
                    mList.add("测试");
                    mList.add("测试");
                    mList.add("测试");
                    mAdapter.notifyDataSetChanged();
                }
                Toast.makeText(MainActivity.this,"第"+position+"项被点击了",Toast.LENGTH_SHORT).show();
            }
        });

(P.s:在适配器类中重写isEnabled(int position)可以控制ITEM能否被选择)
  而点击事件的内容就是我们要实现的另一个功能——数据更新。
  
  2.简单的数据更新
  
  可见上面监听器代码中设置了,当position值为1时,清空mList集合并添加了新的数据,但此时还需要一个步骤才能再视图中更新数据,那就是让适配器调用notifyDataSetChanged()。要注意的是,position是从0开始的,如果有HeaderView则其position为0。另外HeaderView和FooterView能使用setOnClickListener(),因为它们是单独的视图。(可能说的不太准确)
这里写图片描述
  
  还有一些功能小弟还没研究透,真是感慨编程的博大精深啊,哈哈哈哈(无奈的苦笑)。
  参考资料:

  1. http://www.jianshu.com/p/7a66063b7889
  2. http://www.jianshu.com/p/49cb5c30788f
  3. 网络零散的资料

                                                          2016年7月24日
                                                          星期日
      

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值