安卓开发
2025年9月25日大约 9 分钟
第一节--创建项目
创建了项目及项目目录,
新创建的目录结构如图
然后重写了onCreate方法onCreate()
是应用启动时最早执行的方法(早于任何 Activity),适合做全局配置,然后在AndroidManifest.xml对项目基本信息进行定义
- 补充知识
安卓开发四大件:Activity(活动) - 用户界面载体
Service(服务) - 后台任务处理器
BroadcastReceiver(广播接收器) - 系统事件监听器
ContentProvider(内容提供器) - 数据共享桥梁
安卓应用由许多组件构成,这些组件通过Intent传递数据,例如页面跳转
完整组件列表
组件 | 类比 | 作用 | 示例 |
---|---|---|---|
Activity | 浏览器标签页 | 用户界面载体 | 微信聊天窗口 |
Service | 后台进程 | 执行后台任务 | 音乐播放器 |
BroadcastReceiver | 事件监听器 | 响应系统事件 | 低电量提醒 |
ContentProvider | 数据接口 | 共享数据 | 通讯录访问 |
Fragment | 页面模块 | 界面组合单元 | 新闻详情页 |
Intent | 信使 | 组件间通信 | 页面跳转 |
第二节--# 实现闪屏页,启动app
在activity目录下新建两个类,SplashActivity
和GuideActivity
(GuideActivity是app 引导页)
实现:
1. SplashActivity.class
package com.example.myapp.activity;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.LinearLayout;
import com.example.myapp.R;
public class SplashActivity extends AppCompatActivity {
private LinearLayout ll;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
ll = findViewById(R.id.main_ll);
//设置渐变效果
setAlphaAnimation();
}
/**
* 设置渐变效果
*/
private void setAlphaAnimation() {
//生成动画对象
AlphaAnimation animation = new AlphaAnimation(0.3f, 1.0f);
//设置持续时间3s
animation.setDuration(3000);
//给控件设置动画
ll.setAnimation(animation);
//设置动画监听
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
jump2Activity();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 根据首次启动应用与否跳转到相应界面
*/
private void jump2Activity() {
SharedPreferences sharedPreferences = getSharedPreferences("data", MODE_PRIVATE);
String First = sharedPreferences.getString("isFirst", "0");
Intent intent = new Intent();
if ("0".equals(First)) {
intent.setClass(this, GuideActivity.class);
}else{
intent.setClass(this, MainActivity.class);
}
startActivity(intent);
finish();
}
}
Splash的xml排列
<?xml version="1.0" encoding="utf-8"?>
<!-- 根布局:使用RelativeLayout作为容器,用于构建相对位置布局 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" <!-- 宽度匹配父容器(全屏宽度) -->
android:layout_height="match_parent" <!-- 高度匹配父容器(全屏高度) -->
android:orientation="vertical"> <!-- 无效属性:RelativeLayout不支持方向设置,应删除 -->
<!-- 注意:android:orientation在RelativeLayout中无效 -->
<!-- 主内容容器:垂直方向的LinearLayout -->
<LinearLayout
android:id="@+id/main_l1" <!-- 视图ID:在代码中可通过R.id.main_l1引用此容器 -->
android:layout_centerInParent="true" <!-- 在父容器(RelativeLayout)中居中显示 -->
android:layout_width="wrap_content" <!-- 宽度由内容决定(自适应子视图宽度) -->
android:layout_height="wrap_content" <!-- 高度由内容决定(自适应子视图高度) -->
android:orientation="vertical" <!-- 子元素垂直排列 -->
android:gravity="center"> <!-- 子元素在容器内水平居中显示 -->
<!-- 应用图标视图 -->
<ImageView
android:id="@+id/main_iv" <!-- 视图ID:在代码中可通过R.id.main_iv引用此图片 -->
android:layout_width="200dp" <!-- 固定宽度200dp -->
android:layout_height="200dp"<!-- 固定高度200dp -->
android:src="@mipmap/ic_launcher" /> <!-- 引用mipmap中的应用图标资源 -->
<!-- 注意:实际项目中应使用专门的启动图资源,而非应用图标 -->
<!-- 启动页文本视图 -->
<TextView
android:id="@+id/main_tv" <!-- 视图ID:在代码中可通过R.id.main_tv引用此文本 -->
android:layout_width="wrap_content" <!-- 宽度由文本内容决定 -->
android:layout_height="wrap_content" <!-- 高度由文本内容决定 -->
android:layout_centerInParent="true" <!-- 无效属性:在LinearLayout中不起作用,应删除 -->
android:layout_marginTop="10dp" <!-- 与上方ImageView保持10dp间距 -->
android:text="我是splsh页"/> <!-- 显示文本(建议改为"我是splash页") -->
<!-- 注意:layout_marginTop只在垂直布局中有效,用于设置与上方元素的间距 -->
</LinearLayout> <!-- LinearLayout结束标签 -->
</RelativeLayout> <!-- RelativeLayout结束标签 -->
这是简单的垂直排列ui
,在xml
里定义了这个id
为l1
,所以在java
中用findViewById
方法查找R.id.main_l1
GuideActivity
这个界面由小圆点xml
图标资源guide_white
和guide_blue
组成
以下资源需要放到res/drawable
中
guide_white
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:height="20dp" android:width="20dp"/>
<solid android:color="#fff"/>
</shape>
guide_selector
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:height="20dp" android:width="20dp"/>
<solid android:color="#0687ED"/>
</shape>
class类
package com.example.emptnew.activity;
import static androidx.core.content.ContextCompat.startActivity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; // 需要添加此导入
import androidx.viewpager.widget.ViewPager;
import com.example.emptnew.MainActivity;
import com.example.emptnew.R;
import com.example.emptnew.adapter.GuideAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* 应用引导页Activity
* 功能:展示应用引导页面,首次启动时显示,引导用户了解应用功能
* 包含ViewPager展示多张引导图片,底部圆点指示器和"开始体验"按钮
*/
public class GuideActivity extends AppCompatActivity { // 添加继承AppCompatActivity
// ViewPager控件,用于展示引导页图片
private ViewPager vp;
// 引导页图片的ImageView列表
private List<ImageView> imageViews;
// 引导页图片资源ID数组
private int[] imgs = {R.drawable.y0, R.drawable.y1, R.drawable.y2, R.drawable.y3};
// "开始体验"按钮
private Button btn;
// 底部圆点指示器数组
private ImageView[] dotViews;
// ViewPager适配器
private GuideAdapter adapter;
/**
* Activity创建时的回调方法
* @param savedInstanceState 保存的状态数据
*/
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置布局文件
setContentView(R.layout.activity_guide);
// 初始化ViewPager和按钮
vp = findViewById(R.id.guide_vp);
btn = findViewById(R.id.guide_btn);
// 初始化引导图片
initImgs();
// 初始化底部圆点指示器
initDots();
// 创建适配器并设置给ViewPager
adapter = new GuideAdapter(imageViews);
vp.setAdapter(adapter);
// 设置"开始体验"按钮点击事件
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取SharedPreferences编辑器
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
// 标记用户已看过引导页
editor.putString("isFirst", "1");
// 提交更改
editor.commit();
// 跳转到主界面
Intent intent = new Intent(GuideActivity.this, MainActivity.class);
startActivity(intent);
// 结束当前Activity
finish();
}
});
// 设置ViewPager页面变化监听器
vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
/**
* 页面滚动时回调
* @param position 当前位置
* @param positionOffset 偏移量
* @param positionOffsetPixels 偏移像素
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// 页面滚动时不需要特殊处理
}
/**
* 页面选中时回调
* @param position 选中的页面位置
*/
@Override
public void onPageSelected(int position) {
// 更新底部圆点指示器状态
for (int i = 0; i < dotViews.length; i++) {
if (position == i) {
// 当前页面对应的圆点设置为选中状态
dotViews[i].setImageResource(R.drawable.guide_selector);
} else {
// 其他圆点设置为未选中状态
dotViews[i].setImageResource(R.drawable.guide_white);
}
}
// 如果是最后一页,显示"开始体验"按钮
if (position == dotViews.length - 1) {
btn.setVisibility(View.VISIBLE);
} else {
// 其他页面隐藏按钮
btn.setVisibility(View.GONE);
}
}
/**
* 页面滚动状态变化时回调
* @param state 滚动状态
*/
@Override
public void onPageScrollStateChanged(int state) {
// 滚动状态变化时不需要特殊处理
}
});
}
/**
* 初始化底部圆点指示器
* 创建与引导页数量相同的圆点,并设置点击切换页面功能
*/
private void initDots() {
// 获取圆点容器布局
LinearLayout layout = findViewById(R.id.guide_ll);
// 设置圆点布局参数
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(20, 20);
params.setMargins(10, 0, 10, 0); // 设置圆点间距
// 初始化圆点数组
dotViews = new ImageView[imgs.length];
// 循环创建圆点
for (int i = 0; i < imageViews.size(); i++) {
// 创建圆点ImageView
ImageView imageView = new ImageView(this);
imageView.setLayoutParams(params);
// 默认设置为未选中状态
imageView.setImageResource(R.drawable.guide_white);
// 第一个圆点设置为选中状态
if (i == 0) {
imageView.setImageResource(R.drawable.guide_selector);
}
// 将圆点添加到数组
dotViews[i] = imageView;
// 保存当前位置的副本,用于点击事件
final int position = i;
// 设置圆点点击事件:点击圆点跳转到对应页面
dotViews[i].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
vp.setCurrentItem(position);
}
});
// 将圆点添加到容器布局
layout.addView(imageView);
}
}
/**
* 初始化引导图片
* 创建ImageView列表,设置图片资源和显示属性
*/
private void initImgs() {
// 创建ViewPager布局参数
ViewPager.LayoutParams params = new ViewPager.LayoutParams();
// 初始化ImageView列表
imageViews = new ArrayList<ImageView>();
// 循环创建ImageView并设置图片
for (int i = 0; i < imgs.length; i++) {
// 创建ImageView
ImageView imageView = new ImageView(this);
// 设置布局参数
imageView.setLayoutParams(params);
// 设置图片资源
imageView.setImageResource(imgs[i]);
// 设置图片缩放模式:填充整个ViewPager页面
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
// 添加到列表
imageViews.add(imageView);
}
}
}
splash_activity
尝试编译运行
遇到的问题包括,类名拼错,文件名写错,以及最重要的是缺少图片存放的知识,最后需要将文件存放在res/drawable
中,注意这个for循环中的内容,里面的原点资源需要放到drawable
里面,作为
联网并实现
权限与网络申请
<!-- 权限申请-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<!-- 针对旧版本Android的存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<!-- 硬件声明,没有摄像头也能运行 -->
<uses-feature android:name="android.hardware.camera" android:required="false"/>
package com.example.emptnew.activity;
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 androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.example.emptnew.MainActivity;
import com.example.emptnew.R;
import com.example.emptnew.bean.LoginBean;
import com.google.gson.Gson;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
// UI组件
private EditText edt_name;
private EditText edt_psw;
private Button btn_login;
private Button btn_register;
// 网络相关
private String token;
private int code;
// 使用主线程的Handler
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
Toast.makeText(getApplicationContext(), "登录成功", Toast.LENGTH_LONG).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
}
private void initView() {
edt_name = findViewById(R.id.edt_name);
edt_psw = findViewById(R.id.edt_psw);
btn_login = findViewById(R.id.btn_login);
btn_register = findViewById(R.id.btn_register);
btn_login.setOnClickListener(this);
btn_register.setOnClickListener(this);
}
@Override
public void onClick(View v) { // 参数改为小写v
int id = v.getId();
if (id == R.id.btn_login) {
login();
} else if (id == R.id.btn_register) {
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
}
}
public void login() {
final String username = edt_name.getText().toString().trim(); // 移除static
final String password = edt_psw.getText().toString().trim();
if (TextUtils.isEmpty(username)) {
Toast.makeText(this, "请输入用户名", Toast.LENGTH_SHORT).show();
return;
} else if (TextUtils.isEmpty(password)) {
Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
return;
}
// 创建JSON请求体
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("username", username);
jsonObject.put("password", password);
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(this, "请求参数错误", Toast.LENGTH_SHORT).show();
return;
}
MediaType mediaType = MediaType.parse("application/json; charset=utf-8"); // 修正拼写
RequestBody requestBody = RequestBody.create(mediaType, jsonObject.toString());
// 创建请求
Request request = new Request.Builder()
.url("http://192.168.2.12:8080/login") // 添加协议头
.post(requestBody)
.build();
// 发送请求
new OkHttpClient().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
runOnUiThread(() -> // 修正lambda语法
Toast.makeText(LoginActivity.this, "网络请求失败", Toast.LENGTH_SHORT).show()
);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (!response.isSuccessful()) {
runOnUiThread(() ->
Toast.makeText(LoginActivity.this, "服务器错误: " + response.code(), Toast.LENGTH_SHORT).show()
);
return;
}
String responseBody = response.body().string();
Gson gson = new Gson();
LoginBean loginBean = gson.fromJson(responseBody, LoginBean.class);
if ("200".equals(loginBean.getCode())) {
token = loginBean.getToken();
SharedPreferences.Editor editor = getSharedPreferences("user_data", MODE_PRIVATE).edit();
editor.putString("token", token);
editor.putString("username", username);
editor.apply();
runOnUiThread(() -> {
Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
// 跳转到主界面,而不是Manifest
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
});
} else {
runOnUiThread(() ->
Toast.makeText(LoginActivity.this, "登录失败: " + loginBean.getMsg(), Toast.LENGTH_SHORT).show()
);
}
}
});
}
}
实体类
package com.example.emptnew.bean;
public class LoginBean {
private String msg;
private String code;
private String token;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public LoginBean(String msg, String code, String token) {
this.msg = msg;
this.code = code;
this.token = token;
}
@Override
public String toString() {
return "UserLogin{" +
"msg='" + msg + '\'' +
", code=" + code +
", token='" + token + '\'' +
'}';
}
}