跳至主要內容

LPrincess's App

LPrincess大约 19 分钟androidBlog

android课设记录一下制作过程

SDK:API24("Nougat";Android 7.0) Win10 -> Win11

Win10 -> Win11

详细代码在GitHub:https://github.com/L-mj0open in new window

功能1:App有登陆页面,提供用户名和密码验证

思路

首先,进入登录页面,两个输入框用户名,密码;两个按钮Login和Register;登录成功——欢迎界面;注册——注册界面,注册成功储存到SharedPrefeerences和数据库中,并返回登录页面获取SharePrederences中的数据填充到输入框和密码中;

需要部分重要页面文件:

  • MainActivity.java :主界面
  • MysqlROE21005.java:利用sqlite的SQLiteOpenHelper类创建数据库
  • RegisterROE21005.java:注册页面
  • WelcomeROE21005.java: 登陆成功后的欢迎界面
  • InitpageROE21005.java: 初始活动页面
  • ChannelROE21005.java: 九宫格布局活动——存储和管理与我们布局层九宫格每一个小布局相关功能
  • ChannelAdapterROE21005.java: 创建一个Channel类继承BaseAdapter来作为GridView的适配器——用于网格视图展示数据
  • IntroductionROE21005.java: 个人介绍活动页面
  • BlogLPrincessROE21005.java: blog页面
  • GithubROE21005.java: 外网链接——github
  • BilibiliROE21005.java: 外网链接——bilibili
  • MinesweeperROE21005.java: Game——扫雷
  • ReversiROE21005.java: Game——黑白棋
  • WebviewROE21005.java: 外网链接——跳转指定网址
  • OverviewROE21005.java: 点赞和评论数动态图表显示
  • SuggestionROE21005.java: 提出建议页面显示
  • activity_main_roe21005.xml:登录页面的布局文件
  • activity_register_roe21005.xml:注册页面的布局文件
  • activity_welcome_roe21005.xml:欢迎页面的布局文件
  • acticity_bilibili_roe21005.xml: 外网链接——bilibili
  • activity_blog_roe21005.xml: blog页面
  • activity_github_roe21005.xml: github页面设计
  • activity_initpage_roe21005.xml: 初始页面设计
  • grid_item_roe21005.xml: 九宫格布局文件
  • activity_suggestion_roe21005.xml: 建议页面设计

1.登录界面——MainActivity.java

Log in,是从数据库中查询与输入的用户名、密码相同的记录,若查询成功则登陆成功。不存在则提示用户用户名或密码输入错误。

meditor.commit();
meditor.apply();
  • commit(): 这个方法用于将更改永久保存到SharedPreferences 中。commit() 方法是同步的,它会立即将更改写入持久存储,并返回一个表示操作是否成功的布尔值。由于它是同步的,如果在主线程上调用,可能会导致界面卡顿。
  • apply(): 另一个选项是使用 apply() 方法,它是异步的。apply() 会立即在内存中更改数据,然后异步地将更改写入磁盘,但不会返回任何指示操作成功与否的值。

登录按钮逻辑

  • 获取用户名和密码输入。
  • 使用query()方法查询数据库,检查用户名和密码是否匹配。
  • 如果用户存在,保存用户名到SharedPreferences并跳转到欢迎页面。
  • 如果用户不存在,显示Toast消息。

数据库查询以及SharedPreferences的使用

Cursor cursor = mdbLimengjue.query("logins",new String[]{"username","userpwd"},"username=? and userpwd=?",new String[]{musernameLimengjue,mpasswordLimengjue},null,null,null);

SharedPreferences.Editor meditorLimengjue = msp2Limengjue.edit();
meditorLimengjue.putString("Loginname",mloginnameLimengjue);
meditorLimengjue.apply();

2.注册界面——RegisterROE21005.java

注册时,会先对用户名进行比对,若用户名存在则提醒用户名已存在。设置密码会比对两次输入的密码是否相同,不相同则发出提醒,重新输入。

Android有一个系统自带可以查看数据库的

View-Tool Windows-App Inspection

注册按钮点击事件

  • 当点击注册按钮时,首先检查用户名和密码是否为空。
  • 如果不为空,则查询数据库以检查用户名是否已被注册。
  • 如果用户名未被注册,且两次输入的密码相同,则将用户信息插入到数据库中,并通过SharedPreferences保存用户信息。
  • 如果注册成功,则跳转到登录页面,并显示成功消息。
  • 如果用户名已存在或密码不一致,则显示相应的错误消息。

重要代码解释

// 判断输入是否为空
if(namelimengjue.equals("")||pwd01limengjue.equals("")||pwd02limengjue.equals(""))

// 检查用户名是否存在
Cursor cursorlimengjue = dblimengjue.query("logins",new String[]{"username"},null,null,null,null,null);
while(cursorlimengjue.moveToNext()){
    if(cursorlimengjue.getString(0).equals(namelimengjue)){
        flaglimengjue = false;
        break;
    }
}

// 检查密码是否一致
if(pwd01limengjue.equals(pwd02limengjue)){
    ContentValues cvlimengjue = new ContentValues();
    cvlimengjue.put("username",namelimengjue);
    cvlimengjue.put("userpwd",pwd01limengjue);
    dblimengjue.insert("logins",null,cvlimengjue);
    ...
}

// 跳转登录页面
Intent intentlimengjue = new Intent();
intentlimengjue.setClass(RegisterROE21005.this,MainActivity.class);
startActivity(intentlimengjue);

3.首页界面-Welcome.java & Initpage.java

想要在首页界面放一个标题,需要添加一个Toolbar

设置标题:在布局文件中添加自定义Toolbar然后再活动中将其设置为操作栏

<!-- activity_initpage_roe21005.xml -->
<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    app:title="Welcome to LPrincess's App"/>

注意的是这里的Toolbar是import androidx.appcompat.widget.Toolbar;

// Initpage.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_initpage_roe21005);

    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setTitle("Welcome to LPrincess's App");
}

九宫格实现

布局层——九宫格

  • 创建一个Channel类表示单元格的内容
  • 创建一个Channel类继承BaseAdapter来作为GridView的适配器
  • 在显示布局中需要创建一个grid_item布局文字和图标,后面统一使用该格式生成九宫格。由于后面要对用到的图标文件进行其他活动设置如跳转其他页面,因此都要设置一个id,需要在values文件夹下要创建一个grid_img.xml文件
  • 所有图片文件都是放在drawable文件夹下

Channel类——数据模型类

主要功能是存储和管理与我们布局层九宫格每一个小布局相关功能

ChannelAdapter类——用于网格视图展示数据

  • 适配器的构造函数接收一个 Channel对象的列表和一个 Context 对象。它初始化了成员变量。
// ChannelAdapter.java

public ChannelAdapter(ArrayList<Channel> list, Context context) {
    channelList = list;
    layoutInflater = LayoutInflater.from(context);
}

  • getView 方法:这是适配器中最重要的部分,它负责创建和更新视图:

    • 如果 convertView 为空,这意味着没有可重用的视图,它会通过 layoutInflaterlimengjue 加载新的布局。
    • 创建一个 ViewHolder 来存储视图的引用,以便快速访问,避免每次都调用 findViewById。
    • 根据当前位置的 ChannelROE21005 对象更新视图内容,包括设置文本和根据不同的条件选择不同的图像资源。
// ChannelAdapter.java

public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holderlimengjue = null;

        if(convertView == null) {
            //加载布局
            convertView = layoutInflaterlimengjue.inflate(R.layout.grid_item_roe21005, null);

            holderlimengjue = new ViewHolder();
            holderlimengjue.imgChannel = (ImageView) convertView.findViewById(R.id.channel_img);
            holderlimengjue.decChannel = (TextView) convertView.findViewById(R.id.channel_dec);
            convertView.setTag(holderlimengjue);
        }else{
            holderlimengjue = (ViewHolder) convertView.getTag();
        }

        // 设置图标和文字
        ChannelROE21005 channel= (ChannelROE21005) channelListlimengjue.get(position);
        if(channel!=null){
            holderlimengjue.decChannel.setText(channel.getDec());
            switch (channel.getDec()){
                case "Test1":
                    holderlimengjue.imgChannel.setImageResource(R.drawable.image1);
                    break;
                case "Test2":
                    holderlimengjue.imgChannel.setImageResource(R.drawable.image2);
                    break;
                // 接下来9个设置省略
            }
        }
        return convertView;
    }
  • ViewHolder类——内部类:

用于缓存视图的引用,提高列表滚动的性能

// ChannelAdapter.java
    class ViewHolder{
        ImageView imgChannel;
        TextView decChannel;
    }

InitpageROE21005.java类——实现初始化页面,提供九个选项,每个选项代表不同的功能和页面

  • 类声明和变量定义

    • InitpageROE21005 类继承自 AppCompatActivity,代表一个Android Activity。
    • channelDeclimengjue 和 channelImglimengjue 分别用于存储九宫格中每个选项的描述和图片资源ID。
    • channelListlimengjue 用于存储九宫格中的所有选项,每个选项是 ChannelROE21005 类的一个实例。
    • onCreate(Bundle savedInstanceState) 方法
    • setContentView(R.layout.activity_initpage_roe21005): 设置布局文件。
    • 初始化九宫格视图的元素,并通过 initChannelView 方法填充数据和设置事件监听器。
    • setSupportActionBar(toolbar): 设置工具栏。
  • initChannelView() 方法

    • 初始化 GridView,并将其与相应的布局元素关联。
    • 为 channelImglimengjue 和 channelDeclimengjue 赋值,分别定义了九宫格中每个选项的图像资源和描述。
    • 填充 channelListlimengjue 数组,每个 ChannelROE21005 对象代表一个九宫格选项。
    • 设置 GridView 的适配器 ChannelAdapterROE21005,这个适配器负责渲染每个九宫格选项。
    • 为 GridView 设置点击事件监听器,根据点击的位置启动不同的Activity。
  • 九宫格点击事件处理

    • setOnItemClickListener:为九宫格的每一个选项设置监听器。
    • 根据点击的 position 启动不同的Activity。例如,如果点击的是第一个选项(position为0),则启动 IntroductionROE21005 Activity;如果点击的是第二个选项(position为1),则启动 BlogLPrincessROE21005 Activity,以此类推。

这个Activity是一个功能导航页,它通过一个九宫格的界面展示了应用的不同功能,并且为每一个功能设置了点击事件,点击后将导航到相应的功能页面。这种设计使得用户可以在一个集中的地方访问应用的多个功能。代码使用了多个Android开发常见的组件和技术,如SharedPreferences存储用户信息,GridView展示网格列表,适配器模式动态填充网格内容,以及Intent在Activities之间进行导航。

4.Introduction界面

这个Activity是一个内容丰富的介绍页面,提供了图文介绍、点赞和评论功能。用户可以查看不同的介绍内容,对感兴趣的内容进行点赞和评论。代码中使用了SharedPreferences进行本地存储,SQLiteDatabase进行数据库操作,以及动态地更新视图来增强用户交互体验。通过监听器处理用户的点击事件,并与后端数据库交互,实现了一个动态和互动的介绍页面。

字体设置

使用 Typeface.createFromAsset() 方法设置特定的字体样式给 guitartextlimengjue、pianotextlimengjue 和 dancetextlimengjue。

自定义字体,下载.ttf文件,储存在app根目录下创建assets/fonts/文件目录下

Typeface typeface = Typeface.createFromAsset(getAssets(),"fonts/comic_sans_ms.ttf");
guitartextlimengjue.setTypeface(typeface);

扩充垂直滑动功能

在xml文件定义ScrollView,由于在ScrollView下不能定义多个LinearLayout,因此,在ScrollView下,我们再嵌套一个LinearLayout布局层,在这个布局层里面定义我们的布局。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">

        <ImageView
            android:id="@+id/index"
            android:layout_width="wrap_content"
            android:layout_height="252dp"
            android:src="@drawable/imgin1" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/ViewComments1"
            android:orientation="vertical"
            android:layout_marginTop="16dp">

        </LinearLayout>
       
    </LinearLayout>
</ScrollView>

Introduction界面点赞功能

基本思路:先创建一个表,再创建likes表(id,item_id,likes),先初始化表格,n个按钮,就创建n个id;再写一个addlike函数,查询id,获取id里的likes个数,并+1,再update表,点赞按钮点击时,调用 mysql.addLike 方法增加点赞数。

// Mysql.java

@Override
public void onCreate(SQLiteDatabase db) {
    String createLikesTable = "CREATE TABLE likes (id INTEGER PRIMARY KEY AUTOINCREMENT,item_id INTEGER,likes INTEGER Default 0)";

    db.execSQL(createLikesTable);

    for(int i=1;i<=10;i++){
        ContentValues values = new ContentValues();
        values.put("item_id",i);
        values.put("likes",0);
        db.insert("likes",null,values);
    }
}

public void addLike(int itemId){
  SQLiteDatabase db = this.getWritableDatabase();
  Cursor cursor = null;
  try{
      cursor = db.rawQuery("SELECT likes FROM likes WHERE item_id = ?",new String[]{String.valueOf(itemId)});

      if(cursor !=null && cursor.moveToFirst()){
          @SuppressLint("Range") int currentLikes = cursor.getInt(cursor.getColumnIndex("likes"));
          ContentValues values = new ContentValues();
          values.put("likes",currentLikes+1);
          db.update("likes",values,"item_id=?",new String[]{String.valueOf(itemId)});
      }
  }catch (Exception e){
      e.printStackTrace();
  }finally {
      if(cursor != null){
        cursor.close();
      }
  }
}

再活动层,首先,从布局层初始化id,这里为了方便,第n个按钮就为n(在表中的id),先设置按钮监听,再写,update函数,更新布局层点赞个数

// Introduction.java

btnlikelimengjue1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mysqllimengjue.addLike(1);
        updateLikesDisplay(1);
    }
});

private void updateLikesDisplay(int itemId){
    SQLiteDatabase db = mysqllimengjue.getReadableDatabase();
    Cursor cursor = db.rawQuery("SELECT likes FROM likes Where item_id = ?",new String[]{String.valueOf(itemId)});

    if(cursor.moveToFirst()){
        @SuppressLint("Range") int likes = cursor.getInt(cursor.getColumnIndex("likes"));
        if(itemId == 1){
            TextView tvLikes1 = findViewById(R.id.tv_likes1);
            tvLikes1.setText(String.valueOf(likes));
        }
        else if(itemId == 2){
            TextView tvLikes2 = findViewById(R.id.tv_likes2);
            tvLikes2.setText(String.valueOf(likes));
        }
    }
    // cursor.close();
    // dblimengjue.close();
}

Introduction界面评论功能

基本思路:

布局层需要一个编辑框用于接收评论的文字;一个提交评论按钮;一个动态地更新界面评论的布局框。

效果图:

代码详解:

  • onCreate 方法:

    • 在活动创建时,设置了布局并初始化了各种视图组件。

    • 从资源文件加载字体并设置给文本视图。设置自定义字体:这里我想要设置Comic字体,如何自定义设置?

      • 首先先下载字体.ttf文件,再assets文件夹下创建一个/fonts文件夹,把下载好的字体文件.ttf放在fonts文件目录下。再在定义写入并应用,例如:
      // Introduction.java
      
      Typeface typeface = Typeface.createFromAsset(getAssets(),"fonts/comic_sans_ms.ttf");
      guitartext.setTypeface(typeface);
      
    • 初始化点赞和评论按钮,并设置点击监听器以处理点赞和评论逻辑。

  • 评论处理:

    • 与点赞类似,同样需要先创建comment表,但是这里遇到了一个问题,如果同时把三个评论框里的所有评论存在一个表里,会杂乱,不好管理,因此这里对每一个评论框都分别建立一个comment_n表格。
    // Mysql.java
    
     @Override
    public void onCreate(SQLiteDatabase db) {
        String createCommentsTable1 = "CREATE TABLE comments1 (id INTEGER PRIMARY KEY AUTOINCREMENT,item_id INTEGER,comment text)";
    
        db.execSQL(createCommentsTable1);
    }
    
    
    • 评论按钮点击时,检查编辑文本是否为空,然后调用 mysql.addComment 方法添加评论。
    // Mysql.java
        public  void addComment(int itemId,String comment){
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("item_id", itemId);
        values.put("comment", comment);
        if(itemId==1){
            db.insert("comments1", null, values);
        }
    }
    
    // Introduction.java
    btncomment1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String comment = etComment1.getText().toString();
                // 处理提交评论逻辑
                if(!comment.isEmpty()){
                    int itemId=1;
                    mysql.addComment(itemId,comment);
                    Toast.makeText(Introduction.this,"成功评论:"+comment,Toast.LENGTH_LONG).show();
                    // 清除输入框
                    etComment1.setText("");
                    //更新界面上的评论列表
                    updateCommentsDisplay(1);
                }else{
                    Toast.makeText(Introduction.this,"评论不能为空!", Toast.LENGTH_LONG).show();
                }
    
            }
        });
    
  • 动态更新界面显示:

    • updateCommentsDisplay 方法从数据库获取评论,并将它们显示在界面上。逻辑条件是:model=0表示刚进入界面需要显示已有的,model=1表示获取到新评论,实时动态更新,根据新增的comment_n表总数(count),循环当i==count的时候,创建新布局,插入动态布局中。
    private void updateCommentsDisplay(int model) {
        // Toast.makeText(IntroductionROE21005.this, "count:" + count, Toast.LENGTH_LONG).show();
        Cursor cursor = mysqllimengjue.getComments(1);
        List<String> comments1 = new ArrayList<>();
    
        if(cursor != null && cursor.moveToFirst()){
            do{
                int id = cursor.getInt(1);
                String comment = cursor.getString(2);
                String formattedComment = id + " : " + comment;
                comments1.add(formattedComment);
            }while(cursor.moveToNext());
        }
        if(cursor!=null){
            cursor.close();
        }
        if(model == 0){
            LinearLayout commentsLayout = findViewById(R.id.ViewComments1);
    
            for(String formattedComment : comments1){
                TextView textView = new TextView(this);
                textView.setText(formattedComment);
                textView.setLayoutParams(new LinearLayout.LayoutParams(
                        LinearLayout.LayoutParams.MATCH_PARENT,
                        LinearLayout.LayoutParams.WRAP_CONTENT));
                textView.setPadding(10,10,10,10);
                textView.setTextSize(16);
                commentsLayout.addView(textView);
            }
        }
        else if(model == 1){
            SQLiteDatabase db = mysqllimengjue.getReadableDatabase();
            Cursor cursor_cn = db.rawQuery("SELECT COUNT(*) FROM comments1", null);
            // 遍历Cursor,将评论数据添加到界面上
            int count = 0; // 初始化 count
            if (cursor_cn.moveToFirst()) {
                count = cursor_cn.getInt(0);
            }
            cursor_cn.close(); // 关闭 cursor
            LinearLayout commentsLayout = findViewById(R.id.ViewComments1);
            int i=1;
            for(String formattedComment : comments1){
                if(i==count){
                    TextView textView = new TextView(this);
                    textView.setText(formattedComment);
                    textView.setLayoutParams(new LinearLayout.LayoutParams(
                            LinearLayout.LayoutParams.MATCH_PARENT,
                            LinearLayout.LayoutParams.WRAP_CONTENT));
                    textView.setPadding(10,10,10,10);
                    textView.setTextSize(16);
                    commentsLayout.addView(textView);
                }
                i++;
            }
        }
    }
    

    动态布局introduction.xml文件定义如下:

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/ViewComments3"
            android:orientation="vertical"
            android:layout_marginTop="16dp">
        </LinearLayout>
    

5.外网链接功能

这里讲通用的——输入网址,访问指定网址功能

  • 首先,布局层:需要一个编辑框获取输入的网址,一个按键触发活动,一个WebView布局窗口

  • 活动类:

  • onCreate 方法:

    • 在活动创建时,设置了布局并初始化了按钮、编辑文本和网页视图。
    • 为按钮设置了点击监听器。当按钮被点击时,会从编辑文本获取网址,添加 "https://" 前缀,并在网页视图中加载该网址。
    • 启用了网页视图的 JavaScript 支持,并设置了一个自定义的 WebViewClient 以在应用内处理网页导航。
  • 处理返回键:

    • 覆写了 onBackPressed 方法。当用户按下返回键时,如果网页视图可以后退,则网页视图会后退一页。如果不能后退,活动会按照正常的行为(通常是关闭或后退到上一个活动)。
  • 网页视图(WebView)的使用:

    • 通过 loadUrl 方法加载用户输入的网址。
    • 通过设置 WebViewClient,网页加载和导航操作都在应用内部完成,而不是打开外部浏览器。

这里就以BlogLPrincessROE21005为例

提供了一个简单的WebView来加载和显示博客 "https://blog.lprincess.top"。通过重写 onBackPressed(),它优化了用户的浏览体验,使用户能够在WebView内部导航网页历史,而不是直接退出。通过激活JavaScript支持,确保了网页内容能够正确加载和展示。

  • 导入和类定义

    • BlogLPrincessROE21005 类继承自 Activity,表示它是一个可以在Android应用中运行的独立界面。
  • 成员变量

    • WebView weblimengjue;: 声明了一个WebView对象,用于显示网页内容。
  • onCreate(Bundle savedInstanceState) 方法

    • setContentView(R.layout.activity_blog_roe21005);: 指定了该Activity使用的布局文件是 activity_blog_roe21005.xml
    • weblimengjue = findViewById(R.id.web_view);: 初始化了 weblimengjue对象,将其与布局文件中的WebView组件关联。
    • weblimengjue.loadUrl(url);: 加载了一个指定的URL地址,这里是 "https://blog.lprincess.top",这个网址代表着博客主页。
    • weblimengjue.getSettings().setJavaScriptEnabled(true);: 启用WebView的JavaScript支持。许多网站为了实现动态功能依赖于JavaScript,开启这个设置可以确保这些功能的正常运行。
    • weblimengjue.setWebViewClient(new WebViewClient());: 设置了一个WebViewClient 给 weblimengjue。这允许在 weblimengjue 内部处理各种通知和请求,而不是打开浏览器应用。
  • onBackPressed() 方法

    • 这个方法重写了Activity的 onBackPressed(),提供了自定义的返回键行为。它首先检查 weblimengjue 是否可以回退到之前的页面(即WebView内的历史记录),如果可以,就回退一个历史记录;如果不可以(也就是说已经是第一个页面或 weblimengjue 为null),就执行默认的返回键行为(退出当前Activity)。

6.动态图表统计

首先是需要使用MPAndroidChart库,需要在gradle里添加依赖,如果是直接在项目级和模板级修改添加依赖,会报一个 "Build was configured to prefer settings repositories over project repositories"错误。问题似乎在于项目配置中使用了一种特定的依赖解析方式,与添加的仓库设置冲突。这里的错误信息指出项目被配置为优先使用 settings 文件中的仓库设置,而不是项目级 build.gradle.kts 文件中的设置。

查阅了官方版本文档,从 Gradle 6.8 开始,引入了一种新的依赖解析行为,允许在 settings.gradle 或 settings.gradle.kts 文件中定义全局仓库。这意味着如果您的项目使用这种设置方式,那么所有仓库的配置应该放在 settings.gradle 或 settings.gradle.kts 文件中。

因此解决这个问题可以:

  • 修改settings.gradle.kts文件:添加JitPack仓库到全局仓库列表
pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
        maven(url = "https://jitpack.io")
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
    repositories {
        google()
        mavenCentral()
        maven(url = "https://jitpack.io")
    }
}

  • 然后再模块级build.gradle.kts文件中添加MPAdroidChart依赖
dependencies {
    implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
}

最后再重新Sync更新同步gradel文件,这里使用饼图作为示例,

 <com.github.mikephil.charting.charts.PieChart
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:id="@+id/pieChart"
            android:layout_weight="1"/>

活动层里设置饼图相关参数:

    private void setupPieChart(){
        pieChartlimengjue.setUsePercentValues(true);
        pieChartlimengjue.getDescription().setEnabled(false);
        pieChartlimengjue.setExtraOffsets(5,10,5,5);
        pieChartlimengjue.setDragDecelerationFrictionCoef(0.95f);

        pieChartlimengjue.setDrawHoleEnabled(true);
        pieChartlimengjue.setHoleColor(android.R.color.white);
        pieChartlimengjue.setTransparentCircleRadius(61f);
    }

最后从数据库中加载各个特长的点赞数,获取数据。

7.建议页面

是一个允许用户提交建议和联系信息的界面。它接收用户输入,对输入进行基本校验,然后将数据保存到数据库,并提供反馈。通过使用Toast进行反馈,用户可以直观地知道他们的操作结果。这个Activity通过简单的UI组件和数据库交互来实现基本的用户反馈功能。

设计思路:新增一个保存建议的数据库,包含联系信息和建议内容。然后再设计一个按键,在活动中当监听到按钮被触发,就获取编辑框内的内容,保存到指定表数据库中。

  • 导入和类定义

    • SuggestionROE21005 类继承自 AppCompatActivity,表示它是一个可以展示更复杂内容和操作的Activity。
  • 成员变量

    • EditText edsuggestiontestlimengjue, edcontactInfolimengjue;: 两个EditText对象,分别用于输入建议内容和联系信息。
    • Button btnSubmitlimengjue;: 一个按钮对象,用于触发提交建议的操作。
    • MysqlROE21005 dblimengjue;: 用于与数据库交互,可能用来存储用户提交的建议。
  • onCreate(Bundle savedInstanceState) 方法

    • setContentView(R.layout.activity_suggestion_roe21005);: 设置使用的布局文件是 activity_suggestion_roe21005.xml。
    • 初始化 edsuggestiontestlimengjue, edcontactInfolimengjue, 和 btnSubmitlimengjue,将它们分别与布局文件中的相应组件连接。
    • 实例化 MysqlROE21005 对象 dblimengjue,用于后续数据库操作。
    • 为 btnSubmitlimengjue 设置点击监听器,当按钮被点击时执行 saveSuggestion() 方法。
  • saveSuggestion() 方法

    • 从两个EditText中获取文本内容,并去除字符串两端的空白。
    • 检查用户是否填写了建议和联系方式。如果两者都不为空,调用 dblimengjue.addSuggestion(suggestionlimengjue, contactInfolimengjue) 方法保存用户的建议,并显示一条提交成功的Toast通知。然后清空两个EditText以便下一次输入。
    • 如果用户未填写建议或联系方式,则显示一条提示信息的Toast。

处理报错信息或问题:

报错1

An issue was found when checking AAR metadata:
 
  1.  Dependency 'androidx.activity:activity:1.8.0' requires libraries and applications that depend on it to compile against version 34 or later of the Android APIs.
 
      :app is currently compiled against android-33.
 
      Also, the maximum recommended compile SDK version for Android Gradle
      plugin 7.4.2 is 33.
 
      Recommended action: Update this project's version of the Android Gradle
      plugin to one that supports 34, then update this project to use
      compileSdkVerion of at least 34.
      Note that updating a library or application's compileSdkVersion (which
      allows newer APIs to be used) can be done separately from updating
      targetSdkVersion (which opts the app in to new runtime behavior) and
      minSdkVersion (which determines which devices the app can be installed
      on).

查资料,分析:

Dependency 'androidx.activity:activity:1.8.0' requires libraries and applications that depend on it to 「compile against version 34 or later of the Android APIs」。

从这段信息告诉我们,升级SDK这个是必须的,除非你不用。

  • 解决办法:
    • 1.升级SDK
      • 将build.gradle里的compileSdk=33改成34,再把targetSdk改成34,这一步我会报错,因此尝试第二种方法
    • 2.降低版本
      • 在material里面,修改:
      dependencies {
      //将1.10.0改为1.8.0即可
      //implementation "com.google.android.material:material:1.10.0"
      implementation "com.google.android.material:material:1.8.0"
      }
      

报错2:name<EditText>:No speakable text present

解决方法:

tools:ignore="LabelFor,TextFields,SpeakableTextPresentCheck"

报错3:

触摸目标尺寸太小name\<EditText\>:Touch target size too small

解决方法:

android:layout_height="wrap_content"改成android:layout_height="48dp"

问题1:主页面无法跳转到Register页面

Manifest 声明:验证 Register 是否在您的 AndroidManifest.xml 文件中声明。如果它是一个活动,它应该像这样在 <application> 标签内声明:

因此解决办法:在Mainifest里添加

<activity android:name=".Register"/>

问题2:项目构建成功后,Run按钮是灰色的,并提示Add Comfiguration:

做一次Gradle同步:File -> Sync Project with Gradle Files

问题3:win10可以运行,更新win11后打开项目无法运行

  • 尝试1:做一次Gradle同步
  • 尝试2:Win11系统新增功能——安卓子系统,可以让用户不通过安卓模拟器就能安装apk,非常方便。但是安卓子系统导致安卓模拟器无法启动,这该怎么办?

    • 解决办法:1.在powershell 管理员执行以下命令可以关闭、开启Hyper-V虚拟化,再重启系统生效。
    关闭:bcdedit /set hypervisorlaunchtype off
    

问题4:Run configuration app is not supported in the current project. Cannot obtain the application ID.——用重启解决

有时,简单的重启 IDE 或你的计算机可以解决一些莫名其妙的问题。

参考资料:

  • https://support.google.com/accessibility/android/answer/7158690

  • https://support.google.com/accessibility/android/answer/7158690

  • https://blog.csdn.net/qq_51669241/article/details/122779418

  • https://developer.android.com/reference/android/content/SharedPreferences

  • https://blog.csdn.net/qq_45013261/article/details/117595619

  • https://blog.csdn.net/gyongjia/article/details/89842454

  • https://blog.csdn.net/lyy666888/article/details/79163632

  • https://blog.csdn.net/weixin_33340636/article/details/112085387#

  • https://blog.csdn.net/gyongjia/article/details

  • https://blog.csdn.net/xitongzhijianet/article/details/128969351

  • https://blog.csdn.net/xitongzhijianet/article/details/128969351

  • https://developer.android.google.cn/studio/run/emulator-install-add-files?hl=zh-cn

上次编辑于:
贡献者: L-mj0