Android仿iOS照片应用Moments UI实现

使用recyclerview实现iOS photo应用中moments的动画交互效果

实现思路

如效果图所示,同一组中的item不满时留白,如使用一个recyclerview中嵌套多个gridview,当一个item图片过多时会导致OOM
Photo Moments效果
因此,只使用一个recyclerview不嵌套gridview完成,可以用两种方法实现
1、第一种自定义RecyclerView.ItemDecoration,重写onDrawOver方法,onDrawOver会绘制在最上层,所以它的绘制位置并不受限制。
2、第二种通过GridLayoutManager的setSpanSizeLookup实现,具体分为两部分完成,首先对item进行分组显示,其次实现上滑或下滑时组title替换动效。

分组显示实现

可参考[Google Source SectionedGridRecyclerViewAdapter]https://gist.github.com/gabrielemariotti/e81e126227f8a4bb339c
通过设置item的占位实现组item不满时的留白

1
2
3
4
5
6
7
final GridLayoutManager layoutManager = (GridLayoutManager) (mRecyclerView.getLayoutManager());
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return (isSectionHeaderPosition(position)) ? layoutManager.getSpanCount() : 1;
}
});

添加item时需手动分组,如下根据时间分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void replaceThumbnialsAdapter(List<Thumbnail> thumbnails) {
super.setLists(thumbnails);
List<SectionedGridRecyclerViewAdapter.Section> sections =
new ArrayList<>();
//Sections
if (thumbnails.size() != 0) {
sections.add(new SectionedGridRecyclerViewAdapter.Section(0, DSCThumbnailModel.getCreateTimeDate(thumbnails.get(0))));
}
boolean isSame = false;
for (int i = 0; i < thumbnails.size(); i++) {
if (i + 1 < thumbnails.size()) {
isSame = DSCThumbnailModel.isSameSection(thumbnails.get(i), thumbnails.get(i + 1));
if (!isSame) {
sections.add(new SectionedGridRecyclerViewAdapter.Section(i + 1, DSCThumbnailModel.getCreateTimeDate(thumbnails.get(i + 1))));
}
}
}
//Add your adapter to the sectionAdapter
SectionedGridRecyclerViewAdapter.Section[] dummy = new SectionedGridRecyclerViewAdapter.Section[sections.size()];
sectionedAdapter.setSections(sections.toArray(dummy));
}

替换动效实现

一直悬浮的title用TextView实现,当下一个view的高度小于title控件的高度时,改变title的y轴起始坐标。

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
private TextView mTvTopBar;
private int mTopBarHeight = 0;
private String mLastSectionTitle = "";
mTvTopBar = (TextView) findViewById(R.id.tv_top);
mRecView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
mTopBarHeight = mTvTopBar.getHeight();
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
View view = recyclerView.getLayoutManager().findViewByPosition(mCurrentPosition + 1);
if (view == null) {
return;
}
int position = recyclerView.getChildAdapterPosition(view);
if (sectionedAdapter.isSectionHeaderPosition(position) && view.getTop() <= mTopBarHeight) {
mTvTopBar.setY(-(mTopBarHeight - view.getTop()));
} else {
mTvTopBar.setY(0);
}
if (dy < 0 && mLastSectionTitle != null && sectionedAdapter.isSectionHeaderPosition(position)) {
mTvTopBar.setText(mLastSectionTitle);
} else if ((dy > 0 || dy == 0) && sectionedAdapter.getSection(mCurrentPosition) != null) {
mTvTopBar.setText(sectionedAdapter.getSection(mCurrentPosition).getTitle());
}
mCurrentPosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
if (sectionedAdapter.getSection(mCurrentPosition - 1) != null) {
mLastSectionTitle = sectionedAdapter.getSection(mCurrentPosition - 1).getTitle().toString();
}
}
});