如何解决内存泄漏gridview到listview? - Android

时间:2014-09-05 12:18:07

标签: android listview gridview memory

我尝试制作一个包含每行视图的列表视图。此视图包含2个textviews和1个gridview,即2列。在每一列中,我使用的基本布局由2个textviews组成。

这是在gridview的每个块中使用的基本布局的预览。

enter image description here

这是它的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="match_parent"
android:id="@+id/listview_item">

<TextView
    android:layout_width="220dp"
    android:layout_height="wrap_content"
    android:textSize="18dp"
    android:text="Item Name"
    android:id="@+id/list_item"
    android:maxLines="1"
    android:layout_marginLeft="5dp"
    />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Price"
    android:textSize="18dp"
    android:id="@+id/item_price"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    android:layout_marginRight="5dp"/>

</RelativeLayout>

这是我的第二个视图,其中包含2个textview和1个gridview。

enter image description here

这里是它的xml; - 第二视图 -

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview_item"
android:orientation="vertical">

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="40dp"
    android:text="Kategori"
    android:id="@+id/categories_title_list_layout"/>

<GridView
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:id="@+id/gridView_list_layout"
    android:numColumns="2"
    android:layout_weight="1"/>

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Not: Lorem ipsum dolor sit amet.."
    android:textSize="18dp"
    android:id="@+id/categories_note"/>

</LinearLayout>

这是我最后的观点; - 第三视图 -

enter image description here

这个listview的每一行都是我的第二个视图的形状,它形成了第一个视图。

这是我的适配器创建此视图。 对于第一个视图,我使用此适配器;

public class ItemListAdapter extends BaseAdapter {

private Context context;
private ArrayList<Item> items;

public ItemListAdapter(Context context, ArrayList<Item> items) {
    super();
    this.context = context;
    this.items = items;
}

public int getCount() {
    return items.size();
}

public Object getItem(int i) {
    return items.get(i);
}

public long getItemId(int i) {
    return i;
}

public View getView(final int i, View view, ViewGroup viewGroup) {
    ViewHolder holder;

    if(view == null) {
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater)     context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.listview_item_content, null);

        holder.title = (TextView) view.findViewById(R.id.list_item);
        holder.price = (TextView) view.findViewById(R.id.item_price);

        view.setTag(holder);
    }

    holder = (ViewHolder) view.getTag();
    Typeface face=Typeface.createFromAsset(context.getAssets(), "fonts/roboto.ttf");
    holder.title.setTypeface(face, Typeface.BOLD);
    holder.title.setText(items.get(i).getName());
    holder.title.setTextColor(Color.parseColor(Shop.getInstance().getItemGridTextColor()));
    holder.price.setText(Global.getLocalizedPriceStringByLocale(Shop.getLocale(), items.get(i).getPrice()));
    holder.price.setTextColor(Color.parseColor(Shop.getInstance().getItemGridTextColor()));

    return view;
}

public class ViewHolder {
    public TextView title;
    public TextView price;
}
}

(此适配器获取项目的标题和价格,并将它们放在第一个视图中。)

创建第二个视图的第二个适配器就在这里;

public class CategoryListAdapter extends BaseAdapter {

private Context context;
//private ArrayList<Item> items;
private  ArrayList<Category> currentCategory;

public CategoryListAdapter(Context context,  ArrayList<Category> category) {
    super();
    this.context = context;
    currentCategory = category;

}

public int getCount() {
    return currentCategory.size();
}

public Object getItem(int i) {
    return currentCategory.get(i);
}

public long getItemId(int i) {
    return i;
}

public View getView(final int i, View view, ViewGroup viewGroup) {

    ViewHolder holder;

    if (view == null) {
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.grid_list_layout, null);

        holder.title = (TextView) view.findViewById(R.id.categories_title_list_layout);
        holder.gridView = (GridView) view.findViewById(R.id.gridView_list_layout);
        holder.note = (TextView) view.findViewById(R.id.categories_note);

        view.setTag(holder);
    }

    holder = (ViewHolder) view.getTag();
    Typeface face = Typeface.createFromAsset(context.getAssets(), "fonts/roboto.ttf");
    holder.title.setTypeface(face, Typeface.BOLD);

    if(currentCategory.get(i).getName().equals("")){
        holder.title.setText("Diğer");
    }else{
        holder.title.setText(currentCategory.get(i).getName());
    }

    holder.title.setTextColor(Color.parseColor(Shop.getInstance().getItemGridTextColor()));
    holder.note.setText(currentCategory.get(i).getDeepNote());
    holder.note.setTextColor(Color.parseColor(Shop.getInstance().getItemGridTextColor()));
    holder.gridView.setAdapter(new ItemListAdapter(context, currentCategory.get(i).getItems()));
    holder.gridView.setBackgroundColor(Color.parseColor(Shop.getInstance().getItemGridBackgroundColor()));
    holder.gridView.getBackground().setAlpha(180);

    return view;
}

public class ViewHolder {
    public TextView title;
    public GridView gridView;
    public TextView note;
}
}

我使用此适配器来创建ListView。

这是我的问题。这工作真的很慢。我的意思是ListView会冻结片刻,然后在我尝试向下移动时向下滑动。 关于GridView的高度还有另一个问题。我的GridView的高度是wrap_content,但它的行为不像wrap_content。它显示更大或更小的GridView。

enter image description here

例如;在“Diğer”标题下应该有一个GridView,它只包含1个项目,但是它无法显示完整的文本。在“AdetÜrünler”下,应该有190件商品,但它只能看到其中的20件。

这些是我的问题。对不起我的编码。如果你无法理解我的代码,请问我。 谢谢你的帮助。

2 个答案:

答案 0 :(得分:1)

这个答案不会给你一个明确的解决方案,不是因为我不愿意,而是因为它不可能(甚至更难,不仅仅是查看你的代码,而是非常了解它)。但根据我的经验,我可以告诉你,由于直接引用的对象,这些内存泄漏并不会发生 - 你声明的对象(并继续引用另一个类/对象)依赖于许多其他类等等,并且可能由于对您的任何实例的错误处理而导致内存泄漏,同时引用其他实例。

调试内存泄漏通常是一项非常艰苦的工作,不仅仅是因为正如我上面所述,它有时并不直接依赖于您所声明的内容,而且因为找到解决方案可能并不是一件容易的事。您可以做的最好的事情就是您似乎正在做的事情:DDMS + HPROF。我不知道你有多少知识,但虽然它不是一种通用的方法,this link帮助我找到了代码中的内存泄漏。

虽然看起来微不足道,但调试这类事情的最佳方法是逐步删除代码的一部分(总体而言,意味着使用其他类的实例)并查看HPROF报告的更改方式。

答案 1 :(得分:0)

据我了解你的问题,在滚动屏幕截图中显示的列表时效果不佳?

您的问题的解决方案需要您重写适配器和布局。您的性能问题是由于您使用的相当大的列表项(例如,包含190个项目的单个列表项中的网格),必须在滚动列表期间加载,以及列表项的相对较低的可重用性。

此外,我不想使用观看者。

要摆脱网格,您可以使用包含“标题”,“注释”和其间的单个网格行的对象列表(或如下所示的包装器)。你必须覆盖一些适配器方法,在一个列表视图中使用多个视图类型(如下所示)。

也许您还需要更多代码,将模型映射到新列表中,但毕竟,您的表现应该恢复正常。

我所知道的唯一缺点(并且没有快速解决方案)是由于单个列表项中的高度不同,整个列表的滚动条有时会​​显示奇怪的行为(例如:滚动条滚动条指示符在滚动期间的高度变化)< / p>

<强>包装

public class ListItemWrapper {
    ListItemType type;
    Object content;

    public ListItemWrapper(ListItemType type, Object content) {
        this.type = type;
        this.content = content;
    }

    public enum ListItemType { Title,Note, Content;}
}

<强> ListAdapter

public class ListAdapter extends ArrayAdapter<ListItemWrapper> {

    private LayoutInflater inflater;

    public ListAdapter(Context context, int resource) {
        super(context, resource);
        inflater = LayoutInflater.from(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ListItemWrapper item = getItem(position);
        switch (item.type) {
            case Content: return getViewForGridRow((GridRowContent)item.content, convertView);
            case Note: return getViewForNote((String)item.content, convertView);
            case Title: return getViewForTitle((String) item.content, convertView);

        }
        return convertView; //this case should never happen
    }

    private View getViewForTitle(String content, View convertView) {
        if (convertView == null) {
            //TODO inflate a new view for the title
            convertView =  inflater.inflate(R.layout.titleRowLayout, null);
        }
        //TODO set textview value for the title
        return convertView;
    }

    private View getViewForNote(String content, View convertView) {
        if (convertView == null) {
            //TODO inflate a new view for the note
            convertView =  inflater.inflate(R.layout.noteRowLayout, null);
        }
        //TODO set textview value for the note
        return convertView;
    }

    private View getViewForGridRow(GridRowContent item, View convertView) {
        if (convertView == null) {
            //TODO inflate a new view for a single grid row
            convertView =  inflater.inflate(R.layout.gridRowLayout, null);
        }
        //TODO set values of the grid row (e.g. textview items)
        return convertView;
    }

    @Override
    public int getItemViewType(int position) {
        return getItem(position).type.ordinal();
    }

    @Override
    public int getViewTypeCount() {
        return ListItemType.values().length;
    }
}

我简化了模型的部分,但我希望你能明白这一点。

正如您所看到的,我不使用自定义BaseAdapter,因为arrayadapter已经管理了我的集合。我只需要告诉列表,有不同的项目视图类型以及集合中的哪个项目使用哪种视图类型。

此外,不需要使用任何持有者,因为所有持有和管理不同视图的工作已经由适配器完成 - 例如如果给定的convertView尚未在适配器中缓存,我们只需要膨胀视图。

使用这种方式应该可以提高内存效率,并且性能应该提高,因为只需要在一个步骤中填充更少的视图。

我希望这有帮助,

基督教

修改

无法解释中心消失的网格视图,但这不应该再发生了