Toolbar
在res/values下新建一个styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme.-->
<style name="AppTheme" 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>
</resources>
在colors.xml中加入颜色
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
AndroidManifest.xml中别忘记加入
android:theme="@style/AppTheme">
MainActivity.java
package com.example.materialtest;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
}
activity_main.xml
用android:theme
属性,将Toolbar的主题指定成了ThemeOverlay.AppCompat.Dark.ActionBar
。但是这样指定完了之后又会出现新的问题,如果Toolbar中有菜单按钮,那么弹出的菜单项也会变成深色主题,于是这里使用了app:popupThem
e属性单独将弹出的菜单项指定成了淡色主题。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/toolbar"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
</FrameLayout>
toolbar.xml
在res中添加一个menu文件夹,再添加一个toolbar.xml
showAsAction
主要有以下几种值可选:always
表示永远显示在Toolbar中,如果屏幕空间不够则不显示; ifRoom
表示屏幕空间足够的情况下显示在Toolbar中,不够的话就显示在菜单当中;never
则表示永远显示在菜单当中。注意,Toolbar中的 action按钮只会显示图标,菜单中的action按钮只会显示文字。
<?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/backUp"
android:icon="@drawable/ic_launcher_foreground"
android:title="BackUp"
app:showAsAction="always"/>
<item
android:id="@+id/delect"
android:icon="@drawable/ic_launcher_foreground"
android:title="Delete"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/settings"
android:icon="@drawable/ic_launcher_foreground"
android:title="Settings"
app:showAsAction="never"/>
</menu>
MainAcyivity.java
增加点击事件
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.backUp:
Toast.makeText(this, "You clicked BackUp", Toast.LENGTH_SHORT).show();
break;
case R.id.delect:
Toast.makeText(this, "You clicked Delete", Toast.LENGTH_SHORT).show();
break;
case R.id.settings:
Toast.makeText(this, "You clicked Settings", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
return true;
}
滑动菜单
DrawerLayout
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/toolbar"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</FrameLayout>
<!--android:layout_gravity="start",这是必须有的,指定滑动菜单在左还是在右。start是根据系统语言的习惯(比如中文是从左到右,那么start就是默认左边)进行布局-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:text="This is menu"
android:textSize="30sp"
android:background="#FFF"/>
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.java
点击按钮打开滑动菜单
@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.drawer_layout);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_launcher_foreground);
}
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.backUp:
Toast.makeText(this, "You clicked BackUp", Toast.LENGTH_SHORT).show();
break;
case R.id.delete:
Toast.makeText(this, "You clicked Delete", Toast.LENGTH_SHORT).show();
break;
case R.id.settings:
Toast.makeText(this, "You clicked Settings", Toast.LENGTH_SHORT).show();
break;
case android.R.id.home:
//保证这里的行为和XML中定义的一致
mDrawerLayout.openDrawer(GravityCompat.START);
break;
default:
//break;
}
return true;
}
NavigationView
添加依赖
implementation 'com.google.android.material:material:1.0.0'
implementation 'de.hdodenhof:circleimageview:3.1.0'
MainActivity.java
@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.drawer_layout);
NavigationView navigationView = (NavigationView)findViewById(R.id.nav_view);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_launcher_foreground);
}
//默认选中call菜单
navigationView.setCheckedItem(R.id.nav_call);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem item) {
mDrawerLayout.closeDrawers();
return true;
}
});
}
nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:checkableBehavior="single">
<!-- group表示一个组,android:checkableBehavior="single"表示组中的所有菜单项只能单选 -->
<item
android:id="@+id/nav_call"
android:title="Call"
android:icon="@drawable/ic_launcher_foreground"/>
<item
android:id="@+id/nav_friends"
android:title="Friends"
android:icon="@drawable/ic_launcher_foreground"/>
<item
android:id="@+id/nav_location"
android:title="Location"
android:icon="@drawable/ic_launcher_foreground"/>
<item
android:id="@+id/nav_mail"
android:title="Mail"
android:icon="@drawable/ic_launcher_foreground"/>
<item
android:id="@+id/nav_task"
android:title="Tasks"
android:icon="@drawable/ic_launcher_foreground"/>
</group>
</menu>
nav_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="180dp">
<!-- 180dp是NavigationView比较合适的高度 -->
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:id="@+id/icon_image"
android:src="@mipmap/ic_launcher"
android:layout_centerInParent="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/userName"
android:text="sakuraLoveFly@qq.com"
android:textSize="14sp"
android:layout_alignParentBottom="true"
android:textColor="@color/black"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/mail"
android:text="Sakura"
android:textSize="14sp"
android:layout_above="@id/userName"
android:textColor="#150A0A"
/>
</RelativeLayout>
activity_main.xml
<com.google.android.material.navigation.NavigationView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/nav_view"
android:layout_gravity="start"
app:menu="@menu/nav_menu"
app:headerLayout="@layout/nav_header"/>
悬浮按钮和可交互提示
FloatingActionButton
activity_main.xml
elevation给FloatingActionButton指定一个高度值,高度值越大,投影范围越大,投影效果越淡
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fab"
android:src="@drawable/ic_launcher_foreground"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
app:elevation="8dp"
/>
MainActivity.java
FloatingActionButton floatingActionButton = (FloatingActionButton)findViewById(R.id.fab);
floatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "FAB clicked", Toast.LENGTH_SHORT).show();
}
});
SnackBar
互动式提示
MainActivity.java
floatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Toast.makeText(MainActivity.this, "FAB clicked", Toast.LENGTH_SHORT).show();
Snackbar.make(view,"Data deleted",Snackbar.LENGTH_SHORT).setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "Data restored", Toast.LENGTH_SHORT).show();
}
}).show();
}
});
CoordinatorLayout
解决漂浮控件被遮挡问题
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/toolbar"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fab"
android:src="@drawable/ic_launcher_foreground"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
app:elevation="8dp"
/>
<!-- elevation给FloatingActionButton指定一个高度值,高度值越大,投影范围越大,投影效果越淡 -->
</androidx.coordinatorlayout.widget.CoordinatorLayout>
卡片式布局
CardView
添加依赖
注:Glide库是一个超级强大的图片加载库,不仅可以用来加载本地图片,还可以加载网络图片,GIF图片,甚至是本地视频
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
FruitAdapter
首先调用Glide.with()
方法并传人一个Context
、Activity
或Fragment
参数,然后调用load()
方法去加载图片,可以是一个URL
地址,也可以是一个本地路径,或者是一个资源id,最后调用into()
方法将图片设置到具体某一个ImageView中就可以了。
package com.example.materialtest;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> fruitList ;
private Context context;
public FruitAdapter(List<Fruit> list){
fruitList = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(context == null){
context = parent.getContext();
}
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(FruitAdapter.ViewHolder holder, int position) {
Fruit fruit = fruitList.get(position);
//holder.imageView.setImageResource(fruit.getImageId());
//自动压缩图片,不用担心像素过高
Glide.with(context).load(fruit.getImageId()).into(holder.imageView);
holder.textView.setText(fruit.getName());
}
@Override
public int getItemCount() {
return fruitList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
CardView cardView;
ImageView imageView;
TextView textView;
public ViewHolder(View itemView) {
super(itemView);
cardView = (CardView) itemView;
imageView = itemView.findViewById(R.id.image_view);
textView = itemView.findViewById(R.id.text_view);
}
}
}
MainActivity
这里用的GridLayoutManager
,传入两个参数:第一个是context,第二个是列数
RecyclerView recyclerView = findViewById(R.id.recycler_view);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,2);
recyclerView.setLayoutManager(gridLayoutManager);
fruitAdapter = new FruitAdapter(list);
recyclerView.setAdapter(fruitAdapter);
AppBarLayout
解决RecycllerView把ToolBar挡住的问题
-
AppBarLayout 只有作为 CoordinatorLayout 的直接子 View 时才能正常工作,为了让 AppBarLayout 能够知道何时滚动其子 View,我们还应该在
CoordinatorLayout
布局中提供一个可滚动 View,我们称之为Scrolling View
.
Scrolling View
和AppBarLayout
之间的关联,通过将 Scrolling View 的 Behavior 设为AppBarLayout.ScrollingViewBehavior
来建立。 -
scroll
表示向上滚动时Toolbar滚动并实现隐藏,enenterAlways
表示向下滚动时Toolabr向下滚动并重现,snap
表示当Toolbar还没有完全隐藏或显示时,根据当前滚动的距离,选择隐藏还是显示
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/toolbar"
android:background="?attr/colorPrimary"
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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recycler_view"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
下拉刷新
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/swipe_refresh"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recycler_view"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
@Override
protected void onCreate(Bundle savedInstanceState) {
swipeRefresh = findViewById(R.id.swipe_refresh);
swipeRefresh.setColorSchemeColors(R.color.colorPrimaryDark);
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshFruits();
}
});
}
private void refreshFruits() {
new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
initFruit();
//通知数据发生变化
fruitAdapter.notifyDataSetChanged();
//刷新数据结束,并隐藏数据条
swipeRefresh.setRefreshing(false);
}
});
}
}).start();
}
可折叠式标题栏
CollapsingToolbarLayout
由于CoordinatorLayout
本身已经可以响应滚动事件了,因此我们在它的内部就需要使用NestedScrollView
或RecyclerView
这样的布局。另外,这里还通过app:layout_behavio
r属性指定了一个布局行为,这和之前在RecyclerView中的用法是一模一样的。
FruitAdapter.java
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(context == null){
context = parent.getContext();
}
View view = LayoutInflater.from(context).inflate(R.layout.fruit_item,parent,false);
final ViewHolder holder = new ViewHolder(view);
holder.cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
Fruit fruit = fruitList.get(position);
Intent intent = new Intent(context,FruitActivity.class);
intent.putExtra(FruitActivity.FRUIT_NAME,fruit.getName());
intent.putExtra(FruitActivity.FRUIT_IMAGE_ID,fruit.getImageId());
context.startActivity(intent);
}
});
return holder;
}
FruitActivity.java
package com.example.materialtest;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> fruitList ;
private Context context;
public FruitAdapter(List<Fruit> list){
fruitList = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(context == null){
context = parent.getContext();
}
View view = LayoutInflater.from(context).inflate(R.layout.fruit_item,parent,false);
final ViewHolder holder = new ViewHolder(view);
holder.cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
Fruit fruit = fruitList.get(position);
Intent intent = new Intent(context,FruitActivity.class);
intent.putExtra(FruitActivity.FRUIT_NAME,fruit.getName());
intent.putExtra(FruitActivity.FRUIT_IMAGE_ID,fruit.getImageId());
context.startActivity(intent);
}
});
return holder;
}
@Override
public void onBindViewHolder(FruitAdapter.ViewHolder holder, int position) {
Fruit fruit = fruitList.get(position);
//holder.imageView.setImageResource(fruit.getImageId());
//自动压缩图片,不用担心像素过高
Glide.with(context).load(fruit.getImageId()).into(holder.imageView);
holder.textView.setText(fruit.getName());
}
@Override
public int getItemCount() {
return fruitList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
CardView cardView;
ImageView imageView;
TextView textView;
public ViewHolder(View itemView) {
super(itemView);
cardView = (CardView) itemView;
imageView = itemView.findViewById(R.id.image_view);
textView = itemView.findViewById(R.id.text_view);
}
}
}
activity_fruiy.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="250dp"
android:id="@+id/appBar">
<!--提供了一个可以折叠的Toolbar-->
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<!--layout_scrollFlags:
scroll 随着水果内容详情的滚动而滚动
exitUntilCollapsed 随着滚动完成折叠之后就保留在界面上,不在移出屏幕
-->
</com.google.android.material.appbar.CollapsingToolbarLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fruit_image_view"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"/>
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/toolbar"
app:layout_collapseMode="pin"/>
<!--app:layout_collapseMode指定折叠模式:
parallax 在折叠过程中出现一定的错位
pin 在折叠过程中位置始终保持不变
-->
</com.google.android.material.appbar.AppBarLayout>
<!--水果内容-->
<!--NestedScrollView与AppBarLayout是平级的-->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="35dp"
app:cardCornerRadius="4dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fruit_content_text"
android:layout_margin="10dp"/>
</androidx.cardview.widget.CardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:layout_anchor="@id/appBar"
app:layout_anchorGravity="bottom|end" />
<!--layout_anchorGravity:将悬浮按钮定位在标题栏区域的右下角-->
<!--AppBarLayout悬浮标题出现在水果栏-->
</androidx.coordinatorlayout.widget.CoordinatorLayout>