具有非常多视图的ViewPager会抛出java.lang.OutOFMemoryError

时间:2013-09-29 11:19:04

标签: java android bitmap android-viewpager

我有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,视图)负责按数据库中的数据填充页面。

3 个答案:

答案 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。

解决方案是:

  • 在ViewPager要求时创建视图(在适配器的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;
    }       
}

}