Android实战-智慧城市

创建项目

创建项目

    创建一个新项目,命名为“智慧城市”,包名com.SmartCity

引入阿里云仓库

    在build.gradle中添加阿里云云效maven的依赖
    (具体配置方式可以参考在Android项目中使用阿里云maven)

添加第三方依赖包

    把所需要用到的第三方工具的依赖添加进app的build.gradle文件中,因为我们事先引入过阿里云仓库,所以这些第三方依赖包会从仓库中下载

添加权限

    在清单文件AndroidManifest.xml中添加权限,先添加上,暂时不做动态申请

1
2
3
4
5
6
<!--    网络权限-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 外部读取权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 外部写入权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

允许http请求

    在Android 9及以上的版本中,不支持http访问,在xml文件夹中创建一个新文件”http_request.xml”

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>

    在清单文件的application标签中添加

1
2
3
4
5
6
<application
...
...
android:networkSecurityConfig="@xml/http_request"
...
...>

修改默认样式

    创建安卓项目后的软件样式并不如人意,在values → themes → Base application theme中的parent值修改掉

软件设计

启动页

    软件的第一页,在MainActivity中使用SharedPreferences轻量储存库读取本地是否已经存有账号、密码和ip端口,如果有,则将它存入SoftData.java类的类变量中方便其他页面使用,如果没有则使其值为noValue,等待这些数据加载完毕后即进入下一个页面WelcomActivity

静态全局变量库

    userName和password用来储存账号和密码,ipPort储存ip端口,token会在用户登录后记录用户的token值
    SoftData.java

1
2
3
4
5
6
7
8
package com.SmartCity;

public class SoftData {
public static String userName;//用户名
public static String password;//用户密码
public static String ipPort;//ip端口
public static String token;//用户登录后的token值
}

首页程序设计

    在MainActivity中读取账号、密码、ip端口等,读取完毕后跳转至下一页WelcomActivity
    MainActivity.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
package com.SmartCity;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;

import com.SmartCity.Welcom.WelcomActivity;

public class MainActivity extends AppCompatActivity {
private SharedPreferences sharedPreferences;//本地轻量储存库

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();//初始化界面
getData();//获取数据
startNextActivity();//启动下个页面
}

// 初始化界面
private void init() {
sharedPreferences = getSharedPreferences("SoftData",MODE_PRIVATE);
}

private void getData() {
// 读取本地账号和密码并赋值SoftData类的类变量,如果没有则返回noValue
SoftData.userName = sharedPreferences.getString("userName","noValue");
SoftData.password = sharedPreferences.getString("password","noValue");
SoftData.ipPort = sharedPreferences.getString("ipPort","noValue");//ip端口
}

// 启动下个页面
private void startNextActivity(){
Intent intent = new Intent(this, WelcomActivity.class);
startActivity(intent);
finish();
}
}

页面布局

    该页不需要展示什么数据,所以布局比较简单
    activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:src="@mipmap/soft_icon"
android:layout_width="120dp"
android:layout_marginBottom="20dp"
android:layout_height="120dp"/>
<TextView
android:text="智慧城市"
android:textSize="26sp"
android:textColor="@color/black"
android:layout_marginBottom="160dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

引导页

    在引导页WelcomActivity中使用了ViewPager2+Fragment的方式实现滑动窗口,当ViewPager2滑动到最后一个item,Fragment中显示“立即体验”和“ip端口设置”两个按钮,对应事件用户登录和设置ip端口,引导页底部显示小圆点,小圆点和引导页对应,点击小圆点可以切换到对应的页面

小圆点背景的实现

    创建一个seletor类型的xml文件,设置其在被选中或不被选中使显示的背景不同,那么就要再创建两个不同色的圆形图片或xml作为它的选项

point_true/false.xml

    在drawable中创建一黑一白两个小圆点,作为小圆点被选中和不被选中的两个背景色

point_selector.xml

    创建一个selector类型xml文件做小圆点的背景,使小圆点被选中和未被选中时展示不同的背景

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<!--把刚刚创建的一黑一白两个背景图添加进来-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/point_true" android:state_selected="true"/>
<item android:drawable="@drawable/point_false" android:state_selected="false"/>
</selector>

通用的反序列化对象类

    每个页面都能用的最简单的反序列化对象类
    Bean.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
package com.SmartCity;

//通用的最简单的反序列化对象
public class Bean {

private int code;
private String msg;

public int getCode() {
return code;
}

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

public String getMsg() {
return msg;
}

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

封装OkHttp

    把用到的OkHttp请求方式封装到MyHttpRequest类中,在该类中自动请求并返回已经反序列化完毕的对象,使用了异步请求+线程等待的方式
    参数说明

  1. url: 请求的地址,只填ip端口的后半部分即可
  2. json: put或post提交的json,get时可以传递一个null
  3. cls: 反序列化对象的class
  4. token: 用户token,不需要token时,则传递null
  5. type: 请求的类型,默认为get,可选post、put和get
    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
    package com.SmartCity;

    import com.google.gson.Gson;

    import org.jetbrains.annotations.NotNull;

    import java.io.IOException;
    import java.util.concurrent.CountDownLatch;

    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.MediaType;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.RequestBody;
    import okhttp3.Response;

    public class MyHttpRequest {
    private Gson gson = new Gson();

    // url地址,要发送的json字符串,用户token,反序列化对象的class,请求类型(默认get)
    public Object myHttp(String url,String json,Class cls,String token,String type){
    final Object[] bean = new Object[]{null};

    CountDownLatch countDownLatch = new CountDownLatch(1);//线程等待
    OkHttpClient okHttpClient = new OkHttpClient();

    RequestBody requestBody = null;
    Request request = null;
    Request.Builder builder = new Request.Builder().url("http://" + SoftData.ipPort + url);

    // 判断是否为post或get
    if (type.equals("post")){
    requestBody = RequestBody.create(json, MediaType.parse("application/json"));
    builder.post(requestBody);
    }
    else if (type.equals("put")){
    requestBody = RequestBody.create(json, MediaType.parse("application/json"));
    builder.put(requestBody);
    }
    // 是否需要添加请求头
    if (token != null){
    request = builder.addHeader("authorization",token).build();
    }
    else {
    request = builder.build();
    }
    // 请求结果
    okHttpClient.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(@NotNull Call call, @NotNull IOException e) {
    bean = null;
    countDownLatch.countDown();
    }

    @Override
    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
    if (response.isSuccessful()){
    // 反序列化
    bean = gson.fromJson(response.body().string(),cls);
    }
    else {
    bean = null;
    }
    countDownLatch.countDown();
    }
    });
    try {
    countDownLatch.await();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    return bean;
    }
    }

封装用户注册和用户登录方法

    用户登录和用户注册可能会被多次调用,所以把他们封装到User.java类中方便随时调用,它的主要作用是作为中继,设置登录或注册时的json参数,调用MyHttpRequest发送注册或登录请求

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

import android.util.Log;

import com.SmartCity.RegisAndLogin.LoginBean;

public class User {
private static final String TAG = "User";
private MyHttpRequest myHttpRequest = new MyHttpRequest();

// 用户登录
public Object Login(String userName,String password){
String json = "{\n" +
"\"username\":\"" + userName + "\",\n" +
"\"password\":\"" + password + "\"\n" +
"}";
Log.d(TAG, "Register: \n" + json);
return myHttpRequest.myHttp("/prod-api/api/login",json, LoginBean.class,null,"post");
}

// 用户注册
public Object Register(String userName,String nickName,String phonenumber,String password){
String json = "{\n" +
"\"userName\": \"" + userName + "\",\n" +
"\"nickName\": \"" + nickName + "\",\n" +
"\"password\": \"" + password + "\",\n" +
"\"phonenumber\": \"" + phonenumber + "\",\n" +
"\"sex\": \"0\"\n" +
"}";
Log.d(TAG, "Register: \n" + json);
return myHttpRequest.myHttp("/prod-api/api/register",json,LoginBean.class,null,"post");
}
}

EditText和Button的Style

    创建一个drawable类型xml文件,定义EditText和Button的样式,等下要用到这两个

白色边框透明背景的圆角Button

    创建button_style.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景-->
<solid android:color="#00000000"/>
<!-- 圆角-->
<corners android:topRightRadius="10dp"
android:topLeftRadius="10dp"
android:bottomLeftRadius="10dp"
android:bottomRightRadius="10dp"/>
<!-- 边框-->
<stroke android:width="2dp" android:color="@color/white"/>
</shape>

白色背景的圆角EditText

    创建edit_text_style.xml文件

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white"/>
<corners android:bottomRightRadius="10dp"
android:topLeftRadius="10dp"
android:topRightRadius="10dp"
android:bottomLeftRadius="10dp"/>
<stroke android:width="1dp" android:color="@android:color/darker_gray"/>
</shape>

ip端口设置页面

    进入ViewPager2的最后一个item后,点击右上角的ip端口设置则进入ip端口设置页,用于设置ip端口的值,在该页面中用到了上面自定义的Button和EditText样式

页面布局

    activity_ip_port.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
<?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="@android:color/holo_blue_dark"
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:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/black"
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:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:background="@drawable/edit_text_style"
android:layout_marginTop="68dp"
android:hint="请输入ip端口"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:lines="1"
android:inputType="text"
android:gravity="center"
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="48dp"/>
<Button
android:background="@drawable/edit_text_style"
android:text="保存端口"
android:textColor="@color/black"
android:textSize="18sp"
android:id="@+id/button"
android:layout_marginTop="28dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>

程序设计

    这一页相对比较简单,布局也简单,操作也简单,就是读取写入一下本地轻量库中的ip端口就好了
    IpPortActivity.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
package com.SmartCity.Welcom;

import androidx.appcompat.app.AppCompatActivity;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.SmartCity.R;
import com.SmartCity.SoftData;

public class IpPortActivity extends AppCompatActivity {
private EditText editText;//IP端口的输入框
private Button button;//保存按钮
private SharedPreferences sharedPreferences;//本地轻量库

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ip_port);
init();//初始化页面
submit();//设置监听事件
}

private void submit() {
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String string = editText.getText().toString();
// 输入是否不为空
if (!TextUtils.isEmpty(string)){
// 把ip端口的值储存到本地
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("ipPort",string);
editor.commit();
SoftData.ipPort = string;//添加到SoftData类中
Toast.makeText(IpPortActivity.this,"IP端口设置成功",Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(IpPortActivity.this,"IP端口不可为空",Toast.LENGTH_SHORT).show();
}
}
});
}

private void init() {
editText = findViewById(R.id.editText);
button = findViewById(R.id.button);
sharedPreferences = getSharedPreferences("SoftData",MODE_PRIVATE);

// 读取SoftData类中是否存在ip端口,如果有则在编辑框中显示端口
if (!SoftData.ipPort.equals("noValue")){
editText.setText(SoftData.ipPort);
}
}

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

创建Fragment作为ViewPager2的item

    创建一个Fragment页面,该页面作为ViewPager2的item显示引导页轮播图的图片,当ViewPager2滑动到最后一个item时,该Fragment页面显示按钮

item的布局

    fragment_welcom.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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_height="match_parent"
android:layout_width="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="@+id/imageView"
android:scaleType="fitXY"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:text="IP端口设置"
android:id="@+id/button2"
android:textStyle="bold"
android:visibility="gone"
android:textSize="18sp"
android:textColor="@android:color/holo_blue_light"
android:layout_alignParentRight="true"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/button1"
android:textColor="@color/white"
android:textSize="18sp"
android:visibility="gone"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="80dp"
android:text="立即体验"
android:textStyle="bold"
android:background="@drawable/button_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>

item的程序设计

    WelcomFragment.java作为前几页时没什么故事,相比而言最后一页可谓是沧桑,每一页展示其对应的轮播图,当最后一页时,显示“立即体验”和“IP端口设置”按钮

  1. 立即体验按钮: 读取SoftData中的ipPort(ip端口),若ipPort值为”noValue”,说明并没有设置过ip端口,跳转到ip端口设置界面,若有ip端口则读取SoftData中的userName和password,若没有则跳转到用户登录,有就登录,登录成功就跳转到主页
  2. ip端口设置按钮: 跳转到ip端口设置界面
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
package com.SmartCity.Welcom;

import android.content.Intent;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.SmartCity.Home.HomeActivity;
import com.SmartCity.IpPortActivity;
import com.SmartCity.R;
import com.SmartCity.RegisAndLogin.LoginActivity;
import com.SmartCity.RegisAndLogin.LoginBean;
import com.SmartCity.SoftData;
import com.SmartCity.User;

public class WelcomFragment extends Fragment implements View.OnClickListener {
private View viewRoot;
private int itemBack,position;//构造器中传入的图片资源,item定位
private ImageView imageView;//背景
private Button button1;//立即体验
private TextView button2;//ip端口设置
private Intent intent = new Intent();//跳转界面
private User user = new User();//用户注册登录封装类
private LoginBean loginBean;//反序列化对象
private Handler handler;//handler通信

public WelcomFragment(int itemBack,int position) {
this.itemBack = itemBack;//传入图片的id
this.position = position;//传入当前页面的position
}

@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); }

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (viewRoot == null){ viewRoot = inflater.inflate(R.layout.fragment_welcom, container, false); }
myHandler();//handler线程通信
init();//初始化界面
setBack();//设置图片背景
setButton();//设置按钮显示
return viewRoot;
}

// handler线程通信
private void myHandler() {
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
userLogin();//自动登录
}
}
};
}

// 初始化界面
private void init() {
imageView = viewRoot.findViewById(R.id.imageView);
button1 = viewRoot.findViewById(R.id.button1);
button2 = viewRoot.findViewById(R.id.button2);
}

// 设置背景图
private void setBack() { imageView.setImageResource(itemBack); }

// 如果是最后一页,设置按钮显示,并设置按钮事件
private void setButton() {
if (position == 4){
button1.setVisibility(View.VISIBLE);
button2.setVisibility(View.VISIBLE);
}
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}

// 自动登录,当本地储存有账号密码,并异步请求成功时在handler中被调用
private void userLogin() {
// 登陆成功跳转到主页面
if (loginBean != null && loginBean.getCode() == 200){
Toast.makeText(getContext(),"自动登录成功",Toast.LENGTH_SHORT).show();
SoftData.token = loginBean.getToken();
intent.setClass(getContext(), HomeActivity.class);
startActivity(intent);
getActivity().finish();
}
else {
// 自动登录失败,则跳转到手动登录页面
Toast.makeText(getContext(),"请求失败,试试手动登录吧",Toast.LENGTH_SHORT).show();
intent.setClass(getContext(), LoginActivity.class);
startActivity(intent);
}
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button1:
// "立即体验"按钮响应的事件
startNextActivity();
break;
case R.id.button2:
// 跳转到ip端口设置界面
startIpPortActivity();
break;
}
}

// "立即体验"按钮响应的事件
private void startNextActivity() {
if (SoftData.ipPort.equals("noValue")){
// 跳转到ip端口设置界面
Toast.makeText(getContext(),"请先设置ip端口",Toast.LENGTH_SHORT).show();
startIpPortActivity();//跳转到ip端口设置页面
}
// 根据本地账号密码储存情况,确定手动登录还是自动登录
else if (SoftData.userName.equals("noValue") && SoftData.password.equals("noValue")){
Toast.makeText(getContext(),"请先登录",Toast.LENGTH_SHORT).show();
intent.setClass(getContext(), LoginActivity.class);
startActivity(intent);//跳转到用户登录
}
// 调用自动登录方法,返回反序列化对象后向handler发送消息
else {
new Thread(){
@Override
public void run() {
super.run();
loginBean = (LoginBean) user.Login(SoftData.userName,SoftData.password);
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
}.start();
}
}

// 跳转到ip端口设置页面
private void startIpPortActivity() {
intent.setClass(getContext(), IpPortActivity.class);
startActivity(intent);
}
}

ViewPager2的适配器

    创建一个ViewPagerAdapter.java类作为ViewPager2控件的适配器,该类继承了FragmentStateAdapter类

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

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;

import java.util.List;

public class ViewPagerAdapter extends FragmentStateAdapter {
private List<Fragment> fragments;

// 构造器中参数传入fragment管理类、lifecycle、和fragment的集合
public ViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, List<Fragment> fragments) {
super(fragmentManager, lifecycle);
this.fragments = fragments;
}

@NonNull
@Override
// 创建Fragment
public Fragment createFragment(int position) {
return fragments.get(position);
}

@Override
// 获取页面总数
public int getItemCount() {
return fragments.size();
}
}

WelcomActivity中实现

    在WelcomActivity中将上面设置的适配器、Fragment等串联,实现引导页滑动轮播、底部小圆点根据轮播位置变化、点击小圆点轮播图改变

页面布局

    activity_welcom.xml中除了一个ViewPager2和五个小点,真的没什么了

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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 滑动窗体-->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!-- 5个小点-->
<LinearLayout
android:gravity="center"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="100dp">
<LinearLayout
android:id="@+id/point0"
android:orientation="vertical"
android:background="@drawable/point_selector"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="10dp"
android:layout_height="10dp"/>
<LinearLayout
android:id="@+id/point1"
android:orientation="vertical"
android:background="@drawable/point_selector"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="10dp"
android:layout_height="10dp"/>
<LinearLayout
android:id="@+id/point2"
android:orientation="vertical"
android:background="@drawable/point_selector"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="10dp"
android:layout_height="10dp"/>
<LinearLayout
android:id="@+id/point3"
android:orientation="vertical"
android:background="@drawable/point_selector"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="10dp"
android:layout_height="10dp"/>
<LinearLayout
android:id="@+id/point4"
android:orientation="vertical"
android:background="@drawable/point_selector"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="10dp"
android:layout_height="10dp"/>
</LinearLayout>
</RelativeLayout>

程序设计

    这里有个注意点,ViewPager2的滑动监听使用注册(register)的方式添加viewPager2.registerOnPageChangeCallback(),然后重写它的onPageSelected()方法,会在页面滑动切换结束后回调
    WelcomActivity.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
package com.SmartCity.Welcom;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;

import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;

import com.SmartCity.R;

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

public class WelcomActivity extends AppCompatActivity implements View.OnClickListener {
private LinearLayout[] points = new LinearLayout[5];//储存5个小点
private LinearLayout point;//储存上一个小圆点
private ViewPager2 viewPager2;//滑动窗体
private List<Fragment> fragments;//储存ViewPager中显示的Fragment
public static WelcomActivity welcomActivity;//使其他页面调用

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcom);
init();//初始化界面
submit();//设置监听
setViewPager();//设置ViewPager的背景
}

private void init() {
//五个小圆点
points[0] = findViewById(R.id.point0);
points[1] = findViewById(R.id.point1);
points[2] = findViewById(R.id.point2);
points[3] = findViewById(R.id.point3);
points[4] = findViewById(R.id.point4);
viewPager2 = findViewById(R.id.viewPager2);
fragments = new ArrayList<>();//fragment页面储存的集合
welcomActivity = WelcomActivity.this;//使其它类中可以调用该变量关闭HomeActivity
}

private void submit() {
//给小圆点添加onClick监听
points[0].setOnClickListener(this);
points[1].setOnClickListener(this);
points[2].setOnClickListener(this);
points[3].setOnClickListener(this);
points[4].setOnClickListener(this);

// 页面切换完成时回调
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
setPoint(position);//设置小圆点
}
});
}

// 设置小圆点的背景色
private void setPoint(int position) {
if (point != null){
point.setSelected(false);
}
point = points[position];
points[position].setSelected(true);
}


@Override
// 点击小圆点时,使ViewPager2切换页面
public void onClick(View v) {
switch (v.getId()){
case R.id.point0:
viewPager2.setCurrentItem(0);
break;
case R.id.point1:
viewPager2.setCurrentItem(1);
break;
case R.id.point2:
viewPager2.setCurrentItem(2);
break;
case R.id.point3:
viewPager2.setCurrentItem(3);
break;
case R.id.point4:
viewPager2.setCurrentItem(4);
break;
}
}

private void setViewPager() {
// 创建Fragment作为ViewPager2的item,并按构造函数要求传入当页背景的资源文件和int类型的position
fragments.add(new WelcomFragment(R.mipmap.guide_img1,0));
fragments.add(new WelcomFragment(R.mipmap.guide_img2,1));
fragments.add(new WelcomFragment(R.mipmap.guide_img3,2));
fragments.add(new WelcomFragment(R.mipmap.guide_img4,3));
fragments.add(new WelcomFragment(R.mipmap.guide_img5,4));
// 实例化ViewPager2的适配器
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(),getLifecycle(),fragments);
viewPager2.setAdapter(viewPagerAdapter);//添加适配器
}
}

注册和登录

    用户注册和用户登录,因为已经封装好了注册登录模块,所以这一页实现起来会比较简单

注册登录的反序列化对象

    创建注册登录的反序列化对象LoginBean.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
package com.SmartCity.RegisAndLogin;

public class LoginBean {

private int code;
private String msg;
private String token;

public int getCode() {
return code;
}

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

public String getMsg() {
return msg;
}

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

public String getToken() {
return token;
}

public void setToken(String token) {
this.token = token;
}
}

用户登录页面

页面布局

    两个编辑框和按钮都使用了自定义的编辑框的样式
    activity_login.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
<?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="@android:color/holo_blue_dark"
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:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/black"
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:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:background="@drawable/edit_text_style"
android:layout_marginTop="68dp"
android:hint="请输入用户名"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:lines="1"
android:inputType="text"
android:gravity="center"
android:id="@+id/userName"
android:layout_width="match_parent"
android:layout_height="48dp"/>
<EditText
android:background="@drawable/edit_text_style"
android:layout_marginTop="18dp"
android:hint="请输入密码"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:lines="1"
android:inputType="textPassword"
android:gravity="center"
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="48dp"/>
<LinearLayout
android:orientation="horizontal"
android:layout_marginTop="28dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:background="@drawable/edit_text_style"
android:text="立即登录"
android:textColor="@color/black"
android:textSize="18sp"
android:id="@+id/login"
android:layout_marginLeft="20dp"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<Button
android:background="@drawable/edit_text_style"
android:text="注册账号"
android:textColor="@color/black"
android:textSize="18sp"
android:id="@+id/register"
android:layout_marginLeft="10dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

程序设计

    点击用户登录时调用封装的用户登录方法,登录失败会提示失败原因,点击注册时跳转到注册页面
    LoginActivity.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
package com.SmartCity.RegisAndLogin;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.SmartCity.Home.HomeActivity;
import com.SmartCity.R;
import com.SmartCity.SoftData;
import com.SmartCity.User;
import com.SmartCity.Welcom.WelcomActivity;

public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
private Button login,register; //登录和注册按钮
private EditText userName,password;//账号和密码
private User user = new User();//注册登录的封装类
private LoginBean loginBean;//反序列化对象
private Handler handler;//handler通信
private Intent intent = new Intent();//intent跳转
private SharedPreferences sharedPreferences;//本地轻量库

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
myHandler();//handler通信
init();//初始化界面
submit();//设置监听
}

// handler通信
private void myHandler() {
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
startNextActivity();
}
}
};
}

// 初始化界面
private void init() {
login = findViewById(R.id.login);//立即登录按钮
register = findViewById(R.id.register);//注册按钮
userName = findViewById(R.id.userName);//用户名编辑框
password = findViewById(R.id.password);//密码编辑框
sharedPreferences = getSharedPreferences("SoftData",MODE_PRIVATE);//本地轻量库
}

// 设置监听
private void submit() {
login.setOnClickListener(this);
register.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.login:
Login();//输入账号密码后,用户登录
break;
case R.id.register:
Register();//跳转到用户注册
break;
}
}

// 输入用户名和密码后,用户登录
private void Login() {
String userNameContent = userName.getText().toString();
String passwordContent = password.getText().toString();
// 判断是否为空
if (TextUtils.isEmpty(userNameContent)){
Toast.makeText(this,"请输入用户名",Toast.LENGTH_SHORT).show();
}
else if (TextUtils.isEmpty(passwordContent)){
Toast.makeText(this,"请输入密码",Toast.LENGTH_SHORT).show();
}
else if (passwordContent.length() < 6){
Toast.makeText(this,"密码至少6位",Toast.LENGTH_SHORT).show();
}
else {
new Thread(){
@Override
public void run() {
super.run();
// 拿到登录接口的反序列化对象
loginBean = (LoginBean) user.Login(userNameContent,passwordContent);
// 向handler发送消息
Message message = Message.obtain();
message.what = 1;
handler.sendMessage(message);
}
}.start();
}
}

// 请求到登录接口的返回数据后调用
private void startNextActivity() {
// 判断请求结果
if (loginBean == null){
MyToast("登录失败: 未知原因");
}
else if (loginBean.getCode() != 200){
MyToast("登录失败: " + loginBean.getMsg());
}
else {
MyToast("登陆成功");
// 请求成功,写入本地轻量库,赋值SoftData.java类中的类变量
SoftData.userName = userName.getText().toString();
SoftData.password = userName.getText().toString();
SoftData.token = loginBean.getToken();
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("userName",userName.getText().toString());
editor.putString("password",password.getText().toString());
editor.commit();
// 跳转到主页面
intent.setClass(this, HomeActivity.class);
startActivity(intent);
WelcomActivity.welcomActivity.finish();
finish();
}
}

// 跳转到登录页面
private void Register() {
intent.setClass(this,RegisterActivity.class);
startActivity(intent);
}

// Toast弹窗
private void MyToast(String content){
Toast.makeText(this,content,Toast.LENGTH_SHORT).show();
}

// 点击左上角关闭页面
public void finishActivity(View view) { finish(); }
}

用户注册页面

页面布局

    与用户登录页面相同,编辑框和按钮也都使用了相同的样式
   &enspactivity_register.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
<?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="@android:color/holo_blue_dark"
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:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/black"
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:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:background="@drawable/edit_text_style"
android:layout_marginTop="68dp"
android:hint="请输入用户名"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:lines="1"
android:inputType="text"
android:gravity="center"
android:id="@+id/userName"
android:layout_width="match_parent"
android:layout_height="48dp"/>
<EditText
android:background="@drawable/edit_text_style"
android:layout_marginTop="18dp"
android:hint="请输入昵称"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:lines="1"
android:inputType="text"
android:gravity="center"
android:id="@+id/nickName"
android:layout_width="match_parent"
android:layout_height="48dp"/>
<EditText
android:background="@drawable/edit_text_style"
android:layout_marginTop="18dp"
android:hint="请输入手机号"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:lines="1"
android:inputType="number"
android:gravity="center"
android:id="@+id/phoneNumber"
android:layout_width="match_parent"
android:layout_height="48dp"/>
<EditText
android:background="@drawable/edit_text_style"
android:layout_marginTop="18dp"
android:hint="请输入密码"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:lines="1"
android:inputType="textPassword"
android:gravity="center"
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="48dp"/>
<LinearLayout
android:orientation="horizontal"
android:layout_marginTop="28dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:background="@drawable/edit_text_style"
android:text="立即注册"
android:textColor="@color/black"
android:textSize="18sp"
android:id="@+id/register"
android:layout_marginLeft="20dp"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
<Button
android:background="@drawable/edit_text_style"
android:text="已有账号"
android:onClick="finishActivity"
android:textColor="@color/black"
android:textSize="18sp"
android:layout_marginLeft="10dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

程序设计

    RegisterActivity.java,主要对4个输入框内容进行判断,输入正确的话即调用封装在User类中的用户注册方法,并判断其返回的注册结果

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

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.SmartCity.R;
import com.SmartCity.User;

public class RegisterActivity extends AppCompatActivity {
private Button register;//注册按钮
private EditText userName,nickName,phonenumber,password;//四个编辑框
private User user = new User();//注册登录封装类
private LoginBean loginBean;//反序列化对象
private Handler handler;//handler通信

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
myHandler();//handler通信
init();//初始化界面
submit();//设置监听
}

// handler通信
private void myHandler() {
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
userRegister();//判断注册结果
}
}
};
}

// 判断注册结果
private void userRegister() {
if (loginBean == null){
MyToast("注册失败: 未知原因");
}
if (loginBean.getCode() != 200){
MyToast("注册失败: " + loginBean.getMsg());
}
else {
MyToast("注册成功,可以登陆了");
finish();
}
}

// 初始化界面
private void init() {
register = findViewById(R.id.register);
userName = findViewById(R.id.userName);
nickName = findViewById(R.id.nickName);
phonenumber = findViewById(R.id.phoneNumber);
password = findViewById(R.id.password);
}

// 设置监听
private void submit() {
register.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
UserRegister();
}
});
}

// 用户点击注册
private void UserRegister() {
String userNameContent = userName.getText().toString();
String nickNameContent = nickName.getText().toString();
String phonenumberContent = phonenumber.getText().toString();
String passwordContent = password.getText().toString();
// 判断输入的内容
if (TextUtils.isEmpty(userNameContent)){
MyToast("用户名不能为空");
}
else if (TextUtils.isEmpty(nickNameContent)){
MyToast("昵称不可为空");
}
else if (TextUtils.isEmpty(phonenumberContent)){
MyToast("手机号不可为空");
}
else if (phonenumberContent.length() < 11){
MyToast("手机号不小于11位");
}
else if (TextUtils.isEmpty(passwordContent)){
MyToast("密码不可为空");
}
else if (passwordContent.length() < 6){
MyToast("密码不小于6位");
}
else {
new Thread(){
@Override
public void run() {
super.run();
// 拿到反序列化对象,向handler发送消息
loginBean = (LoginBean) user.Register(userName.getText().toString(),nickName.getText().toString(),phonenumber.getText().toString(),password.getText().toString());
Message message = Message.obtain();
message.what = 1;
handler.sendMessage(message);
}
}.start();
}
}

// 结束页面
public void finishActivity(View view) { finish(); }

// toast
private void MyToast(String content){
Toast.makeText(this,content,Toast.LENGTH_SHORT).show();
}
}

主页面

    主页面是个大工程,首先用ViewPager2+Fragment的方式实现了4页切换,与引导页不同的是每页有一个单独的Fragment页面,底部的四个按钮与四个页面相呼应

菜单切换

    底部四个菜单与四个页面对应切换即可,与引导页的小点大同小异,创建4个Fragment页面(分别对应首页、全部服务、新闻、个人中心)并添加到List中,实例化ViewPagerAdapter时把List作为参数发送给适配器

主页面布局

    如果觉得布局臃肿,可以将顶部标题栏和底部菜单栏分开布局,然后使用<include layout="" />的方式将其添加进来
    activity_home.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
<?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:gravity="center"
android:background="@color/blue"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="58dp">
<TextView
android:text="智慧城市"
android:textStyle="bold"
android:id="@+id/title"
android:textSize="18sp"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<!-- Content-->
<androidx.viewpager2.widget.ViewPager2
android:background="@color/darker_gray"
android:id="@+id/viewPager2"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp"/>
<!-- foot-->
<LinearLayout
android:background="@color/white"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="58dp">
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_weight="1"
android:layout_width="0dp"
android:id="@+id/button0"
android:layout_height="match_parent">
<ImageView
android:background="@drawable/home_back"
android:layout_width="26dp"
android:layout_height="26dp"/>
<TextView
android:text="首页"
android:textSize="13dp"
android:textColor="@color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_weight="1"
android:layout_width="0dp"
android:id="@+id/button1"
android:layout_height="match_parent">
<ImageView
android:background="@drawable/service_back"
android:layout_width="26dp"
android:layout_height="26dp"/>
<TextView
android:text="全部服务"
android:textSize="13dp"
android:textColor="@color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_weight="1"
android:layout_width="0dp"
android:id="@+id/button2"
android:layout_height="match_parent">
<ImageView
android:background="@drawable/news_back"
android:layout_width="26dp"
android:layout_height="26dp"/>
<TextView
android:text="新闻"
android:textSize="13dp"
android:textColor="@color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_weight="1"
android:layout_width="0dp"
android:id="@+id/button3"
android:layout_height="match_parent">
<ImageView
android:background="@drawable/userself_back"
android:layout_width="26dp"
android:layout_height="26dp"/>
<TextView
android:text="个人中心"
android:textSize="13dp"
android:textColor="@color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

主页面程序设计

    说明一下,HomeActivity.java这里只配置了主页面,包括顶部标题栏和底部菜单栏以及4个Fragment在ViewPager2的显示,把题目中的五个要求做了分离,其余四个要求(轮播图、推荐服务、专题、新闻分类)作为一个单独的模块放在了“智慧城市页(HomeFragment0)”的代码中,HomeActivity只能算是实现了五个模块中的最后一个

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

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;

import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.SmartCity.R;
import com.SmartCity.Welcom.ViewPagerAdapter;

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

public class HomeActivity extends AppCompatActivity implements View.OnClickListener {
private ViewPager2 viewPager2;//滑动窗体
private LinearLayout[] buttons = new LinearLayout[4];//底部菜单
private LinearLayout button;//上一个按钮
private List<Fragment> fragments = new ArrayList<>();//储存ViewPager2的4个item
private String[] titleContent = new String[]{"智慧城市","全部服务","新闻","个人中心"};//标题的内容
private TextView title;//标题

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
init();//初始化界面
submit();//设置监听
setViewPager();//加载ViewPager2
}

// 初始化界面
private void init() {
viewPager2 = findViewById(R.id.viewPager2);
title = findViewById(R.id.title);//顶部标题
// 底部菜单栏四个按钮
buttons[0] = findViewById(R.id.button0);
buttons[1] = findViewById(R.id.button1);
buttons[2] = findViewById(R.id.button2);
buttons[3] = findViewById(R.id.button3);
// 把四个fragment页面添加到List集合中
fragments.add(new HomeFragment0());
fragments.add(new HomeFragment1());
fragments.add(new HomeFragment2());
fragments.add(new HomeFragment3());
}

// 设置监听
private void submit() {
// 底部菜单栏事件监听
buttons[0].setOnClickListener(this);
buttons[1].setOnClickListener(this);
buttons[2].setOnClickListener(this);
buttons[3].setOnClickListener(this);
// 给滑动窗体注册页面切换监听
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
setButton(position);//设置菜单栏变化
}
});
}

// 改变菜单栏状态
private void setButton(int position){
if (button != null){
// 还原上个按钮的颜色
button.getChildAt(0).setSelected(false);
((TextView) button.getChildAt(1)).setTextColor(getResources().getColor(R.color.black));
}
// 记录点击的按钮
button = buttons[position];
// 设置选中的按钮的颜色
buttons[position].getChildAt(0).setSelected(true);
((TextView) buttons[position].getChildAt(1)).setTextColor(getResources().getColor(R.color.blue));
// 设置标题
title.setText(titleContent[position]);
}

private void setViewPager() {
// 适配器依然是前面WelcomActivity页中使用的适配器
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(),getLifecycle(),fragments);
viewPager2.setAdapter(viewPagerAdapter);
}

// 当菜单栏被点击时,切换到对应的页面
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button0:
viewPager2.setCurrentItem(0);
break;
case R.id.button1:
viewPager2.setCurrentItem(1);
break;
case R.id.button2:
viewPager2.setCurrentItem(2);
break;
case R.id.button3:
viewPager2.setCurrentItem(3);
break;
}
}
}

    到这里为止,已经可以显示四个可滑动的页面,称他们为“智慧城市页”、“全部服务页”、“新闻列表页”、“个人中心页”

    下一篇中主要写“智慧城市页”的实现。欲知后事如何,且听下回分解