I'm using a horizontal RecyclerView with PagerSnapHelper to make it look like a ViewPager. It's displaying CardViews.
My question is, how can I make it show a small peek of the edge of the next and previous card? The user needs to see a little of those cards so they understand intuitively that they need to swipe horizontally so they can view other cards.
I'll also note that the current card always needs to be centered, even for the first one, which would not have a previous card peeking on the left. Also, the design requirements are out of my control; I need to peek, I can't use dot indicators or anything else.
I could use a LinearSnapHelper and make the width of the cards smaller, but then 1) the first item will be left-aligned instead of centered, since there's no card peeking on the left side and 2) how much of each card displays would vary based on the width of the phone.
This seems like it should be a common and simple task, so I hope I'm missing something obvious to make it happen.
After some trial and error, I found a solution. Fortunately, it wasn't as complicated as I thought, although not as clean as I hoped.
So start with a RecyclerView and its Adapter, use a LinearLayoutManager with a Horizontal orientation, add in a PagerSnapHelper and so on... then to fix the specific issue in this question I made some adjustments to the adapter:
private var orientation: Int? = null
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
orientation = (recyclerView.layoutManager as LinearLayoutManager).orientation
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// Kludge to adjust margins for horizontal, ViewPager style RecyclerView
if (orientation != LinearLayout.VERTICAL) {
holder.itemView.layoutParams = (holder.itemView.layoutParams as RecyclerView.LayoutParams).apply {
val displayMetrics = DisplayMetrics()
baseActivity.windowManager.defaultDisplay.getMetrics(displayMetrics)
// To show the edge of the next/previous card on the screen, we'll adjust the width of our MATCH_PARENT card to make
// it just slightly smaller than the screen. That way, no matter the size of the screen, the card will fill most of
// it and show a hint of the next cards.
val widthSubtraction = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40f, displayMetrics).toInt()
width = displayMetrics.widthPixels - widthSubtraction
// We always want the spot card centered. But the RecyclerView will left-align the first card and right-align the
// last card, since there's no card peeking on that size. We'll adjust the margins in those two places to pad it out
// so those cards appear centered.
// Theoretically we SHOULD be able to just use half of the amount we shrank the card by, but for some reason that's
// not quite right, so I'm adding a fudge factor developed via trial and error to make it look better.
val fudgeFactor = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6f, displayMetrics).toInt()
val endAdjustment = (widthSubtraction / 2) - fudgeFactor
marginStart = if (position == 0) endAdjustment else 0
marginEnd = if (position == (itemCount - 1)) endAdjustment else 0
}
}
}
Of course, you'll want to change 40f and 6f to the appropriate values for your use case.
In case you're wondering, I have the orientation check because in my case, the same adapter is used for two different RecyclerViews. One is a simple vertical list. The other is horizontal and functions like a ViewPager, which greatly increased the complexity.
Related
Horizontal recyclerView (DiscreteScrollView library) with scaling set to 50% between selected view and others doesn't keep spacing the same. Example below shows that spacing between 1/2 and 4/5 are more spacious than views next to currently selected item on center. It happens gradually by scrolling, for an example going from item 3 to item 2 will be moving item 3/4 apart from each other when onScroll is happening.
spacing between items example
I Thought about checking the current width of view when onScroll is happening and programatically reduce the left/right margin by applying new layout params so the space would stay the same.
Trying:
override fun onScroll(
scrollPosition: Float,
currentPosition: Int,
newPosition: Int,
currentHolder: CurrencyAdapter.ViewHolder?,
newCurrent: CurrencyAdapter.ViewHolder?
) {
val layoutParams = currentHolder.layout.layoutParams as ConstraintLayout.LayoutParams
}
}
produces
ClassCastException: androidx.recyclerview.widget.RecyclerView$LayoutParams cannot be cast to androidx.constraintlayout.widget.ConstraintLayout$LayoutParams
1. Is it possible to manipulate layoutParams from within viewHolder?
2. Are there any other options to set fixed spacing?
EDIT:
adding DividerItemDecoration shows better where the problem is coming from:
DividerItemDecoration
3. Is ItemDecoration the right direction for fixing the issue then?
I am using the default RecyclerView fast scroll and I followed this guide to support it.
Now, the problem is that the thumb resizes its height as per the size of the data set. For large items like 100 and above, the thumb becomes very small and almost becomes difficult to respond to dragging.
Please is there any way I can set minimum height for the fast scroll thumb.
I solved this problem by copying the FastScroller class from
android.support.v7.widget.FastScroller
Then I removed the fast scroll enabled from the xml and applied fastscroller using the below code:
StateListDrawable verticalThumbDrawable = (StateListDrawable) getResources().getDrawable(R.drawable.fastscroll_sunnah);
Drawable verticalTrackDrawable = getResources().getDrawable(R.drawable.fastscroll_line_drawable);
StateListDrawable horizontalThumbDrawable = (StateListDrawable)getResources().getDrawable(R.drawable.fastscroll_sunnah);
Drawable horizontalTrackDrawable = getResources().getDrawable(R.drawable.fastscroll_line_drawable);
Resources resources = getContext().getResources();
new FastScroller(recyclerView, verticalThumbDrawable, verticalTrackDrawable,
horizontalThumbDrawable, horizontalTrackDrawable,
resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
Inside the FastScroller Class I extended the defaultWidth:
FastScroller(RecyclerView recyclerView, StateListDrawable verticalThumbDrawable,
Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
Drawable horizontalTrackDrawable, int defaultWidth, int scrollbarMinimumRange,
int margin) {
...
this.defaultWidth = defaultWidth;
...
Then I updated the code in this method:
void updateScrollPosition(int offsetX, int offsetY) {
...
mVerticalThumbHeight = Math.max(defaultWidth * 4, Math.min(verticalVisibleLength,
(verticalVisibleLength * verticalVisibleLength) / verticalContentLength));
...
...
mHorizontalThumbWidth = Math.max(defaultWidth * 4, Math.min(horizontalVisibleLength,
(horizontalVisibleLength * horizontalVisibleLength) / horizontalContentLength));
...
}
This ensures that the minimum thumb height/width is 4 times the default width.
This is only a partial answer; I'm missing (at least) one piece of the puzzle, but hopefully someone else can figure it out.
Once you've added the necessary attributes to your <RecyclerView> tag (as mentioned in the answer linked in OP's question), the sizing/positioning of the scrollbar thumb is controlled by three methods inside LinearLayoutManager:
int computeVerticalScrollRange(): The size of the scrollbar's track.
int computeVerticalScrollExtent(): The size of the scrollbar's thumb.
int computeVerticalScrollOffset(): The distance between the top of the scrollbar's track and the top of the scrollbar's thumb.
The units for these methods is arbitrary; you can use anything you'd like as long as all three methods share the same units. By default, LinearLayoutManager will use one of two sets of units:
mSmoothScrollbarEnabled == true: Use units based on the pixel sizes of the visible items in the RecyclerView.
mSmoothScrollbarEnabled == false: Use units based on the positions of the visible items in the RecyclerView's adapter.
To control the size of the scrollbar's thumb yourself, you'll have to override these methods... but here's the piece I'm missing: In all of my experimentation, computeVerticalScrollExtent() is never called by the system. That said, we can still show some progress here.
First, I've created a simple adapter that shows 500 CardViews with the item's position inside. I've enabled fast scrolling with some really simple (but ugly) drawables. Here's what the scrollbar looks like with just a default LinearLayoutManager implementation:
As you've found, with 500 (small) items, the scrollbar thumb is really small and quite hard to tap on. We can make the scrollbar dramatically larger by overriding computeVerticalScrollRange() to just return a fixed constant... I picked 5000 essentially at random just to show the major change:
Of course, now the scrollbar doesn't work like you'd expect; scrolling the list by dragging on it as normal moves the thumb much more than it should, and fast scrolling the list by dragging on the thumb moves the list much less than it should.
On my device, with the randomly-chosen range of 5000, overriding computeVerticalScrollOffset() as follows makes the scrollbar thumb move perfectly as I scroll the list by dragging on it:
#Override
public int computeVerticalScrollRange(RecyclerView.State state) {
return 5000;
}
#Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
return (int) (super.computeVerticalScrollOffset(state) / 23.5f);
}
However, this still doesn't fix the second issue: dragging on the thumb itself doesn't correctly scroll the list. As I mentioned above, it would seem like the appropriate thing to do here would be to override computeVerticalScrollExtent(), but the system never invokes this method. I've even overridden it to simply throw an exception, and my app never crashes.
Hopefully this at least helps point people in the right direction for a full solution.
PS: The implementations of computeVerticalScrollRange() and computeVerticalScrollOffset() I've included in this answer are intentionally simple (read: bogus). "Real" implementations would be much more complex; the default LinearLayoutManager implementations take into account device orientation, the first and last visible items in the list, the number of items off-screen in both directions, smooth scrolling, various layout flags, and so on.
This is a known issue, opened in August 2017: https://issuetracker.google.com/issues/64729576
Still waiting for recommendations on how to manage RecyclerView with fast scroll on large amounts of data. No reply from Google on that issue so far on recommendations :(
The answer from Nabil is similar to a workaround mentioned in the issue. Nabil's answer copies the FastScroller class and modifies it to ensure a minimum thumb size, and the workaround in the issue extends FastScroller (but it has to stay in the android.support.v7.widget package) to ensure the minimum thumb size.
Nabil Mosharraf Hossain's answer solved this issue. I just wanted to post some details for future reference.
By default, instead of calling computeVerticalScrollExtent() in LinearLayoutManager, thumb size and thumb position are calculated in FastScroller class in updateScrollPosition method.
This is the formula for thumb size:
this.mVerticalThumbHeight =
Math.min(verticalVisibleLength, verticalVisibleLength * verticalVisibleLength / verticalContentLength);
verticalVisibleLength is a length of the part of the recycler's content that is visible on the screen. verticalContentLength is length of the whole content of the recycler. So the thumb takes up from the screen the same proportion as visible content takes up from the whole content. Which means that if recycler content is very long - thumb would be very small.
To prevent this, we can override mVerticalThumbHeight as Nabil Mosharraf Hossain suggested. I did it this way, so the thumb would still has the same proportional size but not lower that 100 pixels:
this.mVerticalThumbHeight = Math.max(100,
Math.min(verticalVisibleLength, verticalVisibleLength * verticalVisibleLength / verticalContentLength));
But. Overriding only the thumb size would lead to some inconsistencies in thumb movement near the top and the bottom of the screen because its position would behave the same way like the thumb is still very small. To prevent this we can also override thumb position.
Default formula for thumb position doesn't take the thumb size into consideration and just uses the fact the it is proportional to the screen size. So I found this formula to work. But I can't quite remember what does it do exactly.
this.mVerticalThumbCenterY = (int)((verticalVisibleLength - mVerticalThumbHeight) * offsetY
/ ((float)verticalContentLength - verticalVisibleLength) + mVerticalThumbHeight / 2.0);
I have found a solution to adjust the size of the scrollbar thumb, however I'm not completely there yet. Just like the answer of Ben P. I've used the default properties app:fastScrollEnabled and all the track & thumb drawable properties and created a custom LinearLayoutManager.
class FastScrollLayoutManager(context: Context) : LinearLayoutManager(context) {
override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State?): Int {
return super.scrollVerticallyBy(dy * 2, recycler, state)
}
override fun computeVerticalScrollRange(state: RecyclerView.State): Int {
return super.computeVerticalScrollRange(state) / 2
}
override fun computeVerticalScrollOffset(state: RecyclerView.State): Int {
return super.computeVerticalScrollOffset(state) / 2
}
}
By dividing the result of computeVerticalScrollRange() and computeVerticalScrollOfsett() with 2, the scroll thumb will become 2x bigger. If you then multiply the scrolling speed with 2, the scroll speed will be on the right scale again. Dragging the thumb of the scrollbar will be working perfectly and the size of the thumb will be 2x bigger than before.
However, this is not yet a final solution. By increasing the scroll speed, the 'normal' scrolling (not using the scrollbar) won't be smooth anymore.
What I'm trying to achieve:
--- Stack Layout / Relative Layout---
- some widget (e.g. Label) -
- some widget (e.g. Label) -
- ListView -
-------------------------------------
However, I also want the following scroll behaviour:
Top widgets disappear first then ListView starts scrolling. Basically, I want a "natural" scrolling behaviour.
One way I can achieve this is by making the whole page a ListView and putting the widgets as a Header for the ListView
But that has one problem... which I think is a bug in Xamarin.Forms:
If you have a long Label (what else to hold text?), it will not display all of it. It will actually make it scrollable and display only part of it at a time. What makes this even worse is that you cannot scroll the Label "easily", you have to try multiple times to make it scroll the label instead of the page, it's obviously bugged. That happens even if the page itself has hit the end (i.e. can't scroll any more), the Label still can't be scrolled easily.
Is there another way or a workaround to achieve what I want?
As one of the comments suggests, the best is to set the HeightRequest of the Label to the needed value.
Here is how I measure the height of the text on Android (you'll need DependencyService, if you want to call this function from Xamarin.Forms):
double measureString(string text, string font, double fontSize, double width)
{
var textView = new TextView(global::Android.App.Application.Context);
textView.Typeface = Android.Graphics.Typeface.Create(font, Android.Graphics.TypefaceStyle.Normal);
textView.SetText(text, TextView.BufferType.Normal);
textView.SetTextSize(Android.Util.ComplexUnitType.Px, (float)(fontSize * Xamarin.Forms.Forms.Context.Resources.DisplayMetrics.ScaledDensity));
int widthMeasureSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec((int)(width * Xamarin.Forms.Forms.Context.Resources.DisplayMetrics.Density), width == 0 ? Android.Views.MeasureSpecMode.Unspecified : Android.Views.MeasureSpecMode.Exactly);
int heightMeasureSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec(0, Android.Views.MeasureSpecMode.Unspecified);
textView.Measure(widthMeasureSpec, heightMeasureSpec);
textView.SetIncludeFontPadding(false);
return (width == 0 ? textView.MeasuredWidth : textView.MeasuredHeight) / Xamarin.Forms.Forms.Context.Resources.DisplayMetrics.Density;
}
from an answer to one of my other questions I found an Google Demo of a ListView subclass that allows item reorder.
The demo works great, but I am having some trouble to understand how the it works:
When an item is dragged above/below the bounds of the ListView, the ListView starts scrolling up/down to reveal new items. The necessary calculation uses different parameters of the underling ScrollView:
public boolean handleMobileCellScroll(Rect r) {
int offset = computeVerticalScrollOffset();
int height = getHeight();
int extent = computeVerticalScrollExtent();
int range = computeVerticalScrollRange();
int hoverViewTop = r.top;
int hoverHeight = r.height();
if (hoverViewTop <= 0 && offset > 0) {
smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);
return true;
}
if (hoverViewTop + hoverHeight >= height && (offset + extent) < range) {
smoothScrollBy(mSmoothScrollAmountAtEdge, 0);
return true;
}
return false;
}
heightis the height of the ListView itself
offsetis the scroll position = how many units/pixels have been scrolled up/down
rangeis the height of the complete content.
extent - well, what is this?
ListView inherits computeVerticalScrollExtent() from View and the docu says:
Compute the vertical offset of the vertical scrollbar's thumb within
the horizontal range. This value is used to compute the position of
the thumb within the scrollbar's track.
If one looks at the code computeVerticalScrollExtent() is not implemented by one of the sublasses but only directly by View: It simply returns the height of the view.
This makes sense: If the ListView/ScrollView has a height of 500, the part of the scroll content that is visible at a time is also 500. Is this the meaning of the ScrollExtent? Why is ScrollExtent necessary? Why not simply use getHeight() directly?
I think I am missing something and I am happy about any hint!
compute*ScrollOffset - Defines the distance between the start of the scrollable area and the top of the current view window inside the scrollable area. So for example, if your list has 10 items and you've scrolled down so the 3rd item is at the top-most visible item, then the offset is 3 (or 3*itemHeight, see below).
compute*ScrollExtent - Defines the size of the current view window inside the scrollable area. So for example, if your list has 10 items and you can currently see 5 of those items, then the extent is 5 (or 5*itemHeight, see below).
compute*ScrollRange - Defines the size of the current scrollable area. So for example, if your list has 10 items then the range is 10 (or 10*itemHeight, see below).
Note that all these methods can return values in different units depending on their implementation, so for the examples above, I am using the indices, but in some cases these methods will return pixel values equivalent to the width or height of the items.
In particular, the LinearLayoutManager of the RecyclerView will return indices if the 'smooth scrollbar' feature is disabled, otherwise it will return pixel values. See ScrollbarHelper in the support library for more information.
Additional reading: https://groups.google.com/forum/#!topic/android-developers/Hxe-bjtQVvk
It's kinda late, but hope it's ok.
1) The method actually is implemented by the subclasses. For example, this is what it looks like for AbsListView (ListView's superclass).
2) View's height can be different from its vertical scroll's height - just imagine a View with weird top/bottom padding .
These two points pretty much make other questions irrelevant :)
This is a sample code which might help you to understand as to how to get scrollBar top and bottom using computeVerticalScrollExtent:
scrollbarTop = (float)this.computeVerticalScrollExtent()/this.computeVerticalScrollRange()*this.computeVerticalScrollOffset();
scrollbarBottom = (float)this.computeVerticalScrollExtent()/this.computeVerticalScrollRange()*(this.computeVerticalScrollExtent()+this.computeVerticalScrollOffset());
According to the article from here:
I found this explanation correct.
ListView with 30 items has scrollRange equals to 3000, that is due to scrollRange = numberOfItems * 100, thus scrollExtent = numberOfVisibleItems * 100 and scrollOffset = numberOfScrolledItems * 100. You can find evidance of these words in the source code of AbsListView
So I've searched high and low but couldn't find an answer to my question. What I basically need is the behavior provided by Android's ViewFlipper or ViewPager but I want to display portions of the left and right views, to indicate the user there are elements to scroll, instead of having the selected view occupying the whole screen.
I would also like to add some effects to the left and side views, like dimming and scaling then down a little. Is it possible to do it with the stock ViewFlipper or ViewPager or do I have to roll out my own view group, à la cover flow (http://www.inter-fuser.com/2010/01/android-coverflow-widget.html)?
(P.S. I don't want to use the Gallery widget, that component sucks).
This is what we need to display once a view is selected (left and right views are still displayed):
Flinging left or right would transition the main view out, dimming and reducing it a little and doing the opposite with the next or previous view.
I would like to give an update to anyone who might want the same feature. A lot of progress has been made to implement this feature so far and now the view is working exactly as we need it to.
The ViewPager has a method called setPageMargin(). This method can receive a negative value which will make the fragments/views to overlap each other. To arrive at the desired layout, we first dynamically calculated the left and right margins by a percentage of the screen. This can be done statically as well but since we will be targeting a range of different screen sizes, this seems to be the best approach.
Later we set the ViewPager's page margin to 2 times the size of the side margins. This makes the views snap back together. However, at this time, there will be more than one view being displayed by the ViewPager.
All you have left to do is to either apply a transform (scale) to the views to the left and right (Android 3.0+) or add some more margins around them to shrink them to the right size (pre 3.0).
The OnPageChangeListener.onPageScrolled() can be used to track the ViewPager's scrolling. A smooth
transformation can be achieved. The code looks like this:
private OnPageChangeListener onPageChangeListener = new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// positionOffset varies from 0 to 1 and indicates how far the view is
// from the center of the ViewPager. When a view is selected (centered), this
// value is 0.
// Fades the text in an out while the ViewPager scrolls from one view to another.
// On our final design the text views are fixed to the center of the screen and
// do not scroll along with the views.
if (positionOffset < 0.5) {
setTextViewAlpha(1 - positionOffset * 2);
} else {
setTextViewAlpha((positionOffset - 0.5f) * 2);
}
// It's surprisingly complicated to get the current object being displayed by
// a ViewPager (there's no getCurrentObject method). ScaleableFragment is just
// a custom class to allow an adapter to cache it internally and also correctly
// manage a fragment's lifecycle and restore from a saved state.
ScaleableFragment sampleFragment = (ScaleableFragment) ((ShowHeroShotImageFragmentPagerAdapter) pager
.getAdapter()).getItem(position);
// Calculates by how much the current view will be scaled down. The RATIO_SCALE
// is 0.3 in our case, which makes the side views 70% of the size of the
// center view. When centered, the scale will be 1. When
// fully scrolled, the scale will be 0.7.
float scale = 1 - (positionOffset * RATIO_SCALE);
// Just a shortcut to findViewById(R.id.image).setScale(scale);
sampleFragment.scaleImage(scale);
// Now, if not in the last element, scale the next one up (in opposite direction).
if (position + 1 < pager.getAdapter().getCount()) {
sampleFragment = (ScaleableFragment) ((ShowHeroShotImageFragmentPagerAdapter) pager.getAdapter())
.getItem(position + 1);
scale = positionOffset * RATIO_SCALE + (1 - RATIO_SCALE);
sampleFragment.scaleImage(scale);
}
}
// Once scrolling is done. Make sure the views are in the right scale (1 for center,
// 0.7 for sides). Required as the onPageScrolled() method does not guarantee it
// will interpolate to 1.0 precisely.
#Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
setTextViewAlpha(1);
ScaleableFragment sampleFragment = (ScaleableFragment) ((ShowHeroShotImageFragmentPagerAdapter) pager
.getAdapter()).getItem(pager.getCurrentItem());
sampleFragment.scaleImage(1);
sampleFragment.enableClicks();
if (pager.getCurrentItem() > 0) {
sampleFragment = (ScaleableFragment) ((ShowHeroShotImageFragmentPagerAdapter) pager.getAdapter())
.getItem(pager.getCurrentItem() - 1);
sampleFragment.scaleImage(1 - RATIO_SCALE);
sampleFragment.disableClicks();
}
if (pager.getCurrentItem() + 1 < pager.getAdapter().getCount()) {
sampleFragment = (ScaleableFragment) ((ShowHeroShotImageFragmentPagerAdapter) pager.getAdapter())
.getItem(pager.getCurrentItem() + 1);
sampleFragment.scaleImage(1 - RATIO_SCALE);
sampleFragment.disableClicks();
}
}
}
};
This is it. I didn't post the full solution but I hope this is enough to get someone else started.
P.S. On 3.0+ enable hardware acceleration. Without it, the scrolling looked choppy on a samsung galaxy tab 10.1.
I played some with it, and my solution is pretty simple. I'm posting it here in case someone is interested.
First, the layout for each fragment should allow some gaps on either side. the simplest way I found of doing this is using weights:
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.1" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.8"
android:orientation="vertical" >
<!-- fragment contents go here -->
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.1" />
This will leave a nice 10% margin on either side. Be sure not to set a background on these, since it will cover the neighbor fragments.
Now for the pager itself: you need to use negative margins, as AngraX explained, but also be sure to pre-load the subsequent fragments, as they are visible before they would be regularly. I got good results with this:
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setPageMargin(getResources().getDisplayMetrics().widthPixels / -7);
mPager.setOffscreenPageLimit(2);
the -7 value leaves the edge of the next fragment visible, play around with your specific composition. I also recommend framing each fragment.
Well, here's a potential answer that might be close to what you're looking for, but not exactly what you're looking for: there's a project called ViewFlow that I think provides the ability to notify the user that there are more views via indicators on the screen. It also provides the ability to buffer views to the left and right of your current view, so you might be able to poke at the code to render small parts of them by basically shrinking what it views as its available screen size.
Edit: Silly me; I should read the final question before I answer. :) I don't think you can do this with a ViewFlipper or Viewpager, unfortunately.
Just use a ViewPager and play with the width of the fragments and margins. I have done so and it works fine.