Android 进阶之重要的控件 CardView
Android working with Card View and Recycler View
翻译:林晓海
文章地址:http://www.androidhive.info/2016/05/android-working-with-card-view-and-recycler-view/
CardView 是 Material Design 引入的又一个重要的新控件,使用CardView可以用一个带有阴影和圆弧的卡片来展示信息,CardView继承自FragmentLayout,可以通过兼容包的形式向后兼容到Androd 2.x版本。
可以将CardView和RecycleView结合实现漂亮的UI效果,通过这篇文章我们可以通过创建一个漂亮的Music app 来学习如何结合CardView和RecycleView。
怎样添加CardView?
为了在你的app中使用CardView,添加CardView依赖到build.gradle,然后同步下项目:
dependencies { |
要使用CardView就要在布局文件中添加<android.support.v7.widget.CardView> 然后在节点里面添加其他的UI控件。下面的布局中CardView只有一个简单的TextView。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
创建新项目
通过 File ⇒ New Project 的方式在Android Studio中创建一个新项目,然后在弹出选择Activity的模板页中选择Empty Activity。下载 res.zip 然后将其添加到你刚刚创建项目的资源文件夹上,这个资源文件夹中包含了专辑封面以及其他需要的图标。
将下面的字符串,颜色和尺寸资源添加到strings.xml, colors.xml and dimens.xml文件中.
strings.xml
<resources> |
colors.xml
|
dimens.xml
<resources> |
- 打开 build.gradle 往上面添加CardView, RecyclerView 和 Glide 的依赖. RecyclerView 用于以网格的方式显示专辑信息,CardView 用于现实单个专辑信息,Glide用于显示专辑封面图片。
build.gradle |
- 为了创建单个专辑的实例,我们使用一个单独的model类来存放专辑姓名,歌曲数目和封面图片,下面是这个类的源码:
Abum.javapackage info.androidhive.cardview;
public class Album {
private String name;
private int numOfSongs;
private int thumbnail;
public Album() {
}
public Album(String name, int numOfSongs, int thumbnail) {
this.name = name;
this.numOfSongs = numOfSongs;
this.thumbnail = thumbnail;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumOfSongs() {
return numOfSongs;
}
public void setNumOfSongs(int numOfSongs) {
this.numOfSongs = numOfSongs;
}
public int getThumbnail() {
return thumbnail;
}
public void setThumbnail(int thumbnail) {
this.thumbnail = thumbnail;
}
} - 我们还需要一个xml布局文件用于现实专辑卡片,这里我们新建一个album_card.xml 布局文件,这里我们增加了<android.support.v7.widget.CardView> 并且添加了专辑姓名,歌曲数目,和专辑封面等信息。并且我们还添加了三个点的按钮,在我们按下它的时候会弹出Popup 菜单。
album_card.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
创建一个menu文件,这个menu将会在用户点击弹出菜单的按钮的时候作为菜单显现出来的。
menu_album.xml
<menu 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">
<item
android:id="@+id/action_add_favourite"
android:orderInCategory="100"
android:title="@string/action_add_favourite" />
<item
android:id="@+id/action_play_next"
android:orderInCategory="101"
android:title="@string/action_play_next" />
</menu>为了使用RectycleView我们需要一个Adapter类它通过将有用的数据inflates album_card.xml 布局.因此我们需要创建一个AlbumsAdapter.java的类然后添加如下的内容:
AlbumsAdapter.java
package info.androidhive.cardview; |
- 打卡main Activity 的布局文件activity_main.xml和content_main 添加AppBarLayout, CollapsingToolbarLayout, Toolbar and RecyclerView.
activity_main.xml
|
content_main.xml
|
10.最后打开MainActivity.java 做一些必要的修改.
- initCollapsingToolbar() 在toolbar展开或者合上的时候显示或者隐藏toolbar。
- prepareAlbums() 添加RecycleView所需要的样本专辑数据。
- GridLayoutManager 用于将RecyclerView 以网格的方式显示出来。
- GridSpacingItemDecoration 用于添加RecycleView网格item的空白边界。
- AlbumsAdapter 创建后将分配给RecycleViewm,RecycleView通过网格的形式将其显示出来。MainActivity.java
package info.androidhive.cardview;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private AlbumsAdapter adapter;
private List<Album> albumList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
initCollapsingToolbar();
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
albumList = new ArrayList<>();
adapter = new AlbumsAdapter(this, albumList);
RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(10), true));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
prepareAlbums();
try {
Glide.with(this).load(R.drawable.cover).into((ImageView) findViewById(R.id.backdrop));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Initializing collapsing toolbar
* Will show and hide the toolbar title on scroll
*/
private void initCollapsingToolbar() {
final CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle(" ");
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
appBarLayout.setExpanded(true);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
collapsingToolbar.setTitle(getString(R.string.app_name));
isShow = true;
} else if (isShow) {
collapsingToolbar.setTitle(" ");
isShow = false;
}
}
});
}
/**
* Adding few albums for testing
*/
private void prepareAlbums() {
int[] covers = new int[]{
R.drawable.album1,
R.drawable.album2,
R.drawable.album3,
R.drawable.album4,
R.drawable.album5,
R.drawable.album6,
R.drawable.album7,
R.drawable.album8,
R.drawable.album9,
R.drawable.album10,
R.drawable.album11};
Album a = new Album("True Romance", 13, covers[0]);
albumList.add(a);
a = new Album("Xscpae", 8, covers[1]);
albumList.add(a);
a = new Album("Maroon 5", 11, covers[2]);
albumList.add(a);
a = new Album("Born to Die", 12, covers[3]);
albumList.add(a);
a = new Album("Honeymoon", 14, covers[4]);
albumList.add(a);
a = new Album("I Need a Doctor", 1, covers[5]);
albumList.add(a);
a = new Album("Loud", 11, covers[6]);
albumList.add(a);
a = new Album("Legend", 14, covers[7]);
albumList.add(a);
a = new Album("Hello", 11, covers[8]);
albumList.add(a);
a = new Album("Greatest Hits", 17, covers[9]);
albumList.add(a);
adapter.notifyDataSetChanged();
}
/**
* RecyclerView item decoration - give equal margin around grid item
*/
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
}
}
/**
* Converting dp to pixel
*/
private int dpToPx(int dp) {
Resources r = getResources();
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
}
}