当前位置: 移动技术网 > 移动技术>移动开发>Android > Android实战【可可爱爱一零一动植物志】(开发)

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

2020年07月08日  | 移动技术网移动技术  | 我要评论

开发进展

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

    效果:
    在这里插入图片描述

  • 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:

     <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我实在没搞懂,暂时放在这里了。
    效果:
    在这里插入图片描述

- 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>
</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添加了分享图标。
在这里插入图片描述
究其原因,我们刚才是在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源码中的解析菜单文件的代码,所以这句话就没必要了。
效果:
在这里插入图片描述
另外,我们还可以在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。默认状态如下:
在这里插入图片描述
可以看到,不同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就会数组越界
    }
    });

最后效果如下:
在这里插入图片描述

本文地址:https://blog.csdn.net/benzenene/article/details/107126595

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网