kmshack / Android-ParallaxHeaderViewPager

DEPRECATED
Apache License 2.0
1.39k stars 336 forks source link

How to Keep Track of Scroll Position and Header Size Across Tabs (Enhancement) :) #11

Open nasserprofessional opened 9 years ago

nasserprofessional commented 9 years ago

I was having some problems implementing this because of it losing scroll position and not maintaining header size across tabs when I tried to get the scroll position working.

This is my solution:

A way you can hack it to make it remember scroll position as well as keep track of the header state across all tabs.

Not the best solution but might give someone some ideas:

SampleListFragment: package com.kmshack.newsstand;

import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;

import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast;

public class SampleListFragment extends ScrollTabHolderFragment implements OnScrollListener {

private static final String ARG_POSITION = "position";

Map<String, String> map = new HashMap<String, String>(); int scrollh;

private ListView mListView; private ArrayList mListItems;

private int mPosition;

private int index = -1; private int top = 0;

int sheight; int mMinHeaderHeight;

int mHeaderHeight; public static Fragment newInstance(int position) { SampleListFragment f = new SampleListFragment(); Bundle b = new Bundle(); b.putInt(ARG_POSITION, position); f.setArguments(b); return f; }

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPosition = getArguments().getInt(ARG_POSITION);

mListItems = new ArrayList<String>();

for (int i = 1; i <= 100; i++) {
    mListItems.add(i + ". item - currnet page: " + (mPosition + 1));
}

}

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_list, null);

mListView = (ListView) v.findViewById(R.id.listView);
mMinHeaderHeight = getResources().getDimensionPixelSize(R.dimen.min_header_height);
mHeaderHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
View placeHolderView = inflater.inflate(R.layout.view_header_placeholder, mListView, false);
mListView.addHeaderView(placeHolderView);

return v;

}

@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState);

mListView.setOnScrollListener(this);
mListView.setAdapter(new ArrayAdapter<String>(getActivity(), R.layout.list_item, android.R.id.text1, mListItems));

}

@Override public void onResume() { super.onResume();

  if(index!=-1){
     //mListView.setSelectionFromTop(index, top);

  }

 // if(MainActivity.getscrollh()==288){

    //int nin =Integer.parseInt( map.get(""+mPosition+"n"+index));
    //mListView.setSelectionFromTop(nin, -mMinHeaderHeight+200);

    //mListView.setSelectionFromTop(nin, 1);
  //}

}

@Override public void onPause() { super.onPause(); try{ index = mListView.getFirstVisiblePosition(); View v = mListView.getChildAt(0); top = (v == null) ? 0 : v.getTop();

     map.put(""+mPosition+"n"+index, ""+index);

  }
  catch(Throwable t){
     t.printStackTrace();
  }

}

@Override public void adjustScroll(int scrollHeight) { scrollh=scrollHeight;

MainActivity.setscrollh(scrollHeight); if (scrollHeight == 0 && mListView.getFirstVisiblePosition() >= 0) { return; }

if(MainActivity.getscrollh()==288){

    int nin =Integer.parseInt( map.get(""+mPosition+"n"+index));
    mListView.setSelectionFromTop(nin, -mMinHeaderHeight+90);
}else{

    mListView.setSelectionFromTop(1, scrollHeight);
}

}

@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mScrollTabHolder != null)

    mScrollTabHolder.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount, mPosition);

 try{

     index = mListView.getFirstVisiblePosition();
     View v = mListView.getChildAt(0);
     top = (v == null) ? 0 : v.getTop();

     map.put(""+mPosition+"n"+index, ""+index);
 }
     catch(Throwable t){
         t.printStackTrace();
      }

}

@Override public void onScrollStateChanged(AbsListView view, int scrollState) { // nothing }

}

MainActivity: package com.kmshack.newsstand;

import android.annotation.TargetApi; import android.graphics.RectF; import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.util.SparseArrayCompat; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBarActivity; import android.text.Spannable; import android.text.SpannableString; import android.util.TypedValue; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.AbsListView; import android.widget.ImageView;

import com.astuetz.PagerSlidingTabStrip; import com.flavienlaurent.notboringactionbar.AlphaForegroundColorSpan; import com.flavienlaurent.notboringactionbar.KenBurnsSupportView; import com.nineoldandroids.view.ViewHelper;

public class MainActivity extends ActionBarActivity implements ScrollTabHolder, ViewPager.OnPageChangeListener {

static int scrollh;

private static AccelerateDecelerateInterpolator sSmoothInterpolator = new AccelerateDecelerateInterpolator();

private KenBurnsSupportView mHeaderPicture; private View mHeader;

private PagerSlidingTabStrip mPagerSlidingTabStrip; private ViewPager mViewPager; private PagerAdapter mPagerAdapter;

private int mActionBarHeight; private int mMinHeaderHeight; private int mHeaderHeight; private int mMinHeaderTranslation; private ImageView mHeaderLogo;

private RectF mRect1 = new RectF(); private RectF mRect2 = new RectF();

private TypedValue mTypedValue = new TypedValue(); private SpannableString mSpannableString; private AlphaForegroundColorSpan mAlphaForegroundColorSpan;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

mMinHeaderHeight = getResources().getDimensionPixelSize(R.dimen.min_header_height);
mHeaderHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
mMinHeaderTranslation = -mMinHeaderHeight + getActionBarHeight();

setContentView(R.layout.activity_main);

mHeaderPicture = (KenBurnsSupportView) findViewById(R.id.header_picture);
mHeaderPicture.setResourceIds(R.drawable.pic0, R.drawable.pic1);
mHeaderLogo = (ImageView) findViewById(R.id.header_logo);
mHeader = findViewById(R.id.header);

mPagerSlidingTabStrip = (PagerSlidingTabStrip) findViewById(R.id.tabs);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setOffscreenPageLimit(4);

mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
mPagerAdapter.setTabHolderScrollingContent(this);

mViewPager.setAdapter(mPagerAdapter);

mPagerSlidingTabStrip.setViewPager(mViewPager);
mPagerSlidingTabStrip.setOnPageChangeListener(this);
mSpannableString = new SpannableString(getString(R.string.actionbar_title));
mAlphaForegroundColorSpan = new AlphaForegroundColorSpan(0xffffffff);

ViewHelper.setAlpha(getActionBarIconView(), 0f);

getSupportActionBar().setBackgroundDrawable(null);

}

@Override public void onPageScrollStateChanged(int arg0) { // nothing }

@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // nothing }

@Override public void onPageSelected(int position) { SparseArrayCompat scrollTabHolders = mPagerAdapter.getScrollTabHolders(); ScrollTabHolder currentHolder = scrollTabHolders.valueAt(position);

currentHolder.adjustScroll((int) (mHeader.getHeight() + ViewHelper.getTranslationY(mHeader)));

}

@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount, int pagePosition) { if (mViewPager.getCurrentItem() == pagePosition) { int scrollY = getScrollY(view); ViewHelper.setTranslationY(mHeader, Math.max(-scrollY, mMinHeaderTranslation)); float ratio = clamp(ViewHelper.getTranslationY(mHeader) / mMinHeaderTranslation, 0.0f, 1.0f); interpolate(mHeaderLogo, getActionBarIconView(), sSmoothInterpolator.getInterpolation(ratio)); setTitleAlpha(clamp(5.0F * ratio - 4.0F, 0.0F, 1.0F)); } }

@Override public void adjustScroll(int scrollHeight) { // nothing }

public int getScrollY(AbsListView view) { View c = view.getChildAt(0); if (c == null) { return 0; }

int firstVisiblePosition = view.getFirstVisiblePosition();
int top = c.getTop();

int headerHeight = 0;
if (firstVisiblePosition >= 1) {
    headerHeight = mHeaderHeight;
}

return -top + firstVisiblePosition * c.getHeight() + headerHeight;

}

public static float clamp(float value, float max, float min) { return Math.max(Math.min(value, min), max); }

private void interpolate(View view1, View view2, float interpolation) { getOnScreenRect(mRect1, view1); getOnScreenRect(mRect2, view2);

float scaleX = 1.0F + interpolation * (mRect2.width() / mRect1.width() - 1.0F);
float scaleY = 1.0F + interpolation * (mRect2.height() / mRect1.height() - 1.0F);
float translationX = 0.5F * (interpolation * (mRect2.left + mRect2.right - mRect1.left - mRect1.right));
float translationY = 0.5F * (interpolation * (mRect2.top + mRect2.bottom - mRect1.top - mRect1.bottom));

ViewHelper.setTranslationX(view1, translationX);
ViewHelper.setTranslationY(view1, translationY - ViewHelper.getTranslationY(mHeader));
ViewHelper.setScaleX(view1, scaleX);
ViewHelper.setScaleY(view1, scaleY);

}

private RectF getOnScreenRect(RectF rect, View view) { rect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); return rect; }

@TargetApi(Build.VERSION_CODES.HONEYCOMB) public int getActionBarHeight() { if (mActionBarHeight != 0) { return mActionBarHeight; }

if(Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB){
    getTheme().resolveAttribute(android.R.attr.actionBarSize, mTypedValue, true);
}else{
    getTheme().resolveAttribute(R.attr.actionBarSize, mTypedValue, true);
}

mActionBarHeight = TypedValue.complexToDimensionPixelSize(mTypedValue.data, getResources().getDisplayMetrics());

return mActionBarHeight;

}

private void setTitleAlpha(float alpha) { mAlphaForegroundColorSpan.setAlpha(alpha); mSpannableString.setSpan(mAlphaForegroundColorSpan, 0, mSpannableString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); getSupportActionBar().setTitle(mSpannableString); }

@TargetApi(Build.VERSION_CODES.HONEYCOMB) private ImageView getActionBarIconView() {

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
    return (ImageView)findViewById(android.R.id.home);
}

return (ImageView)findViewById(android.support.v7.appcompat.R.id.home);

}

public class PagerAdapter extends FragmentPagerAdapter {

private SparseArrayCompat<ScrollTabHolder> mScrollTabHolders;
private final String[] TITLES = { "Page 1", "Page 2", "Page 3", "Page 4"};
private ScrollTabHolder mListener;

public PagerAdapter(FragmentManager fm) {
    super(fm);
    mScrollTabHolders = new SparseArrayCompat<ScrollTabHolder>();
}

public void setTabHolderScrollingContent(ScrollTabHolder listener) {
    mListener = listener;
}

@Override
public CharSequence getPageTitle(int position) {
    return TITLES[position];
}

@Override
public int getCount() {
    return TITLES.length;
}

@Override
public Fragment getItem(int position) {
    ScrollTabHolderFragment fragment = (ScrollTabHolderFragment) SampleListFragment.newInstance(position);

    mScrollTabHolders.put(position, fragment);
    if (mListener != null) {
        fragment.setScrollTabHolder(mListener);
    }

    return fragment;
}

public SparseArrayCompat<ScrollTabHolder> getScrollTabHolders() {
    return mScrollTabHolders;
}

}

public static void setscrollh(int h){ scrollh=h;

}

public static int getscrollh(){

return scrollh;

}

}

I added a hashmap that keeps track of the scrolling position for each listview. Views settings are automatically added to the hashmap and then their settings are loaded back when needed.

I also wrote a function in the activity that basically keeps track of the scroll height. Obviously the header height is changing in 1 listview and when we switch tabs we want to be able to ask the activity the height of the original header. We then do a check to see if it was the minimum header size or the full header size then we simply do some tweaking to align things a bit.

There are probably better ways to do it... this is just an idea that allows you to keep track of scroll positions across listviews and maintain headers.

:)

If anyone comes up with anything better or this helps in anyway do let me know.

hustwht commented 8 years ago

I found a solution for the problem, in the function below @Override public void adjustScroll(int scrollHeight) { if (scrollHeight == 0 && mListView.getFirstVisiblePosition() >= 1) { return; }

    mListView.setSelectionFromTop(1, scrollHeight);

}

the problem is the scrollHeight == 0 , if the fragment has a titlebar, then the value is not 0, should be the height of titlebar, because scrollHeight = header.height - header.translationY, I changed the value, the I keep the scroll position when across the tabs. I am so sorry for my English, I hope you understand me.

hustwht commented 8 years ago

@nasserprofessional

Sakshamgupta20 commented 6 years ago

when activity is destroyed we have to store the scrollable position so for that

public static int index = -1; public static int top = -1;

@override public void onPause() { super.onPause(); index =list.getFirstVisiblePosition(); View v = list.getChildAt(0); top = (v == null) ? 0 : (v.getTop() - list.getPaddingTop()); } @override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("index",index); outState.putInt("top",top); }

and then in onloadfinished/in oncreateview add if(index != -1) { list.setSelectionFromTop( index, top); }