我正在使用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的这些文件
非常感谢你的时间,并为这篇长篇文章感到抱歉,尽量做到尽可能的解释。
答案 0 :(得分:2)
经过一些更普遍的搜索列表视图和他们的表现后,我跨过一篇博文,其中有一段非常有趣的帖子说:
永远不要将ListView的高度设置为wrap_content。如果你有所有 你的数据在本地可用,它可能看起来不那么糟糕,但它变成了 当你不这样做时尤其麻烦。如果你使用wrap_content 你的ListView,这是发生的事情:第一个getView调用完成, convertView为null,并加载位置0。现在,位置1是 已加载,但它会传递您刚刚为位置0生成的视图 它的convertView。然后位置2加载相同的视图,等等 上。这样做是为了布局ListView,因为它必须弄清楚 应该有多高,你没有明确告诉它。一旦它有了 贯穿所有这些职位,将该视图传回 位置0表示另一个getView调用,然后是位置1并打开 加载了getView而没有convertView。你最终会结束 看到getView被调用的频率是你的两到三倍 预期。这不仅会影响性能,还可以获得 一些真正令人困惑的问题。
我并不完全理解它,因为我不熟悉开发,但在阅读之后我决定使用预定义的dp值更改布局高度和宽度并设置android:scaleType="centerCrop"
。这解决了增长堆问题,滚动变得非常顺利。
我希望有人发现它像我一样有用,如果有人能从技术上解释它,我会非常有兴趣阅读它。非常感谢你提前!