1

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

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

I/dalvikvm-heap:将堆(碎片情况)增长到 ...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

非常感谢您抽出宝贵的时间,并为这篇长篇文章感到抱歉,只是试图尽可能地解释一下。

4

1 回答 1

2

在进行了一些更一般的搜索 abot 列表视图及其性能之后,我浏览了一篇博客文章,该文章的最后一段非常有趣,上面写着:

永远不要将 ListView 的高度设置为 wrap_content。如果您的所有数据都在本地可用,它可能看起来并没有那么糟糕,但是当您没有时它会变得特别麻烦。如果您在 ListView 上使用 wrap_content,会发生以下情况:第一次 getView 调用完成,convertView 为空,并且位置 0 被加载。现在,位置 1 已加载,但它传递了您刚刚为位置 0 生成的视图作为它的转换视图。然后位置 2 加载相同的视图,依此类推。这样做是为了布置 ListView,因为它必须弄清楚它应该有多高,而您没有明确告诉它。一旦它运行完所有这些位置,该视图将被传递回位置 0 以进行另一个 getView 调用,然后位置 1 和 on 加载 getView 而没有 convertView。您最终会看到调用 getView 的频率是您预期的两倍或三倍。这不仅会降低性能,而且还会遇到一些非常令人困惑的问题。

来源:阅读最后一段

我不完全理解它,因为我是开发新手,但在阅读后我决定使用预定义的 dp 值和 set 更改布局高度和宽度android:scaleType="centerCrop"。这解决了增长堆问题,滚动变得非常流畅。

我希望有人能像我一样发现它有帮助,如果有人能从技术上更详细地解释它,我会非常有兴趣阅读它。非常感谢您!

于 2014-10-16T11:46:59.173 回答