102
社区成员
发帖
与我相关
我的任务
分享实验要求:开发一个自己感兴趣,又能让老师感兴趣的移动App,具体功能不定。
实验内容:类似于百词斩的大学生四六级托福英语单词打卡背诵移动你APP,并且拥有与百词斩一样背诵四六级与托福单词、错题本、注册登陆账号、设置每日背诵单词的目标以及每日打卡的功能,且将四级单词六级单词和托福单词的目标和打卡分开统计。
使用Java语言开发安卓应用。
使用Android Studio作为开发工具。

Android app源码目录结构通常包括以下几个部分:
src:存放应用程序的源代码,包括所有的C/C++代码和AndroidManifest.xml文件。
AndroidManifest是系统的清单文件,应用所需要的权限以及Android四大组件需要在这个地方申明
res:存放应用程序的资源文件,包括布局文件、图片、音频、视频等。
libs:存放应用程序依赖的库文件。
AndroidManifest.xml:应用程序的配置文件,描述了应用程序的基本信息和组件声明等信息。
build.gradle:Gradle构建脚本文件,用于配置构建过程和依赖管理。
proguard-rules.pro:ProGuard规则文件,用于配置代码混淆和优化规则。
app/src/main:这是应用程序的主要源代码目录,其中包含主要的Java或Kotlin代码。
app/src/test:存放应用程序的测试代码。
app/src/androidTest:用于Android设备或模拟器上的测试代码。
app/libs:存放应用程序依赖的库文件。
app/build:存放构建过程中生成的文件,包括编译后的代码和资源文件。
app/res/values:存放应用程序的资源文件,包括样式、颜色和字符串等。

系统的本质使用的是sqlite,在本地中进行数据的增删改查。
如上图所示是系统的目录接口,Activity目录中包含的是系统的页面,cuociActivity是错题页面,执行了queryAllerrorbyuser
方法进行错题的获取。结合danciAdapter和ListView将数据显示在UI中。danciActivity是背单词页面,有四个选项,其中正确的选项从asset中的词汇进行搜索获取。其他三个选项通过Utils目录中的RandUtils生成的随机数进行获取。danciinfoActivity是正确答案选择,通过代码中的Bundle bundle = getIntent().getExtras();获取出单词和正确的单词意思。LaunchActivity
为闪屏页面,Picasso.with(LaunchActivity.this).load("https://media-image1.baydn.com/soup_pub_image/drqaga/4f818317723f1125e8ba70645b9c0965.b867fe695902f26685e90e7814a31fae.jpeg%22). into(i);加载显示网络图片。经过三秒以后会跳转到LoginActiivty即登录页面,如下代码
//当计时结束,跳转至主界面
handler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(LaunchActivity.this, LoginActivity.class);
startActivity(intent);
finish();
}
}, 3000);
LoginActivity为登录页面,RegisterActivity为注册页面,登录注册使用了Sqlite的查询和插入语句。如下代码所示
String sql = "select * from user where account = ?";
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = db.rawQuery(sql, new String[]{account});
Adapter文件夹是适配器,主要用来显示列表,包括单词列表和答案列表
Bean文件夹是java实体类
Calendar中的DakaActiviy是打卡页面,如下图所示,打卡页面使用的是第三方的日历控件,根据在首页面设置的计划,如果完成每天所需要学习的单词数量即可打卡。在打卡页面中通过getmarkday()方法,使用sqlite查询语句获取出完成打卡的时间。通过
cv.setStartEndDate("2020.6","2050.1")
.setInitDate(current)
.setMultiDate(finishday)
.init();
方法,显示成功打卡的标志。

Http文件夹是用来获取首页的图片
SQL文件夹是数据库相关,用来创建用户表和数据表
Utils文件夹是工具类,RandUtils是随机数工具类,SPUilts是SharePreference的工具类,用来保存简要的文字到Android本地,WordSQLUtils是单词查询的工具类,其中包含了很多Sqlite语句,用来查询错词本等。
MainAc是系统的主页面,主页面的背景是网络请求,使用Retrofit访问https://openapi.youdao.com/api/ ,使用Json解析数据返回,使用如下代码显示图片,Picasso是用来显示图片的第三方包
JSONArray image = j1.getJSONArray("share_img_urls");
String url = (String) image.get(0);
ImageView i1 = findViewById(R.id.image_main);
Picasso.with(MainActivity.this).load(url).into(i1);
Assets文件夹放置了系统的词汇量。包括六级,四级。单词的载入在Bean文件夹下的WordList
类getwordlist方法,可以获取出对应文件的单词。
3.3源码展示、
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.myapplication">
<!-- 互联网权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/icon"
android:label="@string/app_name"
android:roundIcon="@mipmap/icon"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
android:usesCleartextTraffic="true">
<activity android:name=".Calendar.DakaActivity"></activity>
<activity android:name=".Activity.LoginActivity">
</activity>
<activity android:name=".Activity.RegisterActivity"/>
<activity android:name=".Activity.LaunchActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Activity.danciinfoActivity" />
<activity android:name=".Activity.cuociActivity" />
<activity android:name=".Activity.danciActivity" />
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustPan" />
</application>
</manifest>
MainActivity
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.example.myapplication.Activity.cuociActivity;
import com.example.myapplication.Activity.danciActivity;
import com.example.myapplication.Activity.danciinfoActivity;
import com.example.myapplication.Bean.Juzi;
import com.example.myapplication.Bean.Word;
import com.example.myapplication.Bean.WordInfo;
import com.example.myapplication.Bean.WordList;
import com.example.myapplication.Calendar.DakaActivity;
import com.example.myapplication.SQL.UserHelper;
import com.example.myapplication.Utils.SPUtils;
import com.example.myapplication.http.BuildPath;
import com.example.myapplication.http.GetBgInfoAPI;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.List;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.http.Url;
public class MainActivity extends AppCompatActivity {
private UserHelper myUserHelper = new UserHelper(this);
private RadioGroup rp_btn ;
private WordList wordList;
public static List<Word>data;
private TextView jihua_tv;
private TextView num_tv;
private TextView day_tv;
private TextView num_day;
private String filename;
private String plannum;
private TextView sp;
private String tempdays ;
private int mrdcs;
private int hfdst;
private String TAG= "MainActivity";
private Boolean setting = false;
private int status_le;
public static Juzi jz1 = new Juzi();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
initmenu();
rp_btn.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
day_tv.setText(tempdays);
switch (checkedId) {
case R.id.siji_rtb:
filename = "四级";
setjihua(filename);
showDialog(filename,data.size(),10);
if (SPUtils.contains(MainActivity.this, "planname")) {
SPUtils.put(MainActivity.this, "planname", filename);
}
break;
case R.id.liuji_rtb:
filename = "六级";
setjihua(filename);
showDialog(filename,data.size(),10);
if (SPUtils.contains(MainActivity.this, "planname")) {
SPUtils.put(MainActivity.this, "planname", filename);
}
break;
case R.id.tuofu_rtb:
filename = "test";
setjihua(filename);
showDialog(filename,data.size(),10);
if (SPUtils.contains(MainActivity.this, "planname")) {
SPUtils.put(MainActivity.this, "planname", filename);
}
break;
}
}
});
final DrawerLayout drawerLayout=findViewById(R.id.drawerlayout);
//给按钮添加一个监听器
findViewById(R.id.top_view_left_iv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//打开侧滑菜单
drawerLayout.openDrawer(GravityCompat.START);
}
});
}
@Override
protected void onResume() {
super.onResume();
}
public void gotoxuexiclick(View v){
if(!setting){
Toast.makeText(MainActivity.this,"你还没选择学习的词库哦",Toast.LENGTH_SHORT).show();
}
else {
Intent intent = new Intent();
intent.setClass(MainActivity.this, danciActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("num",mrdcs);
if(SPUtils.contains(MainActivity.this,"status"+filename)){
int status = 0;
//这个计划今天学了几个单词
status = (int) SPUtils.get(MainActivity.this,"status"+filename,status);
bundle.putInt("status",status);
}
else {
bundle.putInt("status",0);
}
bundle.putString("filename",filename);
intent.putExtras(bundle);
startActivity(intent);
}
}
public void init(){
rp_btn = (RadioGroup)findViewById(R.id.btn_rg);
jihua_tv = (TextView)findViewById(R.id.jihua);
num_tv = (TextView)findViewById(R.id.num);
day_tv = (TextView)findViewById(R.id.day);
num_day = (TextView)findViewById(R.id.wordday);
plannum = num_tv.getText().toString();
sp = (TextView)findViewById(R.id.num_sp);
tempdays = day_tv.getText().toString();
}
public void setjihua(String filename){
wordList = new WordList(this,filename);
data = wordList.getwordlist();
Log.e(TAG,"data个数"+data.size());
jihua_tv.setText(filename+"计划");
String strlast ="";
strlast = plannum+data.size()+"个";
num_tv.setText(strlast);
}
public void cunchujihua(String plan,int num){
SQLiteDatabase dbr = myUserHelper.getReadableDatabase();
SQLiteDatabase dbw = myUserHelper.getWritableDatabase();
String name = new String();
name = (String) SPUtils.get(this,"username",name);
Log.i(TAG, "username---------------"+name);
Cursor cursor;
String [] query = new String[]{name,plan};
ContentValues value = new ContentValues();
value.put("Username",name);
value.put("Planname",plan);
value.put("PlanDayNum",num);
try {
cursor = dbr.query("userPlan",null,"Username=?and PlanName=?",query,null,null,null);
if(cursor.getCount()==0){
dbw.insert("userPlan",null,value);
}
else {
dbw.update("userPlan",value,"Username=?and PlanName=?",query);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void showDialog(String planname,int sum_num,int num){
setting = true;
final int[] real_num = {num};
final AlertDialog.Builder setPlanDialog = new AlertDialog.Builder(MainActivity.this);
setPlanDialog.setTitle("设置"+planname+"计划");
setPlanDialog.setIcon(R.drawable.setting);
setPlanDialog.setMessage("来设置/修改你的计划");
View v = (LinearLayout)getLayoutInflater().inflate(R.layout.dialog_view,null);
setPlanDialog.setView(v);
Spinner sp_num = (Spinner)v.findViewById(R.id.sp_set_num);
TextView sp_day = (TextView)v.findViewById(R.id.sp_set_day);
sp_num.setSelection(num/5-2,true);
sp_day.setText(String.valueOf(sum_num/num)+"天");
sp_num.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
real_num[0] = Integer.parseInt(parent.getItemAtPosition(position).toString());
String days = String.valueOf(sum_num/ real_num[0]);
String text = days+"天";
sp_day.setText(text);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
setPlanDialog.setPositiveButton("设置/修改",new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
mrdcs = real_num[0];
hfdst = sum_num/real_num[0];
sp.setText(String.valueOf(mrdcs));
day_tv.setText(tempdays+hfdst+"天");
cunchujihua(filename,mrdcs);
Toast.makeText(MainActivity.this,"计划每天"+mrdcs+"这么多天"+hfdst,Toast.LENGTH_SHORT).show();
}
});
setPlanDialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mrdcs = num;
hfdst = sum_num/num; sp.setText(String.valueOf(mrdcs)+"个");
day_tv.setText(tempdays+hfdst+"天");
cunchujihua(filename,mrdcs);
Toast.makeText(MainActivity.this,"计划每天"+mrdcs+"这么多天"+hfdst,Toast.LENGTH_SHORT).show();
}
});
setPlanDialog.create().show();
}
public void initmenu(){
TextView tv_username = findViewById(R.id.username_show);
String name = (String) SPUtils.get(MainActivity.this,"username","--");
tv_username.setText("用户:"+name);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://openapi.youdao.com/api/")
.build();
String url = BuildPath.getTodayEnglish();
GetBgInfoAPI server = retrofit.create(GetBgInfoAPI.class);
Call<ResponseBody> task = server.getJson(url);
task.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(response.code()== HttpURLConnection.HTTP_OK){
try {
String result = response.body().string();
JSONObject j1 = new JSONObject(result);
jz1= new Juzi();
jz1.setContent(j1.getString("content"));
jz1.setTrans(j1.getString("translation"));
JSONArray image = j1.getJSONArray("share_img_urls");
String url = (String) image.get(0);
ImageView i1 = findViewById(R.id.image_main);
Picasso.with(MainActivity.this).load(url).into(i1);
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
public void menuClick(View v){
Intent i = new Intent();
switch (v.getId()){
case R.id.btn1:
gotoxuexiclick(v);
break;
case R.id.btn2:
i.setClass(MainActivity.this, cuociActivity.class);
startActivity(i);
break;
case R.id.btn3:
Bundle b = new Bundle();
b.putInt("type",0);
b.putString("planName",filename);
i.putExtras(b);
i.setClass(MainActivity.this, DakaActivity.class);
startActivity(i);
break;
case R.id.btn4:
SPUtils.clear(MainActivity.this);
finish();
break;
}
}
}
其余代码会打包发给课代表,就不在报告里体现,各部分的作用在前文中已经体现过了
问题:我想实现打卡的功能,但不知道如何将打卡所需要的日历引用
解决:https://blog.csdn.net/JX_Cesare/article/details/81084020 借鉴了此篇博客,根据在首页面设置的计划,如果完成每天所需要学习的单词数量即可打卡。在打卡页面中通过getmarkday()方法,使用sqlite查询语句获取出完成打卡的时间。通过此方法,显示成功打卡的标志。
cv.setStartEndDate("2020.6","2050.1")
.setInitDate(current)
.setMultiDate(finishday)
.init();
这次实验我设计并开发了一个基于Android平台的背单词应用,我认为背单词是英语学习过程中必不可少的一部分,然而,传统的纸质卡片方式效率低下,难以管理,所以我做了一个类似于百词斩的移动APP。
在项目的初期阶段,我花了大量时间进行需求分析和功能设计。通过使用Android Studio和Java语言,我成功实现了用户注册登录系统、单词列表展示、目标设置和每日打卡功能。其中,单词列表的获取和展示使用了RecyclerView和自定义适配器,确保了界面的流畅性和用户体验。实现用户注册登录系统是整个项目中的重要一环。我采用了身份验证和实时数据库,用户登录后可以查看错题本和制定每日学习目标。每日打卡功能则通过本地数据库管理用户的学习记录,用户可以每天记录学习进度,系统会自动统计并反馈给用户。这一功能不仅增加了用户参与度,还促进了学习的持续性和效果。
总的来说,本次移动平台课程的期末大实验让我收获颇丰。我不仅成功地开发了一款功能完善的背单词应用,还提升了自己在移动应用开发方面的技能和经验。未来,我希望能进一步完善应用的功能,比如增加更多考试单词库、优化用户界面,以及引入个性化学习推荐系统。