I have a layout similar to Foursquare (image), where a View (in their case, a map) is behind a ListView, but is visible because the first element is transparent.
I need the View behind the ListView to take touch and swipe events. Is there a way to make only that first invisible item of the ListView ignore these events and allow the events to propagate to the View behind it, but have the rest of the ListView items take the events as usual?
I'm just hoping I won't have to completely abandon the ListView and use a ScrollView instead, because I've already implemented a lot of ListView specific features.
ScrollView on the back will work normaly unless you click the button:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="#+id/bottom"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/text"
android:text="asdfasdfa Put more text for testing sdfasdfasdfasdfasdfff
afdasdfasdfasdfasdfasdf
asdfasdfa"/>
</ScrollView>
<LinearLayout
android:id="#+id/top"
android:layout_height="match_parent"
android:layout_width="match_parent">
<Button
android:id="#+id/butt"
android:layout_height="100dp"
android:layout_width="200dp"
android:text="Test"/>
</LinearLayout>
</FrameLayout>
Both of the layers/frames have height and width set to match_parent (full screen in this case). And you can basicaly put anything you want into both of this layers as you need it.
I know that everyone's recommended that we should never use ListView and ScrollView together, and I totally agree. However, I'm currently stuck with a very simple pattern like 8tracks' profile page (as shown in the image below), which include an area for the user profile and a list of mixes they made. So basically, it's desirable that users can just scroll down that page, which means the profile part will get on top of the screen and gradually out of view, and at the same time the below list is scrolled too:
However, at the moment, all I can do is to include a ListView within a LinearLayout, just like my sketch here.
With this design, I can only scroll the list down, while the profile area stays at the same place, which sucks. So I'm looking for any idea to make the whole page scrollable, not just the list. Please help and thanks.
EDITED: I'm sorry for the misleading question. My problem is even more complicated because the content of the tabs are not just ListView - some tab contains LinearLayout or GridView instead. Again, what I want to achieve is to make the whole page scrollable, but ScrollView can't help because if the content of a tab is a ListView or GridView, these views will be collapsed and more importantly - this goes against the design rule.
I know this is late, but I'm the current developer for 8tracks. The (old) 2.x app you have shown above is being rewritten, but I can show you what the old dev did for the profile page.
Before going into that I must say that this is not the best way to do this, but the 8tracks app (2.x) is old.
So back to the code…
The ProfileActivity contains a ProfileFragment.
The main layout you see with the Follow button (and the profile image) is this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- Image, name, location -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dip" >
<com.gdub.widget.ImageViewClickable
android:id="#+id/dj_avatar"
android:layout_width="110dip"
android:layout_height="110dip"
android:layout_marginRight="10dip"
android:background="#drawable/default_avatar_max200"
android:scaleType="centerCrop" />
<com.gdub.widget.CollapsingTextView
android:id="#+id/dj_location"
style="#style/mix.counts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dip"
android:layout_toRightOf="#id/dj_avatar" />
<ViewSwitcher
android:id="#+id/profile_action_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/dj_location"
android:layout_toRightOf="#id/dj_avatar" >
<Button
android:id="#+id/follow_btn"
style="#style/white_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/follow" />
<Button
android:id="#+id/edit_profile_btn"
style="#style/white_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/edit_profile" />
</ViewSwitcher>
</RelativeLayout>
<TextView
android:id="#+id/dj_bio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="-25dip"
android:gravity="left|center_vertical"
android:lineSpacingExtra="2dip"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:textColor="#color/default_text"
android:textSize="15sp" />
<include
android:id="#+id/profile_tabs"
layout="#layout/profile_tabs" />
</LinearLayout>
And profile_tabs…
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:orientation="horizontal">
<include
android:id="#+id/profile_mixes_button"
layout="#layout/profile_tab" />
<include
android:id="#+id/profile_followers_button"
layout="#layout/profile_tab" />
<include
android:id="#+id/profile_following_button"
layout="#layout/profile_tab" />
</LinearLayout>
So as you can see it's a regular layout with three buttons "simulating" tabs.
The contents of the tabs is also dictated by a ViewSwitcher:
<?xml version="1.0" encoding="utf-8"?>
<ViewSwitcher xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/profile_view_switcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inAnimation="#anim/fade_in_300"
android:outAnimation="#anim/fade_out_300"
android:background="#color/white">
<include
android:id="#+id/profile_loading"
layout="#layout/loading_view_full" />
<ListView
android:id="#+id/profile_content_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#00000000"
android:divider="#null"
android:dividerHeight="0dip"
android:fadingEdge="none" />
</ViewSwitcher>
That shows a loading wheel and then switches to the listview. There is no other scrollable ViewGroup.
And that's basically it.
Now if you wanted to make the WHOLE thing scroll, then you need to use a custom adapter and set the above layout as the Header (or at least use getItemType in the adapter in a clever way). That way the whole screen is a List (with all the optimizations a list has).
We (ab)use this in the new 8tracks App under dev. ;)
Try to use the following on your listview.
listview.addHeaderView(v);
Also rememeber, you must call this method before calling setAdapter() on your listview.
include your linearlayout where you have the user details and the tabs and add it as a header to the list.
You can try to make the profile and the tabs the header of the listview, then updating the contents of the listview when the tabs are pressed. I don't know if you want the tabs to disappear from view as you scroll, though.
According to the UI guide lines and best practices, it is advisable not to use Scrollable content inside Scrollview and doing that prevents the scrolling of the Scrollable content.
When you put two scrollview android just get confused which scroll view is touched. So sometimes it gets unable to deliver touch event.
But if still you want to achieve the scrolling functionality you can manage it by using the onTouch event of the particular view. And you need to design your layout accordingly.
But even if the requirement forces you to make such layouts. Try this…
Say case is somewhat like this….
<ScrollView android:id=”#+id/parent_scroll”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:background=”#drawable/dotted_bg”
android:focusableInTouchMode=”false”>
<LinearLayout />
<LinearLayout />
<LinearLayout >
<ScrollView android:id=”#+id/child_scroll”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:background=”#drawable/text_box_bg”>
<TextView android:id=”#+id/text_description”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:textColor=”#color/gray”
android:textSize=”12dip”
android:padding=”5dip”
android:scrollbars=”vertical”/>
<!–ScrollView>
</LinearLayout>
Step 1 : Provide unique id to both the scrollview.
Step 2 : get reference of that two scrollview in your activity.
parentScroll=(ScrollView)findViewById(R.id.parent_scroll);
childScroll=(ScrollView)findViewById(R.id.child_scroll);
Step 3: Now set touch listeners for both.
parentScroll.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
Log.v(TAG,”PARENT TOUCH”);
findViewById(R.id.child_scroll).getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
});
childScroll.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
Log.v(TAG,”CHILD TOUCH”);
// Disallow the touch request for parent scroll on touch of child view
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
I hope it will help you.
This is my View Hierarchy:
-RelativeLayout [rootView] (root)
LinearLayout [menu_holder] (menu fragment layout)
RelativeLayout [app_holder] (app content layout)
Action Bar (custom view to implement ActionBar < 2.1
FrameLayout [content_fragment] (layout to load content fragment)
FloatingMenu (custom LinearLayout to show a floating widget)
<com.example.app.views.widgets.RootLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drag_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/white" >
<LinearLayout
android:id="#+id/menu_holder"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/red"
android:orientation="vertical" />
<com.example.app.views.widgets.AppHolder
android:id="#+id/app_holder"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/gray" >
<com.example.app.widgets.ActionBar
android:id="#+id/action_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="#+id/content_holder"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/action_bar"
android:background="#drawable/backrepeat" >
</FrameLayout>
<com.example.app.widgets.FloatingMenu
android:id="#+id/floating_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:padding="15dp"
android:visibility="gone" />
</com.example.app.views.widgets.AppHolder>
</com.example.app.views.widgets.RootLayout>
Now I load both menu_holder & app_holder into the rootView, such that app_holder stays on top. Now when the user presses MENU button, app_holder slides towards right to show the underlying menu fragment on the left, like in the picture below:
I have used a scroller inside a runnable to slide app_holder towards right. I also call invalidate() every 16ms on the rootView after offsetting my app_holder.
Its working fine, but problem comes when any content gets loaded in the content_fragment (visibility set or loaded from web), when the menu fragment is visible. The invalidated view (out of bounds) calls requestLayout() and the following layout call resets the app_holder to its initial position. I have implemented lazy loading listviews on my content_fragment, so any update on the listview (notifyDataSetChanged etc.) initiates a relayout call.
How to stop this from occurring ?
Overriding and commenting requestLayout() on all dynamically loaded views on the right will fix the issue, but is there any other proper way ?
Consider the Hotmail app for Android. When you check an e-mail item, three buttons appear at the bottom: [Mark Read] [Mark Unread] [Delete]
When you uncheck it, the buttons go away again.
What's the layout for this? I've tried this, but it yields scrolling problems at the bottom (can't see last item):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/darker_gray"
android:orientation="horizontal"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:paddingTop="5dip" >
<Button
android:id="#+id/bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:text="#string/mark_read" />
</LinearLayout>
Then, I also need to show/hide this stuff?
Changing the visibility of bottom linearlayout will show/hide it. You'll need to give it an id and then
LinearLayout bottomLayout = (LinearLayout)findViewById(R.id.someId);
bottomLayout.setVisibility(View.GONE)// or View.VISIBLE
As for the scrolling problem, that occurs because the RelativeLayout overlays view components, so you can either show/hide the button overlaying the bottom of the ListView or change the Relativelayout to a LinearLayout so that the ListView ends before the button and change the visibility.
Though I'm not sure this will look very good when you suddenly show the button and the ListView has to resize itself.
Note on visibility
setVisibility(View.GONE);
will remove the view from the layout and other component may resize due to this. However using
setVisibility(View.INVISIBLE);
keeps the space the view took up in the layout and simply makes the view invisible and no resizing will occur.
I am not sure how to do this? I want a static view at the bottom of another layout that the user can slide up to show another view. I'm not sure what this feature is called but I know the Facebook app does this and so does ESPN and Google plus. Thanks!
The simplest thing to use would be a SlidingDrawer.
As long as you want to slide up from the bottom (or the right) this will work beautifully. It doesn't work if you want to slide something down from the top or in from the left however.
To use it you just need something like this in your XML layout:
<SlidingDrawer
android:id="#+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:handle="#+id/handle"
android:content="#+id/content">
<ImageView
android:id="#id/handle"
android:layout_width="88dip"
android:layout_height="44dip" />
<GridView
android:id="#id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</SlidingDrawer>
Where the ImageView is the "handle" (the thing you drag up and down to open the drawer) and the GridView is the whatever content you want the drawer to hold (It can be any type of view, not just a GridView).