作为一名优秀的Android开发者,熟练运用ArrayAdapter是必不可少的技能之一。ArrayAdapter是一个非常重要的适配器类,它可以把数据源集合中的数据绑定到ListView、GridView等控件上,使得我们在Android应用中展现数据更为方便和高效。
今天我们就来深入理解ArrayAdapter,掌握它的原理、用法以及优化方法,让我们在日常的开发中更加得心应手!
一、ArrayAdapter的原理
在正式介绍ArrayAdapter的原理之前,我们需要先了解ArrayAdapter的主要成员变量和构造函数参数:
```java
public class ArrayAdapter
...
private final Object mLock = new Object();
private int mResource;
private int mDropDownResource;
private int mTextViewResourceId;
private boolean mNotifyOnChange = true;
private Context mContext;
private List
private ArrayList
private ArrayFilter mFilter;
private LayoutInflater mInflater;
...
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource) {
this(context, resource, 0, new ArrayList
}
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @IdRes int textViewResourceId) {
this(context, resource, textViewResourceId, new ArrayList
}
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull T[] objects) {
this(context, resource, 0, Arrays.asList(objects));
}
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @IdRes int textViewResourceId, @NonNull T[] objects) {
this(context, resource, textViewResourceId, Arrays.asList(objects));
}
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List
this(context, resource, 0, objects);
}
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @IdRes int textViewResourceId, @NonNull List
super();
mContext = context;
mInflater = LayoutInflater.from(context);
mResource = mDropDownResource = resource;
mObjects = objects;
mTextViewResourceId = textViewResourceId;
}
}
```
- mLock:一个Object对象,为了确保多线程访问的安全而使用的锁。
- mResource:列表项的布局文件的资源id。
- mTextViewResourceId:需要展示的TextView的资源id,如果不指定,则默认为0,在后续的代码中不会用到它。
- mNotifyOnChange:一个boolean值,表示当数据源对象改变时,是否通知ListView刷新列表。
- mContext:上下文对象。
- mObjects:数据源集合。
- mOriginalValues:一个ArrayList集合,用于保存一个原始的、未经过滤的、未排序的数据集合,这个变量在后续的筛选操作中用得到。
- mFilter:实现了Filterable接口的ArrayFilter对象,用于进行筛选操作。
- mInflater:LayoutInflater对象,用于将布局文件转换成View对象。
以上是ArrayAdapter的关键数据成员变量,接下来分析一下它的原理。
我们都知道,ListView、GridView等控件是由多个列表项组成的,每个列表项是由多个控件组成的,例如TextView、ImageView等。因此,在填充数据时,我们需要针对每个列表项中的控件来填充数据。这就需要我们指定一个列表项的布局文件,ArrayAdapter就是通过这个布局文件来确定ListView中每个列表项的样式。
构造ArrayAdapter的时候,我们需要传递一个布局资源id作为参数,ArrayAdapter会根据这个布局资源id获取到一个View对象,然后将数据填充到这个View对象的控件中,最终将这个View对象返回。
在展示数据时,ListView为每个列表项在屏幕中分配一个位置,并通过这个位置获取到相应的对象,然后从这个对象中获取数据,并通过控件的id来定位到要填充数据的位置。填充完数据之后,就是ListView进行布局和绘制的过程了。
总结起来,ArrayAdapter的原理就是:
- 获取布局文件中的View对象。
- 填充View对象中的控件。
- 返回填充好数据的View对象,让ListView进行展示。
二、ArrayAdapter的使用方法
上面我们介绍了ArrayAdapter的原理,下面我们结合一个具体的案例来看一下如何使用ArrayAdapter。在这个案例中,我们首先创建一个数据源集合,然后创建一个ListView,接着利用ArrayAdapter来实现数据源集合中的数据绑定到ListView上。
```java
public class MainActivity extends AppCompatActivity {
private ListView mLv;
private ArrayAdapter
private List
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
initAdapter();
mLv.setAdapter(mAdapter);
}
private void initView() {
mLv = findViewById(R.id.lv);
}
private void initData() {
mData = new ArrayList<>();
for (int i = 0; i < 100; i++) {
mData.add("Item " + i);
}
}
private void initAdapter() {
mAdapter = new ArrayAdapter<>(this, R.layout.item, mData);
}
}
```
在这个案例中,我们创建了一个ListView控件,然后初始化数据源集合mData。接着,通过调用ArrayAdapter的构造函数来创建一个适配器对象mAdapter,在构造函数中,我们传入当前Activity对象、列表项的布局文件的资源id、以及数据源集合mData。最后,把ArrayAdapter对象mAdapter传给ListView的setAdapter()方法,就可以让数据源集合mData中的数据展现在ListView中了。
其中,列表项布局文件item.xml是一个简单的布局文件,只有一个TextView控件:
```xml
android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:padding="10dp" android:textColor="@android:color/black" /> ``` 由于ArrayAdapter默认是使用TextView控件来展示数据的,因此在布局文件中需要先定义一个TextView控件,然后在调用ArrayAdapter构造函数时,指定这个TextView控件的资源id,如果没有指定,则默认使用android.R.id.text1这个资源id。 三、ArrayAdapter的优化方法 在使用ArrayAdapter的过程中,我们同样需要关注一些优化问题。 1. ViewHolder机制 在ListView的滑动过程中,会经过很多个列表项,由于每个列表项都是一个View对象,而View对象的创建和销毁是需要消耗一定资源的,因此,在ListView中使用ViewHolder机制来重复利用View对象,可以大大减少对象的创建和销毁,提高ListView的滑动性能。 具体实现就是在适配器的getView()方法中,使用一个静态内部类ViewHolder来缓存View对象。ViewHolder中存储着View对象中要用到的每一个控件的引用,以便下次使用时直接获取到控件,避免了findViewById()方法的调用,从而提高了性能。 ```java public class MainActivity extends AppCompatActivity { private ListView mLv; private ArrayAdapter private List @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); initAdapter(); mLv.setAdapter(mAdapter); } private void initView() { mLv = findViewById(R.id.lv); } private void initData() { mData = new ArrayList<>(); for (int i = 0; i < 100; i++) { mData.add("Item " + i); } } private void initAdapter() { mAdapter = new ArrayAdapter @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.item, null); holder = new ViewHolder(); holder.mTvTitle = convertView.findViewById(android.R.id.text1); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.mTvTitle.setText(mData.get(position)); return convertView; } }; } static class ViewHolder { TextView mTvTitle; } } ``` 上面的代码中,我们定义了一个静态内部类ViewHolder,它保存着一个TextView对象的引用,用于保存ListView的所有子项中要显示的控件。在getView()方法中,我们通过convertView来获取到缓存的View对象。如果convertView为null,说明这个View还没有被缓存,我们需要创建一个新的ViewHolder对象,并将控件的引用赋值给它,最后将ViewHolder对象通过setTag()方法添加到convertView中。如果convertView不为null,那么就从convertView中通过getTag()方法获取到ViewHolder对象,这个对象中存储的就是ListView的所有子项中要显示的控件的引用,因此我们可以通过这个ViewHolder直接拿到子项的控件,避免了频繁调用findViewById()方法的开销。 2. 异步加载图片 在列表项中展示图片是很常见的操作,但是有时会出现因为加载图片占用主线程导致列表控件卡顿或者显示不出图片的情况。这时我们需要使用异步加载的方式来展示图片。推荐使用Glide框架来实现异步加载图片,它可以通过链式调用方式来设置图片加载的配置,还提供了缓存策略等优化功能。 在ArrayAdapter中实现异步加载图片比较简单,只需要将图片加载的代码放在getView()方法中即可。具体实现如下: ```java public class MainActivity extends AppCompatActivity { private ListView mLv; private ArrayAdapter private List private RequestManager mGlide; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); initAdapter(); mLv.setAdapter(mAdapter); } private void initView() { mLv = findViewById(R.id.lv); } private void initData() { mData = new ArrayList<>(); for (int i = 0; i < 100; i++) { mData.add("http://dummyimage.com/400x400/444444/ffffff&text=Item" + i); } mGlide = Glide.with(this); } private void initAdapter() { mAdapter = new ArrayAdapter @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.item, null); holder = new ViewHolder(); holder.mIvImage = convertView.findViewById(R.id.iv_image); holder.mTvTitle = convertView.findViewById(android.R.id.text1); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } String imageUrl = mData.get(position); holder.mTvTitle.setText("Item " + position); mGlide.load(imageUrl).into(holder.mIvImage); return convertView; } }; } static class ViewHolder { TextView mTvTitle; ImageView mIvImage; } } ``` 上面的代码中,我们首先为数据源集合mData添加了100个图片路径,用于演示异步加载的效果。然后,我们在适配器的构造函数中创建了一个RequestManager对象mGlide,它负责加载图片。在getView()方法中,我们通过Glide框架的with()方法获取到一个RequestManager对象,然后使用load()方法来加载图片,最后调用into()方法将图片展示到ImageView控件上。 需要注意的是,在使用Glide加载网络图片时,需要在AndroidManifest.xml文件中添加网络权限: ```xml ``` 四、总结 ArrayAdapter是一个非常实用的适配器类,它可以将数据源中的数据绑定到ListView、GridView等控件上。我们了解了ArrayAdapter的原理、用法和优化方法,通过学习这些内容,相信大家已经可以使用ArrayAdapter轻松地实现列表的数据绑定了!