1

我有 ViewPager,它通常会存储大约 110 页,每个页面都由 SQLite 数据库中的数据填充,并且有很多 TextViews 和几个 ImageViews。但是如此大量的视图会消耗大量内存,因此会抛出java.lang.OutOFMemoryError: bitmap size超出VM预算. 因此,我尝试以 2 种方式来实现这一点 - 一开始我尝试仅使用 3 个页面并在用户将页面从中间页面滑动到右侧页面时收听,然后我将视图重新添加到 ViewPager 并将 setCurrentItem 重新添加到中间页面。不幸的是,页面滑动并不顺畅,而且有问题。在第二次尝试中,我用空白视图填充所有页面并听用户的位置在哪里,并在他的位置、位置-1 和位置+1 中添加数据填充视图。页面滑动足够流畅,但是如果用户从位置 2(初始页面可以是任何页面)运行 ViewPager 并滑动到位置 100,仍然会出现java.lang.OutOFMemoryError: bitmap size超出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(database record id, view) 负责用数据库中的数据填充页面。

4

3 回答 3

2

抱歉,但您应该考虑不使用 viewpager。

Viewpager 会一次性启动所有的 Fragment,这对手机来说实在是太多了。

您可以尝试使用FragmentStatePagerAdapter

http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html

一次只能加载几个片段。

您还可以将手势检测添加到您的应用程序中,同时手动管理您的片段,并添加自定义动画。这可能有点困难。

于 2013-09-29T11:39:33.977 回答
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

于 2013-09-29T11:43:53.820 回答
0

您正在提前创建所有视图!如果要显示 100 页,则内存中有 100 页的视图。它必须抛出 OutOfMemoryError。

解决方案是:

  • 当 ViewPager 要求时创建视图(在instantiateItem(ViewGroup container, int position)您的适配器中)。
  • 删除(删除)它们destroyItem(ViewGroup container, int position, Object object)

如果你正确地实现它,内存中的视图永远不会超过 3 页。

以下示例Integer.MAX_VALUE在 a 中显示页面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;
    }       
}

}

于 2013-09-29T12:09:05.000 回答