Android实战-智慧城市(6)

上一节,“全部服务页”和“新闻页”开发Android实战-智慧城市(5)

    在开发”服务“之前,得线对之前的HomeFragment0和HomeFragment1做出一些修改,使他们能够跳转到对应的服务页面
    在HomeFragment0中,添加startActivityService(String serviceName)方法,把”推荐服务“点击时的服务名的Toast删掉,使其调用startActivityService(String serviceName)方法并传入服务名,然后根据服务名和服务页面class的对应关系做跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//    跳转到对应的服务页面,只需要传入正确的服务名就OK了
private void startActivityService(String serviceName){
// 服务名与服务页面一定要成对应关系
String[] allServiceName = new String[]{"城市地铁"};
Class[] allServiceClass = new Class[]{MetroActivity.class};
Intent intent = new Intent();
if (allServiceName.length == allServiceClass.length){
int sum = 0;
for (int i = 0; i < allServiceName.length; i++) {
if (allServiceName[i].equals(serviceName)){
sum++;
intent.setClass(getContext(),allServiceClass[i]);
startActivity(intent);
break;
}
}
if (sum == 0){
Toast.makeText(getContext(),"没有找到对应的服务",Toast.LENGTH_SHORT).show();
}
}
else {
Toast.makeText(getContext(),"页面跳转对应关系错误",Toast.LENGTH_SHORT).show();
}
}

    在HomeFragment1中大同小异,把RecyclerView的item点击事件的服务名的Toast删掉,使其调用startActivityService(String serviceName)并传入服务名

1
//同上

软件设计

城市地铁

创建经过站点的线路的反序列化对象

    经过当前所在的站点的所有路线的json反序列化对象
    StationLinesBean.java

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package com.SmartCity.Metro;

import java.util.List;

//当前站点的所有路线的json反序列化对象
public class StationLinesBean {

private String msg;
private int code;
private List<DataBean> data;

public String getMsg() {
return msg;
}

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

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public List<DataBean> getData() {
return data;
}

public void setData(List<DataBean> data) {
this.data = data;
}

public static class DataBean {
private int lineId;
private String lineName;
private PreStepBean preStep;
private NextStepBean nextStep;
private String currentName;
private int reachTime;

public int getLineId() {
return lineId;
}

public void setLineId(int lineId) {
this.lineId = lineId;
}

public String getLineName() {
return lineName;
}

public void setLineName(String lineName) {
this.lineName = lineName;
}

public PreStepBean getPreStep() {
return preStep;
}

public void setPreStep(PreStepBean preStep) {
this.preStep = preStep;
}

public NextStepBean getNextStep() {
return nextStep;
}

public void setNextStep(NextStepBean nextStep) {
this.nextStep = nextStep;
}

public String getCurrentName() {
return currentName;
}

public void setCurrentName(String currentName) {
this.currentName = currentName;
}

public int getReachTime() {
return reachTime;
}

public void setReachTime(int reachTime) {
this.reachTime = reachTime;
}

public static class PreStepBean {
private String name;
private List<LinesBean> lines;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<LinesBean> getLines() {
return lines;
}

public void setLines(List<LinesBean> lines) {
this.lines = lines;
}

public static class LinesBean {
private int lineId;
private String lineName;

public int getLineId() {
return lineId;
}

public void setLineId(int lineId) {
this.lineId = lineId;
}

public String getLineName() {
return lineName;
}

public void setLineName(String lineName) {
this.lineName = lineName;
}
}
}

public static class NextStepBean {
private String name;
private List<LinesBean> lines;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<LinesBean> getLines() {
return lines;
}

public void setLines(List<LinesBean> lines) {
this.lines = lines;
}

public static class LinesBean {
private int lineId;
private String lineName;

public int getLineId() {
return lineId;
}

public void setLineId(int lineId) {
this.lineId = lineId;
}

public String getLineName() {
return lineName;
}

public void setLineName(String lineName) {
this.lineName = lineName;
}
}
}
}
}

创建线路列表的item布局

    只有简单的数据显示,线路名、下一站、剩余时间
    activity_metro_item.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_marginTop="5dp"
android:layout_marginRight="15dp"
android:layout_marginLeft="15dp"
android:layout_marginBottom="5dp"
android:layout_height="68dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:layout_height="0dp">
<TextView
android:text="线路名称: "
android:id="@+id/stationName"
android:gravity="center_vertical"
android:layout_width="0dp"
android:layout_height="match_parent"
android:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/black"
android:layout_weight="1"/>
<TextView
android:text="下一站: "
android:id="@+id/nextStation"
android:gravity="center_vertical"
android:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/black"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
<TextView
android:text="s分钟后到达本站"
android:id="@+id/times"
android:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/black"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>

创建线路列表的适配器

    在适配器中设置了item的click,当item点击时,返该item的线路对应的id和线路名
    MetroAdapter.java

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
package com.SmartCity.Metro;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.SmartCity.R;

import java.util.List;

public class MetroAdapter extends RecyclerView.Adapter<MetroAdapter.MyViewHolder> {
private List<String> lineNames,nextSteps;//线路名称、下一站名称
private List<Integer> reachTimes,lineIds;//到达本站时长
private Context context;

public MetroAdapter(List<String> lineNames, List<String> nextSteps, List<Integer> reachTimes, List<Integer> lineIds, Context context) {
this.lineNames = lineNames;
this.nextSteps = nextSteps;
this.reachTimes = reachTimes;
this.lineIds = lineIds;
this.context = context;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.activity_metro_item,parent,false);
return new MyViewHolder(view);
}

@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.times.setText(reachTimes.get(position) + "分钟后到达本站");
holder.nextStation.setText("下一站: " + nextSteps.get(position));
holder.stationName.setText("线路名称: " + lineNames.get(position));
}

@Override
public int getItemCount() {
return lineNames.size();
}

public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView nextStation,stationName,times;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
nextStation = itemView.findViewById(R.id.nextStation);
stationName = itemView.findViewById(R.id.stationName);
times = itemView.findViewById(R.id.times);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onRecyclerItemListener != null){
onRecyclerItemListener.OnItemClick(lineIds.get(getAbsoluteAdapterPosition()),lineNames.get(getAbsoluteAdapterPosition()));
}
}
});
}
}

private OnRecyclerItemListener onRecyclerItemListener;

public void setOnRecyclerItemListener(OnRecyclerItemListener onRecyclerItemListener) {
this.onRecyclerItemListener = onRecyclerItemListener;
}

public interface OnRecyclerItemListener{
void OnItemClick(int lineId,String lineName);
}
}

页面布局

    注意搜索框右边”搜索“按钮的显示和隐藏,若要使搜索框显示软键盘时右下角有”搜索“按钮,需要给EditText添加android:imeOptions=”actionSearch”
    activity_metro.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Title-->
<LinearLayout
android:background="@color/blue"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="58dp">
<LinearLayout
android:onClick="finishActivity"
android:gravity="center"
android:layout_width="58dp"
android:layout_height="58dp">
<ImageView
android:src="@mipmap/back"
android:layout_width="20dp"
android:layout_height="20dp"/>
</LinearLayout>
<LinearLayout
android:gravity="center"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="城市地铁"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:gravity="center"
android:layout_width="58dp"
android:layout_height="58dp">

</LinearLayout>
</LinearLayout>
<!-- Content-->
<LinearLayout
android:orientation="vertical"
android:gravity="center_horizontal"
android:background="@color/darker_gray"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:hint="搜索地铁站"
android:id="@+id/search_edit"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:lines="1"
android:inputType="text"
android:layout_margin="10dp"
android:imeOptions="actionSearch"
android:background="@drawable/edit_text_style"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="38dp"/>
<Button
android:text="搜索"
android:visibility="gone"
android:id="@+id/search_button"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="16sp"
android:layout_marginRight="10dp"
android:background="@drawable/button_style2"
android:layout_width="58dp"
android:layout_height="38dp"/>
</LinearLayout>
<ScrollView
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="我的位置: 建国门"
android:id="@+id/userStation"
android:textColor="@color/black"
android:textSize="20sp"
android:layout_marginLeft="5dp"
android:textStyle="bold"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.recyclerview.widget.RecyclerView
android:visibility="visible"
android:background="@color/white"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:gravity="center"
android:visibility="gone"
android:id="@+id/metroMapGroup"
android:orientation="vertical"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/red"
android:gravity="center"
android:text="我求求你\n好好看看北京的地铁线路吧"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/metroMap"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
</LinearLayout>

程序设计

    陌生点:键盘有下角”搜索“按钮响应事件;这里虽然实现了站点线路的列表显示、站点搜索等,但是点击某一线路跳转到地铁线路详情的页面暂时还没有添加
    MetroActivity.xml

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package com.SmartCity.Metro;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.SmartCity.MyHttpRequest;
import com.SmartCity.NewsHome.MyLinearLayoutManager;
import com.SmartCity.R;
import com.SmartCity.SoftData;
import com.bumptech.glide.Glide;

import java.util.ArrayList;
import java.util.List;

public class MetroActivity extends AppCompatActivity {
private String userPosition = "建国门";//默认的用户位置
private Handler handler;//线程通信
private MyHttpRequest myHttpRequest = new MyHttpRequest();//服务器通信封装
private TextView userStation;//用户当前所在的地铁站
private RecyclerView recyclerView;//地铁站的线路列表
private StationLinesBean stationLinesBean;//当前站点的所有路线的json反序列化对象
private List<String> lineNames,nextSteps;//线路名称、下一站名称
private List<Integer> reachTimes,lineIds;//到达本站时长、线路id
private EditText search_edit;//搜索输入框
private Button search_button;//搜索按钮
private MetroAdapter metroAdapter;//列表适配器
private ImageView metroMap;//北京地铁线路图
private LinearLayout metroMapGroup;//北京地铁线路图的父View

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_metro);
myHandler();//线程通信
init();//初始化界面
submit();//设置监听
getStationLine(userPosition);//获取当前站点的所有路线
}

// 线程通信
private void myHandler() {
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
getStationData();//获得站点的所有数据
}
}
};
}


// 从站点所有数据中获取我们用到的数据
private void getStationData() {
if (stationLinesBean == null){
Toast.makeText(this,"线路信息获取失败: 未知原因",Toast.LENGTH_SHORT).show();
}
else if (stationLinesBean.getCode() != 200){
Toast.makeText(this,"线路信息获取失败: " + stationLinesBean.getMsg(),Toast.LENGTH_SHORT).show();
}
else {
setList();//清空或创建搜索的站点的线路的集合
// 判断是否存在该线路
if (stationLinesBean.getData().size() != 0){
// 存在就正常加载显示
metroMapGroup.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
for (int i = 0; i < stationLinesBean.getData().size(); i++) {
lineNames.add(stationLinesBean.getData().get(i).getLineName());//线路名
nextSteps.add(stationLinesBean.getData().get(i).getNextStep().getName());//下一站站名
reachTimes.add(stationLinesBean.getData().get(i).getReachTime());//到达本站的剩余时间
lineIds.add(stationLinesBean.getData().get(i).getLineId());//线路id
}
setRecycler();//设置列表显示数据
}
else {
// 不存在就显示北京地铁线路图,提示不存在
metroMapGroup.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
}
}
}

// 设置列表显示数据
private void setRecycler() {
if (metroAdapter == null){
// 线路名、下一站站名、剩余时间、线路id
metroAdapter = new MetroAdapter(lineNames,nextSteps,reachTimes,lineIds,this);
MyLinearLayoutManager myLinearLayoutManager = new MyLinearLayoutManager(this, LinearLayoutManager.VERTICAL,false);
recyclerView.setAdapter(metroAdapter);
recyclerView.setLayoutManager(myLinearLayoutManager);
// 添加一条默认的分割线
recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
// item点击监听事件
metroAdapter.setOnRecyclerItemListener(new MetroAdapter.OnRecyclerItemListener() {
@Override
public void OnItemClick(int lineId,String lineName) {
Intent intent = new Intent(MetroActivity.this,LinesActivity.class);
intent.putExtra("lineId",lineId);//把线路的id传递过去
intent.putExtra("lineName",lineName);//把线路名传递过去
startActivity(intent);//跳转到地铁线路详情页面
}
});
}
else {
// 刷新列表
metroAdapter.notifyDataSetChanged();
}
}

// 初始化界面
private void init() {
userStation = findViewById(R.id.userStation);
recyclerView = findViewById(R.id.recyclerView);
search_edit = findViewById(R.id.search_edit);
search_button = findViewById(R.id.search_button);
metroMap = findViewById(R.id.metroMap);
metroMapGroup = findViewById(R.id.metroMapGroup);
// 加载北京地铁线路图总览,当搜索不到输入的站点时会展示
Glide.with(this).load("http://" + SoftData.ipPort + "/prod-api/profile/upload/image/2021/05/08/554f2392-1e1c-4449-b95c-327a5f7ec91d.jpeg")
.into(metroMap);
}

private void submit() {
// 编辑框内容改变回调
search_edit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) { }

@Override
public void afterTextChanged(Editable s) {
String searchContent = search_edit.getText().toString();
// 显示隐藏搜索按钮
if (TextUtils.isEmpty(searchContent)){
search_button.setVisibility(View.GONE);
}
else {
search_button.setVisibility(View.VISIBLE);
}
}
});


// 搜索线路的按钮点击
search_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 去除空格
String searchContent = search_edit.getText().toString().replaceAll(" ","");
search(searchContent);
}
});

// 响应键盘右下角搜索
search_edit.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH){
// 去除空格
String searchContent = search_edit.getText().toString().replaceAll(" ","");
search(searchContent);
}
return false;
}
});
}

// 获取站点的所有线路
private void getStationLine(String userPosition) {
new Thread(){
@Override
public void run() {
super.run();
stationLinesBean = (StationLinesBean) myHttpRequest.myHttp("/prod-api/api/metro/list?currentName=" + userPosition,null,StationLinesBean.class,null,"get");
Message message = Message.obtain();
message.what = 1;
handler.sendMessage(message);
}
}.start();
}

// 搜索线路
@SuppressLint("SetTextI18n")
private void search(String searchContent) {
if (TextUtils.isEmpty(searchContent)){
Toast.makeText(this,"搜索内容不可为空",Toast.LENGTH_SHORT).show();
}
else {
userStation.setText("我的位置: " + searchContent);//设置位置显示为搜索框中输入的位置
getStationLine(searchContent);
}
}


// 清空或创建列表
private void setList() {
if (lineNames != null){
// 清空列表项
lineNames.clear();
nextSteps.clear();
reachTimes.clear();
lineIds.clear();
}
else {
// 创建列表
lineNames = new ArrayList<>();
nextSteps = new ArrayList<>();
reachTimes = new ArrayList<>();
lineIds = new ArrayList<>();
}
}

// 关闭页面
public void finishActivity(View view) { finish(); }
}

地铁详情页面

    地铁详情页面其实就是你在上页点击了某个地铁路线后,显示的这个线路的详细信息

地铁详情的反序列化对象

    获取到当前地铁线路的详细信息,线路经过的所有站点、线路名、起点、终点等信息的json反序列化对象
    LineStationsBean.java

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
package com.SmartCity.Metro;

import java.util.List;

//当前线路的所有站点的json反序列化对象
public class LineStationsBean {

private String msg;
private int code;
private DataBean data;

public String getMsg() {
return msg;
}

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

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public DataBean getData() {
return data;
}

public void setData(DataBean data) {
this.data = data;
}

public static class DataBean {
private int id;
private String name;
private String first;
private String end;
private String startTime;
private String endTime;
private int cityId;
private int stationsNumber;
private int km;
private String runStationsName;
private List<MetroStepListBean> metroStepList;
private int remainingTime;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getFirst() {
return first;
}

public void setFirst(String first) {
this.first = first;
}

public String getEnd() {
return end;
}

public void setEnd(String end) {
this.end = end;
}

public String getStartTime() {
return startTime;
}

public void setStartTime(String startTime) {
this.startTime = startTime;
}

public String getEndTime() {
return endTime;
}

public void setEndTime(String endTime) {
this.endTime = endTime;
}

public int getCityId() {
return cityId;
}

public void setCityId(int cityId) {
this.cityId = cityId;
}

public int getStationsNumber() {
return stationsNumber;
}

public void setStationsNumber(int stationsNumber) {
this.stationsNumber = stationsNumber;
}

public int getKm() {
return km;
}

public void setKm(int km) {
this.km = km;
}

public String getRunStationsName() {
return runStationsName;
}

public void setRunStationsName(String runStationsName) {
this.runStationsName = runStationsName;
}

public List<MetroStepListBean> getMetroStepList() {
return metroStepList;
}

public void setMetroStepList(List<MetroStepListBean> metroStepList) {
this.metroStepList = metroStepList;
}

public int getRemainingTime() {
return remainingTime;
}

public void setRemainingTime(int remainingTime) {
this.remainingTime = remainingTime;
}

public static class MetroStepListBean {
private Object searchValue;
private Object createBy;
private String createTime;
private Object updateBy;
private String updateTime;
private Object remark;
private ParamsBean params;
private int id;
private String name;
private int seq;
private int lineId;
private String firstCh;

public Object getSearchValue() {
return searchValue;
}

public void setSearchValue(Object searchValue) {
this.searchValue = searchValue;
}

public Object getCreateBy() {
return createBy;
}

public void setCreateBy(Object createBy) {
this.createBy = createBy;
}

public String getCreateTime() {
return createTime;
}

public void setCreateTime(String createTime) {
this.createTime = createTime;
}

public Object getUpdateBy() {
return updateBy;
}

public void setUpdateBy(Object updateBy) {
this.updateBy = updateBy;
}

public String getUpdateTime() {
return updateTime;
}

public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}

public Object getRemark() {
return remark;
}

public void setRemark(Object remark) {
this.remark = remark;
}

public ParamsBean getParams() {
return params;
}

public void setParams(ParamsBean params) {
this.params = params;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getSeq() {
return seq;
}

public void setSeq(int seq) {
this.seq = seq;
}

public int getLineId() {
return lineId;
}

public void setLineId(int lineId) {
this.lineId = lineId;
}

public String getFirstCh() {
return firstCh;
}

public void setFirstCh(String firstCh) {
this.firstCh = firstCh;
}

public static class ParamsBean {
}
}
}
}

地铁站列表的item布局

    最前端的地铁图标不是累赘,一定要添加的
    activity_lines_item.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="48dp"
android:layout_width="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:src="@mipmap/subway"
android:layout_width="28dp"
android:id="@+id/icon"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:layout_height="28dp"/>
<View
android:layout_width="2dp"
android:background="@color/blue"
android:layout_height="match_parent"/>
<TextView
android:text="name"
android:id="@+id/stationName"
android:textSize="16sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_marginLeft="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

地铁线路中途径的站点列表的适配器

    在加载列表项的内容的方法中对列表item的地铁小图标进行了显示隐藏处理
    LineAdapter.java

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
package com.SmartCity.Metro;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.SmartCity.R;

import java.util.List;

public class LineAdapter extends RecyclerView.Adapter<LineAdapter.MyViewHolder> {
private String runStationsNameContent;//车辆运行站名称
private List<String> lineStation;//线路上所有站点的站点名
private Context context;

public LineAdapter(String runStationsNameContent, List<String> lineStation, Context context) {
this.runStationsNameContent = runStationsNameContent;
this.lineStation = lineStation;
this.context = context;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.activity_lines_item,parent,false);
return new MyViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.stationName.setText(lineStation.get(position));
// 只显示地铁当前所到的站点前的地铁图标
if (lineStation.get(position).equals(runStationsNameContent)){
holder.icon.setVisibility(View.VISIBLE);
}
else {
holder.icon.setVisibility(View.INVISIBLE);
}
}

@Override
public int getItemCount() {
return lineStation.size();
}

public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView stationName;//站点名
private ImageView icon;//地铁图标
public MyViewHolder(@NonNull View itemView) {
super(itemView);
stationName = itemView.findViewById(R.id.stationName);
icon = itemView.findViewById(R.id.icon);
}
}
}

界面布局

    界面布局比较简单,没啥好说的
    activity_lines.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Title-->
<LinearLayout
android:background="@color/blue"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="58dp">
<LinearLayout
android:onClick="finishActivity"
android:gravity="center"
android:layout_width="58dp"
android:layout_height="58dp">
<ImageView
android:src="@mipmap/back"
android:layout_width="20dp"
android:layout_height="20dp"/>
</LinearLayout>
<LinearLayout
android:gravity="center"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<TextView
android:text="线路名"
android:id="@+id/lineInfo"
android:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:gravity="center"
android:layout_width="58dp"
android:layout_height="58dp">

</LinearLayout>
</LinearLayout>
<!-- Content-->
<LinearLayout
android:orientation="vertical"
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="始发站"
android:id="@+id/firstStation"
android:gravity="center"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<ImageView
android:src="@mipmap/mark_left"
android:scaleType="fitXY"
android:layout_width="120dp"
android:layout_height="10dp"/>
<ImageView
android:src="@mipmap/mark_right"
android:scaleType="fitXY"
android:layout_width="48dp"
android:layout_height="18dp"/>
<TextView
android:text="终点站"
android:id="@+id/endStation"
android:gravity="center"
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/black"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_marginTop="20dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:text="首班时间: "
android:id="@+id/startTime"
android:textStyle="bold"
android:textColor="@color/black"
android:textSize="15sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="末班时间: "
android:textStyle="bold"
android:id="@+id/endTime"
android:textColor="@color/black"
android:textSize="15sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:text="站数/距离: "
android:textStyle="bold"
android:id="@+id/stationNumber"
android:textColor="@color/black"
android:textSize="15sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="剩余时间: "
android:id="@+id/remainingTime"
android:textStyle="bold"
android:textColor="@color/black"
android:textSize="15sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</LinearLayout>

程序设计

    注意这里使RecyclerView滑动到指定的item的位置,在showRecyclerItem()方法中
    LinesActivity.java

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
157
158
159
160
161
162
163
164
165
166
167
package com.SmartCity.Metro;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.SmartCity.MyHttpRequest;
import com.SmartCity.R;

import java.util.ArrayList;
import java.util.List;

public class LinesActivity extends AppCompatActivity {
private int lineId;//线路id
private String lineName;//线路名
private MyHttpRequest myHttpRequest = new MyHttpRequest();//封装的服务器通信模块
private LineStationsBean lineStationsBean;//当前线路信息的json反序列化对象
private Handler handler;//线程通信
private String firstStationContent,endStationContent,runStationsNameContent,startTimeContent,endTimeContent;//始发站、终点站、车辆运行站名称、首班时间、末班时间
private int runStationsNameIndex;//当前运行车站在线路中的索引值
private int stationNumberContent,kmContent,remainingTimeContent;//站数、距离、剩余时间
private List<String> lineStation = new ArrayList<>();//线路上的所有站点
private TextView firstStation,endStation,startTime,endTime,stationNumber,remainingTime;//始发站、终点站、首班时间、末班时间、站数、剩余时间
private RecyclerView recyclerView;//列表项

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lines);
myHandler();//线程通信
init();//初始化界面
getLineData();//获取线路的数据
}

// 线程通信
private void myHandler() {
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
setLineData();//获取地铁详细信息
}
}
};
}

// 获取地铁详细信息
private void setLineData() {
if (lineStationsBean == null){
Toast.makeText(this,"线路信息获取错误: 未知原因",Toast.LENGTH_SHORT).show();
}
else if (lineStationsBean.getCode() != 200){
Toast.makeText(this,"线路信息获取错误: " + lineStationsBean.getMsg(),Toast.LENGTH_SHORT).show();
}
else {
/*
* 这个接口是我比较佩服的
* stationsNumber(站数)只有当这条路经过建国门的时候才会有值,否则为null
* runStationsName(车辆运行站的名称)
* km(runStationsName到当前站的距离)
* - 在经过建国门的线路上就是runStationsName到当前站(建国门)的距离;
* - 在没经过建国门的线路上,就是runStationsName到始发站的距离
* - 总起来说一句话,离了建国门活不了
*/
firstStationContent = lineStationsBean.getData().getFirst();//始发站
endStationContent = lineStationsBean.getData().getEnd();//终点站
runStationsNameContent = lineStationsBean.getData().getRunStationsName();//车辆运行站
startTimeContent = lineStationsBean.getData().getStartTime();//首班时间
endTimeContent = lineStationsBean.getData().getEndTime();//末班时间
stationNumberContent = lineStationsBean.getData().getStationsNumber();//站数
kmContent = lineStationsBean.getData().getKm();//距离
remainingTimeContent = lineStationsBean.getData().getRemainingTime();//剩余时间
// 拿到所有站点名、拿到车辆当前所在站点在所有站点中的索引值
for (int i = 0; i < lineStationsBean.getData().getMetroStepList().size(); i++) {
// 站点名
lineStation.add(lineStationsBean.getData().getMetroStepList().get(i).getName());
// 索引值
if (lineStationsBean.getData().getMetroStepList().get(i).getName().equals(runStationsNameContent)){
runStationsNameIndex = i;
}
}
showData();//显示详细信息
setRecycler();//显示途径站点列表
}
}

// 显示详细信息
@SuppressLint("SetTextI18n")
private void showData() {
firstStation.setText("始发站\n" + firstStationContent);
endStation.setText("终点站\n" + endStationContent);
startTime.setText("首班时间: " + startTimeContent);
endTime.setText("末班时间: " + endTimeContent);
stationNumber.setText("站数/距离: " +stationNumberContent + "站/" + kmContent + "km");
remainingTime.setText("剩余时间: " + remainingTimeContent + "分钟");
}

// 显示途径站点列表
private void setRecycler() {
LineAdapter lineAdapter = new LineAdapter(runStationsNameContent,lineStation,this);
LinearLayoutManager layoutManager = new LinearLayoutManager(this,RecyclerView.VERTICAL,false);
recyclerView.setAdapter(lineAdapter);
recyclerView.setLayoutManager(layoutManager);
showRecyclerItem(layoutManager,recyclerView,runStationsNameIndex);//滑动RecyclerView到地铁当前所在站点的位置
}

// 滑动RecyclerView到指定的item的位置;列表的布局管理器、列表对象、指定的item的position
private void showRecyclerItem(LinearLayoutManager layoutManager, RecyclerView recyclerView,int position) {
int firstItem = layoutManager.findFirstVisibleItemPosition();//列表中的第一个item
int lastItem = layoutManager.findLastVisibleItemPosition();//列表中的最后一个item
if (position <= firstItem){
// 如果目标是第一个item,则列表滚动至第一个item的位置(其实根本不用动)
recyclerView.scrollToPosition(position);
}
else if (position <= lastItem){
// 其它位置,则计算item到顶部的距离并滑动Y轴
int top = recyclerView.getChildAt(position - firstItem).getTop();
recyclerView.scrollBy(0,top);
}
else {
// 防止出现异常,这行不会被调用到的,除非传入的position有毛病
recyclerView.scrollToPosition(position);
}
}

// 初始化界面
private void init() {
lineId = getIntent().getExtras().getInt("lineId");
lineName = getIntent().getExtras().getString("lineName");
((TextView)findViewById(R.id.lineInfo)).setText(lineName);//显示顶部标题为线路名
firstStation = findViewById(R.id.firstStation);
endStation = findViewById(R.id.endStation);
startTime = findViewById(R.id.startTime);
endTime = findViewById(R.id.endTime);
stationNumber = findViewById(R.id.stationNumber);
remainingTime = findViewById(R.id.remainingTime);
recyclerView = findViewById(R.id.recyclerView);
}

// 获取线路的信息
private void getLineData() {
new Thread(){
@Override
public void run() {
super.run();
lineStationsBean = (LineStationsBean) myHttpRequest.myHttp("/prod-api/api/metro/line/" + lineId,null,LineStationsBean.class,null,"get");
Message message = Message.obtain();
message.what = 1;
handler.sendMessage(message);
}
}.start();
}

// 关闭页面
public void finishActivity(View view) { finish(); }
}

好吧好吧,城市地铁页面就这么结束了,下一节是智慧巴士的开发,Android实战-智慧城市(7)