Android-ObservableScrollView
Android-ObservableScrollView copied to clipboard
Toolbar and SlidingTabLayout is overlaying ObservableRecyclerView
First of all a heartily gratitude for all of your hard works. I liked this library very much. Currently I am trying to figure out how it is working. And got stucked in between. I am using ViewPagerTabActivity and ViewPagerRecyclerViewFragment example of yours.
ViewPagerTabActivity
public class ViewPagerTabActivity extends AppCompatActivity implements ObservableScrollViewCallbacks {
private static final String TAG = ViewPagerTabActivity.class.getSimpleName();
private static final boolean IS_DEBUG = true;
private Context mContext;
private View mHeaderView;
private View mToolbarView;
private ViewPager mPager;
private NavigationAdapter mPagerAdapter;
private SlidingTabLayout slidingTabLayout;
private int mBaseTranslationY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewpagertab);
mContext = ViewPagerTabActivity.this;
setSupportActionBar(((Toolbar) findViewById(R.id.toolbar)));
mHeaderView = findViewById(R.id.header);
ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation));
mToolbarView = findViewById(R.id.toolbar);
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new NavigationAdapter(getSupportFragmentManager(), new ProductDBAdapter(mContext));
mPager.setAdapter(mPagerAdapter);
slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1);
slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.float_color));
slidingTabLayout.setDistributeEvenly(true);
slidingTabLayout.setViewPager(mPager);
slidingTabLayout.setPadding(0, mHeaderView.getHeight(), 0, 0);
// When the page is selected, other fragments' scrollY should be adjusted
// according to the toolbar status(shown/hidden)
slidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
propagateToolbarState(toolbarIsShown());
}
@Override
public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {
if(dragging) {
int toolbarHeight = mToolbarView.getHeight();
float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView);
if(firstScroll) {
if(-toolbarHeight < currentHeaderTranslationY)
mBaseTranslationY = scrollY;
}
float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0);
ViewPropertyAnimator.animate(mHeaderView).cancel();
ViewHelper.setTranslationY(mHeaderView, headerTranslationY);
// if (Config.Build.IS_DEBUG && IS_DEBUG)
// Log.d(TAG, "onScrollChanged toolbarHeight|firstScroll|mBaseTranslationY|headerTranslationY: " + toolbarHeight + "|" + firstScroll + "|" + "|" + mBaseTranslationY + "|" +headerTranslationY);
}
}
@Override
public void onDownMotionEvent() {
}
@Override
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
mBaseTranslationY = 0;
Fragment fragment = getCurrentFragment();
if(fragment == null)
return;
View view = fragment.getView();
if(view == null)
return;
// ObservableXxxViews have same API
// but currently they don't have any common interfaces.
adjustToolbar(scrollState, view);
}
private void adjustToolbar(ScrollState scrollState, View view) {
int toolbarHeight = mToolbarView.getHeight();
final Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll);
if(scrollView == null)
return;
int scrollY = scrollView.getCurrentScrollY();
if(scrollState == ScrollState.DOWN){
showToolbar();
} else if(scrollState == ScrollState.UP){
if(toolbarHeight <= scrollY){
hideToolbar();
} else {
showToolbar();
}
} else {
// Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted
if (toolbarIsShown() || toolbarIsHidden()) {
// Toolbar is completely moved, so just keep its state
// and propagate it to other pages
propagateToolbarState(toolbarIsShown());
} else {
// Toolbar is moving but doesn't know which to move:
// you can change this to hideToolbar()
showToolbar();
}
}
}
private Fragment getCurrentFragment() {
return mPagerAdapter.getItemAt(mPager.getCurrentItem());
}
private void propagateToolbarState(boolean isShown) {
int toolbarHeight = mToolbarView.getHeight();
//Set scrollY for the fragments that are not created yet
mPagerAdapter.setScrollY(isShown ? 0 : toolbarHeight);
// Set scrollY for active fragments
for(int i = 0; i < mPagerAdapter.getCount(); i++) {
// Skip current item
if(i == mPager.getCurrentItem())
continue;
// Skip destroyed or not created item
Fragment f = mPagerAdapter.getItemAt(i);
if(f == null)
continue;
View view = f.getView();
if(view == null)
continue;
propagateToolbarState(isShown, view, toolbarHeight);
}
}
private void propagateToolbarState(boolean isShown, View view, int toolbarHeight) {
Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll);
if(scrollView == null)
return;
if(isShown){
//Scroll Up
if(0 < scrollView.getCurrentScrollY())
scrollView.scrollVerticallyTo(0);
} else {
//Scroll Down (to hide listitem_stock_header)
if(scrollView.getCurrentScrollY() < toolbarHeight) {
scrollView.scrollVerticallyTo(toolbarHeight);
}
}
}
private boolean toolbarIsShown() {
return ViewHelper.getTranslationY(mHeaderView) == 0;
}
private boolean toolbarIsHidden() {
return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight();
}
private void showToolbar() {
float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);
if(headerTranslationY != 0) {
ViewPropertyAnimator.animate(mHeaderView).cancel();
ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start();
}
propagateToolbarState(true);
}
private void hideToolbar() {
float headerTranslationY = ViewHelper.getTranslationY(mHeaderView);
int toolbarHeight = mToolbarView.getHeight();
if (headerTranslationY != -toolbarHeight) {
ViewPropertyAnimator.animate(mHeaderView).cancel();
ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start();
}
propagateToolbarState(false);
}
}
Even it's XML is same as given in sample -
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
Padding for ViewPager must be set outside the ViewPager itself
because with listitem_stock_header, EdgeEffect of ViewPager become strange.
-->
<FrameLayout
android:id="@+id/pager_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:popupTheme="@style/Theme.AppCompat.Light.DarkActionBar" />
<com.groshop.jaguar.app.ui.widget.SlidingTabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_height" />
</LinearLayout>
</FrameLayout>
ViewPagerRecyclerViewFragment
public class ViewPagerRecyclerViewFragment extends Fragment {
private static final String TAG = ViewPagerRecyclerViewFragment.class.getSimpleName();
private static final boolean IS_DEBUG = true;
public static final String ARG_INITIAL_POSITION = "ARG_INITIAL_POSITION";
private ArrayList<StockModel> stockModelArrayList;
private StockRecyclerViewAdapter mAdapter;
public static ViewPagerRecyclerViewFragment getInstance(ArrayList<StockModel> stockModelArrayList) {
ViewPagerRecyclerViewFragment fragment = new ViewPagerRecyclerViewFragment();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(Config.Bundle.STOCK_LIST, stockModelArrayList);
fragment.setArguments(bundle);
return fragment;
}
public static ViewPagerRecyclerViewFragment getInstance(ArrayList<StockModel> stockModelArrayList, int initalPosition) {
ViewPagerRecyclerViewFragment fragment = new ViewPagerRecyclerViewFragment();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(Config.Bundle.STOCK_LIST, stockModelArrayList);
bundle.putInt(ARG_INITIAL_POSITION, initalPosition);
fragment.setArguments(bundle);
return fragment;
}
public ViewPagerRecyclerViewFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initVals();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_viewpager_recyclerview, container, false);
Activity parentActivity = getActivity();
final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll);
recyclerView.setLayoutManager(new LinearLayoutManager(parentActivity));
recyclerView.setHasFixedSize(false);
mAdapter = new StockRecyclerViewAdapter(parentActivity, stockModelArrayList);
recyclerView.setAdapter(mAdapter);
// setDummyDataWithHeader(recyclerView, headerView);
if (parentActivity instanceof ObservableScrollViewCallbacks) {
// Scroll to the specified offset after layout
Bundle args = getArguments();
if (args != null && args.containsKey(ARG_INITIAL_POSITION)) {
final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0);
ScrollUtils.addOnGlobalLayoutListener(recyclerView, new Runnable() {
@Override
public void run() {
recyclerView.scrollVerticallyToPosition(initialPosition);
}
});
}
// TouchInterceptionViewGroup should be a parent view other than ViewPager.
// This is a workaround for the issue #117:
// https://github.com/ksoichiro/Android-ObservableScrollView/issues/117
recyclerView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root));
recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity);
}
return view;
}
private void initVals() {
Bundle bundle = getArguments();
if(null != bundle && bundle.containsKey(Config.Bundle.STOCK_LIST)) {
stockModelArrayList = (ArrayList<StockModel>) bundle.get(Config.Bundle.STOCK_LIST);
} else
stockModelArrayList = new ArrayList<>();
}
}
And XML for fragment is almost similar except few changes.
<com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true"
android:overScrollMode="never"
android:scrollIndicators="right"
android:scrollbarAlwaysDrawVerticalTrack="true"
android:scrollbarStyle="insideOverlay"
android:scrollbarThumbVertical="@color/primary_text_disable_color"
android:scrollbars="vertical" />
If observablerecycler's adapter has some problem then here it goes -
StockRecyclerViewAdapter
public class StockRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = StockRecyclerViewAdapter.class.getSimpleName();
private static final boolean IS_DEBUG = true;
private static final int VIEW_TYPE_HEADER = 0;
private static final int VIEW_TYPE_ITEM = 1;
private LayoutInflater mInflater;
private ArrayList<StockModel> mItems;
private Context context;
public StockRecyclerViewAdapter(Context context, ArrayList<StockModel> items) {
this.context = context;
mInflater = LayoutInflater.from(context);
mItems = items;
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public int getItemViewType(int position) {
return (position == 0) ? VIEW_TYPE_HEADER : VIEW_TYPE_ITEM;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_HEADER) {
return new HeaderViewHolder(mInflater.inflate(R.layout.listitem_stock_header, parent, false), context);
} else {
return new ItemViewHolder(mInflater.inflate(R.layout.listitem_stock, parent, false), context);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
TextView lblDescription;
SquareNetworkImageView imgProduct;
CardView container;
if (viewHolder instanceof ItemViewHolder) {
lblDescription = ((ItemViewHolder) viewHolder).textView;
imgProduct = ((ItemViewHolder) viewHolder).img;
container = ((ItemViewHolder) viewHolder).container;
} else {
lblDescription = ((HeaderViewHolder) viewHolder).textView;
imgProduct = ((HeaderViewHolder) viewHolder).img;
container = ((HeaderViewHolder) viewHolder).container;
}
lblDescription.setText(mItems.get(position).getProductName());
if (null != mItems.get(position).getImageServer()
&& Patterns.WEB_URL.matcher(mItems.get(position).getImageServer()).matches()) {
imgProduct.setImageUrl(mItems.get(position).getImageServer(),
Jaguar.getInstance().getImageLoader());
}
container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StockModel data = (StockModel) v.getTag();
// TODO display overlay for changing stock
}
});
container.setTag(mItems.get(position));
}
static class HeaderViewHolder extends RecyclerView.ViewHolder {
TextView textView;
SquareNetworkImageView img;
CardView container;
public HeaderViewHolder(View view, Context context) {
super(view);
textView = (TextView) view.findViewById(android.R.id.text1);
textView.setTextColor(context.getResources().getColor(R.color.gray_blue));
img = (SquareNetworkImageView) view.findViewById(R.id.img_product);
container = (CardView) view.findViewById(R.id.cv_container);
}
}
static class ItemViewHolder extends RecyclerView.ViewHolder {
TextView textView;
SquareNetworkImageView img;
CardView container;
public ItemViewHolder(View view, Context context) {
super(view);
textView = (TextView) view.findViewById(android.R.id.text1);
textView.setTextColor(context.getResources().getColor(R.color.gray_blue));
img = (SquareNetworkImageView) view.findViewById(R.id.img_product);
container = (CardView) view.findViewById(R.id.cv_container);
}
}
}
It has two different layouts -
listitem_stock_header
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:minHeight="?attr/actionBarSize" />
<android.support.v7.widget.CardView
android:id="@+id/cv_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="?attr/actionBarSize"
app:cardCornerRadius="2dp"
app:cardMaxElevation="2dp"
app:cardUseCompatPadding="true"
app:contentPaddingBottom="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"
android:weightSum="4">
<com.groshop.jaguar.app.ui.widget.SquareNetworkImageView
android:id="@+id/img_product"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.75"
android:padding="?android:attr/listPreferredItemPaddingLeft"
android:scaleType="fitXY" />
<TextView
android:id="@android:id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3.25"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
And other is ### listitem_stock ###
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/cv_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="?attr/actionBarSize"
app:cardCornerRadius="2dp"
app:cardMaxElevation="2dp"
app:cardUseCompatPadding="true"
app:contentPaddingBottom="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"
android:weightSum="4">
<com.groshop.jaguar.app.ui.widget.SquareNetworkImageView
android:id="@+id/img_product"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.75"
android:padding="?android:attr/listPreferredItemPaddingLeft"
android:scaleType="fitXY" />
<TextView
android:id="@android:id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3.25"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
</LinearLayout>
</android.support.v7.widget.CardView>
And this is what I get as result -

If you see there are two images. One is at initial state (observe red markings) which is showing tool bar whereas other is toolbar in hidden state.
Issue is that, first 2 objects of observablerecyclerview is always remaining in hidden state, no matter what.
Thanks! :)
@jimitpatel Found any solution for this?
@vikalppatelce I have stopped using this library and using core AOSP. It's mixture of CoordinatingLayout, AppBarLayout, ViewPager
@jimitpatel Cool Thanks for reverting! :+1: