SlideShare a Scribd company logo
1 of 82
Download to read offline
How to make scrolling effect like GooglePlay’s homepage
Reece Cheng
91APP Android Engineer
loveuph114@gmail.com
UX
Hey!Reece!
你能不能把我們的⾸首⾴頁做成跟
GooglePlay 的⾸首⾴頁⼀一樣?
RD
簡單!
⽤用 CoordinatorLayout 就好了了
A week later…
因為底層 NestedScrollView 的 Fling 機制有問題
造成 Fling 無法傳給 AppBarLayout
以致於無法正確滑動
FlingBehavior
smooth-app-bar-layout
⼿手指往上滑時
1. Header 會被內容推上去,並推到螢幕外
2. Tab 會被內容推上去,並卡在畫⾯面最上
3. 當 Tab 往上移動碰到 Toolbar 時,Too
要被 Tab 推上去
⼿手指往下滑時
1. Tab 與 Toolbar 要被拉下來來,並卡在最
2. 等內容滑到最頂端時,Header 與 Tab
下來來,並回到原本的位⼦子
Screen / RecyclerView
Decoration
Tab
Toolbar
ViewPager
Header
Tab
Toolbar
Fragment
Parent Child+
<FrameLayout>
<ViewPager />
<RelativeLayout>
<ImageView />
<SlidingTabLayout />
</RelativeLayout>
<Toolbar />
</FrameLayout>
Parent.xml
1
Header Scrolling
IScrollingParent Child+IScrolling
scrolling
header translation, switch child
public interface IScrollingParent {
void onChildScrolled(ScrollingView scrollingView, int pagePosition, int scrollY, int dy);
}
IScrollingParent
監聽 Child 的滑動,並且根據 scrollY 與 dy 移動 Header 與 Toolbar
public interface IScrollingChild {
void adjustScroll(float headerTranslationY, boolean isHeaderExpanded);
void setScrollingParent(IShopHomeScrollingParent scrolling);
void setPagePosition(int position);
}
IScrollingChild
滑動時通知 Parent,並根據 Parent 的狀狀態與事件調整 Child 的畫⾯面
public class ScrollingChildFragment implements IScrollingChild {
private IScrollingParent mScrollingParent;
private int mPagePosition;
@Override
public void adjustScroll(float headerTranslationY, boolean isHeaderExpanded) {
}
@Override
public void setScrollingParent(IScrollingParent scrolling) {
mScrollingParent = scrolling;
}
@Override
public void setPagePosition(int position) {
mPagePosition = position;
}
}
ChildFragment
mRecyclerView.addOnScrollListener(mScrollListener);
}
@Override
public void adjustScroll(float headerTranslationY, boolean isHeaderExpanded) {
}
@Override
public void setScrollingParent(IScrollingParent scrolling) {
mScrollingParent = scrolling;
}
@Override
public void setPagePosition(int position) {
mPagePosition = position;
}
class ScrollingOnScrollListener extends RecyclerView.OnScrollListener {
private int mScrollY = 0;
@Override
public void onScrolled(RecyclerView view, int dx, int dy) {
mScrollY += dy;
mScrollingParent.onChildScrolled(view, mPagePosition, mScrollY, dy);
}
public int getScrollY() {
return mScrollY;
}
}
}
ChildFragment
public class ScrollingChildFragment implements IScrollingChild {
private IScrollingParent mScrollingParent;
private int mPagePosition;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mRecyclerView.addOnScrollListener(new ScrollingOnScrollListener());
}
@Override
public void adjustScroll(float headerTranslationY, boolean isHeaderExpanded) {
}
@Override
public void setScrollingParent(IScrollingParent scrolling) {
mScrollingParent = scrolling;
}
@Override
public void setPagePosition(int position) {
mPagePosition = position;
}
class ScrollingOnScrollListener extends RecyclerView.OnScrollListener {
private int mScrollY = 0;
@Override
public void onScrolled(RecyclerView view, int dx, int dy) {
mScrollY += dy;
mScrollingParent.onChildScrolled(view, mPagePosition, mScrollY, dy);
ChildFragment
public class ParentAdapter extends FragmentPagerAdapter {
private final ArrayList<Fragment> mFragments = new ArrayList<>();
public ParentAdapter(FragmentManager fm) {
super(fm);
}
}
ParentAdapter
public class ParentAdapter extends FragmentPagerAdapter {
private final ArrayList<Fragment> mFragments = new ArrayList<>();
private IScrollingParent mScrollingParent;
public ParentAdapter(FragmentManager fm) {
super(fm);
}
public void setIScrollingParent(IScrollingParent parent) {
mScrollingParent = parent;
}
}
ParentAdapter
public class ParentAdapter extends FragmentPagerAdapter {
private final ArrayList<Fragment> mFragments = new ArrayList<>();
private IScrollingParent mScrollingParent;
public ParentAdapter(FragmentManager fm) {
super(fm);
}
public void setIScrollingParent(IScrollingParent parent) {
mScrollingParent = parent;
}
public void addPageFragment(Fragment fragment) {
mFragments.add(fragment);
if(fragment instanceof IScrollingChild) {
int position = mFragments.indexOf(fragment);
IScrollingChild child = (IScrollingChild) fragment;
child.setPagePosition(position);
if (mScrollingParent != null) {
child.setScrollingParent(mScrollingParent);
}
}
}
}
ParentAdapter
public class ScrollingParentFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
ParentAdapter adapter = new ParentAdapter(getFragmentManager());
adapter.addPageFragment(getChildFragment(1));
adapter.addPageFragment(getChildFragment(2));
adapter.addPageFragment(getChildFragment(3));
mViewPager.setAdapter(pagerAdapter);
}
}
ParentFragment
public class ScrollingParentFragment implements IScrollingParent {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
ParentAdapter adapter = new ParentAdapter(getFragmentManager());
adapter.addPageFragment(getChildFragment(1));
adapter.addPageFragment(getChildFragment(2));
adapter.addPageFragment(getChildFragment(3));
mViewPager.setAdapter(pagerAdapter);
}
@Override
public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) {
mHeader.setTranslationY(-scrollY);
}
}
ParentFragment
2
Decoration
Screen / RecyclerView
Decoration
Tab
Toolbar
public class ScrollingChildFragment implements IScrollingChild {
...
class ScrollingDecoration extends RecyclerView.ItemDecoration {
private int mHeaderHeight;
public ShopHomeScrollingDecoration(height) {
super();
mHeaderHeight = height;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if(parent.getChildAdapterPosition(view) == 0) {
outRect.top = mHeaderHeight;
}
}
}
}
ChildFragment
public class ScrollingChildFragment implements IScrollingChild {
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mRecyclerView.addOnScrollListener(new ScrollingOnScrollListener());
mRecyclerView.addItemDecoration(new ScrollingDecoration(getHeaderHeight()));
}
...
class ScrollingDecoration extends RecyclerView.ItemDecoration {
private int mHeaderHeight;
public ShopHomeScrollingDecoration(height) {
super();
mHeaderHeight = height;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if(parent.getChildAdapterPosition(view) == 0) {
outRect.top = mHeaderHeight;
}
}
}
}
ChildFragment
?
Answer
其他 Child 不知道⽬目前 Child 與 Header 的滑動狀狀況
public interface IScrollingChild {
void adjustScroll(float headerTranslationY, boolean isHeaderExpanded);
void setScrollingParent(IShopHomeScrollingParent scrolling);
void setPagePosition(int position);
}
3
adjustScroll
public class ParentAdapter extends FragmentPagerAdapter {
private final ArrayList<Fragment> mFragments = new ArrayList<>();
private SparseArrayCompat<IScrollingChild> mIScrollingChildList;
private IScrollingParent mScrollingParent;
public ParentAdapter(FragmentManager fm) {
super(fm);
mIScrollingChildList = new SparseArrayCompat<>();
}
public void setIScrollingParent(IScrollingParent parent) {
mScrollingParent = parent;
}
public IScrollingChild getIScrollingChild(int position) {
return mIScrollingChildList.get(position);
}
public void addPageFragment(Fragment fragment) {
mFragments.add(fragment);
if(fragment instanceof IScrollingChild) {
int position = mFragments.indexOf(fragment);
IScrollingChild child = (IScrollingChild) fragment;
child.setPagePosition(position);
mIScrollingChildList.put(position, child);
if (mScrollingParent != null) {
child.setScrollingParent(mScrollingParent);
}
ParentAdapter
public class ScrollingParentFragment implements ISCrollingParent {
...
ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (positionOffsetPixels > 0) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
if(scrolling != null) {
scrolling.adjustScroll(mHeader.getTranslationY());
}
}
}
@Override
public void onPageSelected(int position) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
if(scrolling != null) {
scrolling.adjustScroll(mHeader.getTranslationY());
}
}
@Override
public void onPageScrollStateChanged(int state) {}
};
}
ParentFragment
public class ScrollingParentFragment implements ISCrollingParent {
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
...
mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}
...
ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (positionOffsetPixels > 0) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
if(scrolling != null) {
scrolling.adjustScroll(mHeader.getTranslationY());
}
}
}
@Override
public void onPageSelected(int position) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
ParentFragment
public class ScrollingChildFragment implements IScrollingChild {
private IScrollingParent mScrollingParent;
private int mPagePosition;
protected RecyclerView mRecyclerView;
ScrollingOnScrollListener mScrollListener;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mRecyclerView.addOnScrollListener(new ScrollingOnScrollListener());
mRecyclerView.addItemDecoration(new ScrollingDecoration(getHeaderHeight()));
}
@Override
public void adjustScroll(float headerTranslationY, boolean isHeaderExpanded) {
int scrollY = mOnScrollListener.getScrollY();
mRecyclerView.scrollBy(0, -scrollY);
mRecyclerView.scrollBy(0, (int) -headerTranslationY);
}
@Override
public void setScrollingParent(IScrollingParent scrolling) {
mScrollingParent = scrolling;
}
@Override
public void setPagePosition(int position) {
mPagePosition = position;
}
ChildFragment
4
Scrolling Effect Detail
Scrolling Effect Detail
Finger swipe up
Finger swipe down
IsPageChanging
Finger swipe up
• Header 會被內容推上去,並推到螢幕外
• Tab 會被內容推上去,並卡在畫⾯面最上⽅方
• 當 Tab 往上移動碰到 Toolbar 時,Toolbar 要被 Tab 推上去
Finger swipe up
• Header 會被內容推上去,並推到螢幕外
• Tab 會被內容推上去,並卡在畫⾯面最上⽅方
• 當 Tab 往上移動碰到 Toolbar 時,Toolbar 要被 Tab 推上去
Screen / RecyclerView
Tab
Header
mHeaderTopLimit
Tab
Header
= HeaderHeight - TabHeight
100
400
public class ScrollingParentFragment implements IScrollingParent {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}
@Override
public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) {
int headerTranslationY = Math.abs((int) mHeader.getTranslationY());
if (dy > 0) {
// Finger swipe up
if (headerTranslationY < mHeaderTopLimit) {
int scroll = Math.min(headerTranslationY + dy, mHeaderTopLimit);
mHeader.setTranslationY(-scroll);
}
}
}
ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (positionOffsetPixels > 0) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
if(scrolling != null) {
scrolling.adjustScroll(mHeaderHeight, mHeader.getTranslationY(), mIsHeaderExpanded);
}
ParentFragment
Finger swipe up
• Header 會被內容推上去,並推到螢幕外
• Tab 會被內容推上去,並卡在畫⾯面最上⽅方
• 當 Tab 往上移動碰到 Toolbar 時,Toolbar 要被 Tab 推上去
Screen / RecyclerView
Tab
ToolbarTab
Toolbar
freeSpace
freeSpace = HeaderHeight
- (TabHeight + ToolbarHeight)
public class ScrollingParentFragment implements IScrollingParent {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}
@Override
public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) {
int headerTranslationY = Math.abs((int) mHeader.getTranslationY());
if (dy > 0) {
// Finger swipe up
if (headerTranslationY < mHeaderTopLimit) {
int scroll = Math.min(headerTranslationY + dy, mHeaderTopLimit);
mHeader.setTranslationY(-scroll);
if(scroll > freeSpaceBeforePushToolbar) {
int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY());
int toolbarScroll = Math.min(toolbarTranslationY + dy, toolbarHeight);
mToolbar.setTranslationY(-toolbarScroll);
}
}
}
}
...
@Override
public void onPageSelected(int position) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
ParentFragment
Scrolling Effect Detail
Finger swipe up
Finger swipe down
IsPageChanging
Finger swipe down
• Tab 與 Toolbar 要被拉下來來,並卡在最上⾯面
• 內容滑到最頂端時,Header 與 Tab 要被拉下來來,並回到原本的位⼦子
Finger swipe down
• Tab 與 Toolbar 要被拉下來來,並卡在最上⾯面
• 內容滑到最頂端時,Header 與 Tab 要被拉下來來,並回到原本的位⼦子
Screen / RecyclerView
Tab
Toolbar
public class ScrollingParentFragment implements IScrollingParent {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}
@Override
public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) {
int headerTranslationY = Math.abs((int) mHeader.getTranslationY());
if (dy > 0) {
// Finger swipe up
...
} else {
// Finger swipe down
int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY());
if(toolbarTranslationY > 0) {
int scroll = Math.max(toolbarTranslationY + dy, 0);
mToolbar.setTranslationY(-scroll);
}
}
}
ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
ParentFragment
public class ScrollingParentFragment implements IScrollingParent {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}
@Override
public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) {
int headerTranslationY = Math.abs((int) mHeader.getTranslationY());
if (dy > 0) {
// Finger swipe up
...
} else {
// Finger swipe down
int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY());
if(toolbarTranslationY > 0) {
int scroll = Math.max(toolbarTranslationY + dy, 0);
mToolbar.setTranslationY(-scroll);
int move = toolbarTranslationY - scroll;
int headerScroll = headerTranslationY - move;
mHeader.setTranslationY(-headerScroll);
}
}
}
ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
ParentFragment
Finger swipe down
• Tab 與 Toolbar 要被拉下來來,並卡在最上⾯面
• 內容滑到最頂端時,Header 與 Tab 要被拉下來來,並回到原本的位⼦子
Screen / RecyclerView
Tab
Toolbar
Tab
Toolbar
freeSpace
public class ScrollingParentFragment implements IScrollingParent {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}
@Override
public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) {
int headerTranslationY = Math.abs((int) mHeader.getTranslationY());
if (dy > 0) {
// Finger swipe up
...
} else {
// Finger swipe down
int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY());
if(toolbarTranslationY > 0) {
int scroll = Math.max(toolbarTranslationY + dy, 0);
mToolbar.setTranslationY(-scroll);
int move = toolbarTranslationY - scroll;
int headerScroll = headerTranslationY - move;
mHeader.setTranslationY(-headerScroll);
}
if (scrollY < freeSpaceBeforePushToolbar) {
mHeader.setTranslationY(-scrollY);
}
}
}
ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
ParentFragment
Scrolling Effect Detail
Finger swipe up
Finger swipe down
IsPageChanging
onPageScrolled() / onPageSelected()
adjustScroll()
onChildScrolled()
int headerScroll = headerTranslationY - move;
mHeader.setTranslationY(-headerScroll);
}
if (scrollY < freeSpaceBeforePushToolbar) {
mHeader.setTranslationY(-scrollY);
}
}
}
ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (positionOffsetPixels > 0) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
if(scrolling != null) {
scrolling.adjustScroll(mHeader.getTranslationY());
}
}
}
@Override
public void onPageSelected(int position) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
if(scrolling != null) {
scrolling.adjustScroll(mHeader.getTranslationY());
}
}
@Override
public void onPageScrollStateChanged(int state) {
mIsPageChanging = state != ViewPager.SCROLL_STATE_IDLE;
}
};
}
ParentFragment
public class ScrollingParentFragment implements IScrollingParent {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}
@Override
public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) {
if(mIsPageChanging) {
return;
}
int headerTranslationY = Math.abs((int) mHeader.getTranslationY());
if (dy > 0) {
// Finger swipe up
if (headerTranslationY < mHeaderTopLimit) {
int scroll = Math.min(headerTranslationY + dy, mHeaderTopLimit);
mHeader.setTranslationY(-scroll);
if(scroll > freeSpaceBeforePushToolbar) {
int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY());
int toolbarScroll = Math.min(toolbarTranslationY + dy, toolbarHeight);
mToolbar.setTranslationY(-toolbarScroll);
}
}
ParentFragment
5
Polished
Parallax
Toolbar background animation
Polished
}
}
}else {
// Finger swipe down
int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY());
if(toolbarTranslationY > 0) {
int scroll = Math.max(toolbarTranslationY + dy, 0);
mToolbar.setTranslationY(-scroll);
int move = toolbarTranslationY - scroll;
int headerScroll = headerTranslationY - move;
mHeader.setTranslationY(-headerScroll);
}
if (scrollY < freeSpaceBeforePushToolbar) {
mHeader.setTranslationY(-scrollY);
}
}
}
private void scrollHeader(int scroll) {
mHeader.setTranslationY(-scroll);
mMainImage.setTranslationY((scroll * 2/3));
}
ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (positionOffsetPixels > 0) {
IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position);
if(scrolling != null) {
scrolling.adjustScroll(mHeader.getTranslationY());
}
}
}
ParentFragment
return;
}
int headerTranslationY = Math.abs((int) mHeader.getTranslationY());
if (dy > 0) {
// Finger swipe up
if (headerTranslationY < mHeaderTopLimit) {
int scroll = Math.min(headerTranslationY + dy, mHeaderTopLimit);
scrollHeader(scroll);
if(scroll > freeSpaceBeforePushToolbar) {
int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY());
int toolbarScroll = Math.min(toolbarTranslationY + dy, toolbarHeight);
mToolbar.setTranslationY(-toolbarScroll);
}
}
}else {
// Finger swipe down
int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY());
if(toolbarTranslationY > 0) {
int scroll = Math.max(toolbarTranslationY + dy, 0);
mToolbar.setTranslationY(-scroll);
int move = toolbarTranslationY - scroll;
int headerScroll = headerTranslationY - move;
scrollHeader(headerScroll);
}
if (scrollY < freeSpaceBeforePushToolbar) {
scrollHeader(scroll);
}
}
}
ParentFragment
Parallax
Toolbar background animation
Polished
public class ScrollingParentFragment implements IScrollingParent {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}
@Override
public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) {
if(mIsPageChanging) {
return;
}
...
int newHeaderTranslationY = Math.abs(mHeader.getTranslationY());
if(newHeaderTranslationY < freeSpaceBeforePushToolbar) {
if(!mIsHeaderExpanded) {
mIsHeaderExpanded = true;
doHeaderBackgroundAnimation(true);
}
} else {
if(mIsHeaderExpanded) {
mIsHeaderExpanded = false;
doHeaderBackgroundAnimation(false);
}
}
}
private void scrollHeader(int scroll, IScrollingChild childScrolling) {
mHeader.setTranslationY(-scroll);
ParentFragment
6
Summary
Screen / RecyclerView
Tab
Toolbar
IScrollingParent
- onChildScrolled()
IScrollingChild
- adjustScroll()
- onHeaderTranslation()
END

More Related Content

Similar to How to make scrolling effect like GooglePlay’s homepage

Android basic 4 Navigation Drawer
Android basic 4 Navigation DrawerAndroid basic 4 Navigation Drawer
Android basic 4 Navigation DrawerEakapong Kattiya
 
Android Code Puzzles (DroidCon Amsterdam 2012)
Android Code Puzzles (DroidCon Amsterdam 2012)Android Code Puzzles (DroidCon Amsterdam 2012)
Android Code Puzzles (DroidCon Amsterdam 2012)Danny Preussler
 
Intro to Advanced JavaScript
Intro to Advanced JavaScriptIntro to Advanced JavaScript
Intro to Advanced JavaScriptryanstout
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Robert DeLuca
 
Developing Google Glass
Developing Google GlassDeveloping Google Glass
Developing Google GlassJohnny Sung
 
Beyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsBeyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsRebecca Murphey
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developersPavel Lahoda
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon Berlin
 
Soundreader.classpathSoundreader.project Soundre.docx
Soundreader.classpathSoundreader.project  Soundre.docxSoundreader.classpathSoundreader.project  Soundre.docx
Soundreader.classpathSoundreader.project Soundre.docxwhitneyleman54422
 
Leaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGLLeaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGLgerbille
 
Simplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackSimplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackGabor Varadi
 
Webgl para JavaScripters
Webgl para JavaScriptersWebgl para JavaScripters
Webgl para JavaScriptersgerbille
 
Hi AndroidAnnotations
Hi AndroidAnnotationsHi AndroidAnnotations
Hi AndroidAnnotationsTsung-Yeh Lee
 

Similar to How to make scrolling effect like GooglePlay’s homepage (20)

Android basic 4 Navigation Drawer
Android basic 4 Navigation DrawerAndroid basic 4 Navigation Drawer
Android basic 4 Navigation Drawer
 
Android Code Puzzles (DroidCon Amsterdam 2012)
Android Code Puzzles (DroidCon Amsterdam 2012)Android Code Puzzles (DroidCon Amsterdam 2012)
Android Code Puzzles (DroidCon Amsterdam 2012)
 
Intro to Advanced JavaScript
Intro to Advanced JavaScriptIntro to Advanced JavaScript
Intro to Advanced JavaScript
 
Event recvr ss
Event recvr ssEvent recvr ss
Event recvr ss
 
Mini curso Android
Mini curso AndroidMini curso Android
Mini curso Android
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
 
Developing Google Glass
Developing Google GlassDeveloping Google Glass
Developing Google Glass
 
Beyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsBeyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS Apps
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developers
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
 
Soundreader.classpathSoundreader.project Soundre.docx
Soundreader.classpathSoundreader.project  Soundre.docxSoundreader.classpathSoundreader.project  Soundre.docx
Soundreader.classpathSoundreader.project Soundre.docx
 
Solid principles
Solid principlesSolid principles
Solid principles
 
Android Animation (in tamil)
Android Animation (in tamil)Android Animation (in tamil)
Android Animation (in tamil)
 
Leaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGLLeaving Flatland: getting started with WebGL
Leaving Flatland: getting started with WebGL
 
Vaadin 7
Vaadin 7Vaadin 7
Vaadin 7
 
Simplified Android Development with Simple-Stack
Simplified Android Development with Simple-StackSimplified Android Development with Simple-Stack
Simplified Android Development with Simple-Stack
 
Webgl para JavaScripters
Webgl para JavaScriptersWebgl para JavaScripters
Webgl para JavaScripters
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Hi AndroidAnnotations
Hi AndroidAnnotationsHi AndroidAnnotations
Hi AndroidAnnotations
 
Fact, Fiction, and FP
Fact, Fiction, and FPFact, Fiction, and FP
Fact, Fiction, and FP
 

Recently uploaded

Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfDrew Moseley
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringHironori Washizaki
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commercemanigoyal112
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsSensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsChristian Birchler
 

Recently uploaded (20)

Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdf
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their Engineering
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commerce
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsSensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
 

How to make scrolling effect like GooglePlay’s homepage

  • 1. How to make scrolling effect like GooglePlay’s homepage
  • 2. Reece Cheng 91APP Android Engineer loveuph114@gmail.com
  • 4.
  • 7.
  • 8.
  • 9. 因為底層 NestedScrollView 的 Fling 機制有問題 造成 Fling 無法傳給 AppBarLayout 以致於無法正確滑動
  • 11. ⼿手指往上滑時 1. Header 會被內容推上去,並推到螢幕外 2. Tab 會被內容推上去,並卡在畫⾯面最上 3. 當 Tab 往上移動碰到 Toolbar 時,Too 要被 Tab 推上去 ⼿手指往下滑時 1. Tab 與 Toolbar 要被拉下來來,並卡在最 2. 等內容滑到最頂端時,Header 與 Tab 下來來,並回到原本的位⼦子
  • 13.
  • 15. <FrameLayout> <ViewPager /> <RelativeLayout> <ImageView /> <SlidingTabLayout /> </RelativeLayout> <Toolbar /> </FrameLayout> Parent.xml
  • 16.
  • 19. public interface IScrollingParent { void onChildScrolled(ScrollingView scrollingView, int pagePosition, int scrollY, int dy); } IScrollingParent 監聽 Child 的滑動,並且根據 scrollY 與 dy 移動 Header 與 Toolbar
  • 20. public interface IScrollingChild { void adjustScroll(float headerTranslationY, boolean isHeaderExpanded); void setScrollingParent(IShopHomeScrollingParent scrolling); void setPagePosition(int position); } IScrollingChild 滑動時通知 Parent,並根據 Parent 的狀狀態與事件調整 Child 的畫⾯面
  • 21. public class ScrollingChildFragment implements IScrollingChild { private IScrollingParent mScrollingParent; private int mPagePosition; @Override public void adjustScroll(float headerTranslationY, boolean isHeaderExpanded) { } @Override public void setScrollingParent(IScrollingParent scrolling) { mScrollingParent = scrolling; } @Override public void setPagePosition(int position) { mPagePosition = position; } } ChildFragment
  • 22. mRecyclerView.addOnScrollListener(mScrollListener); } @Override public void adjustScroll(float headerTranslationY, boolean isHeaderExpanded) { } @Override public void setScrollingParent(IScrollingParent scrolling) { mScrollingParent = scrolling; } @Override public void setPagePosition(int position) { mPagePosition = position; } class ScrollingOnScrollListener extends RecyclerView.OnScrollListener { private int mScrollY = 0; @Override public void onScrolled(RecyclerView view, int dx, int dy) { mScrollY += dy; mScrollingParent.onChildScrolled(view, mPagePosition, mScrollY, dy); } public int getScrollY() { return mScrollY; } } } ChildFragment
  • 23. public class ScrollingChildFragment implements IScrollingChild { private IScrollingParent mScrollingParent; private int mPagePosition; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mRecyclerView.addOnScrollListener(new ScrollingOnScrollListener()); } @Override public void adjustScroll(float headerTranslationY, boolean isHeaderExpanded) { } @Override public void setScrollingParent(IScrollingParent scrolling) { mScrollingParent = scrolling; } @Override public void setPagePosition(int position) { mPagePosition = position; } class ScrollingOnScrollListener extends RecyclerView.OnScrollListener { private int mScrollY = 0; @Override public void onScrolled(RecyclerView view, int dx, int dy) { mScrollY += dy; mScrollingParent.onChildScrolled(view, mPagePosition, mScrollY, dy); ChildFragment
  • 24. public class ParentAdapter extends FragmentPagerAdapter { private final ArrayList<Fragment> mFragments = new ArrayList<>(); public ParentAdapter(FragmentManager fm) { super(fm); } } ParentAdapter
  • 25. public class ParentAdapter extends FragmentPagerAdapter { private final ArrayList<Fragment> mFragments = new ArrayList<>(); private IScrollingParent mScrollingParent; public ParentAdapter(FragmentManager fm) { super(fm); } public void setIScrollingParent(IScrollingParent parent) { mScrollingParent = parent; } } ParentAdapter
  • 26. public class ParentAdapter extends FragmentPagerAdapter { private final ArrayList<Fragment> mFragments = new ArrayList<>(); private IScrollingParent mScrollingParent; public ParentAdapter(FragmentManager fm) { super(fm); } public void setIScrollingParent(IScrollingParent parent) { mScrollingParent = parent; } public void addPageFragment(Fragment fragment) { mFragments.add(fragment); if(fragment instanceof IScrollingChild) { int position = mFragments.indexOf(fragment); IScrollingChild child = (IScrollingChild) fragment; child.setPagePosition(position); if (mScrollingParent != null) { child.setScrollingParent(mScrollingParent); } } } } ParentAdapter
  • 27. public class ScrollingParentFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ ParentAdapter adapter = new ParentAdapter(getFragmentManager()); adapter.addPageFragment(getChildFragment(1)); adapter.addPageFragment(getChildFragment(2)); adapter.addPageFragment(getChildFragment(3)); mViewPager.setAdapter(pagerAdapter); } } ParentFragment
  • 28. public class ScrollingParentFragment implements IScrollingParent { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ ParentAdapter adapter = new ParentAdapter(getFragmentManager()); adapter.addPageFragment(getChildFragment(1)); adapter.addPageFragment(getChildFragment(2)); adapter.addPageFragment(getChildFragment(3)); mViewPager.setAdapter(pagerAdapter); } @Override public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) { mHeader.setTranslationY(-scrollY); } } ParentFragment
  • 29.
  • 32. public class ScrollingChildFragment implements IScrollingChild { ... class ScrollingDecoration extends RecyclerView.ItemDecoration { private int mHeaderHeight; public ShopHomeScrollingDecoration(height) { super(); mHeaderHeight = height; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if(parent.getChildAdapterPosition(view) == 0) { outRect.top = mHeaderHeight; } } } } ChildFragment
  • 33. public class ScrollingChildFragment implements IScrollingChild { ... @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mRecyclerView.addOnScrollListener(new ScrollingOnScrollListener()); mRecyclerView.addItemDecoration(new ScrollingDecoration(getHeaderHeight())); } ... class ScrollingDecoration extends RecyclerView.ItemDecoration { private int mHeaderHeight; public ShopHomeScrollingDecoration(height) { super(); mHeaderHeight = height; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if(parent.getChildAdapterPosition(view) == 0) { outRect.top = mHeaderHeight; } } } } ChildFragment
  • 34.
  • 35. ?
  • 36. Answer 其他 Child 不知道⽬目前 Child 與 Header 的滑動狀狀況
  • 37. public interface IScrollingChild { void adjustScroll(float headerTranslationY, boolean isHeaderExpanded); void setScrollingParent(IShopHomeScrollingParent scrolling); void setPagePosition(int position); }
  • 39. public class ParentAdapter extends FragmentPagerAdapter { private final ArrayList<Fragment> mFragments = new ArrayList<>(); private SparseArrayCompat<IScrollingChild> mIScrollingChildList; private IScrollingParent mScrollingParent; public ParentAdapter(FragmentManager fm) { super(fm); mIScrollingChildList = new SparseArrayCompat<>(); } public void setIScrollingParent(IScrollingParent parent) { mScrollingParent = parent; } public IScrollingChild getIScrollingChild(int position) { return mIScrollingChildList.get(position); } public void addPageFragment(Fragment fragment) { mFragments.add(fragment); if(fragment instanceof IScrollingChild) { int position = mFragments.indexOf(fragment); IScrollingChild child = (IScrollingChild) fragment; child.setPagePosition(position); mIScrollingChildList.put(position, child); if (mScrollingParent != null) { child.setScrollingParent(mScrollingParent); } ParentAdapter
  • 40. public class ScrollingParentFragment implements ISCrollingParent { ... ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (positionOffsetPixels > 0) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); if(scrolling != null) { scrolling.adjustScroll(mHeader.getTranslationY()); } } } @Override public void onPageSelected(int position) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); if(scrolling != null) { scrolling.adjustScroll(mHeader.getTranslationY()); } } @Override public void onPageScrollStateChanged(int state) {} }; } ParentFragment
  • 41. public class ScrollingParentFragment implements ISCrollingParent { ... @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ ... mViewPager.setOnPageChangeListener(mOnPageChangeListener); } ... ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (positionOffsetPixels > 0) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); if(scrolling != null) { scrolling.adjustScroll(mHeader.getTranslationY()); } } } @Override public void onPageSelected(int position) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); ParentFragment
  • 42. public class ScrollingChildFragment implements IScrollingChild { private IScrollingParent mScrollingParent; private int mPagePosition; protected RecyclerView mRecyclerView; ScrollingOnScrollListener mScrollListener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mRecyclerView.addOnScrollListener(new ScrollingOnScrollListener()); mRecyclerView.addItemDecoration(new ScrollingDecoration(getHeaderHeight())); } @Override public void adjustScroll(float headerTranslationY, boolean isHeaderExpanded) { int scrollY = mOnScrollListener.getScrollY(); mRecyclerView.scrollBy(0, -scrollY); mRecyclerView.scrollBy(0, (int) -headerTranslationY); } @Override public void setScrollingParent(IScrollingParent scrolling) { mScrollingParent = scrolling; } @Override public void setPagePosition(int position) { mPagePosition = position; } ChildFragment
  • 43.
  • 45. Scrolling Effect Detail Finger swipe up Finger swipe down IsPageChanging
  • 46. Finger swipe up • Header 會被內容推上去,並推到螢幕外 • Tab 會被內容推上去,並卡在畫⾯面最上⽅方 • 當 Tab 往上移動碰到 Toolbar 時,Toolbar 要被 Tab 推上去
  • 47. Finger swipe up • Header 會被內容推上去,並推到螢幕外 • Tab 會被內容推上去,並卡在畫⾯面最上⽅方 • 當 Tab 往上移動碰到 Toolbar 時,Toolbar 要被 Tab 推上去
  • 49. public class ScrollingParentFragment implements IScrollingParent { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mViewPager.setOnPageChangeListener(mOnPageChangeListener); } @Override public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) { int headerTranslationY = Math.abs((int) mHeader.getTranslationY()); if (dy > 0) { // Finger swipe up if (headerTranslationY < mHeaderTopLimit) { int scroll = Math.min(headerTranslationY + dy, mHeaderTopLimit); mHeader.setTranslationY(-scroll); } } } ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (positionOffsetPixels > 0) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); if(scrolling != null) { scrolling.adjustScroll(mHeaderHeight, mHeader.getTranslationY(), mIsHeaderExpanded); } ParentFragment
  • 50.
  • 51. Finger swipe up • Header 會被內容推上去,並推到螢幕外 • Tab 會被內容推上去,並卡在畫⾯面最上⽅方 • 當 Tab 往上移動碰到 Toolbar 時,Toolbar 要被 Tab 推上去
  • 52. Screen / RecyclerView Tab ToolbarTab Toolbar freeSpace freeSpace = HeaderHeight - (TabHeight + ToolbarHeight)
  • 53. public class ScrollingParentFragment implements IScrollingParent { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mViewPager.setOnPageChangeListener(mOnPageChangeListener); } @Override public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) { int headerTranslationY = Math.abs((int) mHeader.getTranslationY()); if (dy > 0) { // Finger swipe up if (headerTranslationY < mHeaderTopLimit) { int scroll = Math.min(headerTranslationY + dy, mHeaderTopLimit); mHeader.setTranslationY(-scroll); if(scroll > freeSpaceBeforePushToolbar) { int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY()); int toolbarScroll = Math.min(toolbarTranslationY + dy, toolbarHeight); mToolbar.setTranslationY(-toolbarScroll); } } } } ... @Override public void onPageSelected(int position) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); ParentFragment
  • 54.
  • 55. Scrolling Effect Detail Finger swipe up Finger swipe down IsPageChanging
  • 56. Finger swipe down • Tab 與 Toolbar 要被拉下來來,並卡在最上⾯面 • 內容滑到最頂端時,Header 與 Tab 要被拉下來來,並回到原本的位⼦子
  • 57. Finger swipe down • Tab 與 Toolbar 要被拉下來來,並卡在最上⾯面 • 內容滑到最頂端時,Header 與 Tab 要被拉下來來,並回到原本的位⼦子
  • 59. public class ScrollingParentFragment implements IScrollingParent { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mViewPager.setOnPageChangeListener(mOnPageChangeListener); } @Override public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) { int headerTranslationY = Math.abs((int) mHeader.getTranslationY()); if (dy > 0) { // Finger swipe up ... } else { // Finger swipe down int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY()); if(toolbarTranslationY > 0) { int scroll = Math.max(toolbarTranslationY + dy, 0); mToolbar.setTranslationY(-scroll); } } } ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { ParentFragment
  • 60. public class ScrollingParentFragment implements IScrollingParent { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mViewPager.setOnPageChangeListener(mOnPageChangeListener); } @Override public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) { int headerTranslationY = Math.abs((int) mHeader.getTranslationY()); if (dy > 0) { // Finger swipe up ... } else { // Finger swipe down int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY()); if(toolbarTranslationY > 0) { int scroll = Math.max(toolbarTranslationY + dy, 0); mToolbar.setTranslationY(-scroll); int move = toolbarTranslationY - scroll; int headerScroll = headerTranslationY - move; mHeader.setTranslationY(-headerScroll); } } } ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { ParentFragment
  • 61.
  • 62. Finger swipe down • Tab 與 Toolbar 要被拉下來來,並卡在最上⾯面 • 內容滑到最頂端時,Header 與 Tab 要被拉下來來,並回到原本的位⼦子
  • 64. public class ScrollingParentFragment implements IScrollingParent { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mViewPager.setOnPageChangeListener(mOnPageChangeListener); } @Override public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) { int headerTranslationY = Math.abs((int) mHeader.getTranslationY()); if (dy > 0) { // Finger swipe up ... } else { // Finger swipe down int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY()); if(toolbarTranslationY > 0) { int scroll = Math.max(toolbarTranslationY + dy, 0); mToolbar.setTranslationY(-scroll); int move = toolbarTranslationY - scroll; int headerScroll = headerTranslationY - move; mHeader.setTranslationY(-headerScroll); } if (scrollY < freeSpaceBeforePushToolbar) { mHeader.setTranslationY(-scrollY); } } } ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { ParentFragment
  • 65.
  • 66. Scrolling Effect Detail Finger swipe up Finger swipe down IsPageChanging
  • 68. int headerScroll = headerTranslationY - move; mHeader.setTranslationY(-headerScroll); } if (scrollY < freeSpaceBeforePushToolbar) { mHeader.setTranslationY(-scrollY); } } } ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (positionOffsetPixels > 0) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); if(scrolling != null) { scrolling.adjustScroll(mHeader.getTranslationY()); } } } @Override public void onPageSelected(int position) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); if(scrolling != null) { scrolling.adjustScroll(mHeader.getTranslationY()); } } @Override public void onPageScrollStateChanged(int state) { mIsPageChanging = state != ViewPager.SCROLL_STATE_IDLE; } }; } ParentFragment
  • 69. public class ScrollingParentFragment implements IScrollingParent { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mViewPager.setOnPageChangeListener(mOnPageChangeListener); } @Override public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) { if(mIsPageChanging) { return; } int headerTranslationY = Math.abs((int) mHeader.getTranslationY()); if (dy > 0) { // Finger swipe up if (headerTranslationY < mHeaderTopLimit) { int scroll = Math.min(headerTranslationY + dy, mHeaderTopLimit); mHeader.setTranslationY(-scroll); if(scroll > freeSpaceBeforePushToolbar) { int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY()); int toolbarScroll = Math.min(toolbarTranslationY + dy, toolbarHeight); mToolbar.setTranslationY(-toolbarScroll); } } ParentFragment
  • 72. } } }else { // Finger swipe down int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY()); if(toolbarTranslationY > 0) { int scroll = Math.max(toolbarTranslationY + dy, 0); mToolbar.setTranslationY(-scroll); int move = toolbarTranslationY - scroll; int headerScroll = headerTranslationY - move; mHeader.setTranslationY(-headerScroll); } if (scrollY < freeSpaceBeforePushToolbar) { mHeader.setTranslationY(-scrollY); } } } private void scrollHeader(int scroll) { mHeader.setTranslationY(-scroll); mMainImage.setTranslationY((scroll * 2/3)); } ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (positionOffsetPixels > 0) { IScrollingChild scrolling = mPagerAdapter.getIScrollingChild(position); if(scrolling != null) { scrolling.adjustScroll(mHeader.getTranslationY()); } } } ParentFragment
  • 73. return; } int headerTranslationY = Math.abs((int) mHeader.getTranslationY()); if (dy > 0) { // Finger swipe up if (headerTranslationY < mHeaderTopLimit) { int scroll = Math.min(headerTranslationY + dy, mHeaderTopLimit); scrollHeader(scroll); if(scroll > freeSpaceBeforePushToolbar) { int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY()); int toolbarScroll = Math.min(toolbarTranslationY + dy, toolbarHeight); mToolbar.setTranslationY(-toolbarScroll); } } }else { // Finger swipe down int toolbarTranslationY = Math.abs((int) mToolbar.getTranslationY()); if(toolbarTranslationY > 0) { int scroll = Math.max(toolbarTranslationY + dy, 0); mToolbar.setTranslationY(-scroll); int move = toolbarTranslationY - scroll; int headerScroll = headerTranslationY - move; scrollHeader(headerScroll); } if (scrollY < freeSpaceBeforePushToolbar) { scrollHeader(scroll); } } } ParentFragment
  • 74.
  • 76.
  • 77. public class ScrollingParentFragment implements IScrollingParent { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ mViewPager.setOnPageChangeListener(mOnPageChangeListener); } @Override public void onChildScrolled(ScrollingView view, int pagePosition, int scrollY, int dy) { if(mIsPageChanging) { return; } ... int newHeaderTranslationY = Math.abs(mHeader.getTranslationY()); if(newHeaderTranslationY < freeSpaceBeforePushToolbar) { if(!mIsHeaderExpanded) { mIsHeaderExpanded = true; doHeaderBackgroundAnimation(true); } } else { if(mIsHeaderExpanded) { mIsHeaderExpanded = false; doHeaderBackgroundAnimation(false); } } } private void scrollHeader(int scroll, IScrollingChild childScrolling) { mHeader.setTranslationY(-scroll); ParentFragment
  • 78.
  • 80. Screen / RecyclerView Tab Toolbar IScrollingParent - onChildScrolled() IScrollingChild - adjustScroll() - onHeaderTranslation()
  • 81.
  • 82. END