其实,今天主要讲的不是ListView,而是列表的开发,说到列表的开发可以用到的控件就有ListView,GridView以及RecycleView等等。鉴于这些控件用法的相似性,今天就以List View作为代表展开。同样根据所见即所得原则,咋们来看看ListView是什么梗,请看大屏幕:
我想当你看到这两张图片的时候,你一定会想:我的天!怎么能这么炫酷,这真的是一个ListView所能做到的事情吗???如果我说当然不是了,我估计你有打死我的冲动了。不过,事实上就是用一个ListView完成的,如果说第一幅图你能相信是用一个ListView完成的,那么第二幅图就有那么些许难以置信了吧,其实第二幅图只是手法上多了那么些许变化,从而诞生了这样的界面。好了,咋就不扯淡了,我们来看看实现的过程吧,首先是布局文件:
Note:本节的开发是在之前的导航栏的基础上做的开发,如果你没有看之前的文章点击打开链接,而你在看完这篇文章后又不知道怎么做,那么请看我之前的文章。如果你觉得自己能理解我的思路,你完全可以自己建立一个工程,毕竟我之前的开发只是给这次的开发提供了一个显示的容器而已。
fragment_first.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:background="@color/yellow" android:orientation="vertical"> <ListView android:id="@+id/lv_chat_list" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:divider="@color/transparent" android:dividerHeight="@dimen/activity_vertical_margin" android:listSelector="@color/transparent" android:padding="@dimen/activity_vertical_margin" android:scrollbars="none"></ListView> </LinearLayout>
item_chat.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="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/iv_icon_item" android:layout_width="32dp" android:layout_height="32dp" android:layout_marginLeft="@dimen/activity_horizontal_margin" /> <TextView android:id="@+id/tv_chat_name_item" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="@dimen/small_margin" android:textSize="@dimen/mid_text_size" /> </LinearLayout> <TextView android:id="@+id/tv_chat_message_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/message_margin" android:textSize="@dimen/mid_text_size" /> </LinearLayout>
FirstFragment.java
package com.teachmodel.fragment; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import com.teachmodel.R; import com.teachmodel.adapter.ChatAdapter; import com.teachmodel.bean.Chat; import java.util.ArrayList; import java.util.List; /** * Created by windbreaker on 16/3/23. */ public class FirstFragment extends Fragment { private View v; private ListView mListView; private List<Chat> mList; private ChatAdapter mChatAdapter; private String chats[] = {"Hi", "Hello", "what is your name?", "My name is DuBe.", "What?逗逼?", "Yeah....", "Ha...,How interesting", "Thank you."}; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { v = inflater.inflate(R.layout.fragment_first, null); init(); return v; } private void init() { mListView = (ListView) v.findViewById(R.id.lv_chat_list); mList = new ArrayList<>(); for (int i = 0; i < chats.length; i++) { Chat mChat = new Chat(); mChat.setName("机器人"); mChat.setMessage(chats[i]); mChat.setIcon(R.mipmap.ic_launcher); mList.add(mChat); } mChatAdapter = new ChatAdapter(mList, getActivity()); mListView.setAdapter(mChatAdapter); } }
ChatAdapter.java
package com.teachmodel.adapter; 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 com.teachmodel.R; import com.teachmodel.bean.Chat; import java.util.List; /** * Created by windbreaker on 16/3/26. */ public class ChatAdapter extends BaseAdapter { private List<Chat> mList; private LayoutInflater mLayoutInflater; public ChatAdapter(List<Chat> mList, Context context) { this.mList = mList; mLayoutInflater = LayoutInflater.from(context); } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return mList.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder mViewHolder = null; if (convertView == null) { mViewHolder = new ViewHolder(); convertView = mLayoutInflater.inflate(R.layout.item_chat, null); mViewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon_item); mViewHolder.tv_message = (TextView) convertView.findViewById(R.id.tv_chat_message_item); mViewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_chat_name_item); convertView.setTag(mViewHolder); } else { mViewHolder = (ViewHolder) convertView.getTag(); } mViewHolder.tv_name.setText(mList.get(position).getName()); mViewHolder.tv_message.setText(mList.get(position).getMessage()); mViewHolder.iv_icon.setImageResource(mList.get(position).getIcon()); return convertView; } class ViewHolder { TextView tv_name; TextView tv_message; ImageView iv_icon; } }
Chat.java
package com.teachmodel.bean; /** * Created by windbreaker on 16/3/26. */ public class Chat { private String name;//用户名 private String message;//发送的信息 private int icon;//用户头像 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public int getIcon() { return icon; } public void setIcon(int icon) { this.icon = icon; } }至此,我们来看看运行效果,请看大屏幕:
图3
看到这个截图,我想你一定会说:“说好的第一张图的效果呢??那么好看的界面怎么就变成这么丑了,我满满的期待都被你坑毁了!!!”对此,我弱弱的声明一下:欺骗你感情不是我本意,我的本意是那种功能布局可以这么实现,而且我也确实实现了这种列表式布局。你要是执着于第一,二张图的效果,那么请你看完这篇文章后找个美工设计一下图片然后更换一下布局就OK了。好了,话不多说,功能虽然实现了,但是我想你一定看得云里雾里,所以接下来让我来给你详细讲讲这些代码都是什么梗。首先是主布局文件FirstFragment.xml,这个文件里包含了一个线性布局和一个listView控件,这个控件里有这四个属性你可能会用到:
android:divider="@color/transparent"(设置分割线颜色为透明);
android:dividerHeight="@dimen/activity_vertical_margin"(设置分割线的高度);
android:listSelector="@color/transparent"(设置单击列表项时的显示颜色,不设置的话默认单击是有颜色的);
android:scrollbars="none"(设置没有滑动条)
然后是FirstFragment.java,首先咋们先获取ListView实例:mListView = (ListView) v.findViewById(R.id.lv_chat_list);
这个时候的mListView没有任何列表数据,所以咋们要给它一个列表数据,这个列表数据的由来是这样的:定义一个Chat类型队列List<Chat> mList,并在接下来的生命周期方法里初始化内存空间mList = new ArrayList<>();此时的队列为空,咋们要给这个队列插入数据:
for (int i = 0; i < chats.length; i++) {
Chat mChat = new Chat();
mChat.setName("机器人");
mChat.setMessage(chats[i]);
mChat.setIcon(R.mipmap.ic_launcher);
mList.add(mChat);
}
看到这里你可能会说,Chat这个类是什么梗???其实这个Chat是一个自定义的类,也就是咋们自己定义的。这个类的三个属性我已经注释得很清楚了。咋们最主要是要知道这三个属性是怎么得出来的,难道是凭空捏造出来的?事实,这三个属性是咋们从实现中抽象出来的,什么叫做抽象出来的呢?咋们来看图3的列表项,经过观察,我们可以看出每一个列表项都有一个头像,用户名和发送的内容。经过抽象可以看成是一个对象有用户名,发送内容和头像这三个属性。再经过特定语言的抽象,可以把用户名和发送内容抽象成字符串类型,头像抽象成整型。其实头像也可以抽象成字符串类型如果走的是网络获取头像的话,但这里我们用的是本地的图片所以抽象成整型了。
既然队列里有数据了,咋们就可以把它传递给mListView了吧,但是怎么才能这么数据能有个合理的排列呢?如果我说直接传给mListView,它就会自己排列了,你敢信吗?如果你信了,那我只能说你还是图样图森破了,我自己都不敢相信它能自己排列。为了能让它按照我们的设想进行排列,我们这里需要用到适配器Adapter.也就是我们ChatAdapter这个自定义类。
对于这个类的操作,首先我们要让ChatAdapter继承BaseAdapter,然后重写4个get方法,其中getCount()和getView()必须重写。接着咋们要为这个类定义两个属性:private List<Chat> mList(用于获取Chat队列);private LayoutInflater mLayoutInflater(用于引用布局文件);然后就是重写构造函数
public ChatAdapter(List<Chat> mList, Context context) {
this.mList = mList;
mLayoutInflater = LayoutInflater.from(context);
}
最后是对队列数据的排列,这个步骤在getView()方法中处理:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder mViewHolder = null;
if (convertView == null) {
mViewHolder = new ViewHolder();
convertView = mLayoutInflater.inflate(R.layout.item_chat, null);//引用布局文件
//获取实例布局文件中的控件ID,并初始化实例
mViewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon_item);
mViewHolder.tv_message = (TextView) convertView.findViewById(R.id.tv_chat_message_item);
mViewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_chat_name_item);
convertView.setTag(mViewHolder);
} else {
mViewHolder = (ViewHolder) convertView.getTag();
}
//将队列数据和相应的控件绑定并显示
mViewHolder.tv_name.setText(mList.get(position).getName());
mViewHolder.tv_message.setText(mList.get(position).getMessage());
mViewHolder.iv_icon.setImageResource(mList.get(position).getIcon());
return convertView;
}
请原谅我暂时不想对这个方法多说什么,咋们就先看着吧,记住这样做能把要显示的显示出来就OK。至于为什么要这么写,以后再做详细介绍。搞完适配器后,在FirstFragment中加上
mChatAdapter = new ChatAdapter(mList, getActivity());(初始化适配器)
mListView.setAdapter(mChatAdapter);(将mListView于适配器绑定并填装数据)
运行一下程序就可以看到图三的效果了。
不知道你看了图三的布局方式有没觉得很奇怪,因为QQ或者微信的聊天对话都是一左一右的,咋们是不是也可以实现这种布局呢?这当然是可以的,不过咋们要做一些修改:
1.增加一个右侧显示的布局文件item_chat_right.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="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_icon_right_item" android:layout_width="32dp" android:layout_height="32dp" android:layout_alignParentRight="true" android:layout_marginLeft="@dimen/activity_horizontal_margin" /> <TextView android:id="@+id/tv_chat_name_right_item" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="@dimen/small_margin" android:layout_toLeftOf="@id/iv_icon_right_item" android:textSize="@dimen/mid_text_size" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_chat_message_right_item" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="@dimen/message_margin" android:textSize="@dimen/mid_text_size" /> </RelativeLayout> </LinearLayout>
2.修改一下适配器ChatAdapter.java中的getView()方法:
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder mViewHolder = null; if (convertView == null) { mViewHolder = new ViewHolder(); if (position % 2 == 0) { convertView = mLayoutInflater.inflate(R.layout.item_chat, null);//引用布局文件 //获取实例布局文件中的控件ID,并初始化实例 mViewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon_item); mViewHolder.tv_message = (TextView) convertView.findViewById(R.id.tv_chat_message_item); mViewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_chat_name_item); } else { convertView = mLayoutInflater.inflate(R.layout.item_chat_right, null);//引用布局文件 //获取实例布局文件中的控件ID,并初始化实例 mViewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon_right_item); mViewHolder.tv_message = (TextView) convertView.findViewById(R.id.tv_chat_message_right_item); mViewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_chat_name_right_item); } convertView.setTag(mViewHolder); } else { mViewHolder = (ViewHolder) convertView.getTag(); } //将队列数据和相应的控件绑定并显示 mViewHolder.tv_name.setText(mList.get(position).getName()); mViewHolder.tv_message.setText(mList.get(position).getMessage()); mViewHolder.iv_icon.setImageResource(mList.get(position).getIcon()); return convertView; }
private void init() { mListView = (ListView) v.findViewById(R.id.lv_chat_list); mList = new ArrayList<>(); for (int i = 0; i < chats.length; i++) { Chat mChat = new Chat(); if (i % 2 == 0) { mChat.setName("白马秋风"); mChat.setMessage(chats[i]); mChat.setIcon(R.mipmap.my); } else { mChat.setName("机器人"); mChat.setMessage(chats[i]); mChat.setIcon(R.mipmap.ic_launcher); } mList.add(mChat); } mChatAdapter = new ChatAdapter(mList, getActivity()); mListView.setAdapter(mChatAdapter); }运行一下,效果请看大屏幕:
有没有觉得很神奇呢?如果没有,咋们可来做个更神奇的,比如和机器人聊天。
为了和机器人聊天,我们需要做一些修改:
1.修改first_fragment.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:background="@color/yellow" android:orientation="vertical"> <ListView android:id="@+id/lv_chat_list" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:divider="@color/transparent" android:dividerHeight="@dimen/activity_vertical_margin" android:listSelector="@color/transparent" android:padding="@dimen/activity_vertical_margin" android:scrollbars="none"></ListView> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/et" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" /> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="send" /> </LinearLayout> </LinearLayout>
package com.teachmodel.fragment; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import com.teachmodel.R; import com.teachmodel.adapter.ChatAdapter; import com.teachmodel.bean.Chat; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; /** * Created by windbreaker on 16/3/23. */ public class FirstFragment extends Fragment { private View v; private ListView mListView; private List<Chat> mList; private ChatAdapter mChatAdapter; private Hashtable<String, String> ansChats; private EditText et_chat; private Button btn_chat; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { v = inflater.inflate(R.layout.fragment_first, null); init(); return v; } private void init() { et_chat = (EditText) v.findViewById(R.id.et); btn_chat = (Button) v.findViewById(R.id.btn); mListView = (ListView) v.findViewById(R.id.lv_chat_list); mList = new ArrayList<>(); ansChats = new Hashtable<>(); ansChats.put("Hi", "Hello"); ansChats.put("What is your name?", "My name is DuBe."); ansChats.put("What?DouBi?", "Yeah...."); ansChats.put("Ha...,How interesting", "Thank you."); mChatAdapter = new ChatAdapter(mList, getActivity()); mListView.setAdapter(mChatAdapter); btn_chat.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Chat mChat = new Chat(); mChat.setName("白马秋风"); mChat.setIcon(R.mipmap.my); mChat.setMessage(et_chat.getText().toString()); mChatAdapter.addItem(mChat); getAns(et_chat.getText().toString()); et_chat.setText(""); } }); } private void getAns(String question) { Chat mChat = new Chat(); mChat.setName("机器人"); mChat.setIcon(R.mipmap.ic_launcher); if (ansChats.get(question) != null) { mChat.setMessage(ansChats.get(question)); } else { mChat.setMessage("I dont know what you ask?"); } mChatAdapter.addItem(mChat); mListView.setSelection(mListView.getBottom()); } }
package com.teachmodel.adapter; 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 com.teachmodel.R; import com.teachmodel.bean.Chat; import java.util.List; /** * Created by windbreaker on 16/3/26. */ public class ChatAdapter extends BaseAdapter { private List<Chat> mList; private LayoutInflater mLayoutInflater; public ChatAdapter(List<Chat> mList, Context context) { this.mList = mList; mLayoutInflater = LayoutInflater.from(context); } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return mList.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder mViewHolder = null; if (convertView == null) { mViewHolder = new ViewHolder(); if (position % 2 == 0) { convertView = mLayoutInflater.inflate(R.layout.item_chat, null);//引用布局文件 //获取实例布局文件中的控件ID,并初始化实例 mViewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon_item); mViewHolder.tv_message = (TextView) convertView.findViewById(R.id.tv_chat_message_item); mViewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_chat_name_item); } else { convertView = mLayoutInflater.inflate(R.layout.item_chat_right, null);//引用布局文件 //获取实例布局文件中的控件ID,并初始化实例 mViewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon_right_item); mViewHolder.tv_message = (TextView) convertView.findViewById(R.id.tv_chat_message_right_item); mViewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_chat_name_right_item); } convertView.setTag(mViewHolder); } else { mViewHolder = (ViewHolder) convertView.getTag(); } //将队列数据和相应的控件绑定并显示 mViewHolder.tv_name.setText(mList.get(position).getName()); mViewHolder.tv_message.setText(mList.get(position).getMessage()); mViewHolder.iv_icon.setImageResource(mList.get(position).getIcon()); return convertView; } class ViewHolder { TextView tv_name; TextView tv_message; ImageView iv_icon; } public void addItem(Chat mChat) { mList.add(mChat); notifyDataSetChanged(); } }然后,我们运行一下,并在输入框中输入一些特定和非特定的话,看看结果如何,请看大屏幕:
至此今天讲解的内容就暂时告一段落了,关于如何处理列表项单击事件将会在后续的讲解中提到,目前暂时不讲了,如果你有空的话可以去看看别人的相关博客。下面是本次项目的工程压缩包:点击打开链接。
你看我对你们多好,我都被自己的奉献精神给感动了..............