说说 Android 的 Material Design 设计(
我们使用 CardView 与 RecyclerView 来·实现一个各种猫的卡片式展示列表吧O(∩_∩)O~
1 CardView 控件
1.1 引入依赖库
打开 app/build.gradle,添加依赖库:
dependencies {
...
compile 'com.android.support:recyclerview-v7:24.2.1'
compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.github.bumptech.glide:glide:4.8.0'
}
最后一行引入的 Glide,它是一个快速高效的 Android 图片加载库,注重于平滑的滚动 。 Glide 支持拉取,解码和展示视频快照,图片,和 GIF 动画 。
1.2 卡片式布局
CardView 控件可实现卡片式布局。它其实是一个 FrameLayout,只不过多了圆角与阴影功能。
布局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:material="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
...
</android.support.v4.widget.DrawerLayout>
我们在 DrawerLayout 中新增了 RecyclerView,并让它占满整个空间(宽与高都是 match_parent)。
1.3 “猫”实体类
public class Cat {
//类型
private String type;
//图片资源 ID
private int imgId;
public Cat(String type, int imgId) {
this.type = type;
this.imgId = imgId;
}
public String getType() {
return type;
}
public int getImgId() {
return imgId;
}
}
1.4 RecyclerView 的 item 布局
在 layout 目录下新建 cat_item.xml,内容为:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:material="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
material:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<ImageView
android:id="@+id/cat_image"
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/cat_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="5dp"
android:textSize="16sp"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
注意:这里事先已把猫的相关图片放置在 res/drawable 目录中。
因为每张图片的长宽比例不一样,所以我们把 android:scaleType
设置为 centerCrop,让图片按照原比例充满整个 ImageView,并裁剪超出的部分。
1.5 RecyclerView 适配器
public class CatAdapter extends RecyclerView.Adapter<CatAdapter.ViewHolder> {
private Context context;
private List<Cat> cats = new ArrayList<>();
public CatAdapter(List<Cat> cats) {
this.cats = cats;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (context == null) {//设置上下文环境
context = parent.getContext();
}
View view = LayoutInflater.from(context).inflate(R.layout.cat_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Cat cat = cats.get(position);
holder.type.setText(cat.getType());
Glide.with(context).load(cat.getImgId()).into(holder.image);
}
@Override
public int getItemCount() {
return cats.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
CardView cardView;
ImageView image;
TextView type;
public ViewHolder(View itemView) {
super(itemView);
cardView = (CardView) itemView;
image = (ImageView) itemView.findViewById(R.id.cat_image);
type = (TextView) itemView.findViewById(R.id.cat_type);
}
}
}
- 在 onBindViewHolder 中使用 Glide 来加载图片。
- Glide.with() 可传入 Context、Activity、FragmentActivity 或 Fragment,创建RequestManager 对象。
- RequestManager .load() 用于加载图片,可以传入 File、resourceId、URI 等类型的参数,创建 DrawableRequestBuilder 对象。
- DrawableRequestBuilder .into() 用于指定放置图片的 ImageView。
- Glide 本身会对图片进行压缩,所以就算是很大的图片,也可以放心使用啦O(∩_∩)O~
1.6 活动类
public class MainActivity extends AppCompatActivity {
private DrawerLayout drawerLayout;
private Cat[] cats = {new Cat("布偶猫", R.drawable.cat1),
new Cat("异国短毛猫", R.drawable.cat2),
new Cat("波斯猫", R.drawable.cat3),
new Cat("美国短毛猫", R.drawable.cat4),
new Cat("挪威森林猫", R.drawable.cat5),
new Cat("日本短尾猫", R.drawable.cat6),
new Cat("俄罗斯蓝猫", R.drawable.cat7),
new Cat("伯曼猫", R.drawable.cat8),
new Cat("缅因猫", R.drawable.cat9),
new Cat("埃及猫", R.drawable.cat10)};
private List<Cat> catList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
initCats();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
int spanCount = 2;
GridLayoutManager layoutManager = new GridLayoutManager(this, spanCount);
recyclerView.setLayoutManager(layoutManager);
CatAdapter adapter = new CatAdapter(catList);
recyclerView.setAdapter(adapter);
}
private void initCats() {
catList.clear();
for (int i = 0; i < 100; i++) {//随机添加 100 只猫的信息
Random random = new Random();
int index = random.nextInt(cats.length);
catList.add(cats[index]);
}
}
...
}
- initCats() 在 cats 数组中随机挑选了 100 只猫。
- 使用了 GridLayoutManager 布局,它接收两个参数:Context 与总列数。
卡片式布局漂亮吧?O(∩_∩)O~
2 优化
2.1 解决顶部工具栏被遮挡问题
目前设计中的卡片式布局存在一个问题,就是会遮挡顶部工具栏。
传统做法是让 RecyclerView 向下偏移顶部工具栏的高度。
AppBarLayout 是 Design Support 库提供的一个垂直方向的 LinearLayout,它封装很多滚动事件。
修改布局文件:
...
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
material:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
...
- 使用 AppBarLayout 包裹 Toolbar。
- 为 RecyclerView 的 material:layout_behavior 指定 appbar_scrolling_view_behavior 事件。
经过这两步修改,工具栏与卡片式布局就可以和谐共处啦O(∩_∩)O~
2.2 工具栏响应滚动事件
<android.support.v7.widget.Toolbar
...
material:layout_scrollFlags="scroll|enterAlways|snap"
/>
我们在 Toolbar 中新增了 material:layout_scrollFlags 属性,并设置了三种场景中的显示策略。从左到右,场景依次为向上滚动时、向下滚动时和还未完全显示与隐藏时。
属性 | 说明 |
---|---|
scroll | 一起滚动,会被隐藏。 |
enterAlways | 一起滚动,一直显示。 |
snap | 根据滚动距离,来判断是显示还是隐藏。 |
当用户向上滑动内容时,隐藏工具栏;当用户向上滑动屏幕时,再显示工具栏。这是 Material Design 中的一条重要设计,极大地提升了用户的阅读体验O(∩_∩)O~