20212326 2023-2024-2 《移动平台开发与实践》课程大作业设计

20212326李齐琨 2024-06-22 12:36:26

1.实验内容

实验要求:开发一个自己感兴趣,又能让老师感兴趣的移动App,具体功能不定。
实验内容:类似于百词斩的大学生四六级托福英语单词打卡背诵移动你APP,并且拥有与百词斩一样背诵四六级与托福单词、错题本、注册登陆账号、设置每日背诵单词的目标以及每日打卡的功能,且将四级单词六级单词和托福单词的目标和打卡分开统计。

2.实验过程:

2.1开发环境与工具选择:

使用Java语言开发安卓应用。
使用Android Studio作为开发工具。

3.2. 内容设计:

img

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:存放应用程序的资源文件,包括样式、颜色和字符串等。

img

系统的本质使用的是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();

方法,显示成功打卡的标志。

img

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;
        }
    }
}


其余代码会打包发给课代表,就不在报告里体现,各部分的作用在前文中已经体现过了

3.实验结果展示

4.遇到的问题及解决方案

问题:我想实现打卡的功能,但不知道如何将打卡所需要的日历引用
解决:https://blog.csdn.net/JX_Cesare/article/details/81084020 借鉴了此篇博客,根据在首页面设置的计划,如果完成每天所需要学习的单词数量即可打卡。在打卡页面中通过getmarkday()方法,使用sqlite查询语句获取出完成打卡的时间。通过此方法,显示成功打卡的标志。

cv.setStartEndDate("2020.6","2050.1")
        .setInitDate(current)
        .setMultiDate(finishday)
        .init();

5.实验心得

这次实验我设计并开发了一个基于Android平台的背单词应用,我认为背单词是英语学习过程中必不可少的一部分,然而,传统的纸质卡片方式效率低下,难以管理,所以我做了一个类似于百词斩的移动APP。
在项目的初期阶段,我花了大量时间进行需求分析和功能设计。通过使用Android Studio和Java语言,我成功实现了用户注册登录系统、单词列表展示、目标设置和每日打卡功能。其中,单词列表的获取和展示使用了RecyclerView和自定义适配器,确保了界面的流畅性和用户体验。实现用户注册登录系统是整个项目中的重要一环。我采用了身份验证和实时数据库,用户登录后可以查看错题本和制定每日学习目标。每日打卡功能则通过本地数据库管理用户的学习记录,用户可以每天记录学习进度,系统会自动统计并反馈给用户。这一功能不仅增加了用户参与度,还促进了学习的持续性和效果。
总的来说,本次移动平台课程的期末大实验让我收获颇丰。我不仅成功地开发了一款功能完善的背单词应用,还提升了自己在移动应用开发方面的技能和经验。未来,我希望能进一步完善应用的功能,比如增加更多考试单词库、优化用户界面,以及引入个性化学习推荐系统。

...全文
290 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

102

社区成员

发帖
与我相关
我的任务
社区描述
实验报告
android 高校
社区管理员
  • blackwall0321
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧