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

Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️anilsa9823
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....ShaimaaMohamedGalal
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 

Recently uploaded (20)

Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 

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