Android自定义ArrayAdapter <string>变得非常慢

时间:2015-08-12 05:04:08

标签: android android-listview

我正在学习Android,并关注自定义ListView项目的this教程。

然而,我已经创建了自己的ListView项目,当我加载应用程序时(在我的Galaxy S4,物理设备上)它变得非常慢。

当我使用simple_list_item_1作为我的列表视图时,一切都运行顺畅,但是当我使用自己的自定义项时,它运行得非常慢。我不明白为什么会这样。似乎没有我创建的昂贵(并且绝对不是无限运行)操作。

我也注意到即使我只有5个listItem,getView方法也会被调用15次左右。对此原因的解释也是受欢迎的。 (他们可能有关系)

对于我的活动,我使用了Android Studio(1.2.2)标准“导航抽屉活动”。我只是在onCreateView方法中添加了东西。现在看起来像这样:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container, false);

        /* Start of my custom code */
        //Create some list items
        String[] words = {"Defenestration", "Indicative", "Executive", "Developmental", "Consciousness"};
        //The list in the Fragment
        ListView list = (ListView) rootView.findViewById(R.id.mainList);
        //The custom ListAdapter
        ListAdapter la = new ShaggyAdapter(getActivity(), words);
        //A built in listadapter for testing
        //ListAdapter la2 = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, words);
        list.setAdapter(la);

        //Create listener
        list.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                        String word = String.valueOf(parent.getItemAtPosition(position));
                        Toast.makeText(getActivity(), word, Toast.LENGTH_SHORT).show();
                    }
                });

        /* End of my custom code */
        return rootView;
    }

自定义适配器如下所示:

class ShaggyAdapter extends ArrayAdapter<String>{
    private static final String TAG = "coo";
    public ShaggyAdapter(Context context, String[] words) {
        super(context, R.layout.shaggy_item, words);

    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = LayoutInflater.from(getContext());

        if (convertView == null){
            convertView = inflater.inflate(R.layout.shaggy_item, parent, false);
            Log.i(TAG, "inflate");
        }else{
            Log.i(TAG, "Don't inflate");
        }

        String word = getItem(position);
        TextView name = (TextView) convertView.findViewById(R.id.itemName);

        name.setText(word);

        return convertView;

    }
}

自定义列表项如下所示:

<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="wrap_content"
    android:columnCount="5">

    <ImageView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:id="@+id/itemImage"
        android:layout_row="0"
        android:layout_column="0"
        android:src="@drawable/no_profile"
        android:layout_margin="8dp"
        android:layout_rowSpan="2"
        android:contentDescription="@string/shaggy_item_image_description" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="@string/shaggy_item_name_placeholder"
        android:id="@+id/itemName"
        android:layout_row="0"
        android:layout_column="1"
        android:layout_margin="8dp"
        android:layout_marginTop="14dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="@string/shaggy_item_new_tag"
        android:id="@+id/itemNew"
        android:layout_row="0"
        android:layout_column="2"
        android:layout_marginTop="14dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="@string/shaggy_item_date_placeholder"
        android:id="@+id/itemDate"
        android:layout_row="1"
        android:layout_column="1"
        android:layout_margin="8dp"
        android:layout_columnSpan="2" />

    <ImageView
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:id="@+id/itemStar"
        android:layout_row="0"
        android:layout_column="3"
        android:src="@drawable/rating_star_1"
        android:layout_margin="8dp"
        android:layout_marginTop="14dp"
        android:layout_rowSpan="2"
        android:contentDescription="@string/shaggy_item_star_description" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="@string/shaggy_item_rating_placheholder"
        android:id="@+id/itemRating"
        android:layout_row="0"
        android:layout_column="4"
        android:layout_margin="8dp"
        android:layout_marginTop="14dp"
        android:layout_rowSpan="2" />

</GridLayout>

任何建议都将受到高度赞赏。

2 个答案:

答案 0 :(得分:1)

我找到了答案,我犯了一个非常愚蠢的错误。我在配置文件图像视图中使用的图像是2000x2000px,我在80x80dp的imageview中显示它。我注意到内存使用率突然增加了一倍。

使用较小的图像(目前为300x300px)使一切运行得非常流畅。我今天学到的东西:

- 使用正确尺寸的图片! Android并不喜欢处理图片。

我也会按照 Boss 群众之王的建议使用保持模式,以使其更加流畅。

答案 1 :(得分:1)

  

为什么findViewById这么慢?为什么View Holder   模式更快?

当你不使用Holder时,getView()方法会多次调用findViewById(),因为你的行将不在View中。因此,如果List中有1000行,而990行将不在View中,则990次将再次被称为findViewById()。

Holder设计模式用于View缓存 - Holder(任意)对象保存每行的子窗口小部件,当行超出View时,将不会调用findViewById()但View将被回收并且将从中获取窗口小部件支架

if (convertView == null) {
   convertView = inflater.inflate(layout, null, false);
   holder = new Holder(convertView);
   convertView.setTag(holder); // setting Holder as arbitrary object for row
}
else { // view recycling
   // row already contains Holder object
   holder = (Holder) convertView.getTag();
}

// set up row data from holder
titleText.setText(holder.getTitle().getText().toString());

Holder类看起来像:

public class Holder {

   private View row;
   private TextView title;

   public Holder(View row) {
      this.row = row;
   }

   public TextView getTitle() {
      if (title == null) {
         title = (TextView) row.findViewById(R.id.title);
      }
      return title;
   }
}

以下是如何使用ViewHolder模式的第二种方法:

ViewHolder holder;
// view is creating
if (convertView == null) {
   convertView = LayoutInflater.from(mContext).inflate(R.layout.row, parent, false);
   holder = new ViewHolder();   
   holder.title = (TextView) convertView.findViewById(R.id.title);
   holder.icon = (ImageView) convertView.findViewById(R.id.icon);
   convertView.setTag(holder);
}
// view is recycling
else {
   holder = (ViewHolder) convertView.getTag();
}

// set-up row
final MyItem item = mItems.get(position);
holder.title.setText(item.getTitle());
...

private static class ViewHolder {

   public TextView title;
   public ImageView icon;
}

这个Android listview using ViewHolder将帮助您实现相同的目标。

众所周知,Google和AppCompat v7作为支持库发布了名为RecyclerView的新ViewGroup,旨在呈现任何基于适配器的视图。

干杯!!