<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/cardview_dark_background"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
background
设置颜色theme
设置主题色在这里的用法是仅改变Toolbar的主题为深色但是不影响,文字的颜色。
//设置导航栏
setSupportActionBar(toolbar);
//得到这个导航栏
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true); //打开homeAsUp按钮
actionBar.setHomeAsUpIndicator(R.drawable.apple); //为这个按钮设置图片
}
setDisplayHomeAsUpEnabled(true)
启用了返回箭头按钮。在onOptionsItemSelected
方法中,我们处理了箭头按钮的点击事件,通常使用onBackPressed()
方法来执行返回操作。
popupTheme
设置菜单颜色当我们使用theme设置主题颜色以后也会导致Toolbar上的菜单颜色发生变化,这时为了不影响菜单颜色我们又重新设置菜单颜色为浅色。
app命名是为了兼容低版本
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/apple"
android:icon="@drawable/apple"
android:title="apple"
app:showAsAction="always"/>
<item
android:id="@+id/onion"
android:icon="@drawable/onion"
android:title="onion"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/tomato"
android:icon="@drawable/tomato"
android:title="tomato"
app:showAsAction="never"/>
menu>
showAsAction指定按钮的显示位置,常用参数的意义
always
:总是显示在Toolbar中ifRoom
:如果有空间就显示在Toolbar,没有空间就显示在菜单中never
:永远显示在菜单中在MainActivity中使用**setSupportActionBar()**设置导航栏。
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawerlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/cardview_dark_background"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
FrameLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/toolbar" />
androidx.drawerlayout.widget.DrawerLayout>
通过按钮打开这个DrawerLayout
public class MainActivity extends AppCompatActivity {
private DrawerLayout mDrawerlayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mDrawerlayout = (DrawerLayout) findViewById(R.id.drawerlayout);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
actionBar.setDisplayHomeAsUpEnabled(true); //打开homeAsUp按钮
actionBar.setHomeAsUpIndicator(R.drawable.apple); //为这个按钮设置图片
}
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if(item.getItemId() == android.R.id.home){
mDrawerlayout.openDrawer(GravityCompat.START); //点击按钮后打开这个滑动窗口
}
return true;
}
}
android.R.id.home:是HomeAsUp按钮的默认id
GravityCompat.START:用于启动滑动窗口的标识符
地址:https://github.com/hdodenhof/CircleImageView.git
dependencies {
...
implementation 'de.hdodenhof:circleimageview:3.1.0'
}
接下来编写一个新的显示界面nav_herder.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iconimg"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@drawable/img"
android:layout_centerInParent="true"/>
<TextView
android:id="@+id/mailtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="204dp"
android:text="[email protected]"
android:textSize="15sp" />
<TextView
android:id="@+id/userText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/iconimg"
android:layout_marginTop="135dp"
android:gravity="end"
android:text="lukecc0"
android:textSize="15sp" />
RelativeLayout>
在activity_main
中将这个页面和刚才的menu
菜单一起加载到NavigationView
中显示。
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/toolbar"
app:headerLayout="@layout/nav_header"/>
打开滑动窗口后显示效果如下:
注意我们发现apple这个菜单选项是被选中的状态,是因为我们在MainActivity中使用这句代码设置的。nacview
NavigationView nacview = (NavigationView) findViewById(R.id.navigation_view);
nacview.setCheckedItem(R.id.apple); //设置为选中状态
nacview.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.tomato){
Toast.makeText(MainActivity.this, "666", Toast.LENGTH_SHORT).show();
}
if (item.getItemId() == R.id.apple){
//关闭滑动窗口
mDrawerlayout.close();
}
return true;
}
});
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="30dp"
android:src="@drawable/apple"
android:elevation="8dp"/>
这里使用app:elevation
属性来给FloatingActionButton指定一个高度值,高度值越大,投影范围也越大,但是投影效果越淡,高度值越小,投影范围也越小,但是投影效果越浓。
floatingActionButton = (FloatingActionButton) findViewById(R.id.FloatingButton);
floatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "这是一条通知", Snackbar.LENGTH_SHORT)
.setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "你好", Toast.LENGTH_SHORT).show();
}
}).show();
}
});
在这里我们调用了Snackbar的make方法创建一个Snackbar对象,第一个参数是View,只要是任意一个View都可以他可以自己寻找到最外层布局。
然后使用setAction
可以为提示设置点击事件实现交互的目的。
相当于一个增强版的Framelayout,可以监听页面的子控件,然后做出合理响应。
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/cardview_dark_background"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/FloatingButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="30dp"
android:src="@drawable/apple"
android:elevation="8dp"/>
androidx.coordinatorlayout.widget.CoordinatorLayout>
可以看出它自动使得FloatingActionButton向上偏移了。
实际上MaterialCardView也属于FrameLayout,只是额外提供了阴影和圆角等效果。
制作一个RecyclerView的子布局fruit_item.xml,使用MaterialCardView
这个布局方式。
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_margin="5dp"
app:cardCornerRadius="4dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/FruitImage"
android:layout_width="match_parent"
android:layout_height="100dp"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/FruitText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="5dp"
android:textSize="16sp"/>
LinearLayout>
com.google.android.material.card.MaterialCardView>
接下来使用RecyclerView需要制作一个adapter
public class Fruit {
private String name;
private int imageId;
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private Context mcontext;
private List<Fruit> mfruit;
public FruitAdapter(List<Fruit> mfruit) {
this.mfruit = mfruit;
}
class ViewHolder extends RecyclerView.ViewHolder {
FruitItemBinding binding;
public ViewHolder(@NonNull View itemView) {
super(itemView);
binding = FruitItemBinding.bind(itemView);
}
}
@NonNull
@Override
public FruitAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//获取主视图的上下文
if (mcontext == null){
mcontext = parent.getContext();
}
View view = LayoutInflater.from(mcontext).inflate(R.layout.fruit_item, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull FruitAdapter.ViewHolder holder, int position) {
Fruit fruit = mfruit.get(position);
holder.binding.FruitText.setText(fruit.getName());
Glide.with(mcontext).load(fruit.getImageId()).into(holder.binding.FruitImage);
}
@Override
public int getItemCount() {
return mfruit.size();
}
}
并且在activity_main布局中设置recyclerView
控件
最后在MainActivity中启动即可
initFruits();
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,2);
recyclerView.setLayoutManager(gridLayoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
GridLayoutManager
有两个参数,第二个参数意义是列数。
注意使用这种MaterialCardView
需要保证应用主题必须是 Theme.MaterialComponents
或其后代主题。
显示效果如下:
在这里发现我们的Toolbar
不见了,仔细观察发现被recyclerView
遮挡了。这时候就需要另外一个工具——AppBarLayout
用于和recyclerView进行交互作用,
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/cardview_dark_background"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways|snap"/>com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
首先我们定义了一个AppBarLayout将Toolbar放入AppBarLayout中。然后在RecyclerView中使用app:layout_behavior
属性指定了一个布局行为,
appbar_scrolling_view_behavior
这个字符串也是Material
库提供的。
通过app:layout_behavior
这个属性可以解决遮挡问题。可以看出它将recyclerView向下挤压了。
app:layout_behavior="@string/appbar_scrolling_view_behavior"
是用于指定一个视图在滚动时与AppBarLayout
一起协调工作的属性。这个属性通常用于将滚动行为与CoordinatorLayout
和AppBarLayout
一起使用,以实现复杂的滚动效果
还记得我们在父布局使用的CoordinatorLayout
吗?他可以用于监听控件变化。
可以使用 app:layout_scrollFlags
属性来定义 AppBarLayout
的滚动行为。
下面是一些常见的 layout_scrollFlags
标志:
scroll
: 指示该视图可以在滚动时折叠,即它会随着滚动事件的发生而逐渐变小。enterAlways
: 指示该视图在向下滚动时,会立即进入可见状态。通常与 scroll
一起使用。snap
: 指示该视图会在滚动结束时对齐到最近的折叠状态,以产生“捕捉”效果。首先导入依赖:
依赖地址: 谷歌API参考文档
dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
}
我们接下来使用SwipRefreshLayout将RecyclerView包住
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swiprefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
然后我们还需设置具体的刷新逻辑才可以实现刷新功能。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById((int) R.id.swiprefresh);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//调用写好的刷新逻辑
refreshFruits();
}
});
}
//具体刷新逻辑
private void refreshFruits(){
new Thread(new Runnable() {
@Override
public void run() {
try {
//因为本地刷新很快,这样便于我们观察到刷新效果
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
initFruits();
//通知RecyclerView刷新
adapter.notifyDataSetChanged();
//表示刷新事件结束,隐藏进度条
swipeRefreshLayout.setRefreshing(false);
}
});
}
}).start();
}
最终显示效果如下: