在快速滚动时使用自定义ParseQueryAdapter在ListView Fragment上增加堆(frag case)

时间:2014-10-14 15:17:06

标签: android parse-platform android-listfragment custom-adapter android-parsequeryadapter

我正在使用Parse.com android sdk。在我的TabsActivity.java内,我有SearchFragment扩展ListFragment,以便使用自定义ParseQueryAdapter填充ListView。在我的自定义适配器中,我声明了一个名为search_list_item.xml的自定义列表行布局。此布局仅包含ParseImageView

我的问题是,当我快速向下滚动列表时,我的logcat充满了

  

I / dalvikvm-heap:将堆(frag case)增长到... MB进行字节分配

并且listView返回到初始位置(这意味着它返回到第一个项目)。另一方面,如果我慢慢滚动列表,我可以在项目的末尾滚动没有此错误。我该怎么办?

此外,如果我使用默认的ParseQueryAdapter而不使用search_list_item.xml自定义行,我就不会遇到这样的问题。

下面我发布一些我觉得有用的代码:

search_list_item.xml

的代码
<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.parse.ParseImageView
        android:id="@+id/ProfileImage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />



</RelativeLayout>

TabsActivity.Java

的代码
public class TabsActivity extends Activity implements SearchFragment.OnFragmentInteractionListener, ActionBar.TabListener {

    /**
     * The {@link android.support.v4.view.PagerAdapter} that will provide
     * fragments for each of the sections. We use a
     * {@link 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.v13.app.FragmentStatePagerAdapter}.
     */
    SectionsPagerAdapter mSectionsPagerAdapter;

    /**
     * The {@link ViewPager} that will host the section contents.
     */
    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tabs);

        // Set up the action bar.
        final ActionBar actionBar = getActionBar();
        actionBar.setLogo(R.drawable.logo_white);
        actionBar.setDisplayShowTitleEnabled(false);
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.tabs_pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        // When swiping between different sections, select the corresponding
        // tab. We can also use ActionBar.Tab#select() to do this if we have
        // a reference to the Tab.
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                actionBar.setSelectedNavigationItem(position);
            }
        });

        // For each of the sections in the app, add a tab to the action bar.

        actionBar.addTab(
                actionBar.newTab()
                        .setText("Search")
                        .setTabListener(this));

        actionBar.addTab(
                actionBar.newTab()
                        .setText("Secind")
                        .setTabListener(this));

        actionBar.addTab(
                actionBar.newTab()
                        .setText("Third")
                        .setTabListener(this)
        );

        actionBar.addTab(
                actionBar.newTab()
                        .setText("Fourth")
                        .setTabListener(this));


    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.tabs, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {

        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
        // When the given tab is selected, switch to the corresponding page in
        // the ViewPager.
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

    /**
     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
     * one of the sections/tabs/wizard1.
     */
    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }


        @Override
        public Fragment getItem(int index) {

            switch (index) {
                case 0:
                    // Search fragment activity
              return new SearchFragment();
                case 1:
                    // Flirts fragment activity
                    return new SecondFragment();
                case 2:
                    // Explore fragment activity
                    return new ThirdFragment();
                case 3:
                    //Profile fragment activity
                    return new FourthFragment();
        }

        return null;
    }


        @Override
        public int getCount() {
            // Show 4 total wizard1.
            return 4;
        }

    }


    public void onFragmentInteraction(String id) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }

}

SearchFragment.java

的代码
public class SearchFragment extends ListFragment {

    private OnFragmentInteractionListener mListener;
    private CustomDogAdapter mainAdapter;

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public SearchFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        mainAdapter = new CustomAdapter(this.getActivity());


        // Set the ListActivity's adapter to be the PQA
        mainAdapter.setAutoload(false);
        mainAdapter.loadObjects();
        setListAdapter(mainAdapter);


    }



    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }


    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        if (null != mListener) {
            // Notify the active callbacks interface (the activity, if the
            // fragment is attached to one) that an item has been selected.
        }
    }

    /**
    * This interface must be implemented by activities that contain this
    * fragment to allow an interaction in this fragment to be communicated
    * to the activity and potentially other fragments contained in that
    * activity.
    * <p>
    * See the Android Training lesson <a href=
    * "http://developer.android.com/training/basics/fragments/communicating.html"
    * >Communicating with Other Fragments</a> for more information.
    */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        public void onFragmentInteraction(String id);
    }



}

CustomAdapter.java

的代码
public class CustomAdapter extends ParseQueryAdapter<ParseObject> {

    public CustomAdapter(Context context) {
        // Use the QueryFactory to construct a PQA that will only show
        // Todos marked as high-pri
        super(context, new ParseQueryAdapter.QueryFactory<ParseObject>() {
            public ParseQuery create() {
                ParseQuery query = new ParseQuery("Photo");
                query.whereEqualTo("imageName", "profileImage");
//                query.setCachePolicy(ParseQuery.CachePolicy.CACHE_ELSE_NETWORK);
                return query;
            }
        });
    }

    // Customize the layout by overriding getItemView
    @Override
    public View getItemView(ParseObject object, View v, ViewGroup parent) {
        if (v == null) {
            v = View.inflate(getContext(), R.layout.search_list_item, null);
        }

        super.getItemView(object, v, parent);

        // Add and download the image
        ParseImageView image = (ParseImageView) v.findViewById(R.id.profileImage);
        ParseFile photoFile = object.getParseFile("imageFile");
        if (photoFile != null) {
            image.setParseFile(photoFile);
            image.loadInBackground();
//                    (new GetDataCallback() {
//                @Override
//                public void done(byte[] data, ParseException e) {
//                    // nothing to do
//                }
//            });

        }


//        // Add the title view
//        TextView titleTextView = (TextView) v.findViewById(R.id.text1);
//        titleTextView.setText(object.getString("title"));
//
//        // Add a reminder of how long this item has been outstanding
//        TextView timestampView = (TextView) v.findViewById(R.id.timestamp);
//        timestampView.setText(object.getCreatedAt().toString());
        return v;
    }

}

我已经研究了很多次并且遵循了Parse.com的这些文件

1)UI-ParseQueryAdapter

2)MealSpotting

非常感谢你的时间,并为这篇长篇文章感到抱歉,尽量做到尽可能的解释。

1 个答案:

答案 0 :(得分:2)

经过一些更普遍的搜索列表视图和他们的表现后,我跨过一篇博文,其中有一段非常有趣的帖子说:

  

永远不要将ListView的高度设置为wrap_content。如果你有所有   你的数据在本地可用,它可能看起来不那么糟糕,但它变成了   当你不这样做时尤其麻烦。如果你使用wrap_content   你的ListView,这是发生的事情:第一个getView调用完成,   convertView为null,并加载位置0。现在,位置1是   已加载,但它会传递您刚刚为位置0生成的视图   它的convertView。然后位置2加载相同的视图,等等   上。这样做是为了布局ListView,因为它必须弄清楚   应该有多高,你没有明确告诉它。一旦它有了   贯穿所有这些职位,将该视图传回   位置0表示另一个getView调用,然后是位置1并打开   加载了getView而没有convertView。你最终会结束   看到getView被调用的频率是你的两到三倍   预期。这不仅会影响性能,还可以获得   一些真正令人困惑的问题。

来源:Read the last paragraph

我并不完全理解它,因为我不熟悉开发,但在阅读之后我决定使用预定义的dp值更改布局高度和宽度并设置android:scaleType="centerCrop"。这解决了增长堆问题,滚动变得非常顺利。

我希望有人发现它像我一样有用,如果有人能从技术上解释它,我会非常有兴趣阅读它。非常感谢你提前!