android开发分享Android实战【可可爱爱一零一动植物志】(开发)

开发进展2020.7.1 开始页面FrameLayout:下层ImageView,纵向不够长,所以用了这三句代码的组合 android:layout_width=”match_parent” android:layout_height=”match_parent” android:scaleType=”centerCrop”使得图片以中心为基准不变形放大,直到纵向充满屏幕,横向左右两边超出屏幕部分被裁掉。上层放置了一个ProcessBar组件,后台运行一个副线程用于加载文件(但现在还没有


开发进展

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

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

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

     mHandler=new Handler(){      @Override      public 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);          }      }  }; 

    效果:
    Android实战【可可爱爱一零一动植物志】(开发)

  • 2020.7.4 底部导航栏
    创建一个新的Activity。方便起见,我使用了Android自带的Activity模板:
    Android实战【可可爱爱一零一动植物志】(开发)创建完成后,可以看到Project中多出这些文件:
    Android实战【可可爱爱一零一动植物志】(开发)
    *说明:其实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:

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

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

     <fragment  android: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我实在没搞懂,暂时放在这里了。
    效果:
    Android实战【可可爱爱一零一动植物志】(开发)

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

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

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

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

运行起来,确实在ActionBar中出现了用于分享的小图标。然而,对于每一个fragment,它们ActionBar中的需要满足不同的功能,可是上述做法却给所有fragment的ActionBar添加了分享图标。
Android实战【可可爱爱一零一动植物志】(开发)
究其原因,我们刚才是在Activity的Java文件中解析的菜单文件,而Activity的ActionBar是这个Activity下所有fragment共有的。
那么就需要找到一个在fragment自己的Java文件中解析菜单文件的方法:
① 保持菜单文件不变,注释掉刚刚在Activity的Java文件中添加的代码;
② 进入ui包中某一fragment的包中XxxxFragment文件,在onCreateView方法中添加一句setHasOptionsMenu(true);
③ 在onCreateView后面(前面也行)添加onCreateOptionsMenu方法:

        @Override         public 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源码中的解析菜单文件的代码,所以这句话就没必要了。
效果:
Android实战【可可爱爱一零一动植物志】(开发)
另外,我们还可以在fragment的源码中用onOptionItemSelected方法给按钮加动作:

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

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

– 2020.7.7 登录页面
由于在校学生、教职工用学号/工号登录,而校友需要用手机号登录,所以登录页面选用Android自带的Tabbed Activity。默认状态如下:
Android实战【可可爱爱一零一动植物志】(开发)
可以看到,不同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>() {             @Override             public 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就会数组越界     }     }); 

最后效果如下:
Android实战【可可爱爱一零一动植物志】(开发)

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/addevelopment/897750.html

(0)
上一篇 2021年10月22日
下一篇 2021年10月22日

精彩推荐