本文用ListView实现了一个可以展示文本和图片的类似聊天消息的界面。包括ListView的优化,ListView下拉到底部自动刷新以及ListView常用属性等知识。主要参考为网易微专业课程。其中布局文件都已经省略。
效果如下:
创建TextMessage类和ImageMessage类:
public class TextMessage {
private String text;
public TextMessage(String content) {
this.text = content;
}
public String getText() {
return text;
}
public class ImageMessage {
private int imgResId;
public ImageMessage(int imgResId) {
this.imgResId = imgResId;
}
public int getImgResId() {
return imgResId;
}
}
Adapter类
public class MainAdapter extends BaseAdapter {
private static final int VIEW_TYPE_COUNT = 2;
private interface ViewType {
// 必须从0开始,因为ListView内部是用一个数组维护ViewType的。
int TEXT = 0;
int IMAGE =1;
}
private ArrayList<Object> list;
private Context context;
public MainAdapter(Context context,ArrayList<Object> list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
ViewHolder holder = null;
// 如果是文本消息
if (getItemViewType(position) == ViewType.TEXT){
if (convertView == null){
holder = new ViewHolder();
convertView=inflater.inflate(R.layout.text_item,parent,false);
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
TextMessage textMessage = (TextMessage) list.get(position);
holder.text.setText(textMessage.getText());
}else { //如果是图片消息。
if (convertView==null){
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.image_item,parent,false);
holder.image = (ImageView) convertView.findViewById(R.id.image);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
ImageMessage imageMessage = (ImageMessage) list.get(position);
holder.image.setImageResource(imageMessage.getImgResId());
}
return convertView;
}
@Override
public int getViewTypeCount() {
return VIEW_TYPE_COUNT ;
}
@Override
public int getItemViewType(int position) {
if (getItem(position) instanceof TextMessage){
return ViewType.TEXT;
}else {
return ViewType.IMAGE;
}
}
private static class ViewHolder{
ImageView image;
TextView text;
}
}
主界面
public class MainActivity extends AppCompatActivity {
Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
ListView listView = (ListView) findViewById(R.id.list);
final ArrayList<Object> list = new ArrayList<>();
// 模拟数据。
for (int i=0; i<100;i++){
if (i%2==0){
TextMessage textMessage = new TextMessage("我是文本消息");
list.add(textMessage);
}else {
ImageMessage imageMessage = new ImageMessage(R.drawable.image);
list.add(imageMessage);
}
}
final MainAdapter adapter = new MainAdapter(context,list);
listView.setAdapter(adapter);
// 为ListView设置点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String str = "item "+ position;
Toast.makeText(context,str, Toast.LENGTH_SHORT).show();
}
});
// 为ListView设置滑动监听事件
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.d("listener","onScrollStateChange scrollState: " + scrollState);
}
@Override
/**
* @param firstVisibleItem: the index of the first visible cell (ignore if visibleItemCount == 0)
* @param visibleItemCount: the number of visible cells
*/
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// 如果滑到最底部,自动加载新数据。
if(firstVisibleItem+visibleItemCount == totalItemCount){
Toast.makeText(context, "加载新的数据", Toast.LENGTH_SHORT).show();
for (int i = 0 ; i< 100; i++){
TextMessage textMessage = new TextMessage("我是文本消息");
list.add(textMessage);
}
adapter.notifyDataSetChanged();
}
}
});
// 增加数据
Button add = (Button) findViewById(R.id.add_view);
add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String time = Calendar.getInstance().getTime().toString();
list.add(0,new TextMessage(time));
// 通知adapter刷新数据。
adapter.notifyDataSetChanged();
}
});
// 删除数据
Button delete = (Button) findViewById(R.id.delete_view);
delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (list.size() > 0){
list.remove(0);
adapter.notifyDataSetChanged();
}
}
});
}
}
补充
- ListView 可以添加FootView、HeadView和EmptyView。此时要注意怎样才能获得正确的item(
parent.getItemAtPosition(position)
). - OnScrollListener监听ListView三种状态,分别是:
-
SCROLL_STATE_IDLE = 0
, 表示ListView停止滑动。 -
SCROLL_STATE_TOUCH_SCROLL = 1
,表示ListView随手指而滑动。 -
SCROLL_STATE_FLING = 2
,表示手指已经离开屏幕,ListView随惯性而滑动。
注意 在ListView中,如果有一个focusable
属性为true的控件(例如Button),会使onItemClickListenter
的点击事件失效。通过以下方法就可以使点击事件传递到子控件上。
- 在item的根布局里设置
android:descendantFocusability = "blocksDescendants"
属性。 - 在5.0以上的系统,可以设置
android:touchscreenBlocksFoucus = true
属性。 - 在该按钮里设置
android:focusable = "false"
属性。
ListView其他属性:
- 设置分隔条:
android:divider = "@android:color/darker_gray"
- 去除分隔条:
android:divider = "@null"
- 隐藏ListView滚动条:
android:scrollbars = "none"
- 取消ListView的点击效果:
android:listSelector = "@android:color/transparent"
- 设置ListView 显示在第几页:
listView.setSelection(n); //"n"表示第几个item.
listView.smoothScrollBy(distance,duration);
listView.smoothScrollByOffset(offset);
listView.smoothScrollToPosition(index);
- 动态修改ListView:
mData.add(newData);
mAdapter.notifyDataSetChanged();
- 设置ListView顶部和底部滑动背景拉伸效果:
android:overScrollHeader = "@android:coloer/red"
android:overScrollFooter = "@android:coloer/red"
- 设置ListView顶部和底部渐变效果:
android:requireFadingEade = "vertical"
android:fadingEdgeLength = "100dp"
- 设置ListView从底部开始布局:
android:stackFromBottom = "true"
- 设置ListView增加消息自动滚动到底部:
android:transcriptMode = "alwaysSrcoll"
源码地址.