ListView enables us to populate and show thousands of datum within it and it is a a vertical flip up and down. However, many of time, we need to flip horizontally. For example, we may want to make an album to show images and which enables us to flip images one by one. The final look of the project is as the snapshot showing below.
ViewPager is defined in Android android.support.v4 library. One can layout with a ViewPager as below.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
ViewPagerAdapter is the key class which we used to populate Item views of ViewPager. we define a ViewPagerAdapter class by extending PagerAdapter. The minimum required methods which we to override are:
instantiateItem(ViewGroup, int)
destroyItem(ViewGroup, int, Object)
getCount()
isViewFromObject(View, Object)
Get called when need to instantiate and Item, much like getView() in ListView adapter. Besides returning the Item view, you should add the Item view as child view to the param ViewGroup.
Get called when flip away; recycle behavior is defined here.
Return the number of the datum.
/**
* Created by wilson on 2015/9/6.
* An example of ViewPagerAdapter
*/
public class ViewPagerAdapter extends PagerAdapter {
private Context mContext;
private JSONArray mJSONArray; //data source
private HashMap<Integer, ViewPagerItemView> mHashMap;
public ViewPagerAdapter(Context context, JSONArray JSONArray) {
mContext = context;
mJSONArray = JSONArray;
mHashMap = new HashMap<>();
}
@Override
public int getCount() {
return mJSONArray.length();
}
@Override
public boolean isViewFromObject(View view, Object o) {
return view == o;
}
//call when need to instantiate and Item, much like getView() in ListView adapter
@Override
public Object instantiateItem(ViewGroup container, int position) {
Log.d("gyw", "ViewPagerAdapter::instantiateItem(); position:" + position);
ViewPagerItemView itemView;
if (mHashMap.containsKey(position)) { //view is cached but bitmap maybe recycled
itemView = mHashMap.get(position);
itemView.reload();
} else {
itemView = new ViewPagerItemView(mContext);
try {
JSONObject jsonObject = mJSONArray.getJSONObject(position);
itemView.setData(jsonObject);
} catch (JSONException e) {
e.printStackTrace();
}
mHashMap.put(position, itemView);
container.addView(itemView);
}
return itemView;
}
//call when flip away; recycle behavior is defined here
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d("gyw", "ViewPagerAdapter::destroyItem(); position:" + position);
((ViewPagerItemView) object).recycle();
}
}
As inheriting onPageChangeListener, three methods must be overwritten.
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
//This method will be invoked when a new page becomes selected.
}
@Override
public void onPageScrollStateChanged(int i) {
}
Here is an example of How to set OnPageChangeListener within MainActivity.
public class MainActivity extends AppCompatActivity
implements ViewPager.OnPageChangeListener{
private static final int ALBUM_NUM = 12;
private static final int[] ALBUM_RES = {
R.drawable.dog1, R.drawable.dog2, R.drawable.dog3, R.drawable.dog4,
R.drawable.dog5, R.drawable.dog6, R.drawable.dog7, R.drawable.dog8,
R.drawable.dog9, R.drawable.dog10, R.drawable.dog11, R.drawable.dog12,
};
private ViewPager mViewPager;
private ViewPagerAdapter mViewPagerAdapter;
private LinearLayout mLinearLayout;
private ImageView[] indicators;
private JSONArray mJSONArray;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupDataSources();
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mViewPagerAdapter = new ViewPagerAdapter(this, mJSONArray);
mLinearLayout = (LinearLayout) findViewById(R.id.viewGroup);
initialSetImageIndicators();
mViewPager.setAdapter(mViewPagerAdapter);
mViewPager.setOnPageChangeListener(this);
}
private void setupDataSources() {
mJSONArray = new JSONArray();
for (int i = 0; i < ALBUM_NUM; ++i) {
JSONObject object = new JSONObject();
try {
object.put("id", ALBUM_RES[i % ALBUM_RES.length]);
object.put("name", "Image dog" + i);
mJSONArray.put(object);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
setImageIndicators(i);
}
@Override
public void onPageScrollStateChanged(int i) {
}
private void initialSetImageIndicators() {
indicators = new ImageView[ALBUM_NUM];
for (int i = 0; i < ALBUM_NUM; ++i) {
ImageView imageView = new ImageView(this);
if (i == 0) {
imageView.setImageResource(R.drawable.indicator_select);
} else {
imageView.setImageResource(R.drawable.indicator_idle);
}
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.leftMargin = 5;
lp.rightMargin = 5;
indicators[i] = imageView;
mLinearLayout.addView(imageView, lp);
}
}
private void setImageIndicators(int pos) {
for (int i = 0; i < ALBUM_NUM; ++i) {
if (i == pos) {
indicators[i].setImageResource(R.drawable.indicator_select);
} else {
indicators[i].setImageResource(R.drawable.indicator_idle);
}
}
}
}
Tips: Even though we recycled the bitmap objects both in Java layer and C layer. Hoever, GC won't be sure to execute promptly thus you still may receive OutOfMemory(OOM) error. To avoid this, a lazy way is to declare to use android:largeHeap="true" in your application in AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wilson.viewpagerdemo" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:largeHeap="true">
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Tips: Cache Strategy of ViewPager is to cache three views in total. Android will instantiate the next Item view and keep the previous Item view.