ListView学习(一)-ListView使用

 ListView是android最常用的控件之一,可以实现各种列表:例如通讯录,聊天列表,好友列表等等。这里学习一下如何使用android的ListView
1. 实现一个简单的列表
     ListView使用需要与Adapter联合使用,Adapter负责生成View给ListView,最后调用ListView的SetAdapter方法把adapter实例设置进去就创建了一个列表。
下面看下adapter的用法,adapter都要继承自BaseAdapter,继承BaseAdapter要实现四个方法:getCount(返回数据的总条数),getItem(返回一个Item),getItemId(返回一个Item的Id),getView(返回List的Item的布局View,主要实现该方法),例如要实现下边这样的列表。



下边是实现简单列表的代码与注释:

public class MainActivity extends AppCompatActivity {

    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //1.找到listView实例
        listView = (ListView) findViewById(R.id.listView);
        //2.生成listView的数据
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 100; i++) {
            list.add(String.valueOf(new Random().nextDouble()));
        }
        //3.实例化adapter
        TxtAdapter adapter = new TxtAdapter(this, list);
        //4.把adapter设置给listView
        listView.setAdapter(adapter);
    }

    class TxtAdapter extends BaseAdapter {

        private Activity mActivity;
        private List<String> mDataList;

        public TxtAdapter(Activity activity, List<String> list) {
            this.mActivity = activity;
            this.mDataList = list;
        }

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

        @Override
        public Object getItem(int position) {
            return mDataList == null ? null : mDataList.get(position);
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //position参数是当前listView的position
            //convertView是ListView缓存复用的view,每次getView都创建一个view,效率低而且会内存泄漏
            //parent即ListView本身.
            LayoutInflater inflater = LayoutInflater.from(mActivity);
            if (convertView == null) {
                //inflate一个item的布局
                convertView = inflater.inflate(R.layout.layout_txt_item, parent, false);
            }
            TextView txt = (TextView) convertView.findViewById(R.id.txt);
            //给布局TextView赋值
            txt.setText(mDataList.get(position));
            return convertView;
        }
    }
}

注意:
     1.getView的时候一定要使用convertView,不要自己每次getView都创建新的view,这样listview滚动反复创建的view都被listView引用,造成内存泄漏。ListView的view复用,移除屏幕的view会缓存起来,然后赋值给convertView,为我们提升了效率。
     2.LayoutInflater的inflate方法是获取一个布局,第一个参数是布局的resource id,第二个参数是指定该布局的父布局,第三个参数,如果为false,该布局会使用父布局的LayoutParams,为true,使用父布局的LayoutParams并attach到父布局。

2. 使用ViewHolder
      为了进一步提升效率,adapter中可以定义一个ViewHolder来保存对Item布局内控件对引用,减少每次都inflate的消耗。所以上边的adapter可以写成下边的样子:

class TxtAdapter extends BaseAdapter {

    private Activity mActivity;
    private List<String> mDataList;

    public TxtAdapter(Activity activity, List<String> list) {
        this.mActivity = activity;
        this.mDataList = list;
    }

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

    @Override
    public Object getItem(int position) {
        return mDataList == null ? null : mDataList.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //position参数是当前listView的position
        //convertView是ListView缓存复用的view,每次getView都创建一个view,效率低而且会内存泄漏
        //parent即ListView本身.
        ViewHolder viewHolder;
        LayoutInflater inflater = LayoutInflater.from(mActivity);
        if (convertView == null) {
            //实例化ViewHolder
            viewHolder=new ViewHolder();
            //inflate一个item的布局
            convertView = inflater.inflate(R.layout.layout_txt_item, parent, false);
            //把布局内的控件赋值给ViewHolder
            viewHolder.textView=(TextView) convertView.findViewById(R.id.txt);
            //viewHolder存在convertView的tag中,可以在下边getTag中获取
            convertView.setTag(viewHolder);
        }else {
            //convert不为空,直接get到viewHolder
            viewHolder= (ViewHolder) convertView.getTag();
        }
        //给布局TextView赋值
        viewHolder.textView.setText(mDataList.get(position));
        return convertView;
    }

    class ViewHolder{
        TextView textView;
    }
}

3. 更新ListView上的item

     更新ListView使用了MVC模式,adapter是controller,ListView是View,数据List就是Model。我们改变了List的数据之后,例如对传入的dataList增删改查,之后再调用adapter的notifyDataSetChange方法,ListView的item就会更新了。这里ListView使用了设计模式中的观察者模式。

4. ListView的Item多种布局

     ListView中经常会用到多种布局,比如通讯录页面分组标题与具体姓名显示,微信的聊天页面左右两个气泡,等等。例如:



     实现多类型布局,我们同样可以根据传入数据的类型来生成对应的view,但是我们不能一直自己创建view。为了view的重用,需要实现BaseAdapter两个方法,getViewTypeCount(顾名思义,返回有几种布局类型总数),getItemViewType(int position)该方法根据当前position返回使用哪种布局。有了这两个方法,listView就知道在getView方法中返回正确的convertView了。下边是实例代码:

public class ChatActivity extends AppCompatActivity {

    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);
        //1.找到listView实例
        listView = (ListView) findViewById(R.id.listView);
        //2.生成listView的数据
        List<ChatModel> list = new ArrayList<ChatModel>();
        for (int i = 0; i < 100; i++) {
            ChatModel model = new ChatModel();
            model.setIsSelf(new Random().nextBoolean());
            model.setMsg(String.valueOf(new Random().nextDouble()));
            list.add(model);
        }
        //3.实例化adapter
        ChatAdapter adapter = new ChatAdapter(this, list);
        //4.把adapter设置给listView
        listView.setAdapter(adapter);
    }

    class ChatModel {
        private boolean isSelf;
        private String msg;

        public boolean getIsSelf() {
            return isSelf;
        }

        public void setIsSelf(boolean isSelf) {
            this.isSelf = isSelf;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }
    }

    class ChatAdapter extends BaseAdapter {

        private Activity mActivity;
        private List<ChatModel> mDataList;
        private static final int LEFT = 0;
        private static final int RIGHT = 1;

        public ChatAdapter(Activity activity, List<ChatModel> list) {
            this.mActivity = activity;
            this.mDataList = list;
        }

        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public int getItemViewType(int position) {
            if (mDataList.get(position).getIsSelf()) {
                return RIGHT;
            } else {
                return LEFT;
            }
        }

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

        @Override
        public Object getItem(int position) {
            return mDataList == null ? null : mDataList.get(position);
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            LayoutInflater inflater = LayoutInflater.from(mActivity);
            //获取布局类型
            int type = getItemViewType(position);
            if (convertView == null) {
                //实例化ViewHolder
                viewHolder = new ViewHolder();
                //inflate一个item的布局 根据类型不同返回对应布局
                if (type == LEFT) {
                    convertView = inflater.inflate(R.layout.chatting_item_msg_left, parent, false);
                } else {
                    convertView = inflater.inflate(R.layout.chatting_item_msg_right, parent, false);
                }
                //把布局内的控件赋值给ViewHolder
                viewHolder.textView = (TextView) convertView.findViewById(R.id.chatMsg);
                viewHolder.headerImg = (ImageView) convertView.findViewById(R.id.headerImg);
                //viewHolder存在convertView的tag中,可以在下边getTag中获取
                convertView.setTag(viewHolder);
            } else {
                //convert不为空,直接get到viewHolder
                viewHolder = (ViewHolder) convertView.getTag();
            }
            //根据type不同设置对应数据
            if (type == LEFT) {
                viewHolder.headerImg.setImageResource(R.drawable.left_header);
            } else {
                viewHolder.headerImg.setImageResource(R.drawable.right_header);
            }
            viewHolder.textView.setText(mDataList.get(position).getMsg());
            return convertView;
        }

        class ViewHolder {
            ImageView headerImg;
            TextView textView;
        }
    }
}

5. 同时响应ListView的Item和上边按钮

当item的布局上有按钮的时候,按钮会首先获得焦点,item就不能响应点击了。但是有些场景需要同时都可以响应,比如微信表情的下载列表



点击下载按钮下载,点击item进入详情。这个时候需要在item布局内加一个descendantFocusability=“blockDescendants”.下边是官方文档的解释
android:descendantFocusability

Defines the relationship between the ViewGroup and its descendants when looking for a View to take focus. 

Must be one of the following constant values.
Constant Value Description
beforeDescendants 0 The ViewGroup will get focus before any of its descendants. 
afterDescendants 1 The ViewGroup will get focus only if none of its descendants want it. 
blocksDescendants 2 The ViewGroup will block its descendants from receiving focus.

使用了blockDescendants(ViewGroup将阻止它的后代接收焦点)则按钮就不会抢焦点了,这样就可以同时响应按钮与item了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值