以下のコードでこれをどのように解決したかを見てください。これはのカスタムOnScrollListener
です。RecyclerView
GridLayoutManager
ここでより多くの例を見ることができます: https://github.com/lawloretienne/QuickReturn
QuickReturnFooterRecyclerViewFragment.java
public class QuickReturnFooterRecyclerViewFragment extends Fragment {
// region Member Variables
private String[] mValues;
private QuickReturnAnimationType mQuickReturnAnimationType;
private String mLayoutManagerType;
@InjectView(R.id.rv)
RecyclerView mRecyclerView;
@InjectView(R.id.quick_return_tv)
TextView mQuickReturnTextView;
// endregion
// region Constructors
public static QuickReturnFooterRecyclerViewFragment newInstance(Bundle extras) {
QuickReturnFooterRecyclerViewFragment fragment = new QuickReturnFooterRecyclerViewFragment();
fragment.setRetainInstance(true);
fragment.setArguments(extras);
return fragment;
}
public static QuickReturnFooterRecyclerViewFragment newInstance() {
QuickReturnFooterRecyclerViewFragment fragment = new QuickReturnFooterRecyclerViewFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
public QuickReturnFooterRecyclerViewFragment() {
}
// endregion
// region Lifecycle Methods
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getArguments() != null) {
mQuickReturnAnimationType = QuickReturnAnimationType.valueOf(getArguments().getString("quick_return_animation_type"));
mLayoutManagerType = getArguments().getString("layout_manager");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recyclerview_quick_return_footer, container, false);
ButterKnife.inject(this, view);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mValues = getResources().getStringArray(R.array.countries);
if(mLayoutManagerType.equals("linear")){
CountriesLinearLayoutAdapter countriesLinearLayoutAdapter = new CountriesLinearLayoutAdapter(getActivity(), Arrays.asList(mValues));
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
mRecyclerView.setAdapter(countriesLinearLayoutAdapter);
} else if(mLayoutManagerType.equals("grid")) {
CountriesGridLayoutAdapter countriesGridLayoutAdapter = new CountriesGridLayoutAdapter(getActivity(), Arrays.asList(mValues));
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), 2);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.addItemDecoration(new GridSpacesItemDecoration(QuickReturnUtils.dp2px(getActivity(), 8)));
mRecyclerView.setAdapter(countriesGridLayoutAdapter);
}
int footerHeight = getActivity().getResources().getDimensionPixelSize(R.dimen.footer_height);
QuickReturnRecyclerViewOnScrollListener scrollListener;
SpeedyQuickReturnRecyclerViewOnScrollListener scrollListener2;
switch (mQuickReturnAnimationType){
case TRANSLATION_SIMPLE:
if(mLayoutManagerType.equals("grid")){
scrollListener = new QuickReturnRecyclerViewOnScrollListener.Builder(QuickReturnViewType.FOOTER)
.footer(mQuickReturnTextView)
.minFooterTranslation(footerHeight)
.columnCount(2)
.build();
} else {
scrollListener = new QuickReturnRecyclerViewOnScrollListener.Builder(QuickReturnViewType.FOOTER)
.footer(mQuickReturnTextView)
.minFooterTranslation(footerHeight)
.build();
}
mRecyclerView.setOnScrollListener(scrollListener);
break;
case TRANSLATION_SNAP:
if(mLayoutManagerType.equals("grid")){
scrollListener = new QuickReturnRecyclerViewOnScrollListener.Builder(QuickReturnViewType.FOOTER)
.footer(mQuickReturnTextView)
.minFooterTranslation(footerHeight)
.columnCount(2)
.isSnappable(true)
.build();
} else {
scrollListener = new QuickReturnRecyclerViewOnScrollListener.Builder(QuickReturnViewType.FOOTER)
.footer(mQuickReturnTextView)
.minFooterTranslation(footerHeight)
.isSnappable(true)
.build();
}
mRecyclerView.setOnScrollListener(scrollListener);
break;
case TRANSLATION_ANTICIPATE_OVERSHOOT:
if(mLayoutManagerType.equals("grid")){
scrollListener2 = new SpeedyQuickReturnRecyclerViewOnScrollListener.Builder(getActivity(), QuickReturnViewType.FOOTER)
.footer(mQuickReturnTextView)
.columnCount(2)
.build();
} else {
scrollListener2 = new SpeedyQuickReturnRecyclerViewOnScrollListener.Builder(getActivity(), QuickReturnViewType.FOOTER)
.footer(mQuickReturnTextView)
.build();
}
mRecyclerView.setOnScrollListener(scrollListener2);
break;
default:
if(mLayoutManagerType.equals("grid")){
scrollListener = new QuickReturnRecyclerViewOnScrollListener.Builder(QuickReturnViewType.FOOTER)
.footer(mQuickReturnTextView)
.minFooterTranslation(footerHeight)
.columnCount(2)
.build();
} else {
scrollListener = new QuickReturnRecyclerViewOnScrollListener.Builder(QuickReturnViewType.FOOTER)
.footer(mQuickReturnTextView)
.minFooterTranslation(footerHeight)
.build();
}
mRecyclerView.setOnScrollListener(scrollListener);
break;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
ButterKnife.reset(this);
}
// endregion
}
QuickReturnRecyclerViewOnScrollListener.java
public class QuickReturnRecyclerViewOnScrollListener extends RecyclerView.OnScrollListener {
// region Member Variables
private final QuickReturnViewType mQuickReturnViewType;
private final View mHeader;
private final int mMinHeaderTranslation;
private final View mFooter;
private final int mMinFooterTranslation;
private final int mColumnCount;
private final boolean mIsSnappable; // Can Quick Return view snap into place?
private int mPrevScrollY = 0;
private int mHeaderDiffTotal = 0;
private int mFooterDiffTotal = 0;
private List<RecyclerView.OnScrollListener> mExtraOnScrollListenerList = new ArrayList<>();
// endregion
// region Constructors
private QuickReturnRecyclerViewOnScrollListener(Builder builder) {
mQuickReturnViewType = builder.mQuickReturnViewType;
mHeader = builder.mHeader;
mMinHeaderTranslation = builder.mMinHeaderTranslation;
mFooter = builder.mFooter;
mMinFooterTranslation = builder.mMinFooterTranslation;
mColumnCount = builder.mColumnCount;
mIsSnappable = builder.mIsSnappable;
}
// endregion
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// apply another list' s on scroll listener
for (RecyclerView.OnScrollListener listener : mExtraOnScrollListenerList) {
listener.onScrollStateChanged(recyclerView, newState);
}
if (newState == RecyclerView.SCROLL_STATE_IDLE && mIsSnappable) {
int midHeader = -mMinHeaderTranslation/2;
int midFooter = mMinFooterTranslation/2;
switch (mQuickReturnViewType) {
case HEADER:
if (-mHeaderDiffTotal > 0 && -mHeaderDiffTotal < midHeader) {
ObjectAnimator anim = ObjectAnimator.ofFloat(mHeader, "translationY", mHeader.getTranslationY(), 0);
anim.setDuration(100);
anim.start();
mHeaderDiffTotal = 0;
} else if (-mHeaderDiffTotal < -mMinHeaderTranslation && -mHeaderDiffTotal >= midHeader) {
ObjectAnimator anim = ObjectAnimator.ofFloat(mHeader, "translationY", mHeader.getTranslationY(), mMinHeaderTranslation);
anim.setDuration(100);
anim.start();
mHeaderDiffTotal = mMinHeaderTranslation;
}
break;
case FOOTER:
if (-mFooterDiffTotal > 0 && -mFooterDiffTotal < midFooter) { // slide up
ObjectAnimator anim = ObjectAnimator.ofFloat(mFooter, "translationY", mFooter.getTranslationY(), 0);
anim.setDuration(100);
anim.start();
mFooterDiffTotal = 0;
} else if (-mFooterDiffTotal < mMinFooterTranslation && -mFooterDiffTotal >= midFooter) { // slide down
ObjectAnimator anim = ObjectAnimator.ofFloat(mFooter, "translationY", mFooter.getTranslationY(), mMinFooterTranslation);
anim.setDuration(100);
anim.start();
mFooterDiffTotal = -mMinFooterTranslation;
}
break;
case BOTH:
if (-mHeaderDiffTotal > 0 && -mHeaderDiffTotal < midHeader) {
ObjectAnimator anim = ObjectAnimator.ofFloat(mHeader, "translationY", mHeader.getTranslationY(), 0);
anim.setDuration(100);
anim.start();
mHeaderDiffTotal = 0;
} else if (-mHeaderDiffTotal < -mMinHeaderTranslation && -mHeaderDiffTotal >= midHeader) {
ObjectAnimator anim = ObjectAnimator.ofFloat(mHeader, "translationY", mHeader.getTranslationY(), mMinHeaderTranslation);
anim.setDuration(100);
anim.start();
mHeaderDiffTotal = mMinHeaderTranslation;
}
if (-mFooterDiffTotal > 0 && -mFooterDiffTotal < midFooter) { // slide up
ObjectAnimator anim = ObjectAnimator.ofFloat(mFooter, "translationY", mFooter.getTranslationY(), 0);
anim.setDuration(100);
anim.start();
mFooterDiffTotal = 0;
} else if (-mFooterDiffTotal < mMinFooterTranslation && -mFooterDiffTotal >= midFooter) { // slide down
ObjectAnimator anim = ObjectAnimator.ofFloat(mFooter, "translationY", mFooter.getTranslationY(), mMinFooterTranslation);
anim.setDuration(100);
anim.start();
mFooterDiffTotal = -mMinFooterTranslation;
}
break;
case TWITTER:
if (-mHeaderDiffTotal > 0 && -mHeaderDiffTotal < midHeader) {
ObjectAnimator anim = ObjectAnimator.ofFloat(mHeader, "translationY", mHeader.getTranslationY(), 0);
anim.setDuration(100);
anim.start();
mHeaderDiffTotal = 0;
} else if (-mHeaderDiffTotal < -mMinHeaderTranslation && -mHeaderDiffTotal >= midHeader) {
ObjectAnimator anim = ObjectAnimator.ofFloat(mHeader, "translationY", mHeader.getTranslationY(), mMinHeaderTranslation);
anim.setDuration(100);
anim.start();
mHeaderDiffTotal = mMinHeaderTranslation;
}
if (-mFooterDiffTotal > 0 && -mFooterDiffTotal < midFooter) { // slide up
ObjectAnimator anim = ObjectAnimator.ofFloat(mFooter, "translationY", mFooter.getTranslationY(), 0);
anim.setDuration(100);
anim.start();
mFooterDiffTotal = 0;
} else if (-mFooterDiffTotal < mMinFooterTranslation && -mFooterDiffTotal >= midFooter) { // slide down
ObjectAnimator anim = ObjectAnimator.ofFloat(mFooter, "translationY", mFooter.getTranslationY(), mMinFooterTranslation);
anim.setDuration(100);
anim.start();
mFooterDiffTotal = -mMinFooterTranslation;
}
break;
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// apply extra on scroll listener
for (RecyclerView.OnScrollListener listener : mExtraOnScrollListenerList) {
listener.onScrolled(recyclerView, dx, dy);
}
int scrollY = QuickReturnUtils.getScrollY(recyclerView, mColumnCount);
// Log.d("", "onScrolled() : scrollY - "+scrollY);
int diff = mPrevScrollY - scrollY;
// Log.d("", "onScrolled() : diff - "+diff);
if(diff != 0){
switch (mQuickReturnViewType){
case HEADER:
if(diff < 0){ // scrolling down
mHeaderDiffTotal = Math.max(mHeaderDiffTotal + diff, mMinHeaderTranslation);
} else { // scrolling up
mHeaderDiffTotal = Math.min(Math.max(mHeaderDiffTotal + diff, mMinHeaderTranslation), 0);
}
mHeader.setTranslationY(mHeaderDiffTotal);
break;
case FOOTER:
if(diff < 0){ // scrolling down
mFooterDiffTotal = Math.max(mFooterDiffTotal + diff, -mMinFooterTranslation);
} else { // scrolling up
mFooterDiffTotal = Math.min(Math.max(mFooterDiffTotal + diff, -mMinFooterTranslation), 0);
}
mFooter.setTranslationY(-mFooterDiffTotal);
break;
case BOTH:
if(diff < 0){ // scrolling down
mHeaderDiffTotal = Math.max(mHeaderDiffTotal + diff, mMinHeaderTranslation);
mFooterDiffTotal = Math.max(mFooterDiffTotal + diff, -mMinFooterTranslation);
} else { // scrolling up
mHeaderDiffTotal = Math.min(Math.max(mHeaderDiffTotal + diff, mMinHeaderTranslation), 0);
mFooterDiffTotal = Math.min(Math.max(mFooterDiffTotal + diff, -mMinFooterTranslation), 0);
}
mHeader.setTranslationY(mHeaderDiffTotal);
mFooter.setTranslationY(-mFooterDiffTotal);
break;
case TWITTER:
if(diff < 0){ // scrolling down
if(scrollY > -mMinHeaderTranslation)
mHeaderDiffTotal = Math.max(mHeaderDiffTotal + diff, mMinHeaderTranslation);
if(scrollY > mMinFooterTranslation)
mFooterDiffTotal = Math.max(mFooterDiffTotal + diff, -mMinFooterTranslation);
} else { // scrolling up
mHeaderDiffTotal = Math.min(Math.max(mHeaderDiffTotal + diff, mMinHeaderTranslation), 0);
mFooterDiffTotal = Math.min(Math.max(mFooterDiffTotal + diff, -mMinFooterTranslation), 0);
}
mHeader.setTranslationY(mHeaderDiffTotal);
mFooter.setTranslationY(-mFooterDiffTotal);
default:
break;
}
}
mPrevScrollY = scrollY;
}
// region Helper Methods
public void registerExtraOnScrollListener(RecyclerView.OnScrollListener listener) {
mExtraOnScrollListenerList.add(listener);
}
// endregion
// region Inner Classes
public static class Builder {
// Required parameters
private final QuickReturnViewType mQuickReturnViewType;
// Optional parameters - initialized to default values
private View mHeader = null;
private int mMinHeaderTranslation = 0;
private View mFooter = null;
private int mMinFooterTranslation = 0;
private int mColumnCount = 1;
private boolean mIsSnappable = false;
public Builder(QuickReturnViewType quickReturnViewType) {
mQuickReturnViewType = quickReturnViewType;
}
public Builder header(View header){
mHeader = header;
return this;
}
public Builder minHeaderTranslation(int minHeaderTranslation){
mMinHeaderTranslation = minHeaderTranslation;
return this;
}
public Builder footer(View footer){
mFooter = footer;
return this;
}
public Builder minFooterTranslation(int minFooterTranslation){
mMinFooterTranslation = minFooterTranslation;
return this;
}
public Builder columnCount(int columnCount){
mColumnCount = columnCount;
return this;
}
public Builder isSnappable(boolean isSnappable){
mIsSnappable = isSnappable;
return this;
}
public QuickReturnRecyclerViewOnScrollListener build() {
return new QuickReturnRecyclerViewOnScrollListener(this);
}
}
// endregion
}