how do I add margin at the end of a ViewPager? - android

I'm dealing with legacy code that uses a viewpager to display an horizontal list of images(instead of say a listview or recyclerview) in a fragment. The viewpager uses a custom PagerAdapter which overrides the appropriate methods(instantiateItem, getItemPositiion etc). My issue right now is that I want to add some space at the end of the final element of the viewpager, I've attempted to do this by adding a margin to this final elment.
public Object instantiateItem(ViewGroup collection, int position) {
FrameLayout itemView = ViewManager.createView(position, height, type);
int viewWidth = itemView.getLayoutParams().width;
RelativeLayout itemViewWrap = new RelativeLayout(mContext);
if(postion == items.size()-1){
RelativeLayout.LayoutParams itemViewparams = RelaiveLayout.LayoutParams)itemView.getLayoutParams();
itemViewparams.setMargins(0, 0, viewWidth, 0);
}
itemViewWrap.addView(itemView);
collection.addView(itemViewWrap);
return itemView;
}
Unfortunately this does not work and the final element is just not shown. How do I properly achieve this? Here is the xml for the fragment in which this viewpager resides.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.playground.test.views.ItemViewPager
android:id="#+id/viewpager"
android:layout_gravity="center_vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
</RelativeLayout>

You could override the last page's width using ViewPagerAdapter# getPageWidth. To be more than the rest of the page's with the margin you want.
https://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getPageWidth(int)

Related

Change viewpager and viewpager item size dynamically with listview scroll

I have a listview below a viewpager and in the initial state (when nothing has been scrolled), the viewpager shows only one item with a 10dp "preview" of the next and previous items (I have achieved this by setting a negative page margin:viewPager.setPageMargin(-48);). What I am trying to do is, on scrolling down the listview:
1) the listview should "push" the viewpager up, decreasing its height up to a certain point. On reaching that point (some minHeight for the viewpager), the listview should scroll normally with the smaller sized viewpager above it.
2) The next and the previous items in the viewpager should pull inside (towards the central item) and in the final state, three items of the viewpager should be fully displayed. (Images below to illustrate this)
Scrolling up the listview should do the opposite.
I have managed to do part (1) of my task. Here's the code
My viewpager and listview are inside a FrameLayout like this:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:showIn="#layout/activity_main" tools:context=".MainActivity">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:dividerHeight="1dp"
android:divider="#000000"
android:scrollbars="none" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:background="#FFFFFF"/>
</FrameLayout>
I "fake" the listview to be below the viewpager by adding a transaprent header view to the listview and making the heights of both the headeview and the viewpager same. Here's a snippet of the code:
screenWidth = // Screen width of the phone
headerHeight = // Required height of the viewpager and the headerview
headerView = inflater.inflate(R.layout.fake_list_header, listView, false);
headerView.getLayoutParams().height = headerHeight;
headerView.getLayoutParams().width = screenWidth;
viewPager.getLayoutParams().height = headerHeight;
viewPager.getLayoutParams().width = screenWidth;
viewPager.setPageMargin(negativeMargin);
listView.addHeaderView(headerView, null, false);
// Other initializations and stuff
fake_list_header layout file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Finally, my listview OnScrollListener that takes care of adjusting the viewpager height depending on the amount scrolled by the listview and stopping when we reach the minimum height for the viewpager:
OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView absListView, int i) {
}
#Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
if (listview.getFirstVisiblePosition() == 0) {
View firstChild = listview.getChildAt(1); // 0th element is the fake headerview itself
int topY = 0;
if (firstChild != null) {
topY = firstChild.getTop();
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.width = screenWidth;
layoutParams.height = topY;
if (topY < headerHeight && topY >= minHeight) {
// minHeight is the minimum height the viewpager takes, after this point it stops getting smaller
//And vice-versa with headerHeight taking care of the maximum height the viewpager can take
viewpager.setLayoutParams(layoutParams);
}
}
}
}
}
Part (2) of my task is where I am stuck (and running out of ideas), I have tried changing pageMargin of the viewpager with the scroll but the results aren't good (also don't think it is the right approach for achieving something like this). Setting X position of the next(or previous) view in the pager by calling setTranslationX with scroll also isn't working.
Here are some mocks of what I am trying to achieve:
Initial state (nothing scrolled)
Final state (minHeight of viewpager achieved)
Is using viewpager and a listview right way of achieving something like this? I thought of using a horizontal recyclerview instead of a viewpager, but I need the "page by page" scroll behavior of a viewpager for the horizontal scroll/swipe of items. Any suggestions welcome
Try this in your main layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:showIn="#layout/activity_main" tools:context=".MainActivity">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:dividerHeight="1dp"
android:layout_below="#+id/pager"
android:divider="#000000"
android:scrollbars="none" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="150dp"
android:clipToPadding="false"
android:background="#FFFFFF"/>
</RelativeLayout>

ViewPager of views (not fragments) as header of a gridview (Etsy's StaggeredGridView) in a fragment

So, I'm in a situation where I want to add a ViewPager of views (not fragments) as the header of a gridview in a fragment, but my ViewPager doesn't appear when I set it as my headerview. I'm currently using Etsy's StaggeredGridView to allow my gridview to have a header, and I'm using a PagerAdapter instead of FragmentStatePagerAdapter to be able to scroll through views instead of fragments. I was going to just use fragments, but I wasn't sure if this would be good since, I assume, that would be considered as having nested fragments, which are only supported in 4.2+ and through the support library: my app supports 4.0 and above, and at the moment, I'm using native fragments.
I also tried setting a view programatically, but still nothing shows.
I set a toast in the ViewPager to see if anything is actually getting created, and the toast shows up as expected, but still no header. The view that I'm attempting to inflate shouldn't be the problem because if I use it as a header on its own it shows. Here's what I have so far:
#Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//inflate fragment which contains the gridview. Have a pointer to the gridview.
View v = inflater.inflate(R.layout.fragment_menu, container, false);
mGridView = (StaggeredGridView) v.findViewById(R.id.dishesGridView);
View header = inflater.inflate(R.layout.view_pager_header, container, false);
ViewPager mPager = (ViewPager) header.findViewById(R.id.pager);
//list of views to test header
final List<View> views = new ArrayList<View>();
views.add(inflater.inflate(R.layout.menu_header, null));
mPager.setAdapter(new PagerAdapter() {
public View getView(int position) {
return views.get(position);
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
View v = getView(position);
((ViewPager)collection).addView(v);
Toast.makeText(getActivity(),
"Hello ViewPager",
Toast.LENGTH_SHORT).show();
return v;
}
#Override
public void destroyItem(ViewGroup collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
#Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == (View) arg1;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return views.size();
}
});
mGridView.addHeaderView(mPager);
return v;
}
Here's the view_pager_header.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Here's the menu_header.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/resHeader"
android:layout_width="wrap_content"
android:layout_height="100dp" >
<com.platey.IonPlatey.RectangleImageView
android:id="#+id/restaurant_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<ImageView
android:src="#drawable/frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<TextView
android:id="#+id/resType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:paddingRight="15dp"
android:text="ResType Here"
android:textColor="#d82727"
android:textStyle="bold" />
</RelativeLayout>
Reason:
Basically the issue is caused by ViewPager android:layout_height="match_parent".
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
The match_parent is wrong here for sure, because you don't want the header to fill entire parent. But regardless of that - match_parent will not work here in StaggeredGridView.
This is the same as in standard ListView items etc. - match_parent is not allowed and it will be replaced with just wrap_content while performing layout.
Unfortunately wrap_content is not supported by ViewPager because ViewPager cannot wrap it's content (it is not aware of children size).
Solution:
You have two ways to properly display a ViewPager there:
Just specify a static height (for example 50dp)
Extend your ViewPager and perform your own measuring if you don't want static height.
Screenshots
Here is also a screenshots with match_parent. You can see that ViewPager is nor displayed (has zero height):
and with static height:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="50dp" />

Setting viewpager height to content's height

I have an expandablelistview which contains multiple viewpagers, and each viewpager contains multiple pages which each have a linearlayout of data. The data in the linearlayout is dynamic (there could be 5 rows or there could be 20), and each row has a dynamic height (it could have 1 line of text or 2-3).
As the viewpager requires a hard-coded height to be set for it to display, I need to display all of the content in each page's linearlayout, and I can't allow the content within the viewpager to scroll, I'm in the difficult situation of trying to calculate the height of the viewpager's active page view and then dynamically update the height of the viewpager so that the whole linearlayout content is visible. As a new page is swiped to, it should then update the viewpager height accordingly.
I'm attempting to do this within the pager adapter's SetPrimaryItem method, however the height that is coming back is close but not accurate. In my pager page view I have an outer relativelayout with a blue background, and then an inner one with a red background. For some reason the outer (blue) layout is going maybe 70dp further than the bottom of the data, I'm guessing the pager is manipulating its height behind-the-scenes. The inner one (red) behaves correctly and wraps the data within it, so this is the one I'm trying to calculate the height of so I can update the viewpager's height, however its height is incorrectly being calculated as bigger than it is, so after I update the viewpager height there is empty space at the bottom. See the screenshot, as it probably makes more sense than what I just typed:
Another acceptable solution would be to calculate the longest page's height and set the viewpager to that once, and then don't update it as the page changes, but I haven't gone that route yet and would still run into the same issue calculating the correct height.
I've spent a day on this already, any ideas / suggestions for how I can accurately update the viewpager height to fit its contents would be appreciated. I've tried a lot of other solutions on SO and haven't found one that actually worked. Here is where I've gotten so far with it:
Pager view:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v4.view.ViewPager
android:id="#+id/property_details_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
Pager adapter: (SetPrimaryItem method is where I try to update the pager's height dynamically)
public class PropertyDetailsPagerAdapter : PagerAdapter {
private ViewPager pager;
private List<List<KeyValuePair<string, string>>> lists;
public PropertyDetailsPagerAdapter (View view, List<List<KeyValuePair<string, string>>> lists) {
this.pager = view.FindViewById<ViewPager> (Resource.Id.property_details_pager);
this.pager.OffscreenPageLimit = lists.Count;
this.lists = lists;
}
public void Update (List<List<KeyValuePair<string, string>>> lists) {
this.pager.Adapter = null;
this.lists = lists;
this.pager.Adapter = this;
this.pager.Adapter.NotifyDataSetChanged ();
}
#region Setup Views
public override Java.Lang.Object InstantiateItem (View collection, int position) {
var layoutInflater = (LayoutInflater)ApplicationContext.Activity.GetSystemService (Context.LayoutInflaterService);
var view = layoutInflater.Inflate (Resource.Layout.PropertyDetailsPagerItem, null);
var pagerList = view.FindViewById<LinearLayout> (Resource.Id.property_details_pager_list);
var list = this.lists [position];
new PropertyDetailsPagerListAdapter (pagerList, Resource.Layout.PropertyDetailsPagerKeyValueRow, list);
var listViewSwipe = view.FindViewById<LinearLayout> (Resource.Id.property_details_pager_swipe);
listViewSwipe.Visibility = this.lists.Count > 1 ? ViewStates.Visible : ViewStates.Gone;
this.pager.AddView(view);
return view;
}
public override void SetPrimaryItem (View container, int position, Java.Lang.Object #object) {
base.SetPrimaryItem (container, position, #object);
var view = this.pager.GetChildAt (position);
var innerView = view.FindViewById<RelativeLayout> (Resource.Id.property_details_pager_item);
innerView.Measure (0, 0);
var parameters = this.pager.LayoutParameters as RelativeLayout.LayoutParams;
parameters.Height = innerView.MeasuredHeight;// TODO: Why isn't this height accurate?!?!
this.pager.LayoutParameters = parameters;
}
#endregion
#region Infrastructure
// Other irrelevant methods
public override int Count {
get {
return this.lists.Count;
}
}
#endregion
public List<List<KeyValuePair<string, string>>> Lists { get { return this.lists; } }
}
View for each page within the pager:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/blue">
<RelativeLayout
android:id="#+id/property_details_pager_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/red">
<LinearLayout
android:id="#+id/property_details_pager_swipe"
android:layout_below="#+id/dotted"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:visibility="invisible" />
<RelativeLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:background="#color/light_gray">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Swipe for\nmore records"
android:textColor="#color/gray"
android:gravity="right"
android:padding="#dimen/padding" />
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:id="#+id/property_details_pager_list"
android:layout_below="#+id/property_details_pager_swipe"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</RelativeLayout>
</RelativeLayout>
Adapter for each page within the pager:
public class PropertyDetailsPagerListAdapter
{
private int resourceId;
private List<KeyValuePair<string, string>> keyValuePairs;
public PropertyDetailsPagerListAdapter (LinearLayout pagerList, int resourceId, List<KeyValuePair<string, string>> keyValuePairs) {
this.resourceId = resourceId;
this.keyValuePairs = keyValuePairs;
pagerList.RemoveAllViews ();
foreach (var keyValuePair in this.keyValuePairs) {
pagerList.AddView (GetView (keyValuePair));
}
}
public View GetView (KeyValuePair<string, string> keyValuePair) {
var layoutInflater = (LayoutInflater)ApplicationContext.Activity.GetSystemService (Context.LayoutInflaterService);
var view = layoutInflater.Inflate(resourceId, null);
// Assumes Resource.Layout.PropertyDetailsPagerKeyValueRow
view.FindViewById<TextView> (Android.Resource.Id.Text1).Text = keyValuePair.Key;
view.FindViewById<TextView> (Android.Resource.Id.Text2).Text = keyValuePair.Value;
return view;
}
}
I was able to figure it out. The issue was that I was using innerView.Measure (0,0) and then getting the MeasuredHeight, however that was giving a bogus height. After calling Measure correctly, which entails passing a real Width into it, the correct Height is returned!!
public override void SetPrimaryItem (View container, int position, Java.Lang.Object #object) {
base.SetPrimaryItem (container, position, #object);
var view = this.pager.GetChildAt (position);
var innerView = view.FindViewById<RelativeLayout> (Resource.Id.property_details_pager_item);
var desiredWidth = MeasureSpec.MakeMeasureSpec (this.pager.Width, MeasureSpecMode.AtMost);
innerView.LayoutParameters = new RelativeLayout.LayoutParams (RelativeLayout.LayoutParams.WrapContent, RelativeLayout.LayoutParams.WrapContent);
innerView.Measure (desiredWidth, (int)MeasureSpecMode.Unspecified);
var parameters = this.pager.LayoutParameters as RelativeLayout.LayoutParams;
parameters.Height = innerView.MeasuredHeight;
this.pager.LayoutParameters = parameters;
}

Android fullscreen layout with additional layouts underneath, which are scrollable

I have a layout displaying some images and text, with buttons at the bottom.
When a button is pressed, I need to display some new information below the buttons.
So the initial content needs to remain at the same height (height of the device screen), and the new content needs to be added beneath it, allowing the user to scroll down.
When a button is pressed it will ideally need to show the new content like a page anchor, but the part I'm having difficulty is getting the initial content to be fullscreened, and maintain that size when new content is added whilst also making the whole thing scroll-able.
I have been playing with different layouts, different height parameters, android:fillViewport="true" or not etc.
I can provide some XML / further explanation if necessary. But I'm not sure whether what I am aiming to achieve is possible or not. At least I'd like to get a scrollable overall view, with the top layout as fullscreen and some layouts underneath which the user can scroll to.
image:
Try this:
Make ScrollView container and add your layout #1 into it
Set height of layout #1 into the code according per screen height
After button click add layout #2 into ScrollView
UPDATED:
Ok, I can suggest you only this solution (it worked for me in emulator).
XML:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/layout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:onClick="btnClick"/>
</RelativeLayout>
</LinearLayout>
</ScrollView>
Code:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void btnClick(View v){
LinearLayout container = (LinearLayout)findViewById(R.id.container);
ScrollView scroll = (ScrollView)findViewById(R.id.scroll);
RelativeLayout layout1 = (RelativeLayout)findViewById(R.id.layout1);
RelativeLayout layout2 = new RelativeLayout(this);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, 300);
layout2.setBackgroundColor(Color.YELLOW);
layout1.getLayoutParams().height = scroll.getHeight();
scroll.setFillViewport(false);
container.addView(layout2, params);
}
}
I would create a custom ScrollView which sets the height of its first child to be its own height. This will simulate a fullscreen view while still being able to add content below it.
To handle screen size changes properly, the best is to override onSizeChanged() :
public class CustomScrollView extends ScrollView {
// ...
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
View view = getChildAt(0);
if (view != null)
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, h)));
}
}

Android ListView rows in ScrollView not fully displayed - clipped

I encountered a problem when embedding a ListView inside a ScrollView, or at least that's where I guess the problem comes from. The ListView element is a fairly simple one:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/item_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/general_background_list_middle"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingRight="0dp"
android:paddingLeft="0dp">
<ImageView
android:id="#+id/chat_friends_avatar"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginLeft="7dp"
android:layout_marginRight="8dp"
android:paddingRight="0dp"
android:paddingLeft="0dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="#drawable/friends_icon_avatar_default"/>
<TextView
android:id="#+id/chat_message_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/chat_friends_avatar"
android:layout_alignParentTop="true"
android:layout_marginRight="35dp"
android:maxLines="10"
android:textSize="12dp"/>
<TextView
android:id="#+id/chat_friend_name"
android:layout_width="140dp"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
style="#style/SubText"
android:layout_toRightOf="#id/chat_friends_avatar"
android:layout_below="#id/chat_message_text" />
<TextView
android:id="#+id/chat_message_time"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginTop="3dp"
style="#style/SubText"
android:layout_alignParentRight="true"
android:layout_below="#id/chat_message_text" />
</RelativeLayout>
However, when I embed a list of such elements in a ScrollView, in between some other elements, the rows are not fully displayed, they are clipped (see image below) if the text is wrapped. The ListView is instantiated as follows in the ScrollView:
<ListView
android:id="#+id/info_chat_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:cacheColorHint="#color/frame_background_color"
android:clickable="false"
android:divider="#null"
android:footerDividersEnabled="false"
android:focusable="false" >
</ListView>
If the height of the ListView is set to "wrap_content" only the first element is shown. That's why I'm using a method to calculate the height of the rows of the list:
private int getCommentsListHeight() {
if (mChatAdapter != null && mChatAdapter.getCount() != 0) {
if (mChatList != null) {// && mCommentsListItemHeight == 0) {
mCommentsListItemHeight = 0;
for (int i = 0; i < mChatAdapter.getCount(); i++) {
// Get view item height
View viewItem = mChatAdapter
.getView(i, new View(OnAirActivity.this), mChatList);
viewItem.measure(0, 0);
Logger.d(LOGTAG, "View " + i + " measured height = " + viewItem.getMeasuredHeight());
mCommentsListItemHeight += viewItem.getMeasuredHeight();
}
}
//return mChatAdapter.getCount() * mCommentsListItemHeight;
return mCommentsListItemHeight;
} else {
return 0;
}
}
Unfortunately, in case when the text inside the TextView is wrapped, even over several lines, the height of the row element returned by the getMeasuredHeight() method is constant. Also the getLineCount() called on the TextView inside the row element returns 1 even if the text is wrapped.
On the other hand, if this ListView is embedded in a LinearLayout, everything works fine and the full list is displayed with no clipping.
Do you have any suggestions as to what might be wrong here? I really don't like the idea of manually measuring the height of the list elements and it apparently doesn't work but why can't android nicely stretch the ListView inside the ScrollView to fit it all in there?
Clipped list:
Use this method created by https://stackoverflow.com/users/205192/dougw
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
It's a BAD practice to encapsulate ListView within a ScrollView because ListView itself contains scrolling capabilities. You should implement a solution that does not contain such hierarchy of views and I hope it will do the magic :)
Here resource of main layout with ScrollView:
<ScrollView android:layout_height="fill_parent" android:layout_width="fill_parent">
<LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="#+id/parentLayout"/>
</ScrollView>
Here the code to insert items:
parentLayout.removeAllViews();
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (int i = comments.size() - 1; i >= 0; i--) {
CommentInfo comment = comments.get(i);
View view = inflater.inflate(your_resource_id, null, false);
TextView commentsContent =(TextView)view.findViewById(R.id.commentContent);
if (commentsContent != null) {
String data = String.format("%s (by %s, %s)", comment.getCommentText(), comment.getUserName(),
commentsContent.setTextSize(st.getTextSize());
commentsContent.setText(data);
}
parentLayout.addView(view, 0);
}
I had the same problem in my project.You need to create simple LinearLayout inside ScrollView. After that you need create new View with your listview item xml using LayoutInflater. After creation put all data in new View and add to LinearLayout as child view:
linearLayot.addView(newView, position_you_need).
Hope it would help you!
I took the recommendation of not using a ListView element inside a ScrollView to heart and decided to use a slightly brute force method to achieve what I need. Since there is a constant number of up to five list rows that need to be displayed I removed the ListView instantiation from the xml file and replaced it with five instances of rows:
<include android:id="#+id/info_comment_1" layout="#layout/chat_single_message" />
<include android:id="#+id/info_comment_2" layout="#layout/chat_single_message" />
<include android:id="#+id/info_comment_3" layout="#layout/chat_single_message" />
<include android:id="#+id/info_comment_4" layout="#layout/chat_single_message" />
<include android:id="#+id/info_comment_5" layout="#layout/chat_single_message" />
In the Activity class I declare five placeholders for these views:
private RelativeLayout mChatMessages[] = new RelativeLayout[COMMENTS_NUMBER];
and initialize them with:
mChatMessages[0] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_1);
mChatMessages[1] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_2);
mChatMessages[2] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_3);
mChatMessages[3] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_4);
mChatMessages[4] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_5);
Then, whenever a new message is received I use the ChatAdapter (the same I used for the ListView previously) and call its getView() method:
protected void updateChatMessages() {
int msgCount = mChatAdapter.getCount();
for (int i = 0; i < COMMENTS_NUMBER; i++) {
if (msgCount <= i) {
mChatMessages[i].setVisibility(View.GONE);
} else {
mChatMessages[i] = (RelativeLayout) mChatAdapter.getView(i, mChatMessages[i], null);
mChatMessages[i].setVisibility(View.VISIBLE);
}
}
}
I don't inflate the pariculat views ever again since the only thing that changes is the content of each row, not the layout. This means there is no performance penalty here.
This is basically a manual implementation of a ListView with a limited maximum number of elements. This time, however, ScrollView is able to fit them nicely and nothing gets clipped.
For a dynamic number of rows the approach suggested by Layko could be employed with the views being instantiated programatically and added to the LinearLayout inside the ScrollView.
I can see the ListView is inside a ViewPager; one other simple approach to resolving this issue is to add
app:layout_behavior="#string/appbar_scrolling_view_behavior" to the ViewPager in your layout xml as seen below.
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
To prevent the same behavior at the bottom of the list, you can also add android:layout_marginBottom="?attr/actionBarSize" to the ViewPager like so
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_marginBottom="?attr/actionBarSize"/>
This is coming late, but I hope it helps any other person.
try it..
after create all view add bellow line for ScrollView location on screen (x,y)
ScrollView scrollView = (ScrollView)findViewById(R.id.scrollView);
scrollView.smoothScrollTo(0,0);// top location zero index
I had a similar problem. I have a
RelativeLayout
listView
includeLayout
where I include some bottom nav beneath the listView with this
<include
android:id="#+id/includeLayout"
layout="#layout/bottom_nav_bar"
and my listView was clipped - not taking the full height available between the header and bottom nav. I tried various xml settings suggested in this and other threads, but what worked for me was to add
android:layout_alignBottom="#+id/includeLayout"
to my listView. That seemed to pull the listView down to the top of the bottom nav, so that the listView is now using the full available height (and it scrolls as needed).
This works for me
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/activity_vertical_margin">
<TextView
android:id="#+id/tv_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="17sp"
android:text="#string/text_list_devices" />
<ListView
android:id="#+id/lv_paired"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:cacheColorHint="#00000000"
android:layout_above="#+id/signup_t"
android:layout_below="#id/tv_status"
/>
<Button
android:id="#+id/signup_t"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textColor="#android:color/white"
android:layout_alignParentBottom="true"
android:text="Print All Records"
android:typeface="sans"
android:layout_marginLeft="45dp"
android:layout_marginRight="45dp"
android:textSize="24sp"
android:background="#drawable/selector_for_button"
android:textStyle="bold" />
</RelativeLayout>
This is BAD practice. But there are some situations we can not avoid using that. For example dynamic e-commerce layouts we may put multiple lists or recycle views but you don't want to scroll inside a single item height (if accidentally wanted!!). I faced this kind of problem. I fixed using a simple way. I don't tell this is the correct way but it may help some.
!! I used to recycle the view.
(01) Create an Interface to return view height.
public interface AfterViewLoadListener {
void onViewHeightMeasured(int height, String mode);
}
(02) implement with your activity
public class *Activity extends AppCompatActivity implements AfterViewLoadListener{
/** your codes **/
final SimpleListRecycleAdapter order_adapter = new SimpleListRecycleAdapter(this,"ORDER");
}
#Override
public void onViewHeightMeasured(int height, String mode) {
if(mode.equals("ORDER") && height > 0){
recycleView.setMinimumHeight(height);
}
}
(03) inside the recycle view custom adapter
AfterViewLoadListener viewLoadListener = null;
public SimpleListRecycleAdapter(AfterViewLoadListener listener, String mode) {
if(listener instanceof AfterViewLoadListener){
viewLoadListener = listener;
}
this.mode = mode;
}
(04) override the onViewAttachedToWindow method
#Override
public void onViewAttachedToWindow(#NonNull SimpleListViewHolder holder) {
super.onViewAttachedToWindow(holder);
View view = holder.itemView;
view.measure(0, 0);
this.viewMinHeight = view.getMeasuredHeight();
if(!firstFlag){
firstFlag = true;
viewLoadListener.onViewHeightMeasured(this.viewMinHeight*filtered.length(),mode);
}
}
(05) That's it. It worked for me.

Categories

Resources