《Android App开发入门与项目实战》学习与实战- 改进8.5实战项目:记账本,解决BUG

我因为要开发与工作相关的安卓app,而开始接触Java语言和Android Studio。为了更好的学习,我购买了欧阳燊先生编著的《Android App开发入门与项目实战》,这本书给了我很大的帮助,让我的app开发能够顺利进行。

最近,我正在给我的app设计数据库的记录和展示功能,在翻阅《Android App开发入门与项目实战》时,发现第八章的实战项目-记账本 实现了用数据库保存输入的记录,并通过ViewPager与  Fragment组合对记录进行展示,正符合我的项目需求。于是我便仔细研究其这个Module的代码来,待研究透了,再着手进行自己app的相关设计。

在使用教程提供的代码在手机上进行测试的过程中,我发现了一个问题。

问题描述

在做【添加账单】功能的测试时,我分别在2022年10月和2023年10月都保存了支出数据,在【账单列表】 模块里,进入时的默认月份是当前月份(2023年10月),app中自动加载的是2023年10月的记录,左右滑动则展示2023年其它月份的记录,这是没问题的。

但是当我将月份改成2022年10月份以后,问题就出现了。

选择完日期后,fragment中的的记录竟然还是2023年10月份的,左右滑动,发现其它月份的记录也是2023年的,并不是我希望看到的2022年的记录。这显然不是我想要的结果。也说明教材源码是存在BUG的。

我如果照着源码开发自己的app,显然也会出现同样的问题,必须要找到解决办法。

解决办法

通过对代码的细致研究,我感觉问题原因应该是在选择日期后,教材的源码没有对fragment的内容进行更新,导致展示出来的还是在其创建时候的内容。我要找到更新fragment内容的办法。

好在CSDN博客里有丰富的资料,通过搜索,我找到了两篇文章,讲到了相关的内容。

关于ViewPager清除已加载Fragment,重新创建新的Fragment的方法

Android ViewPager+Fragment 动态加载

通过研读这两篇文章,我总算搞清楚了问题原因,知道了解决方法。

我尝试着按这两篇文章的内容,对教材的源码进行了修改,经过测试,之前遇到的修改月份到不同年份,fragment的内容无变化的问题得到了解决。我的解决办法大致如下:

首先是对源码中的BillPagerAdpater.java(这是一个FragmentPagerAdapter)进行修改,添加clear方法和makeFragmentName方法。另外我还添加了自己设计的setYear方法,用于改变这个适配器里的私有变量mYear。

然后再对BillPagerActivity.java文件中的onDateSet方法进行修改,增加判断语句,在年份发生变化时,调用setYear方法变更适配器里的年份,使用clear方法清除掉fragment中的内容,再通过对ViewPager使用setAdapter方法,让Ffragment重新生成新的内容。

修改后的代码

为了能让大家更好的理解,我将修改后的BillPagerAdpater.javaBillPagerActivity.java贴出来。

BillPagerAdpater.java

package com.example.chapter08.adapter;

import android.view.ViewGroup;

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentTransaction;

import com.example.chapter08.fragment.BillFragment;

public class BillPagerAdpater extends FragmentPagerAdapter {
    private int mYear; // 声明当前账单所处的年份
    private final FragmentManager mFragmentManager;  // 添加的内容
    private FragmentTransaction mCurTransaction;  // 添加的内容


    // 碎片页适配器的构造方法
    public BillPagerAdpater(FragmentManager fm, int year) {
        super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        mFragmentManager = fm;   // 添加的内容
        mYear = year;
    }

    // 获取碎片Fragment的个数,一年有12个月
    public int getCount() {
        return 12;
    }

    // 获取指定月份的碎片Fragment
    public Fragment getItem(int position) {
        return BillFragment.newInstance(mYear*100 + (position + 1));
    }

    // 获得指定月份的标题文本
    public CharSequence getPageTitle(int position) {
        return (position + 1) + "月份";
    }


    // setYear方法、clear方法、makeFragmentName方法都是添加的内容
    // 重新设置年份
    public void setYear(int year) {
        mYear = year;
    }
    
    /**
     * 清除缓存fragment
     * @param container ViewPager
     */
    public void clear(ViewGroup container){
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }
        // 根据源码对for语句进行了修改,将mFragmentArrayList.size()改成了12
        // 不改的话允许时会出错,这个项目中固定了fragment的数量为12
        for(int i=0;i<12;i++){  
            long itemId = this.getItemId(i);
            String name = makeFragmentName(container.getId(), itemId);
            Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
            if(fragment != null){//根据对应的ID,找到fragment,删除
                mCurTransaction.remove(fragment);
            }
        }
        mCurTransaction.commitNowAllowingStateLoss();
    }

    /**
     * 等同于FragmentPagerAdapter的makeFragmentName方法,
     * 由于父类的该方法是私有的,所以在此重新定义
     * @param viewId
     * @param id
     * @return
     */
    private static String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
    }


}

BillPagerActivity.java

package com.example.chapter08;

import android.app.DatePickerDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
import android.widget.DatePicker;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.PagerTabStrip;
import androidx.viewpager.widget.ViewPager;

import com.example.chapter08.adapter.BillPagerAdpater;
import com.example.chapter08.util.DateUtil;

import java.util.Calendar;

public class BillPagerActivity extends AppCompatActivity implements
        View.OnClickListener, DatePickerDialog.OnDateSetListener, ViewPager.OnPageChangeListener {
    private TextView tv_month;
    private ViewPager vp_bill; // 声明一个翻页视图对象
    private Calendar calendar = Calendar.getInstance(); // 获取日历实例,里面包含了当前的年月日
    BillPagerAdpater adapter;  // 源码是局部变量,我改成了全局变量
    private int mYear;  // 我添加的,保存年份,在修改日期后会用到这个变量

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bill_pager);
        TextView tv_title = findViewById(R.id.tv_title);
        TextView tv_option = findViewById(R.id.tv_option);
        tv_month = findViewById(R.id.tv_month);
        tv_title.setText("账单列表");
        tv_option.setText("添加账单");
        findViewById(R.id.iv_back).setOnClickListener(this);
        tv_option.setOnClickListener(this);
        tv_month.setOnClickListener(this);
        tv_month.setText(DateUtil.getMonth(calendar));
        // 从布局视图中获取名叫vp_bill的翻页视图
        vp_bill = findViewById(R.id.vp_bill);
        initViewPager(); // 初始化翻页视图
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.iv_back) {
            finish(); // 关闭当前页面
        } else if (v.getId() == R.id.tv_option) {
            Intent intent = new Intent(this, BillAddActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 设置启动标志
            startActivity(intent); // 跳到账单填写页面
        } else if (v.getId() == R.id.tv_month) {
            // 构建一个日期对话框,该对话框已经集成了日期选择器。
            // DatePickerDialog的第二个构造参数指定了日期监听器
            DatePickerDialog dialog = new DatePickerDialog(this, this,
                    calendar.get(Calendar.YEAR), // 年份
                    calendar.get(Calendar.MONTH), // 月份
                    calendar.get(Calendar.DAY_OF_MONTH)); // 日子
            dialog.show(); // 显示日期选择对话框
        }
    }

    @Override
    public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month);
        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        
        // 以下是添加的内容
        int selectedYear = calendar.get(Calendar.YEAR);
        if (selectedYear != mYear) {
            mYear = selectedYear;  // 更新全局变量,用于后续的年份判断
            adapter.setYear(mYear);  // 更新fragment的mYear数据
            adapter.clear(vp_bill);  // 清除已有的fragment
            vp_bill.setAdapter(adapter); // 设置翻页视图的适配器
            vp_bill.setCurrentItem(calendar.get(Calendar.MONTH)); // 设置翻页视图显示第几页
            vp_bill.addOnPageChangeListener(this); // 给翻页视图添加页面变更监听器
        }

        tv_month.setText(DateUtil.getMonth(calendar));
        vp_bill.setCurrentItem(month); // 设置翻页视图显示第几页
    }

    // 初始化翻页视图
    private void initViewPager() {
        // 从布局视图中获取名叫pts_bill的翻页标签栏
        PagerTabStrip pts_bill = findViewById(R.id.pts_bill);
        // 设置翻页标签栏的文本大小
        pts_bill.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);
        // 构建一个商品图片的翻页适配器
        adapter = new BillPagerAdpater(getSupportFragmentManager(), calendar.get(Calendar.YEAR));
        vp_bill.setAdapter(adapter); // 设置翻页视图的适配器
        vp_bill.setCurrentItem(calendar.get(Calendar.MONTH)); // 设置翻页视图显示第几页
        vp_bill.addOnPageChangeListener(this); // 给翻页视图添加页面变更监听器
    }

    // 翻页状态改变时触发
    public void onPageScrollStateChanged(int state) {}

    // 在翻页过程中触发
    public void onPageScrolled(int position, float ratio, int offset) {}

    // 在翻页结束后触发
    public void onPageSelected(int position) {
        calendar.set(Calendar.MONTH, position);
        tv_month.setText(DateUtil.getMonth(calendar));
    }
}

改进了教材源代码后,我的app的也顺利完成了数据记录的保存和展示功能,经过测试,没有出现上述的BUG。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Android应用程序开发入门学习如何使用Android操作系统和开发工具创建应用程序的过程。项目实战是将所学知识应用于实际项目中,以实现特定的功能和目标。通过学习Android应用程序开发入门项目实战,您可以掌握如何创建高质量的Android应用程序,并为您的职业生涯打下坚实的基础。 ### 回答2: 随着智能手机的普及和移动互联网的发展,Android操作系统成为了手机操作系统的霸主。而随之而来的是在Android平台上的应用程序繁荣发展。为了适应这一趋势,许多人都纷纷学习开发Android应用程序。本文将从基础入手,分享一下自己对于Android app开发入门项目实战的经验。 一、Android入门基础 1.1 安装Android Studio Android Studio是一个由Google推出的完整集成开发环境(IDE),供开发者建立Android应用程序。在建立Android开发环境之前,需要先去官网下载并安装Java SDK和Android Studio开发环境。 1.2 学习Java编程 JavaAndroid应用程序的核心编程语言。如果之前没有Java编程经验,那么需要先从Java的基础语法和面向对象编程(OOP)基础开始学习。 1.3 学习Android应用架构 Android应用程序采用MVC、MVP、MVVM等模式,这些模式都有其自己的优缺点。在开发Android应用程序之前,需要了解这些模式之间的区别和比较,自己根据应用场景选择合适的应用程序架构。 1.4 理解Android组件之间的交互 Android应用程序采用组件化的架构,不同的组件之间通过Intent和广播来实现数据和事件的传递。在编写Android应用程序之前,需要理解Activity、Service、BroadcastReceiver等组件之间的交互。 二、Android应用程序开发项目实战 2.1 了解Android应用程序开发项目的流程 从需求分析到应用程序发布,应用程序开发都需要各种环节、各种工具和技能来执行。了解这些应用程序开发流程,能够更加顺利地开发Android应用程序。 2.2 开发Android应用程序界面 Android应用程序的主要部分是界面,因此Android应用程序的界面设计十分重要。Android应用程序界面开发需要设计好自定义控件,采用不同的布局和各种皮肤和风格。 2.3 开发Android应用程序逻辑 Android应用程序逻辑包括了UI与数据之间的交互,数据的更新与处理,网络和IO的操作,多线程的使用等。Android应用程序的逻辑处理,必须严谨、细致。 2.4 Android应用程序调试 Android应用程序的调试是非常重要的,它可以快速改正错误,并且保证应用程序的性能。通过使用Android Studio、logcat等调试工具,开发人员能够快速的跟踪应用程序运行过程中出现的问题。 Android app开发入门项目实战需要开发人员具备扎实的Java基础和Android编程基础。掌握Android架构、组件之间的交互、界面和逻辑开发和调试等技能,才能在Android平台开发出符合要求的高质量应用程序。同时,开发者还可以通过查阅相关技术资料,参加Android学习班、实践等方式,不断地提升自身的技能水平。 ### 回答3: 随着移动智能设备的逐渐普及,人们越来越重视移动应用程序的开发和运用。目前,Android操作系统在全球市场占据优势地位,也成为了广大开发者的首选平台之一。因此,学习 Android app 的开发实战,具有非常重要的现实意义和市场价值。 一、 开发入门 1. 编程语言:JavaAndroid app 的主流编程语言,因此学习Java入门的前提。 2. 开发工具:Android Studio是官方推荐的集成开发环境,它具有丰富的功能和易用性,是Android app 开发的主要开发工具。 3. 开发框架:Android app 的开发离不开开发框架,常用的开发框架有ButterKnife、Glide、Retrofit等,它们可以极大地提高开发效率和代码质量。 二、 项目实战 1. 构建项目:在Android Studio中创建新项目,选择适合自己的项目模板。其中包括了评级、价格、分类等元素。 2. 设计界面:利用界面编辑器创建应用UI界面,这里主要有线性布局、相对布局和约束布局等,根据实际情况选择。 3. 添加组件:添加各种页面元素,例如文本框、按钮、图片等等。 4. 实现功能:在Java代码中实现各种功能,例如响应用户输入的事件,数据网络请求,并对数据进行处理等等。 5. 调试项目:在Android Studio中进行真机测试和模拟测试,检查程序运行状态和效果。 6. 发布应用:实现应用的打包、签名和发布,将应用提交到应用市场等平台。 总之,学习Android app 开发需要掌握Java编程语言、Android开发工具、Android开发框架、UI设计、功能实现和调试等技能,还需要进行实战项目的锻炼,从而获得更多的开发经验和技巧。只有不断地学习和实践,才能够成为一名优秀的Android app 开发者。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

武陵悭臾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值