Android - horizontal scrolling of multiple viewable items
EDIT: See my own answer for easy solution
IMPORTANT: Bounty is offered for clear way to modify ViewPager to s开发者_如何学编程atisfy the scenario outlined below. Please do not offer HorizontalScrollView - I need full Fragment lifecycle scenario covered
I need to implement horizontal scrolling of Fragments
-based views in which one item is in the center and items to the right/left are partially or fully visible. ViewPager
is ill suitable for the task since it's focused on displaying one item at each time.
To make it easier to understand below is a quick sketch in which items 1, 5 and 6 are outside of viewable area. And and want to make this viewable number configurable so for example in portrait view I will only show 2 (or possibly just one) items.
I'm not trying to fit say 3 items on the screen, as long as central item is shown others can be cropped. On the small screen is OK to have 1 central item and as screen grows in size multiple (cropped is OK) items should be shown
I understand that this looks like a gallery but again the items are not simple images but Fragments
with a vertically scrollable list in each fragment
P.S. Found this blogpost by @Commonsware that list 3 different approaches. For my need I like #3
This one has surprisingly easy answer, I'm not even sure why it wasn't posted right away. All that I needed to do to get the exact effect was to override PagerAdapter#getPageWidth
method. By default it returns 1 but if you set it to 0.5 you will get 2 pages, 0.33 will give you 3, etc. Depending on width of the separator between pager items you may have to slightly decrease the value.
See the following snippet:
@Override
public float getPageWidth(final int position) {
// this will have 3 pages in a single view
return 0.32f;
}
Once I wrote something similar as template. In my example I can scroll with the buttons up and down and sidewise. You could modify it a little bit to fulfill your requirements. In my example I have 4 Views arranged like this:
1 2
3 4
It looks like this. On the picture I scroll from view 1 to the right to view 2:
The code consist of this xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:layout_height="350sp"
android:layout_width="320sp">
<LinearLayout
android:id="@+id/viewContainer"
android:background="#CCCCCC"
android:layout_width="640sp"
android:layout_height="700sp">
</LinearLayout>
</LinearLayout>
<TableLayout
android:id="@+id/tableLayout"
android:layout_width="320sp"
android:layout_height="fill_parent"
android:stretchColumns="1"
android:gravity="bottom"
android:layout_alignParentBottom="true">
<TableRow
android:background="#333333"
android:gravity="bottom">
<Button
android:id="@+id/btnUp"
android:layout_width="60sp"
android:layout_height="50sp"
android:text="Lift U"
/>
<Button
android:layout_width="60sp"
android:layout_height="50sp"
android:visibility="invisible"
/>
<Button
android:layout_width="60sp"
android:layout_height="50sp"
android:visibility="invisible"
/>
<Button
android:id="@+id/btnScreenUp"
android:layout_width="60sp"
android:layout_height="50sp"
android:layout_gravity="right"
android:text="Scrn U"
/>
</TableRow>
<TableRow
android:background="#444444"
android:layout_gravity="right">
<Button
android:id="@+id/btnDown"
android:layout_width="60sp"
android:layout_height="50sp"
android:text="Lift D"
/>
<Button
android:id="@+id/btnEnter"
android:layout_width="60sp"
android:layout_height="50sp"
android:text="Enter"
/>
<Button
android:id="@+id/btnScreenLeft"
android:layout_width="60sp"
android:layout_height="50sp"
android:layout_gravity="right"
android:text="Scrn L"
/>
<Button
android:id="@+id/btnScreenDown"
android:layout_width="60sp"
android:layout_height="50sp"
android:layout_gravity="right"
android:text="Scrn D"
/>
<Button
android:id="@+id/btnScreenRight"
android:layout_width="60sp"
android:layout_height="50sp"
android:layout_gravity="right"
android:text="Scrn R"
/>
</TableRow>
</TableLayout>
</FrameLayout>
and this Java code:
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ViewSwitcherTest extends Activity {
private TextView view1, view2, view3, view4;
private Button btnUp, btnEnter, btnDown, btnScreenDown, btnScreenUp, btnScreenLeft, btnScreenRight;
private LinearLayout viewContainer;
// private TableLayout tableLayout;
private LinearLayout.LayoutParams layoutParams;
private DisplayMetrics metrics = new DisplayMetrics();
private int top = 0, left = 0;
private float density = 1.0f;
// private ViewSwitcher switcher;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindowManager().getDefaultDisplay().getMetrics(metrics);
density = metrics.density;
setContentView(R.layout.main);
// Buttons
btnEnter = (Button)findViewById(R.id.btnEnter);
btnUp = (Button)findViewById(R.id.btnUp);
btnDown = (Button)findViewById(R.id.btnDown);
btnScreenDown = (Button)findViewById(R.id.btnScreenDown);
btnScreenUp = (Button)findViewById(R.id.btnScreenUp);
btnScreenLeft = (Button)findViewById(R.id.btnScreenLeft);
btnScreenRight = (Button)findViewById(R.id.btnScreenRight);
// --------
// tableLayout = (TableLayout)findViewById(R.id.tableLayout);
view1 = new TextView(this);
view1.setBackgroundResource(R.drawable.view1);
view1.setHeight((int)(350*density));
view1.setWidth((int)(320*density));
view1.setGravity(Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL);
layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(left, top, 0, 0);
viewContainer = (LinearLayout)findViewById(R.id.viewContainer);
viewContainer.addView(view1, layoutParams);
//Add 2nd view
view2 = new TextView(this);
view2.setBackgroundResource(R.drawable.view2);
view2.setHeight((int)(350*density));
view2.setWidth((int)(320*density));
view2.setGravity(Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL);
layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(left, top, 0, 0);
viewContainer.addView(view2, layoutParams);
//Add 3rd view
view3 = new TextView(this);
view3.setBackgroundResource(R.drawable.view3);
view3.setHeight((int)(350*density));
view3.setWidth((int)(320*density));
view3.setGravity(Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL);
top += 350*density;
left += 640*density*(-1);
layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(left, top, 0, 0);
viewContainer.addView(view3, layoutParams);
//add 4th view
view4 = new TextView(this);
view4.setBackgroundResource(R.drawable.view4);
view4.setHeight((int)(350*density));
view4.setWidth((int)(320*density));
view4.setGravity(Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL);
top += 0;
left += 640*density;
layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(left, top, 0, 0);
viewContainer.addView(view4, layoutParams);
btnEnter.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Quit the application for now
finish();
}
});
btnScreenLeft.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
viewContainer.scrollBy(-10,0);
}
});
btnScreenRight.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
viewContainer.scrollBy(10,0);
}
});
btnScreenUp.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
viewContainer.scrollBy(0,-10);
}
});
btnScreenDown.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
viewContainer.scrollBy(0,10);
}
});
// view1.setOnKeyListener(new OnKeyListener() {
// @Override
// public boolean onKey(View v, int keyCode, KeyEvent event) {
// if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
// viewContainer.scrollBy(0,10);
// return true;
// }
// });
}
}
The big numbers on every screen are black background images with those numbers painted on it. (I didn't post it here because you will probably modify the code anyway).
We are doing something pretty much exactly like you are describing using a Gallery
with Fragments
, and a gallery adapter extending the BaseAdapter
. I would recommend going with a gallery to achieve your goal (we also use a ViewPager
for the view where we do not need to see the other fragments).
I can think of two ways to possibly implement this.
By a ViewSwitcher. Here is an excellent YouTube video showing how it works with an onGestureListener. However, I am not sure if this will work with multiple views like your picture shows.
As an alternative, you could use a HorizontalScrollView. However, this often causes issues if you have one ScrollView inside of a another, but it may be worth a shot!
Let me know how it goes, Good luck!
In addition to MrZander's second answer take a look at this https://stackoverflow.com/a/2655740/935075
Check out this blog post on how to write a custom horizontal scroll view to achieve something similar. The example has only one screen visible at a time, but you should be able to easily modify it for your needs.
If you want to extend ViewPager just override the draw method, and setOffscreenPageLimit(int limit). But, I recommend using FragmentPageAdapter
if your fragments themselves are kind of complex.
You can check the source code here or if you want to check the one in the support package you can do it here.
精彩评论