开发进展

  • 2020.7.1 开始页面
    FrameLayout:下层ImageView,纵向不够长,所以用了这三句代码的组合

     android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"
    

    使得图片以中心为基准不变形放大,直到纵向充满屏幕,横向左右两边超出屏幕部分被裁掉。
    上层放置了一个ProcessBar组件,后台运行一个副线程用于加载文件(但现在还没有可加载的东西,所以暂时用随机数代替)。Handler对象负责接收副线程发来的消息以更新进度,并且判断加载是否完成:

     mHandler=new Handler(){@Overridepublic void handleMessage(@NonNull Message msg) {if(msg.what==0x111){progressBar.setProgress(mProgress);}else{Toast.makeText(MainActivity.this,"耗时操作已完成",Toast.LENGTH_SHORT).show();progressBar.setVisibility(View.GONE);Intent intent=new Intent(MainActivity.this,Moment.class);startActivity(intent);}}};
    

    效果:

  • 2020.7.4 底部导航栏
    创建一个新的Activity。方便起见,我使用了Android自带的Activity模板:
    创建完成后,可以看到Project中多出这些文件:

    *说明:其实Android自带的底部导航栏Activity只有三栏,但由于我的项目需要四栏,所以上图中与“my”相关的文件是我自己添加的。特别注意的是,添加时不是只复制粘贴就完事的,不仅需要增添文件,还要修改很多文件内部的东西。由于我编写之前没有截图,所以下文中就假装Android自带四栏,我主要是要讲明白这几个文件之间的关系时怎样的。
    这些文件中,我比较熟悉的是Java文件Moment,layout包中xml文件activity_moment.xml。在activity_moment.xml文件中可以看到两个组件:一个是BottomNavigationView,一个是fragment。其中,底部导航栏中 app:menu="@menu/bottom_nav_menu"说明控制底部导航栏的代码在bottom_nav_menu文件中体现;fragment中app:navGraph="@navigation/mobile_navigation"则将fragment相关代码指向mobile_navigation文件。
    进入bottom_nav_menu文件,看到了四个相似的item:

     <itemandroid:id="@+id/navigation_home"android:icon="@drawable/moment"android:title="@string/title_home" />
    

    从icon的设定可以确定,这是在设定底部导航栏每一个按钮的属性。所以我找了适合我的项目的图标,替换了原有的icon资源文件,并在value包中找到strings,更改了按钮的文字信息。
    然后进入mobile_navigation文件,看到了四个相似的fragment:

     <fragmentandroid:id="@+id/navigation_home"android:name="com.example.a101.ui.home.HomeFragment"android:label="@string/title_home"tools:layout="@layout/fragment_home" />
    

    其中,layout指向了一个布局文件。进入该文件,看到它的全局设定中有一行:tools:context=".ui.home.HomeFragment"。打开home包,有两个Java文件,其中HomeFragment是Fragment的子类,HomeViewModel是ViewModel的子类。HomeFragment中onCreateView方法中homeViewModel=ViewModelProviders.of(this).get(HomeViewModel.class);将二者联系起来:ViewModelProviders的of(this)方法为当前fragment创建一个ViewModelProvider,ViewModelProvider的get(Class modelClass)为这个ViewModelProvider获取或创建一个与之相连的ViewModel,而homeViewModel就是ViewModel子类的实例对象。ViewModel的用处是获取并储存一个Activity或Fragment的有用数据。当这个Activity或Fragment由于构造发生变化而销毁的时候ViewModel可以留存下来;另外,ViewModel可以用于Activity中多个Fragment的数据共享。inflater的作用是从资源文件中找到特定的布局文件,然后就可以用熟悉的findViewById找想要的组件了。后面用到的LiveData、MutableLiveData、observer我实在没搞懂,暂时放在这里了。
    效果:

  • 2020.7.6 设置ActionBar
    ActionBar是这个东西:

    从Android3.0及之后的版本中,ActionBar都是自带的,就是说你完全不用敲相关的代码,Activity运行的时候它也会出现的。不过,Android自带的ActionBar就像上图所示,单单一个标题,什么组件也没有。然而我们经常需要在ActionBar中放一些按钮,比如朋友圈分享的小相机、返回按钮、筛选按钮等等,来满足更多需求,这就需要我们自定义ActionBar了。
    给Activity自定义ActionBar,需要三个步骤:
    ① 上网找到合适的图标;
    ② 在res中创建一个menu包,里面创建一个xml类型的菜单文件,并添加一个item组件,在组件中设置图标、标题、位置等:

      <?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><item android:id="@+id/share"android:icon="@drawable/share_white"android:title="share"app:showAsAction="always"></item>
    

    ③ 在Activity的Java文件中解析菜单文件:

      @Overridepublic boolean onCreateOptionsMenu(Menu menu) {MenuInflater inflater=getMenuInflater(); //实例化一个MenuInflater对象inflater.inflate(R.menu.menu,menu); //解析菜单文件return super.onCreateOptionsMenu(menu);}
    

    运行起来,确实在ActionBar中出现了用于分享的小图标。然而,对于每一个fragment,它们ActionBar中的需要满足不同的功能,可是上述做法却给所有fragment的ActionBar添加了分享图标。

    究其原因,我们刚才是在Activity的Java文件中解析的菜单文件,而Activity的ActionBar是这个Activity下所有fragment共有的。
    那么就需要找到一个在fragment自己的Java文件中解析菜单文件的方法:
    ① 保持菜单文件不变,注释掉刚刚在Activity的Java文件中添加的代码;
    ② 进入ui包中某一fragment的包中XxxxFragment文件,在onCreateView方法中添加一句setHasOptionsMenu(true);
    ③ 在onCreateView后面(前面也行)添加onCreateOptionsMenu方法:

      @Overridepublic void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {//menu.clear();//不清空就会变成添加进来而不是替换inflater.inflate(R.menu.menu_home, menu);super.onCreateOptionsMenu(menu, inflater);}
    

    说明一下,被注释掉的menu.clear()的作用是在Activity共有的ActionBar中已有组件时为当前fragment清除组件。但由于我前面已经删掉了Activity源码中的解析菜单文件的代码,所以这句话就没必要了。
    效果:

    另外,我们还可以在fragment的源码中用onOptionItemSelected方法给按钮加动作:

      @Overridepublic boolean onOptionsItemSelected(@NonNull MenuItem item) {Toast.makeText(getActivity(),"okk",Toast.LENGTH_SHORT).show();return super.onOptionsItemSelected(item);}
    

    效果就是点击后出现提示“okk”。

  • 2020.7.7 登录页面
    由于在校学生、教职工用学号/工号登录,而校友需要用手机号登录,所以登录页面选用Android自带的Tabbed Activity。默认状态如下:

    可以看到,不同tab下内容是不同的。我原本以为它需要创建两个fragment布局文件,却发现只有一个activity布局文件和一个fragment布局文件。这就很迷了,它怎么做到用一个fragment创建出不同内容的呢?我发现,在这个fragment布局文件中,只有TextView组件,而没有为其添加内容。事实上,文本内容是在PlaceholderFragment类的onCreateView方法中动态添加的:

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_tabbed_activity_test, container, false);
    final TextView textView = root.findViewById(R.id.section_label);
    pageViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {@Overridepublic void onChanged(@Nullable String s) {textView.setText(s);}
    });
    System.out.println("in PlaceholderFragment.onCreateView");
    return root;
    }
    

    但这里面也没有体现文本内容,真正的文本由pageViewModel的getText()方法提供。于是打开PageViewModel文件,发现getText()的返回值mText这样定义的:

    private LiveData<String> mText = Transformations.map(mIndex, new Function<Integer, String>() {
    @Override
    public String apply(Integer input) {System.out.println("in PageViewModel.apply:input="+input);return "Hello world from section: " + input;
    }
    });
    

    由于没学过Function接口,这代码看的我一脸问号,赶紧去补课函数式编程。看到什么是函数式编程思维?的回答中提到“函数式编程关心数据的映射,命令式编程关心解决问题的步骤”,我似懂非懂;又看了函数式编程入门教程,大概理解了前面回答的意思。我们之前学的Java方法其实不算是“很纯”的函数,比起数值的输入输出,我们其实更关心这个方法“能完成什么工作”。具体一些讲,我们允许有不需要传参的方法,有不需要返回值的方法,这就和狭义的“函数”概念不同;除了处理参数,方法还可以完成许多额外的工作;而且对于静态方法,参数往往参与构建逻辑,而不是作为自变量存在。但函数式编程中的方法,一定是”很纯“的函数,就像知乎回答中说的“关心映射”;或者就可以把它理解为一个运算符。
    回到这段代码,这个 new Function<Integer, String>() {…}就是定义了一个从整型到字符串类型的映射,对于每一个输入的整型n,输出“Hello world from section:n”。
    了解了内容切换的原理,我就先在fragment的布局文件中写出登录界面大致的样貌,然后如法炮制,让它根据tab在登录方式提示字中呈现“学号/工号登录”或“手机号登录”:

    private String[] accountLabel={"学号/工号登录:","手机号登录:"};
    private MutableLiveData<Integer> mIndex = new MutableLiveData<>();
    private LiveData<String> mText = Transformations.map(mIndex, new Function<Integer, String>() {
    @Override
    public String apply(Integer input) {System.out.println("in apply:input="+input);return accountLabel[input-1];//不-1就会数组越界
    }
    });
    

    最后效果如下:

  • 2020.7.8 注册页面
    四个页面,调布局调的好辛苦/doge
    Android默认布局方式ConstraintLayout叫做约束布局,依靠组件间的相对位置关系确定组件位置。在design视图中可以直接通过拖拉组件直接进行排版的,此时code里面组件首行红线报错“This view is not constrained. It only has designtime positions, so it will jump to (0,0) at runtime unless you add the constraints ”,如果此时运行程序,就会看到所有组件堆在(0,0)位置。在design视图下排版完成后点击下图右侧小按钮,AS就自动生成相对位置关系(可以在code里面看到app:layout_constraintBottom_toBottomOf="@+id/nav_view"类似的描述),在design视图下也可以看到组件之间的箭头。这时候,拖动一个组件就会影响其它所有组件的位置。这时如果想只改变某一组件位置,先点击下图左侧按钮,删除相对位置关系限制,再拖动组件。拖动完成后别忘了再点下图右侧按钮生成位置关系。

    ConstraintLayout在排版时候非常方便,但是,当组件比较多并且位置关系复杂的时候,ConstraintLayout可能带来灾难(试试就逝世的那种……所以,我在做组件比较多的页面的时候,绝对不选择ConstraintLayout!
    而且,所有布局都可能出现实际运行效果与design视图中看到的效果不一致,有时候是错位,有时候还出现像BottomNavigationView遮挡组件、顶部出现奇怪的白边之类意想不到的bug。这时候就需要调整组件位置(经常用到layout_margin、layout_padding属性),或排查代码,或增添语句。
    效果:

    现在“身份验证”只有固定的5道题,但连接数据库后要建立题库,动态生成题目。到时候题目还会更多样、更有趣。

  • 2020.7.10 动态页面
    动态页面的ActionBar和BottomNavigationBar都已经弄好了,需要做的就是中间显示内容的fragment。所以布局都是在fragment_home.xml中做的。
    我希望它呈现类似朋友圈的效果,层层分析下来,大概是这样一个结构:

    除了pictures采用了3列的GridLayout之外,其余都是横向或纵向的LinearLayout。
    写好后运行,发现顶端出现了奇怪的白边,高度似乎正好和ActionBar相同;而底部BottomNavigationView遮挡了最下方的内容。

    按照BottomNavigationActivity 出现顶部白色条框,底部遮挡内容–解决中的方案,在activity_moment.xml文件的如下位置删除android:paddingTop="?attr/actionBarSize"

    再在上述文件的 < fragment > 组件中增加android:layout_marginBottom="60dp"(60根据我的BottomNavigationView的高度设定的),顶部白条和导航栏遮挡组件的问题就基本解决了。
    然而,实际运行中,ScrollView滑到低的时候最下方组件总是有一点点显示不出来。我尝试在fragment_home.xml文件中< ScrollView >的LinearLayout布局中设置layout_marginBottom属性,但没有效果;给最下方组件设置layout_marginBottom属性也没用。最后,我给最下方组件设置了paddingBottom属性android:paddingBottom="10dp",运行时就显示完全了。
    效果图:

    特别说明,未来肯定需要通过数据库获取动态信息并呈现在页面中的。现在在布局文件中写死的布局,是为了给将来的动态布局确定参数、排个雷。

  • 2020.7.10 日志页面及子页面
    日志页面就是一个ScrollView里面LinearLayout套LinearLayout,没啥好说的。
    效果:

    通过日志页面的搜索功能,得到的某一(种)动物的专属日志。这里面设计了一个时间轴。Android时间轴(Timeline)效果的实现里面讲解非常详细,关键是作者放了代码,可以在自己的电脑上运行看效果。按照教程输入代码并运行,效果如下:

    我研究了一下文件之间的关系,然后修改了一下文字内容以及文字和点的颜色,再在manifests包的AndroidManifest.xml文件中设置ActionBar中显示的标题:android:label="羊驼xx日志"
    效果:

  • 2020.7.10 我的页面
    毫无压力就写出来了。本来中间想用ListView,但写起来好麻烦,也不太会,就用Button代替了,效果也很好。
    效果:

  • 2020.7.11 地图
    在“附近”模块中,我需要获得用户所在位置,以展示周围环境实景(要是实现不了就放地图好咯)。在b站教程Android开发从入门到精通(项目案例版)了解到百度地图提供免费的API,就按照教程去百度地图官网下载。这里面比较大的坑就是密钥获取。每个人AS配置的时候都不太一样,.android文件夹位置也不一样。我的Android Studio下载的比较完整,用到的sdk、gradle什么的都是AS自带的。这种情况下,.android文件是在C盘User下的20190文件夹中找到的,和.Android Studio4.0以及Android Studio Project在一起;而与Android文件夹相距甚远。进入.android文件夹,复制路径,在Terminal中进入该目录下,并输入keytool -list -v -keystore debug.keystore命令即可获得密钥。
    由于b站上这个教程是2016年的,现在4年过去了,官网的网页排版、API版本以及AS提供的功能都有所变化,所以成功下载压缩包后就没有按照b站教程配置,而是查看了官方文档。配置jar包之类的事参见《开发指南》下Android Studio配置文档;创建地图应用时看显示地图文档。
    然而,官方提供的demo是在Activity中添加地图应用,而我需要在fragment中显示地图。尽管二者结构基本相同,但有的语法并不一样,导致程序无法运行。
    起先,我试图将地图初始化的工作放到fragment所在的Activity中,和demo统一战线。结果,程序一进入这个Activity就闪退,其它的fragment也看不了,并且在根据id获取地图组件的时候报空指针异常。没办法,我还是把初始化搬运到地图组件所在的fragment的Java文件中。但在DashboardFragment的onCreateView方法中,SDKInitializer.initialize(getApplicationContext());显示无此函数,上网搜解决方案,发现fragment的上下文对象需要先getActivity,所以上述代码改为SDKInitializer.initialize(getActivity().getApplicationContext());就好了。可是运行起来还是在报错:Binary XML file line #9: Error inflating class com.baidu.mapapi.map.MapView。针对在Activity中添加地图应用的教程提供的解决方案说,onCreate方法中需要将SDKInitializer.initialize(getApplicationContext());放在setContentView(R.layout.activity_main);前面。在onCreateView方法中并没有setContentView(R.layout.activity_main);,但有一句View root = inflater.inflate(R.layout.fragment_dashboard, container, false);是用来将Java文件与layout文件关联的,于是我把初始化代码移到了这句代码之前,再运行,就成功显示地图啦!效果如下:

Android实战【可可爱爱一零一动植物志】(开发)相关推荐

  1. SLAM导航机器人零基础实战系列:(五)树莓派3开发环境搭建——5.Android手机端与robot端ROS网络通信...

    SLAM导航机器人零基础实战系列:(五)树莓派3开发环境搭建--5.Android手机端与robot端ROS网络通信 摘要 通过前面一系列的铺垫,相信大家对整个miiboo机器人的DIY有了一个清晰整 ...

  2. python h5开发_从零搭建移动H5开发项目实战

    从零搭建移动H5开发项目实战 前端H5的前世今身 在Pc的时代,前端技术无疑统治了大多数用户的交互界面!而在移动为王的今天,NA开发在早期占领了大多数用户的交互界面,后来逐渐的前端H5开发找到了自己的 ...

  3. 从零搭建移动H5开发项目实战

    从零搭建移动H5开发项目实战 前端H5的前世今身 在Pc的时代,前端技术无疑统治了大多数用户的交互界面!而在移动为王的今天,NA开发在早期占领了大多数用户的交互界面,后来逐渐的前端H5开发找到了自己的 ...

  4. SLAM导航机器人零基础实战系列:(五)树莓派3开发环境搭建——2.安装ros-kinetic

    SLAM导航机器人零基础实战系列:(五)树莓派3开发环境搭建--2.安装ros-kinetic 摘要 通过前面一系列的铺垫,相信大家对整个miiboo机器人的DIY有了一个清晰整体的认识.接下来就正式 ...

  5. SLAM导航机器人零基础实战系列:(五)树莓派3开发环境搭建——1.安装系统ubuntu_mate_16.04...

    SLAM导航机器人零基础实战系列:(五)树莓派3开发环境搭建--1.安装系统ubuntu_mate_16.04 摘要 通过前面一系列的铺垫,相信大家对整个miiboo机器人的DIY有了一个清晰整体的认 ...

  6. 【项目实战课】从零掌握安卓端Pytorch原生深度学习模型部署

    欢迎大家来到我们的项目实战课,本期内容是<从零掌握安卓端Pytorch原生深度学习模型部署>.所谓项目课,就是以简单的原理回顾+详细的项目实战的模式,针对具体的某一个主题,进行代码级的实战 ...

  7. Android实战技巧之十一:Android Studio和Gradle

    2019独角兽企业重金招聘Python工程师标准>>> 经过两个多月的AS体验,我认为是时候将Android的开发环境迁移到AS上了.目前最新版本是1.0.2(大年30当天升级到1. ...

  8. 【Android实战】记录自学自己定义GifView过程,能同一时候支持gif和其它图片!【有用篇】...

    之前写了一篇博客.<[Android实战]记录自学自己定义GifView过程,具体解释属性那些事! [学习篇]> 关于自己定义GifView的,具体解说了学习过程及遇到的一些类的解释,然后 ...

  9. android listview 列加id,Android实战开发之ListView同一个item显示2列的实现方法

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Android实战开发中,ListView控件用途十分广泛,各种自定义控件多种多样.当项目要求实现一个2列的商品列表形式的界面,我们首先肯定想到用List ...

最新文章

  1. 16位代码段与32位代码段的区别
  2. 很好的分页实例代码(JSP)
  3. 从tcp到netty(一)
  4. JZOJ 5474. 【NOIP2017提高组正式赛】时间复杂度
  5. Array Splitting CodeForces - 1197C
  6. 如何通过query获得一个product的所有附件
  7. 表单必填标星_怎么用JS做form表单验证,要详细代码,求救!(带星号的是必填项)...
  8. 设置View的四个角为圆角
  9. zookeeper源码分析之leader选举
  10. Gradle下载安装 使用本地Maven仓库 IDEA2020.1配置Gradle
  11. 开发你的第一个BLE应用程序—Blinky
  12. Python数值分析案例01--------四阶龙格库塔法解抛体运动
  13. dns解析失败故障问题解决两例
  14. HBase【付诸实践 01】hbase shell 常用命令详解(表操作+数据增删改查+2种查询操作)(hbase-2.4.5 单机版standalone模式)
  15. config.py参数解释
  16. 关于NC6.X企业报表取不了数的问题及其解决方法。
  17. 赚钱游戏APP套路有哪些?
  18. Java实现 蓝桥杯 历届试题 带分数
  19. 结对项目之需求分析与原型设计(导师选择)
  20. 有80%的疾病都是由于不良生活习惯所导致的,常见8大不良习惯,既伤身又伤神

热门文章

  1. 怎么在线去除图片水印
  2. 论文笔记《Item-based Collaborative Filtering Recommendation Algorithms》基于物品的协同过滤算法
  3. [Xcode 实际操作]九、实用进阶-(24)使用Segue(页面的跳转连接)进行页面跳转并传递参数...
  4. http content-type常见文件格式类型
  5. 什么是白皮书?【理解较局限,仅个人学习记录】
  6. html入门学习笔记(2)(html整理)
  7. python时间序列如何拟合曲线_【Python】keras使用LSTM拟合曲线
  8. argument encoding=“UTF-8“ is ignored in MBCS locales
  9. 《软件体系结构》 第十章 软件产品线体系结构
  10. AUTOSAR-RS-BSWAndRTEFeatures(中文版)