I try to implement a layout like below image.
in this layout first ViewPager has fix height and second Viewpager contain RecyclerView.
How can connect scroll RecyclerView in Viewpager to NestedScrollView in parent?
I try this:
nestedScrollView.isFillViewport = true but RecyclerView scroll from top of Viewpager.
I have faced this issue some time ago. Where you want RecyclerView to be Scrolled by NestedScrollView. And nestedScrollingEnabled will not help you because RecyclerView is inside ViewPager.
Solution You can customize the ViewPager to resize the ViewPager to it's current page size on page swipe from this answer.
You can use the following code:
public class WrapContentViewPager extends ViewPager {
private int mCurrentPagePosition = 0;
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
if (wrapHeight) {
View child = getChildAt(mCurrentPagePosition);
if (child != null) {
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
}
}
} catch (Exception e) {
e.printStackTrace();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void reMeasureCurrentPage(int position) {
mCurrentPagePosition = position;
requestLayout();
}
}
Declare it in xml:
<your.package.name.WrapContentViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</your.package.name.WrapContentViewPager>
After that call reMeasureCurrentpage function on page swipe.
final WrapContentViewPager wrapContentViewPager = (WrapContentViewPager) findViewById(R.id.view_pager);
wrapContentViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
wrapContentViewPager.reMeasureCurrentPage(wrapContentViewPager.getCurrentItem());
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Also set android:nestedScrollingEnabled="false" to your RecyclerView.
Set these line on your RecyclerView in XML
android:nestedScrollingEnabled="false"
android:height="wrap_content"
Make your layout like this will work
Related
I would like to know if is it possible to have a PagerAdapter with wrap_content without using getChildCount() and getChildAt(i)?
I have checked the answer below, but since on my adapter doesn't have children it doesn't work.
https://stackoverflow.com/a/20784791/10020799
Does someone know how to make an wrap_content adapter in this case?
My PagerAdapter code is:
public class SectionsPagerAdapter extends PagerAdapter {
private Integer pagerTotal = 10;
public SectionsPagerAdapter(Context c) {
super();
inflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setPagerTotal(Integer item) {
pagerTotal = item;
}
#Override
public int getCount() {
return pagerTotal;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.home_top_page, null);
container.addView(layout);
topMain = (ImageView) layout.findViewById(R.id.top_main);
if(imageBitmapArray != null && imageBitmapArray.length > 0){
topMain.setImageBitmap(imageBitmapArray[position]);
}else{
if(intImageArray != null) {
topMain.setImageResource(R.drawable.top_main_01);
}
}
return layout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
}
You can resize the ViewPager to it's current page size on page swipe from this answer.
You can use the following code:
public class WrapContentViewPager extends ViewPager {
private int mCurrentPagePosition = 0;
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
if (wrapHeight) {
View child = getChildAt(mCurrentPagePosition);
if (child != null) {
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
}
}
} catch (Exception e) {
e.printStackTrace();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void reMeasureCurrentPage(int position) {
mCurrentPagePosition = position;
requestLayout();
}
}
Use it in xml:
<your.package.name.WrapContentViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</your.package.name.WrapContentViewPager>
After that call reMeasureCurrentpage function on page swipe.
final WrapContentViewPager wrapContentViewPager = (WrapContentViewPager) findViewById(R.id.view_pager);
wrapContentViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
wrapContentViewPager.reMeasureCurrentPage(wrapContentViewPager.getCurrentItem());
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Note that a list can have hundreds of items then this is not good to use this wrap_content ViewPager.
Even with a not expandable adapter is possible to use getChildCount() and getChildAt(i) functions.
Based on Khemraj's answer and this post's answer, I was able to solve the problem with the following code:
class:
public class WrapContentViewPager extends ViewPager {
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for(int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
}
if (height != 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
xml:
<your.package.name.WrapContentViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</your.package.name.WrapContentViewPager>
on page swipe:
final WrapContentViewPager wrapContentViewPager = (WrapContentViewPager) findViewById(R.id.view_pager);
wrapContentViewPager.addOnPageChangeListener(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) {
}
});
I am using:
An activity layout containing fragment added programmatically.
Parent fragment has FragmentStatePagerAdapter with view pager, This all content is inside ScrollView.
View pager is filled with some child fragments which has Recyclerview.
Issues are:
View pager fragment has a list, so I have to programmatically set parent view pager height. But I can't do that.
For that, I have used
viewpager.getLayoutParams().height = 20000;
viewpager.requestLayout();`
and
ViewGroup.LayoutParams params = viewPagerBottom.getLayoutParams();
params.height = 20000;
viewPagerBottom.setLayoutParams(params);
Also, I have tried view pager and view pager fragments height property as wrap_content
Can't see view pager content if i dont set scrollview android:layout_height="match_parent" with android:fillViewport="true".
It is parent fragment layout.xml.
<ScrollView
style="#style/scrollDefaultStyle"
android:layout_height="wrap_content"
android:fillViewport="true"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TabLayout .... />
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</ScrollView>
You can customize the ViewPager to resize the ViewPager to it's current page size on page swipe from this answer.
You can use the following code:
public class WrapContentViewPager extends ViewPager {
private int mCurrentPagePosition = 0;
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
if (wrapHeight) {
View child = getChildAt(mCurrentPagePosition);
if (child != null) {
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
}
}
} catch (Exception e) {
e.printStackTrace();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void reMeasureCurrentPage(int position) {
mCurrentPagePosition = position;
requestLayout();
}
}
Declare it in xml:
<your.package.name.WrapContentViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</your.package.name.WrapContentViewPager>
After that call reMeasureCurrentpage function on page swipe.
final WrapContentViewPager wrapContentViewPager = (WrapContentViewPager) findViewById(R.id.view_pager);
wrapContentViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
wrapContentViewPager.reMeasureCurrentPage(wrapContentViewPager.getCurrentItem());
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Note: There could be hundreds of rows in ListView. It's not good idea to use wrap_content for ListView. Either you need to give fixed height for the ListView or else you need to use match_parent.
I am having view pager with 4 different fragments. In this, 3 fragments only contains webview. All the fragments load different contents. I unable to set a height based on the loaded content. For that, I wrote a custom viewpager to measure height of viewpager. For the first viewpager fragment, height is calculated properly. But when navigate to second or third fragment, viewpager height is not calculated accurately. I don't know, what is the issue is in my code and what i need to change to calculate proper height for each fragment after navigate to that view pager page. Here is my view pager and pager adapter codes. Kindly help me.
Custom View pager:
public class CustomViewPager extends ViewPager {
public CustomViewPager (Context context) {
super(context);
}
public CustomViewPager (Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
final View tab = getChildAt(0);
int width = getMeasuredWidth();
int tabHeight = tab.getMeasuredHeight();
if (wrapHeight) {
// Keep the current measured width.
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
}
int fragmentHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, getCurrentItem())).getView());
heightMeasureSpec = MeasureSpec.makeMeasureSpec(tabHeight + fragmentHeight + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()), MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
public void setOnPageChangeListener(OnPageChangeListener listener) {
requestLayout();
invalidate();
super.setOnPageChangeListener(listener);
}
#Override
public void addOnPageChangeListener(OnPageChangeListener listener) {
super.addOnPageChangeListener(listener);
}
public int measureFragment(View view) {
if (view == null)
return 0;
view.measure(0, 0);
return view.getMeasuredHeight();
}
}
PagerAdapter:
public class MerchantsCataAdapter extends FragmentPagerAdapter {
Context context;
Merchant merchant;
Fragment fragment;
private int mCurrentPosition = -1;
private String[] tabs = {"Home", "About Us","Promotions","Store Information"};
public MerchantsCataAdapter(FragmentManager fm, Context context,Merchant merchant) {
super(fm);
this.context = context;
this.merchant = merchant;
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
fragment= new HomeContentFragment().newInstance(merchant.getCompany_home_content(),merchant.getVideo_url());
break;
case 1:
fragment= new AboutusFragment().newInstance(merchant.getFull_description(),merchant.getVideo_url());
break;
case 2:
fragment= new AboutusFragment().newInstance(merchant.getCompany_promotions(),merchant.getVideo_url());
break;
case 3:
fragment= new FragmentStoreInformation().newInstance(merchant.getMerchantId(),merchant.getName());
break;
}
return fragment;
}
#Override
public int getCount() {
return tabs.length;
}
public String getTabView(int position) {
return tabs[position];
}
Finally, I find the answer. Place below property in Webview in xml. This will take webview height automatically based on the content.
android:scrollbarStyle="insideOverlay"
I got the following structure:
<CoordinatorLayout>
<AppBar>
<CollapsingToolbar
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"/>
</AppBar>
<NestedScrollView
android:layout_height="wrap_content"
android:fillViewport="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<ViewPager />
</NestedScrollView>
</CoordinatorLayout>
The fragment inside the ViewPager looks like:
<LinearLayout
android:layout_height="wrap_content">
<RecyclerView
android:layout_height="wrap_content" />
</LinearLayout>
Inside this Fragment's RecyclerView, I got
<RelativeLayout
android:layout_height="wrap_content">
<...>
<RecyclerView
....
android:layout_height="wrap_content" />
</RelativeLayout>
Now the problem is, that the Toolbar gets collapsed correctly when I scroll the List below. But when the Toolbar is fully collapsed, no more scrolling is happening and I can't figure out how to achieve that.
For every RecyclerView, I am using a LinearLayoutManager with AutoMeasureEnabled set to true. Furthermore, HasFixedSize-Flags are set to false.
Any ideas?
//Edit: the content is not fixed; it gets passed in via reactiveUI.
You have to make view pager with dynamic height, I use below custom view pager inside NestedScrollView,
public class CustomPager extends ViewPager {
private View mCurrentView;
public CustomPager(Context context) {
super(context);
}
public CustomPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mCurrentView == null) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
int height = 0;
mCurrentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = mCurrentView.getMeasuredHeight();
if (h > height) height = h;
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void measureCurrentView(View currentView) {
mCurrentView = currentView;
requestLayout();
}
public int measureFragment(View view) {
if (view == null)
return 0;
view.measure(0, 0);
return view.getMeasuredHeight();
}
}
and Override below method into your FragmentStatePagerAdapter or FragmentPagerAdapter
private int mCurrentPosition = -1;
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
if (position != mCurrentPosition) {
Fragment fragment = (Fragment) object;
CustomPager pager = (CustomPager) container;
if (fragment != null && fragment.getView() != null) {
mCurrentPosition = position;
pager.measureCurrentView(fragment.getView());
}
}
}
CollapsingToolbar layout scroll set this propety
app:layout_scrollFlags="scroll|exitUntilCollapsed"
make changes like this , this will work
<CoordinatorLayout>
<AppBar>
<CollapsingToolbar
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"/>
</AppBar>
<ViewPager
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</CoordinatorLayout>
and in your fragments layout take parent as NestedScrollView and it'll work.
I am trying to show horizontal scroll view with images to scroll in two directions.Previously for this i used Gallery View but galleryview is deprected ,i am using horizontal scroll view instead of gallery view but horizontal scroll view is different with gallery view.
Now i have to do two implementation
1) Scrolling in two directions continuously.
2) Center lock feature as same as gallery.
My screen looks like
you can use viewpager,
try this code and modify it as your need .
note : this not my code already found it on the net belong to:
( Dave Smith,#devunwired Date: 8/17/12 PagerActivity).
MainActivity:
public class MainActivity extends Activity{
PagerContainer mContainer;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContainer = (PagerContainer) findViewById(R.id.pager_container);
ViewPager pager = mContainer.getViewPager();
PagerAdapter adapter = new MyPagerAdapter();
pager.setAdapter(adapter);
// Necessary or the pager will only have one extra page to show
// make this at least however many pages you can see
pager.setOffscreenPageLimit(adapter.getCount());
// A little space between pages
pager.setPageMargin(15);}
// Nothing special about this adapter, just throwing up colored views for
// demo
private class MyPagerAdapter extends PagerAdapter{
#Override
public Object instantiateItem(ViewGroup container, int position){
TextView view = new TextView(MainActivity.this);
view.setText("Item " + position);
view.setGravity(Gravity.CENTER);
view.setBackgroundColor(Color.argb(255, position * 50,
position * 10, position * 50));
container.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object)
{
container.removeView((View) object);}
#Override
public int getCount(){
return 5;
}
#Override
public boolean isViewFromObject(View view, Object object)
{
return (view == object);
}}}
PagerContainer:
public class PagerContainer extends FrameLayout implements
ViewPager.OnPageChangeListener {
private ViewPager mPager;
boolean mNeedsRedraw = false;
public PagerContainer(Context context) {
super(context);
init();
}
public PagerContainer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PagerContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setClipChildren(false);
}
#Override
protected void onFinishInflate() {
try {
mPager = (ViewPager) getChildAt(0);
mPager.setOnPageChangeListener(this);
} catch (Exception e) {
throw new IllegalStateException("The root child of PagerContainer must be a
ViewPager");
}
}
public ViewPager getViewPager() {
return mPager;
}
private Point mCenter = new Point();
private Point mInitialTouch = new Point();
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenter.x = w / 2;
mCenter.y = h / 2;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
//We capture any touches not already handled by the ViewPager
// to implement scrolling from a touch outside the pager bounds.
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialTouch.x = (int)ev.getX();
mInitialTouch.y = (int)ev.getY();
default:
ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y -
mInitialTouch.y);
break;
}
return mPager.dispatchTouchEvent(ev);
}
public void onPageScrolled(int position, float positionOffset, int
positionOffsetPixels) {
//Force the container to redraw on scrolling.
//Without this the outer pages render initially and then stay static
if (mNeedsRedraw) invalidate();
}
public void onPageSelected(int position) { }
public void onPageScrollStateChanged(int state) {
mNeedsRedraw = (state != ViewPager.SCROLL_STATE_IDLE);
}
}
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.testviewpager.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#CCC" >
<android.support.v4.view.ViewPager
android:layout_width="150dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal" />
</com.example.testviewpager.PagerContainer>
</RelativeLayout>
hope help you.