实验目的:
- 复习事件处理。
- 学习Intent、Bundle在Activity跳转中的应用。
- 学习RecyclerView、ListView以及各类适配器的用法。
- 学习FloatingActionBar的用法。
去掉标题栏
现在来讨论去掉标题栏的问题
打开文件res/values/styles.xml,添加
1 | <style name="NoTitle" parent="Theme.AppCompat.Light.NoActionBar"> |
打开文件manifests/AndroidManifests.xml
把application里面的android:theme="@style/AppTheme"
改为android:theme="@style/NoTitle"
如果有多个Activity,但是只想去掉某个Activity的标题栏怎么办呢?
那就不要改application的属性,找到需要修改的activity,改成下面的样子
1 | <activity |
效果在后面有
RecyclerView
概述:
RecyclerView标准化了ViewHolder,而且异常的灵活,可以轻松实现ListView实现不了的样式和功能,通过布局管理器LayoutManager可控制Item的布局方式,通过设置Item操作动画自定义Item添加和删除的动画,通过设置Item之间的间隔样式,自定义间隔。
- 设置布局管理器以控制
Item的布局方式,横向、竖向以及瀑布流方式。 - 可设置
Item操作的动画(删除或者添加等) - 可设置
Item的间隔样式(可绘制)
但是关于Item的点击和长按事件,需要用户自己去实现
使用:
首先在build.gradle(Module:app)的dependencies处添加依赖
implementation 'com.android.support:recyclerview-v7:28.0.0'
这里的28.0.0根据
Android项目的sdk版本来选择
然后在res/layout创建一个RecyclerView的单项布局文件activity_single_recyclerview.xml
1 | <--file:activity_single_recyclerview.xml--> |
应该是这个样子

在java/com.example.yourname,比如我的是java/com.janking.sysuhealth
新建一个数据类Food.java,用来给RecyclerView填充数据
这里用了
implements Serializable,是为了方便后面传递Intent
1 | package com.janking.sysuhealth; |
接下来新建一个类MyAdapter.java,用来决定数据信息以及展示的UI
1 | package com.janking.sysuhealth; |
在Activity的xml文件里面添加RecyclerView的组件
1 | <android.support.v7.widget.RecyclerView |
在Activity的java文件里面添加对RecyclerView的解析
1 | private RecyclerView mRecyclerView; |
差不多就是这个样子
不过还没有没有添加FloatingActionBar

ListView
这个就要简单一点了,不过也得添加自己的Adapter
新建一个MyListViewAdapter.java
1 | package com.janking.sysuhealth; |
在Activity的xml文件里面添加ListView的组件
1 | <ListView |
在Activity的java文件里面添加对ListView的解析
1 | private ListView mListView; |
差不多就是这个样子啦

FloatingActionBar
仍然在build.gradle(Module:app)里面添加依赖
版本号记得根据自己项目的版本更改
1 | implementation 'com.android.support:design:28.0.0' |
在activity的xml布局文件中添加
1 | <android.support.design.widget.FloatingActionButton |
现在我们想把之前的RecyclerView和ListView都放在一个Activity里面,然后通过点击FloatingActionBar来切换,并且动态改变FloatingActionBar的图标
在Activity的java文件中添加
1 | private FloatingActionButton mButton; |
这样是通过更改RecyclerView和ListView的Visibility属性来切换两个列表的,并没有切换Activity,那怎么切换Activity呢?
切换Activity|Intent的传送和接收
这里讨论的不是没有灵魂的切换页面,而是要有数据的传递和同步,
不然的话下面一句代码就可以了
1 | startActivity(new Intent(MainActivity.this,SecondActivity.class)); |
这里说明我该篇博客使用的都是
SecondActivity,它是从MainActivity里跳转过来的
两个Activity之间传递数据,数据的附加有两种方式:
一种是 直接 intent.putxx();
另一种是 先bundle.putxx(), 然后再调用public Intent putExtras (Bundle extras) 添加bundle.
其实两种的本质是一样的!
对于即将跳转的页面SecondActivity
这里是使用Bundle,它可以通过调用
1 | public void putBoolean(String key, boolean value); |
等方法放入特定类型的数据,那我们怎么把自己定义的类放进去呢,所以前面我们定义数据类的时候加入了implements Serializable,然后就可以调用
1 | Bundle bundle=new Bundle(); |
然后把它放入Intent
1 | Intent intent=new Intent(SecondActivity.this, Detail.class); |
然后启动
1 | startActivityForResult(intent, 0);//这里采用startActivityForResult来做跳转 |
然后对于新页面:Detail,要怎么接收呢?
最好在OnCreate函数里加上下面代码
1 | private Food display_food; |
这样就可以接收到传过来的Food类数据了
那如果这个页面想再次回去刚刚的SecondActivity还要像它一样使用startActivityForResult吗?
不用!
我在这个Detail页面做了一个返回的ImageButton,然后给它添加事件
1 | //0 表示没有更改,1表示更改过了 |
发现没,这里新建了一个Intent,但是调用的是setResult,因为这次数据传送是作为上次别人传过来的一个结果,有来有回嘛!最后调用finish,数据就又回到之前的Activity了
还是要注意,这里使用了
setResult(1, intent); 或者 setResult(0, intent);
对于收到Detail回应的页面SecondActivity
要处理经过一个来回的数据改变,所以在这个Activity里要重写一个方法
1 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
requestCode:跟之前startActivityForResult过去的参数0一致,因为一个页面可能在不同情况会跳到不同页面,而只有一个处理Result的方法,通过这个参数确认是来自哪一个页面的回应
resultCode:这个其它页面给予回应时对结果的一个修饰,比如这里通过resultCode可以知道传过去的Food数据有没有被修改
RecyclerView和ListView的点击事件
准确的说分为点击和长按
这里实现的是点击跳转到新页面Detail,长按删除该元素
1 | //Click events for RecyclerView |
这里实现的是点击跳转到新页面Detail,长按跳出对话框是否删除该元素
1 | //Click events for ListView |
用java代码更改View背景颜色
1 | top_view = findViewById(R.id.top_view); |
注意我这里的Food类的Color成员是用String存储的,类似于"#BB4C3B",而不是十六进制数字!
PS:从之前的Adapter中的赋值也可以看的出来
更改RecyclerView动画
先在build.gradle里面添加依赖(记得改第二行版本号)
1 | implementation 'jp.wasabeef:recyclerview-animators:2.3.0' |
大概来说呢,就是有两种办法加动画
通过
setItemAnimator示例:
1
2
3
4//SecondActivity.java
//animation remove and add
SlideInRightAnimator animator = new SlideInRightAnimator(new OvershootInterpolator(1f));
mRecyclerView.setItemAnimator(animator);这个例子实现了添加和删除条目的动画
还有这些可以选择
Alpha
1
AlphaInAnimationAdapter
Scale
1
ScaleInAnimationAdapter
Slide
1
2SlideInBottomAnimationAdapter`
`SlideInRightAnimationAdapter`, `SlideInLeftAnimationAdapter通过
setAdapter示例:
1
2
3
4
5//SecondActivity.java
//animation general
SlideInRightAnimationAdapter slideInRightAnimationAdapter = new SlideInRightAnimationAdapter(mAdapter);
slideInRightAnimationAdapter.setFirstOnly(false);
mRecyclerView.setAdapter(new SlideInBottomAnimationAdapter(slideInRightAnimationAdapter));这个例子实现了滚动时的动画
还有这些可以选择
Cool
1
LandingAnimator
Scale
1
2ScaleInAnimator, ScaleInTopAnimator, ScaleInBottomAnimator
ScaleInLeftAnimator, ScaleInRightAnimatorFade
1
2FadeInAnimator, FadeInDownAnimator, FadeInUpAnimator
FadeInLeftAnimator, FadeInRightAnimatorFlip
1
2FlipInTopXAnimator, FlipInBottomXAnimator
FlipInLeftYAnimator,FlipInRightYAnimatorSlide
1
2SlideInLeftAnimator, SlideInRightAnimator, OvershootInLeftAnimator, OvershootInRightAnimator
SlideInUpAnimator, SlideInDownAnimator
我也不明白为什么它们为什么不能独自完成所有动画,看了下Github源码,其实感觉这个Animator功能都实现了,不过好在它们不冲突,所以我干脆两个都加上了

下面是动画效果:

该动画源码GitHub地址
整个项目效果图

设置View控件占据屏幕的比例
如何实现某个控件按照屏幕比例来布局(把Layout姑且也算为控件吧)?
思路一:通过Android已经拥有的方法或者Layout方式来布局
查了下Android的布局类型
- 线性布局(Linear Layout)
- 相对布局(Relative Layout)
- 表格布局(Table Layout)
- 网格视图(Grid View)
- 绝对布局(Absolute Layout)
- 标签布局(Tab Layout)
- 列表视图(List View)
暂时只发现线性布局有这个按照比例布局的功能,我试过,但是不好用,因为我现在的布局就是当时尝试的Linear Layout,然而我还是得尝试别的办法……
不好用不是说不能用,就是布局复杂起来就不太好弄,而且也才刚学,能力有限……
思路二:通过java代码动态获取屏幕的高度和宽度来设置控件的布局
话不多少,直接操作
在layout文件里找到需要改变布局宽度或者高度的控件添加id,我这里是想调整一整个RelativeLayout,这里的高度我是随便设置的,待会就是要改变它

在java代码里添加下列代码(如果是在Activity里最好写在Oncreate()里面)
1 | DisplayMetrics dm = getResources().getDisplayMetrics(); |
注:这个LayoutParams的方式设置的单位是像素即px,而不是dp不过我们通过getResources().getDisplayMetrics()得到的也是屏幕的宽度和高度的像素,按照比例来讲是没有问题,如果想要设置dp的话则需要将像素转换为像素密度了
如果有类名不识别,用
Ctrl+Enter提示
一些笔记
删除顺序
之前写了如下代码
1 | public void onItemLongClick(Viewview,intposition){ |
然后崩溃了,因为我在删除了postion位置的元素之后还使用mAdapter.getItem(position)去获取它,虽然不是一定出错,但是当postion位置的元素不存在时就访问了越界的List……
所以应该改为
1 | public void onItemLongClick(Viewview,intposition){ |
setText的类型
我想让列表左边的圆圈里显示类别的第一个字
1 | holder.mIcon.setText(mDatas.get(position).category[0]); |
这样是错的,暂时忘记了错的原因,也许可能差不多就是setText只能接受String类吧
反正这样写就好了
1 | holder.mIcon.setText(mDatas.get(position).category.subSequence(0,1)); |
RecyclerView和ListView布局
之前我的单个布局文件是这样的(省略其它)
1 | <TextView |
然后发现在RecyclerView中正常,但是ListView中不正常(文字不居中)

改成这样才行
1 | <TextView |
Intent传值而不是引用
当我改了一个Food类型的favorite属性之后,原来Activity的那个数据的属性并没有变,因为传的是值,所以改了favorite属性之后要传回去
参考资料
RecyclerView
去掉标题栏
FloatingActionButton
动态改变空间宽度和高度