Android多布局ListView

1.引言

ListView多布局Item的实现,例如QQ聊天界面

671daf1e33124

1.1 要点讲解

重写getItemViewType()方法对应View是哪个类别,以及getViewTypeCount()方法iew返回 总共多少个类别!然后再getView那里调用getItemViewType获得对应类别,再加载对应的View!

2. 代码实现

2.1 列表

一个简单基础的ListView,可以添加到任意想要添加的位置

1
2
3
4
5
6
7
8
9
10
11
<?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">

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

2.2 区分左右的两个item

靠左对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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:orientation="horizontal"

>

<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/qqtouxiang"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="15dp"
android:layout_marginTop="10dp"
android:src="@drawable/touxiang" />
<TextView
android:id="@+id/qqMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="nihao"
android:textSize="17dp"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
android:textColor="@color/black"
android:background="@drawable/messagebackground"
android:padding="8dp"
/>
</LinearLayout>

靠右对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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:orientation="horizontal"
android:gravity="end"

>


<TextView
android:id="@+id/qqMessage1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@drawable/messagebackground"
android:padding="8dp"
android:layout_marginBottom="10dp"
android:text="nihao"
android:textColor="@color/black"
android:textSize="17dp"
android:gravity="right"
/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/qqtouxiang1"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="15dp"
android:src="@drawable/touxiang" />
</LinearLayout>

2.3 靠左对齐的对象方法

LeftType:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.pageview.Intomessage;

public class LeftType {
private int aIcon; // 应用图标资源ID
private String aName; // 应用名称

public LeftType(int aIcon, String aName) {
this.aIcon = aIcon;
this.aName = aName;
}

public int getaIcon() {
return aIcon;
}

public String getaName() {
return aName;
}
}

RightType:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.pageview.Intomessage;

public class RightType {
int image; // 书名
String bName; // 作者

public RightType(int image, String bName) {
this.image = image;
this.bName = bName;
}

public int getImage() {
return image;
}
public String getbName() {
return bName;
}
}

2.4 类型选择器

这是一个自定义类型的适配器TypeAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.example.pageview.Intomessage;

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.example.pageview.R;

import java.util.ArrayList;

public class TypeAdapter extends BaseAdapter {

// 定义消息类型常量
private static final int TYPE_RIGHT = 0;
private static final int TYPE_LEFT = 1;

private Context mContext; // 上下文
private ArrayList<Object> mData; // 消息数据

public TypeAdapter(Context context, ArrayList<Object> data) {
this.mContext = context;
this.mData = data;
}

@Override
public int getCount() {
return mData.size(); // 返回数据条目数
}

@Override
public Object getItem(int position) {
return mData.get(position); // 返回指定位置的消息对象
}

@Override
public long getItemId(int position) {
return position; // 返回条目的ID为其位置
}

// 判断当前条目的类型
@Override
public int getItemViewType(int position) {
if (mData.get(position) instanceof LeftType) {
return TYPE_LEFT;
} else if (mData.get(position) instanceof RightType) {
return TYPE_RIGHT;
}
return super.getItemViewType(position);
}

// 返回布局类型的数量
@Override
public int getViewTypeCount() {
return 2; // 只有左侧和右侧两种类型
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position); // 获取当前条目类型
ViewHolder1 holder1 = null; // 左侧 ViewHolder
ViewHolder2 holder2 = null; // 右侧 ViewHolder

// 判断是否需要创建新视图
if (convertView == null) {
switch (type) {
case TYPE_LEFT:
holder1 = new ViewHolder1();
convertView = LayoutInflater.from(mContext).inflate(R.layout.type1, parent, false);
holder1.imgIcon = convertView.findViewById(R.id.qqtouxiang);
holder1.txtAname = convertView.findViewById(R.id.qqMessage);
convertView.setTag(holder1); // 设置 ViewHolder
break;
case TYPE_RIGHT:
holder2 = new ViewHolder2();
convertView = LayoutInflater.from(mContext).inflate(R.layout.type2, parent, false);
holder2.imgIcon = convertView.findViewById(R.id.qqtouxiang1);
holder2.txtBauthor = convertView.findViewById(R.id.qqMessage1);
convertView.setTag(holder2); // 设置 ViewHolder
break;
}
} else {
// 复用 ViewHolder
switch (type) {
case TYPE_LEFT:
holder1 = (ViewHolder1) convertView.getTag();
break;
case TYPE_RIGHT:
holder2 = (ViewHolder2) convertView.getTag();
break;
}
}

// 设置数据内容
Object obj = mData.get(position);
switch (type) {
case TYPE_LEFT:
LeftType leftType = (LeftType) obj;
if (leftType != null) {
holder1.imgIcon.setImageResource(leftType.getaIcon());
holder1.txtAname.setText(leftType.getaName());
}
break;
case TYPE_RIGHT:
RightType rightType = (RightType) obj;
if (rightType != null) {
holder2.imgIcon.setImageResource(rightType.getImage());
holder2.txtBauthor.setText(rightType.getbName());
}
break;
}
return convertView; // 返回当前视图
}

// 左侧 ViewHolder
private static class ViewHolder1 {
ImageView imgIcon; // 用户头像
TextView txtAname; // 用户消息
}

// 右侧 ViewHolder
private static class ViewHolder2 {
ImageView imgIcon; // 发送者头像
TextView txtBauthor; // 发送的消息
}
}

2.5 信息回传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package com.example.pageview.Intomessage;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.pageview.R;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class IntoMessage extends AppCompatActivity {
private static final String FILE_NAME = "messages.txt"; // 消息文件名
private ListView listView; // 聊天记录列表
private TypeAdapter myAdapter; // 自定义适配器
private ArrayList<Object> mData; // 消息数据列表
private TextView messageTextView; // 显示消息的 TextView
private TextView inputTextView; // 输入框 TextView
private ImageView backButton; // 返回按钮
private Button sendButton; // 发送按钮

private static final int TYPE_RIGHT = 0; // 右侧消息类型
private static final int TYPE_LEFT = 1; // 左侧消息类型

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_into_message); // 设置布局

// 初始化视图组件
initializeViews();

// 获取传递的数据
Intent intent = getIntent();
String qqName = intent.getStringExtra("qqName");
int qqPhoto = intent.getIntExtra("qqPhoto", 0);
String qqNews = intent.getStringExtra("qqNews");

// 设置消息名称和准备数据
messageTextView.setText(qqName);
mData = new ArrayList<>();
mData.add(new LeftType(qqPhoto, qqNews));

// 设置返回按钮的点击事件
backButton.setOnClickListener(view -> finish());

// 设置发送按钮的点击事件
sendButton.setOnClickListener(view -> sendMessage(qqName));

// 初始化适配器和加载历史消息
myAdapter = new TypeAdapter(this, mData);
listView.setAdapter(myAdapter);
loadMessages(qqName);
}

/**
* 初始化视图组件
*/
private void initializeViews() {
listView = findViewById(R.id.listview);
backButton = findViewById(R.id.imageview1);
messageTextView = findViewById(R.id.qqName);
inputTextView = findViewById(R.id.Textview1);
sendButton = findViewById(R.id.buuton1);
}

/**
* 发送消息
*
* @param qqName 消息发送者的QQ名字
*/
private void sendMessage(String qqName) {
String messageContent = inputTextView.getText().toString().trim();
if (!messageContent.isEmpty()) { // 检查消息内容是否为空
mData.add(new RightType(R.drawable.mylogo, messageContent)); // 创建新消息对象并添加到列表
myAdapter.notifyDataSetChanged(); // 通知适配器数据变化
saveMessages(qqName); // 保存消息到文件
inputTextView.setText(""); // 清空输入框
}
}

/**
* 保存消息到文件
*
* @param qqName 消息发送者的QQ名字
*/
private void saveMessages(String qqName) {
try (FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos))) {
for (Object obj : mData) {
if (obj instanceof RightType) {
RightType rightType = (RightType) obj;
writer.write(qqName + "," + rightType.getbName() + "," + rightType.getImage());
writer.newLine();
}
}
} catch (IOException e) {
Log.e("IntoMessage", "保存消息时出错", e);
}
}

/**
* 从文件加载消息
*
* @param qqName 消息发送者的QQ名字
*/
private void loadMessages(String qqName) {
try (FileInputStream fis = openFileInput(FILE_NAME);
BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
String line;
while ((line = reader.readLine()) != null) {
String[] parts = line.split(",");
// 确保数据格式正确
if (parts.length == 3 && parts[0].equals(qqName)) {
int image = Integer.parseInt(parts[2]);
String bName = parts[1];
mData.add(new RightType(image, bName)); // 创建新的消息对象并添加到列表
}
}
myAdapter.notifyDataSetChanged(); // 通知适配器数据已更改
} catch (IOException e) {
Log.e("IntoMessage", "加载消息时出错", e);
}
}

// 可以选择在适当的时候清除消息文件
// protected void onDestroy() {
// super.onDestroy();
// if (isFinishing()) {
// deleteMessagesFile();
// }
// }
//
// private void deleteMessagesFile() {
// boolean deleted = deleteFile(FILE_NAME);
// if (deleted) {
// Log.d("IntoMessage", "消息文件已成功删除");
// } else {
// Log.e("IntoMessage", "未能删除消息文件");
// }
// }
}

3. 等待优化

  1. 如何传递一些文件,图片类。
  2. 数据如何从数据库中清洗得到。