我有ViewPager,它通常会存储大约110页,每页都填充来自SQLite数据库的数据,并且有很多TextView和几个ImageView。但是如此大量的视图会消耗大量内存,因此会抛出 java.lang.OutOFMemoryError:位图大小超过VM预算。所以我尝试以两种方式对此进行评论 - 一开始我尝试只使用3页并在用户从中间页面向右页面滑动页面时进行监听,之后我将视图重新添加到ViewPager和setCurrentItem返回到中间页面。不幸的是,页面滑动并不顺畅,而且是有缺陷的。在第二次尝试时,我用空白视图填充所有页面并听取用户位置的位置,并在其位置,位置-1和位置+ 1中添加数据填充视图。页面滑动足够流畅,但如果用户从位置2运行ViewPager(初始页面可以是任意)并滑动到位置100,则仍然存在 java.lang.OutOFMemoryError:位图大小超过VM预算
@Override
protected void onCreate(Bundle savedInstanceState)
{
for (int i=0; i < ids.length;i++)
{
viewsToShow[i] = inflater.inflate(R.layout.blank, null);
if(ids[i] == getIntent().getIntExtra(DBC.IE_ID, -1))
currentIDPosition = i;
}
pagerAdapter = new MainPagerAdapter();
pager = (ViewPager) findViewById (R.id.pagerszcz);
pager.setAdapter (pagerAdapter);
for (int i = 0; i < viewsToShow.length; i++)
{
if(i == currentIDPosition-1)
{
viewsToShow[i] = (View) inflater.inflate(R.layout.szczegoly_item, null);
pobierzIZwiazDane(ids[i], viewsToShow[i]); // pobierzIZwiazDane fills view by data from SQLite database and images
pagerAdapter.addView (viewsToShow[i], i);
}
else if (i == currentIDPosition)
{
viewsToShow[currentIDPosition] = (View) inflater.inflate(R.layout.szczegoly_item, null);
pobierzIZwiazDane(ids[currentIDPosition], viewsToShow[currentIDPosition]);
pagerAdapter.addView (viewsToShow[i], i);
}
else if (i == currentIDPosition+1)
{
viewsToShow[i] = (View) inflater.inflate(R.layout.szczegoly_item, null);
pobierzIZwiazDane(ids[i], viewsToShow[i]);
pagerAdapter.addView (viewsToShow[i], i);
}
else
pagerAdapter.addView (viewsToShow[i], i);
}
pagerAdapter.notifyDataSetChanged();
pager.setCurrentItem(currentIDPosition);
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
/* int oldPage=1;
int newPage;*/
public void onPageSelected(int position) {
mSelectedPageIndex = position;
//Log.d("mselect",position+" "+ pager.getCurrentItem());
}
public void onPageScrolled(int arg0, float arg1, int arg2) {
// Swipe to right page
if (pager.getCurrentItem()<currentIDPosition && pager.getCurrentItem() > 0)
{
Log.d("Zmniejszenie", pager.getCurrentItem()+" "+currentIDPosition);
currentIDPosition--;
pagerAdapter.removeView(pager, currentIDPosition-1);
viewsToShow[currentIDPosition-1] = (View) inflater.inflate(R.layout.szczegoly_item, null);
pobierzIZwiazDane(ids[currentIDPosition-1], viewsToShow[currentIDPosition-1]);
pagerAdapter.addView (viewsToShow[currentIDPosition-1], currentIDPosition-1);
new Handler().post(new Runnable() {
public void run() { // Removing view at current postion +2
viewsToShow[currentIDPosition+2] = null;
viewsToShow[currentIDPosition+2] = new View(SzczegolyViewPagerActivity.this);
pagerAdapter.removeView(pager, currentIDPosition+2);
pagerAdapter.addView (viewsToShow[currentIDPosition+2], currentIDPosition+2);
}
});
pagerAdapter.notifyDataSetChanged();
}
else if (pager.getCurrentItem()>currentIDPosition && pager.getCurrentItem() < ids.length-1)
{// Swipe to left page
Log.d("Zwiekszenie", pager.getCurrentItem()+" "+currentIDPosition);
currentIDPosition++;
pagerAdapter.removeView(pager, currentIDPosition+1);
viewsToShow[currentIDPosition+1] = (View) inflater.inflate(R.layout.szczegoly_item, null);
pobierzIZwiazDane(ids[currentIDPosition+1], viewsToShow[currentIDPosition+1]);
pagerAdapter.addView (viewsToShow[currentIDPosition+1], currentIDPosition+1);
new Handler().post(new Runnable() {
public void run() {
viewsToShow[currentIDPosition-2] = new View(SzczegolyViewPagerActivity.this);
pagerAdapter.removeView(pager, currentIDPosition-2);
pagerAdapter.addView (viewsToShow[currentIDPosition-2], currentIDPosition-2);
}
});
pagerAdapter.notifyDataSetChanged();
}
}
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
Log.d("pagec", currentIDPosition+" "+pager.getCurrentItem() + " " +pagerAdapter.getCount() + " " + mSelectedPageIndex);
}
}
});
}
class MainPagerAdapter extends PagerAdapter
{
// This holds all the currently displayable views, in order from left to right.
private ArrayList<View> views = new ArrayList<View>();
private LayoutInflater inflater;
public MainPagerAdapter()
{
inflater = getLayoutInflater();
}
//-----------------------------------------------------------------------------
// Used by ViewPager. "Object" represents the page; tell the ViewPager where the
// page should be displayed, from left-to-right. If the page no longer exists,
// return POSITION_NONE.
@Override
public int getItemPosition (Object object)
{
int index = views.indexOf (object);
if (index == -1)
return POSITION_NONE;
else
return index;
}
//-----------------------------------------------------------------------------
// Used by ViewPager. Called when ViewPager needs a page to display; it is our job
// to add the page to the container, which is normally the ViewPager itself. Since
// all our pages are persistent, we simply retrieve it from our "views" ArrayList.
@Override
public Object instantiateItem (ViewGroup container, int position)
{
View v = views.get (position);
//View cont = inflater.inflate(R.layout.szczegoly_item, container, false);
container.addView (v);
return v;
}
//-----------------------------------------------------------------------------
// Used by ViewPager. Called when ViewPager no longer needs a page to display; it
// is our job to remove the page from the container, which is normally the
// ViewPager itself. Since all our pages are persistent, we do nothing to the
// contents of our "views" ArrayList.
@Override
public void destroyItem (ViewGroup container, int position, Object object)
{
container.removeView (views.get (position));
}
//-----------------------------------------------------------------------------
// Used by ViewPager; can be used by app as well.
// Returns the total number of pages that the ViewPage can display. This must
// never be 0.
@Override
public int getCount ()
{
return views.size();
}
//-----------------------------------------------------------------------------
// Used by ViewPager.
@Override
public boolean isViewFromObject (View view, Object object)
{
return view == object;
}
//-----------------------------------------------------------------------------
// Add "view" to right end of "views".
// Returns the position of the new view.
// The app should call this to add pages; not used by ViewPager.
public int addView (View v)
{
return addView (v, views.size());
}
//-----------------------------------------------------------------------------
// Add "view" at "position" to "views".
// Returns position of new view.
// The app should call this to add pages; not used by ViewPager.
public int addView (View v, int position)
{
views.add (position, v);
return position;
}
//-----------------------------------------------------------------------------
// Removes "view" from "views".
// Retuns position of removed view.
// The app should call this to remove pages; not used by ViewPager.
public int removeView (ViewPager pager, View v)
{
return removeView (pager, views.indexOf (v));
}
//-----------------------------------------------------------------------------
// Removes the "view" at "position" from "views".
// Retuns position of removed view.
// The app should call this to remove pages; not used by ViewPager.
public int removeView (ViewPager pager, int position)
{
// ViewPager doesn't have a delete method; the closest is to set the adapter
// again. When doing so, it deletes all its views. Then we can delete the view
// from from the adapter and finally set the adapter to the pager again. Note
// that we set the adapter to null before removing the view from "views" - that's
// because while ViewPager deletes all its views, it will call destroyItem which
// will in turn cause a null pointer ref.
// pager.setAdapter (null);
views.remove (position);
// pager.setAdapter (this);
return position;
}
//-----------------------------------------------------------------------------
// Returns the "view" at "position".
// The app should call this to retrieve a view; not used by ViewPager.
public View getView (int position)
{
return views.get (position);
}
// Other relevant methods:
// finishUpdate - called by the ViewPager - we don't care about what pages the
// pager is displaying so we don't use this method.
}
pobierzIZwiazDane(数据库记录id,视图)负责按数据库中的数据填充页面。
答案 0 :(得分:1)
抱歉,您应该考虑不使用viewpager。
Viewpager会立即启动您的所有片段,这对手机来说太过分了。
您可以尝试使用 FragmentStatePagerAdapter :
http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html
一次只能加载一些片段。
您还可以向应用添加手势检测,手动管理片段以及添加自定义动画。这可能有点困难。
答案 1 :(得分:1)
如果您在eclipse中创建一个Android项目并允许向导使用Navigation Type = Fixed Tabs + Swipe进行设置,您将在创建的MainActivity中看到一条注释:
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {@link android.support.v4.app.FragmentPagerAdapter} derivative, which
* will keep every loaded fragment in memory. If this becomes too memory
* intensive, it may be best to switch to a
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
*/
所以,正确的答案是切换到FragmentsStatePagerAdapter
答案 2 :(得分:0)
您要提前创建所有视图!如果要显示100个页面,则内存中有100个页面的视图。它必须抛出OutOfMemoryError。
解决方案是:
instantiateItem(ViewGroup container, int position)
中。destroyItem(ViewGroup container, int position, Object object)
如果你正确实现它,内存中的视图永远不会超过3页。
以下示例在Integer.MAX_VALUE
中显示ViewPager
个页面:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyPagerAdapter adapter = new MyPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pagerszcz);
pager.setAdapter(adapter);
pager.setCurrentItem(Integer.MAX_VALUE/2);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private static class MyPagerAdapter extends android.support.v4.view.PagerAdapter{
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//Remove the view added in instantiateItem from the container
container.removeView((View)object);
//delete objects created in instantiateItem (non View classes like Bitmap) if necessary
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TextView tv = new TextView(container.getContext());
tv.setText("#"+position);
//Add you View to the container
container.addView(tv);
return tv;
}
}
}