学习进度条
Android 学习打卡系统之每日总结与打卡记录添加
今日学习时间:2.5 小时
今日代码量:250 行
今日博客:1 篇(Android 学习打卡系统之每日总结与打卡记录添加)
一、核心知识点
每日总结添加
在AddSummaryActivity.java中,用户可以添加每日总结。通过Retrofit调用后端 API,将总结信息发送到服务器。
String blogUrl = etBlogUrl.getText().toString().trim();
if (!blogUrl.isEmpty()) {
if (!blogUrl.startsWith("http://") && !blogUrl.startsWith("https://")) {
blogUrl = "http://" + blogUrl;
}
DailySummary summary = new DailySummary();
summary.setUserId(Long.valueOf(studentId));
summary.setSummaryDate(currentDate);
summary.setBlogUrl(blogUrl);
ApiService apiService = RetrofitClient.getClient().create(ApiService.class);
Call<String> call = apiService.addDailySummary(authToken, summary);
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
Toast.makeText(AddSummaryActivity.this, response.body(), Toast.LENGTH_SHORT).show();
loadHistorySummaries();
etBlogUrl.setText("");
} else {
Toast.makeText(AddSummaryActivity.this, "添加失败: " + response.message(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(AddSummaryActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
打卡记录添加
在AddRecordActivity.java中,用户可以添加编程打卡记录。同样使用Retrofit与后端 API 交互。
String pspStage = etPspStage.getText().toString().trim();
String content = etContent.getText().toString().trim();
String timeSpentStr = etTimeSpent.getText().toString().trim();
if (pspStage.isEmpty()) {
Toast.makeText(this, "请输入PSP阶段", Toast.LENGTH_SHORT).show();
return;
}
ProgrammingRecord record = new ProgrammingRecord();
record.setUserId(String.valueOf(Long.parseLong(studentId)));
record.setRecordDate(currentDate);
record.setPspStage(pspStage);
record.setContent(content);
if (!timeSpentStr.isEmpty()) {
record.setTimeSpent(Integer.parseInt(timeSpentStr));
}
ApiService apiService = RetrofitClient.getClient().create(ApiService.class);
Call<String> call = apiService.addProgrammingRecord(token, record);
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
Toast.makeText(AddRecordActivity.this, response.body(), Toast.LENGTH_SHORT).show();
finish();
} else {
Toast.makeText(AddRecordActivity.this, "添加失败: " + response.message(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(AddRecordActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
二、安全注意事项
URL 格式检查:在添加每日总结时,对用户输入的博客 URL 进行格式检查,确保以http://或https://开头。
数据验证:在添加打卡记录时,对 PSP 阶段和时间输入进行验证,避免无效数据的提交。
三、今日实践成果
实现了每日总结和打卡记录的添加功能,添加成功后会刷新历史记录。
遇到的问题:
网络请求返回错误信息(解决:检查 API 接口和服务器端代码)
数据提交失败(解决:检查数据格式和请求头信息)
相关代码:
package com.example.study0327;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.study0327.api.ApiService;
import com.example.study0327.api.RetrofitClient;
import com.example.study0327.modules.DailySummary;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class AddSummaryActivity extends AppCompatActivity {
private EditText etBlogUrl;
private Button btnAddSummary;
private LinearLayout summaryListLayout;
private String studentId;
private String authToken;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_summary);
// 获取用户登录信息
SharedPreferences prefs = getSharedPreferences("user_prefs", MODE_PRIVATE);
studentId = prefs.getString("student_id", "");
authToken = "Bearer " + prefs.getString("auth_token", "");
if (studentId.isEmpty() || authToken.equals("Bearer ")) {
Toast.makeText(this, "请先登录", Toast.LENGTH_SHORT).show();
finish();
return;
}
etBlogUrl = findViewById(R.id.etBlogUrl);
btnAddSummary = findViewById(R.id.btnAddSummary);
summaryListLayout = findViewById(R.id.summaryListLayout);
// 获取当前日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
String currentDate = sdf.format(new Date());
// 加载历史记录
loadHistorySummaries();
btnAddSummary.setOnClickListener(v -> {
String blogUrl = etBlogUrl.getText().toString().trim();
if (blogUrl.isEmpty()) {
Toast.makeText(this, "请输入博客链接", Toast.LENGTH_SHORT).show();
return;
}
// 检查URL格式,自动添加http://前缀
if (!blogUrl.startsWith("http://") && !blogUrl.startsWith("https://")) {
blogUrl = "http://" + blogUrl;
}
DailySummary summary = new DailySummary();
summary.setUserId(Long.valueOf(studentId));
summary.setSummaryDate(currentDate);
summary.setBlogUrl(blogUrl);
ApiService apiService = RetrofitClient.getClient().create(ApiService.class);
Call<String> call = apiService.addDailySummary(authToken, summary);
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
Toast.makeText(AddSummaryActivity.this, response.body(), Toast.LENGTH_SHORT).show();
loadHistorySummaries(); // 刷新历史记录
etBlogUrl.setText(""); // 清空输入框
} else {
Toast.makeText(AddSummaryActivity.this, "添加失败: " + response.message(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(AddSummaryActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
});
}
private void loadHistorySummaries() {
ApiService apiService = RetrofitClient.getClient().create(ApiService.class);
Call<List<DailySummary>> call = apiService.getDailySummaries(authToken);
call.enqueue(new Callback<List<DailySummary>>() {
@Override
public void onResponse(Call<List<DailySummary>> call, Response<List<DailySummary>> response) {
if (response.isSuccessful() && response.body() != null) {
displaySummaries(response.body());
} else {
Toast.makeText(AddSummaryActivity.this, "获取历史记录失败", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<List<DailySummary>> call, Throwable t) {
Toast.makeText(AddSummaryActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
private void displaySummaries(List<DailySummary> summaries) {
summaryListLayout.removeAllViews();
if (summaries.isEmpty()) {
TextView emptyView = new TextView(this);
emptyView.setText("暂无历史记录");
emptyView.setTextSize(16);
emptyView.setPadding(0, 32, 0, 0);
summaryListLayout.addView(emptyView);
return;
}
LayoutInflater inflater = LayoutInflater.from(this);
for (DailySummary summary : summaries) {
View summaryView = inflater.inflate(R.layout.item_summary, summaryListLayout, false);
TextView tvDate = summaryView.findViewById(R.id.tvDate);
TextView tvBlogUrl = summaryView.findViewById(R.id.tvBlogUrl);
tvDate.setText(summary.getSummaryDate());
tvBlogUrl.setText(summary.getBlogUrl());
// 为每个总结项添加点击事件
summaryView.setOnClickListener(v -> {
String url = summary.getBlogUrl();
// 检查URL格式
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "http://" + url;
}
// 使用浏览器打开链接
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(browserIntent);
});
summaryListLayout.addView(summaryView);
}
}
}
package com.example.study0327;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.study0327.api.ApiService;
import com.example.study0327.api.RetrofitClient;
import com.example.study0327.modules.ProgrammingRecord;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class AddRecordActivity extends AppCompatActivity {
private EditText etPspStage, etContent, etTimeSpent;
private Button btnAddRecord;
private String studentId;
private String token;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_record);
// 获取用户登录信息
SharedPreferences prefs = getSharedPreferences("user_prefs", MODE_PRIVATE);
studentId = prefs.getString("student_id", "");
token = "Bearer " + prefs.getString("auth_token", "").trim();
if (studentId.isEmpty() || token.equals("Bearer ")) {
Toast.makeText(this, "请先登录", Toast.LENGTH_SHORT).show();
finish();
return;
}
etPspStage = findViewById(R.id.etPspStage);
etContent = findViewById(R.id.etContent);
etTimeSpent = findViewById(R.id.etTimeSpent);
btnAddRecord = findViewById(R.id.btnAddRecord);
// 在onCreate方法中添加
Button btnViewRecords = findViewById(R.id.btnViewRecords);
btnViewRecords.setOnClickListener(v -> {
Intent intent = new Intent(AddRecordActivity.this, ViewRecordsActivity.class);
startActivity(intent);
});
// 获取当前日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
String currentDate = sdf.format(new Date());
btnAddRecord.setOnClickListener(v -> {
try {
String pspStage = etPspStage.getText().toString().trim();
String content = etContent.getText().toString().trim();
String timeSpentStr = etTimeSpent.getText().toString().trim();
if (pspStage.isEmpty()) {
Toast.makeText(this, "请输入PSP阶段", Toast.LENGTH_SHORT).show();
return;
}
ProgrammingRecord record = new ProgrammingRecord();
record.setUserId(String.valueOf(Long.parseLong(studentId))); // 设置学号为用户ID
record.setRecordDate(currentDate);
record.setPspStage(pspStage);
record.setContent(content);
if (!timeSpentStr.isEmpty()) {
record.setTimeSpent(Integer.parseInt(timeSpentStr));
}
ApiService apiService = RetrofitClient.getClient().create(ApiService.class);
Call<String> call = apiService.addProgrammingRecord(token, record);
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
Toast.makeText(AddRecordActivity.this, response.body(), Toast.LENGTH_SHORT).show();
finish();
} else {
Toast.makeText(AddRecordActivity.this, "添加失败: " + response.message(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(AddRecordActivity.this, "网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
} catch (NumberFormatException e) {
Toast.makeText(this, "请输入有效的时间", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(this, "添加失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
四、明日计划
学习查看每日总结和打卡记录的功能实现。
研究如何对历史记录进行分页显示。
五、关键收获
进一步掌握了Retrofit在数据提交方面的应用。
了解了数据验证和 URL 格式检查在应用开发中的重要性。

浙公网安备 33010602011771号