Android手机应用开发(三) | Intent、Bundle的使用以及RecyclerView、ListView的应用

实验目的:

  1. 复习事件处理。
  2. 学习Intent、Bundle在Activity跳转中的应用。
  3. 学习RecyclerView、ListView以及各类适配器的用法。
  4. 学习FloatingActionBar的用法。

去掉标题栏

现在来讨论去掉标题栏的问题

打开文件res/values/styles.xml,添加

1
2
3
4
5
6
<style name="NoTitle" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

打开文件manifests/AndroidManifests.xml

application里面的android:theme="@style/AppTheme"

改为android:theme="@style/NoTitle"

如果有多个Activity,但是只想去掉某个Activity的标题栏怎么办呢?

那就不要改application的属性,找到需要修改的activity,改成下面的样子

1
2
3
<activity
android:name=".SecondActivity"
android:theme="@style/NoTitle"></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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<--file:activity_single_recyclerview.xml-->

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="80dp"
xmlns:app="http://schemas.android.com/apk/res-auto">

<TextView
android:id="@+id/rv_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginStart="20dp"
android:layout_marginTop="10dp"
android:background="@drawable/food_icon"
android:gravity="center"
android:text="肉"
android:textColor="@color/colorWhite"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/rv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="100dp"
android:paddingTop="20dp"
android:text="好吃的肉"
android:textColor="@color/colorBlack"
android:textSize="30sp"
app:layout_constraintStart_toEndOf="@id/rv_icon" />

</android.support.constraint.ConstraintLayout>

应该是这个样子

1539499377087

java/com.example.yourname,比如我的是java/com.janking.sysuhealth

新建一个数据类Food.java,用来给RecyclerView填充数据

这里用了implements Serializable,是为了方便后面传递Intent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.janking.sysuhealth;

import android.graphics.Color;

import java.io.Serializable;

public class Food implements Serializable {
private String name;
private String category;
private String nutrition;
private Boolean favorite;
private String color;

public Food(String a, String b, String c, String d){
name = a;
category = b;
nutrition = c;
favorite = false;
color = d;
}

public void setName(String name) {
this.name = name;
}

public void setCategory(String category) {
this.category = category;
}

public void setNutrition(String nutrition) {
this.nutrition = nutrition;
}

public void setFavorite(Boolean favorite) {
this.favorite = favorite;
}

public Boolean getFavorite() {
return favorite;
}

public String getCategory() {
return category;
}

public String getName() {
return name;
}

public String getNutrition() {
return nutrition;
}

public String getColor() {
return color;
}
}

接下来新建一个类MyAdapter.java,用来决定数据信息以及展示的UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package com.janking.sysuhealth;

import android.graphics.Color;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

import jp.wasabeef.recyclerview.animators.holder.AnimateViewHolder;


public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

private List<Food> mDatas;
private MyAdapter.OnItemClickListener onItemClickListener;

public MyAdapter(){
initData();
}

public void initData()
{
mDatas = new ArrayList<Food>();
mDatas.add(new Food("大豆", "粮食", "蛋白质", "#BB4C3B"));
mDatas.add(new Food("十字花科蔬菜","蔬菜","维生素C", "#C48D30"));
mDatas.add(new Food("牛奶","饮品","钙","#4469B0"));
mDatas.add(new Food("海鱼","肉食","蛋白质", "#20A17B"));
mDatas.add(new Food("菌菇类","蔬菜","微量元素", "#BB4C3B"));
mDatas.add(new Food("番茄","蔬菜","番茄红素","#4469B0"));
mDatas.add(new Food("胡萝卜","蔬菜","胡萝卜素", "#20A17B"));
mDatas.add(new Food("荞麦","蔬菜","膳食纤维", "#BB4C3B"));
mDatas.add(new Food("鸡蛋","杂","几乎包含所有营养物质", "#C48D30"));
}


@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
// 实例化展示的view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_single_recyclerview, parent, false);
// 实例化viewholder
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}

@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// 绑定数据
holder.mTv.setText(mDatas.get(position).getName());
holder.mIcon.setText(mDatas.get(position).getCategory().subSequence(0,1));
//listener
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if(onItemClickListener != null) {
int pos = holder.getLayoutPosition();
onItemClickListener.onItemClick(holder.itemView, pos);
}
}
});

holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(onItemClickListener != null) {
int pos = holder.getLayoutPosition();
onItemClickListener.onItemLongClick(holder.itemView, pos);
}
//表示此事件已经消费,不会触发单击事件
return true;
}
});

}

@Override
public int getItemCount()
{
return mDatas.size();
}

public void addNewItem(Food f) {
if(mDatas == null) {
mDatas = new ArrayList<>();
}
mDatas.add(0, f);
notifyItemInserted(0);
}

public void deleteItem(int pos) {
if(mDatas == null || mDatas.isEmpty()) {
return;
}
mDatas.remove(pos);
notifyItemRemoved(pos);
}

public Food getItem(int pos){
if(mDatas == null || mDatas.isEmpty())
return null;
return mDatas.get(pos);
}


public boolean updateData(String name, Boolean favorite) {
for(Food i : mDatas){
if(i.getName().equals(name)){
if(i.getFavorite().equals(favorite))
return false;
i.setFavorite(favorite);
return true;
}
}
return false;

}

/**
* 设置回调监听
*
* @param listener
*/
public void setOnItemClickListener(MyAdapter.OnItemClickListener listener) {
this.onItemClickListener = listener;
}

public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}

public static class ViewHolder extends RecyclerView.ViewHolder {
TextView mTv, mIcon;
public ViewHolder(View itemView) {
super(itemView);
mTv = (TextView) itemView.findViewById(R.id.rv_text);
mIcon = (TextView)itemView.findViewById(R.id.rv_icon);
}



}
}

Activityxml文件里面添加RecyclerView的组件

1
2
3
4
5
<android.support.v7.widget.RecyclerView
android:id="@+id/id_recyclerview"
android:dividerHeight="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />

Activityjava文件里面添加对RecyclerView的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);

//about RecyclerView
mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mAdapter = new MyAdapter();
mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
mRecyclerView.setLayoutManager(mLayoutManager);
//这里省去了点击事件,后面再讨论
//!!!注意,此时这个RecyclerView是用不了的,因为没有绑定adapter,后面在处理动画的时候我才绑定
}

差不多就是这个样子

不过还没有没有添加FloatingActionBar

1539495186387


ListView

这个就要简单一点了,不过也得添加自己的Adapter

新建一个MyListViewAdapter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.janking.sysuhealth;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MyListViewAdapter extends BaseAdapter {
private List<Food> list;

public MyListViewAdapter() {

list = new ArrayList<Food>();
list.add(new Food("收藏夹", "*", "*" , "#000000"));
}
@Override
public int getCount() {
if (list == null) {
return 0;
}
return list.size();
}

@Override
public long getItemId(int i) {
return i;
}

@Override
public Food getItem(int i) {
if (list == null) {
return null;
}
return list.get(i);
}

public void addNewItem(Food f) {
if(list == null) {
list = new ArrayList<>();
}
list.add(f);
notifyDataSetChanged();
}

public void deleteItem(int pos) {
if(list == null || list.isEmpty()) {
return;
}
list.remove(pos);
notifyDataSetChanged();
}

public void deleteItemByName(String name) {
if(list == null || list.isEmpty()) {
return;
}
for(Food i : list){
if(i.getName().equals(name)){
list.remove(i);
System.out.print("successful");
break;
}
}
notifyDataSetChanged();
}

@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
// 新声明一个ViewHoleder变量
ViewHolder viewHolder;
// 当view为空时才加载布局,否则,直接修改内容
if (convertView == null) {
// 通过inflate的方法加载布局,context需要在使用这个Adapter的Activity中传入。
convertView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.activity_single_recyclerview, null);
viewHolder = new ViewHolder();
viewHolder.mTv = (TextView) convertView.findViewById(R.id.rv_text);
viewHolder.mIcon= (TextView) convertView.findViewById(R.id.rv_icon);
convertView.setTag(viewHolder); // 用setTag方法将处理好的viewHolder放入view中
} else { // 否则,让convertView等于view,然后从中取出ViewHolder即可
viewHolder = (ViewHolder) convertView.getTag();
}
// 从viewHolder中取出对应的对象,然后赋值给他们
viewHolder.mTv.setText(list.get(i).getName());
viewHolder.mIcon.setText(list.get(i).getCategory().subSequence(0,1));
// 将这个处理好的view返回
return convertView;
}

private class ViewHolder {
public TextView mTv, mIcon;
}
}

Activityxml文件里面添加ListView的组件

1
2
3
4
5
<ListView
android:visibility="invisible"
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

Activityjava文件里面添加对ListView的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private ListView mListView;
private MyListViewAdapter myListViewAdapter;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);

//about ListView
mListView = (ListView) findViewById(R.id.listView);
myListViewAdapter = new MyListViewAdapter();
mListView.setAdapter(myListViewAdapter);
//这里省去了点击事件,后面再讨论

}

差不多就是这个样子啦

1539495791127

FloatingActionBar

仍然在build.gradle(Module:app)里面添加依赖

版本号记得根据自己项目的版本更改

1
implementation 'com.android.support:design:28.0.0'

activityxml布局文件中添加

1
2
3
4
5
6
7
8
9
10
<android.support.design.widget.FloatingActionButton
android:id="@+id/float_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/collect"
android:backgroundTint="@color/colorWhite"
android:backgroundTintMode="src_atop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_margin="25dp" />

现在我们想把之前的RecyclerViewListView都放在一个Activity里面,然后通过点击FloatingActionBar来切换,并且动态改变FloatingActionBar的图标

Activityjava文件中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private FloatingActionButton mButton;
private boolean isHome;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//about FloatingActionButton
isHome = true;
event();
}
public void event(){
/* 为FloatingActionButton设置点击事件 */
mButton = (FloatingActionButton) findViewById(R.id.float_btn);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(isHome){
findViewById(R.id.id_recyclerview).setVisibility(View.INVISIBLE);
findViewById(R.id.listView).setVisibility(View.VISIBLE);
mButton.setImageResource(R.mipmap.mainpage);
isHome = false;
}else{
findViewById(R.id.id_recyclerview).setVisibility(View.VISIBLE);
findViewById(R.id.listView).setVisibility(View.INVISIBLE);
mButton.setImageResource(R.mipmap.collect);
isHome = true;
}
}
});

}

这样是通过更改RecyclerViewListViewVisibility属性来切换两个列表的,并没有切换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
2
3
public void putBoolean(String key, boolean value);  
public void putByte(String key, byte value);
public void putChar(String key, char value);

等方法放入特定类型的数据,那我们怎么把自己定义的类放进去呢,所以前面我们定义数据类的时候加入了implements Serializable,然后就可以调用

1
2
Bundle bundle=new Bundle();
bundle.putSerializable("Click_Food", myListViewAdapter.getItem(i));

然后把它放入Intent

1
2
Intent intent=new Intent(SecondActivity.this, Detail.class);
intent.putExtras(bundle);

然后启动

1
2
startActivityForResult(intent, 0);//这里采用startActivityForResult来做跳转
//这里传入的参数0很关键,它在后面是用来接收Activity回调数据的依据

然后对于新页面:Detail,要怎么接收呢?

最好在OnCreate函数里加上下面代码

1
2
private Food display_food;
display_food = (Food)getIntent().getSerializableExtra("Click_Food");

这样就可以接收到传过来的Food类数据了

那如果这个页面想再次回去刚刚的SecondActivity还要像它一样使用startActivityForResult吗?

不用!

我在这个Detail页面做了一个返回的ImageButton,然后给它添加事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//0 表示没有更改,1表示更改过了
int change = 0;
back_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent();
Bundle bundle=new Bundle();
if(change > 0){
bundle.putSerializable("display",display_food);
setResult(1, intent);
//Toast.makeText(Detail.this,"change:" + String.valueOf(change), Toast.LENGTH_SHORT).show();
}
else{
setResult(0, intent);
}
intent.putExtras(bundle);
finish();//此处一定要调用finish()方法
}
});

发现没,这里新建了一个Intent,但是调用的是setResult,因为这次数据传送是作为上次别人传过来的一个结果,有来有回嘛!最后调用finish,数据就又回到之前的Activity

还是要注意,这里使用了

setResult(1, intent); 或者 setResult(0, intent);

对于收到Detail回应的页面SecondActivity

要处理经过一个来回的数据改变,所以在这个Activity里要重写一个方法

1
2
3
4
5
6
7
8
9
10
11
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

if (requestCode == 0) {//表示来自Detail跳转的
if (resultCode == 1) {//表示内容更改过
Food f = (Food) data.getSerializableExtra("display");
//change RecyclerView
mAdapter.updateData(f.getName(), f.getFavorite());
myListViewAdapter.addNewItem(f);
}
}
}

requestCode:跟之前startActivityForResult过去的参数0一致,因为一个页面可能在不同情况会跳到不同页面,而只有一个处理Result的方法,通过这个参数确认是来自哪一个页面的回应

resultCode:这个其它页面给予回应时对结果的一个修饰,比如这里通过resultCode可以知道传过去的Food数据有没有被修改

RecyclerView和ListView的点击事件

准确的说分为点击长按

这里实现的是点击跳转到新页面Detail,长按删除该元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Click events for RecyclerView
mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
//go to detail
Intent intent=new Intent(SecondActivity.this, Detail.class);
Bundle bundle=new Bundle();
bundle.putSerializable("Click_Food", mAdapter.getItem(position));
intent.putExtras(bundle);
startActivityForResult(intent, 0);//这里采用startActivityForResult来做跳转,此处的0为一个依据,可以写其他的值,但一定要>=0

}
@Override
public void onItemLongClick(View view, int position) {
//delete
String deleteName = mAdapter.getItem(position).getName();
//顺序很重要,先删除收藏夹中的,再删除总列表中的
myListViewAdapter.deleteItemByName(deleteName);
mAdapter.deleteItem(position);

Toast.makeText(SecondActivity.this,"删除 " + deleteName, Toast.LENGTH_SHORT).show();
}

这里实现的是点击跳转到新页面Detail,长按跳出对话框是否删除该元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//Click events for ListView
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if(i == 0)
return;
// 处理单击事件
Intent intent=new Intent(SecondActivity.this, Detail.class);
Bundle bundle=new Bundle();
bundle.putSerializable("Click_Food", myListViewAdapter.getItem(i));
intent.putExtras(bundle);
startActivityForResult(intent, 0);//这里采用startActivityForResult来做跳转,此处的0为一个依据,可以写其他的值,但一定要>=0
}
});
mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view,final int i, long l) {
if(i == 0)
return false;
// 处理长按事件
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(SecondActivity.this);
alertDialog.setTitle("提示").setMessage("是否确定删除" + myListViewAdapter.getItem(i).getName() + "?").setPositiveButton("确认",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//先更新总列表中元素的数据,再删除
mAdapter.updateData(myListViewAdapter.getItem(i).getName(), false);
myListViewAdapter.deleteItem(i);
}
}).setNegativeButton("取消",null).create().show();
return true;
}
});

用java代码更改View背景颜色

1
2
top_view = findViewById(R.id.top_view);
top_view.setBackgroundColor(Color.parseColor(display_food.getColor()));

注意我这里的Food类的Color成员是用String存储的,类似于"#BB4C3B",而不是十六进制数字!

PS:从之前的Adapter中的赋值也可以看的出来

更改RecyclerView动画

先在build.gradle里面添加依赖(记得改第二行版本号)

1
2
implementation 'jp.wasabeef:recyclerview-animators:2.3.0'
implementation 'com.android.support:support-core-utils:28.0.0'

大概来说呢,就是有两种办法加动画

  1. 通过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
    2
    SlideInBottomAnimationAdapter`
    `SlideInRightAnimationAdapter`, `SlideInLeftAnimationAdapter
  2. 通过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
    2
    ScaleInAnimator, ScaleInTopAnimator, ScaleInBottomAnimator
    ScaleInLeftAnimator, ScaleInRightAnimator
    Fade
    1
    2
    FadeInAnimator, FadeInDownAnimator, FadeInUpAnimator
    FadeInLeftAnimator, FadeInRightAnimator
    Flip
    1
    2
    FlipInTopXAnimator, FlipInBottomXAnimator
    FlipInLeftYAnimator,FlipInRightYAnimator
    Slide
    1
    2
    SlideInLeftAnimator, SlideInRightAnimator, OvershootInLeftAnimator, OvershootInRightAnimator
    SlideInUpAnimator, SlideInDownAnimator

我也不明白为什么它们为什么不能独自完成所有动画,看了下Github源码,其实感觉这个Animator功能都实现了,不过好在它们不冲突,所以我干脆两个都加上了

1539500429315

下面是动画效果:

recyclerlist

该动画源码GitHub地址

整个项目效果图

final


设置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,这里的高度我是随便设置的,待会就是要改变它

1539621188043

在java代码里添加下列代码(如果是在Activity里最好写在Oncreate()里面)

1
2
3
4
5
6
DisplayMetrics dm = getResources().getDisplayMetrics();
RelativeLayout top_layout = findViewById(R.id.top_layout);
ViewGroup.LayoutParams lp = top_layout.getLayoutParams();
lp.height = dm.heightPixels / 3;//设置1/3
lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
top_layout.setLayoutParams(lp); //设置

:这个LayoutParams的方式设置的单位是像素px,而不是dp不过我们通过getResources().getDisplayMetrics()得到的也是屏幕的宽度和高度的像素,按照比例来讲是没有问题,如果想要设置dp的话则需要将像素转换为像素密度了

如果有类名不识别,用Ctrl+Enter提示


一些笔记

删除顺序

之前写了如下代码

1
2
3
4
5
public void onItemLongClick(Viewview,intposition){
//StringdeleteName=mAdapter.getItem(position).name;
mAdapter.deleteItem(position);
Toast.makeText(SecondActivity.this,"删除"+mAdapter.getItem(position).name,Toast.LENGTH_SHORT).show();
}

然后崩溃了,因为我在删除了postion位置的元素之后还使用mAdapter.getItem(position)去获取它,虽然不是一定出错,但是当postion位置的元素不存在时就访问了越界的List……

所以应该改为

1
2
3
4
5
public void onItemLongClick(Viewview,intposition){
StringdeleteName = mAdapter.getItem(position).name;
mAdapter.deleteItem(position);
Toast.makeText(SecondActivity.this,"删除"+deleteName,Toast.LENGTH_SHORT).show();
}

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
2
3
4
5
6
7
8
9
10
<TextView
android:id="@+id/rv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="100dp"
android:gravity="center_vertical"
android:text="好吃的肉"
android:textColor="@color/colorBlack"
android:textSize="30sp"
app:layout_constraintStart_toEndOf="@id/rv_icon"/>

然后发现在RecyclerView中正常,但是ListView中不正常(文字不居中)

改成这样才行

1
2
3
4
5
6
7
8
9
10
<TextView
android:id="@+id/rv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="100dp"
android:paddingTop="20dp"
android:text="好吃的肉"
android:textColor="@color/colorBlack"
android:textSize="30sp"
app:layout_constraintStart_toEndOf="@id/rv_icon" />

Intent传值而不是引用

当我改了一个Food类型的favorite属性之后,原来Activity的那个数据的属性并没有变,因为传的是值,所以改了favorite属性之后要传回去

参考资料

RecyclerView

去掉标题栏

FloatingActionButton

动态改变空间宽度和高度


感谢资助辣条吃!