Android开源项目-Jamendo音乐播放器研究与优化-Adapter相关

【休止符是音乐中无声的节奏进行,恰似此时无声胜有声;而音符是音乐中有声的进行,是唱奏出来的,但相对应的几分音符与几分休止符的时值是相同的】

--- 《五线谱基础教程》


一 自定义Adapter的抽象基类

Android项目开发中经常会用到ListView、GridView等需要和Adapter配合使用的控件,对于界面界面较多,且用到多个不同布局的ListView等时,自然而然的就会面临需要定义多个Adapter类的情况。

一般为了提高灵活性,我们都会让自定义的Adapter继承自BaseAdapter,并重写其中的四个抽象函数,典型的类结构如下:

public class MyBaseAdapter extends BaseAdapter {

	@Override
	public int getCount() {
		return 0;
	}

	@Override
	public Object getItem(int position) {
		return null;
	}

	@Override
	public long getItemId(int position) {
		return 0;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		return null;
	}

}

但仅用上面的代码是不能实现所需功能的,因为Adapter的本意就是给View提供数据的,因此,需要在上面的代码基础上进一步完善,首先由于数据的类型千变万化,因此采用泛型表示类型;其次,数据一般都会有很多条,因此,需要一个容器来存储数据,这里采用常见的ArrayList;最后,Adapter还需要维护一个到View的引用,并且有可能需要用到Android的上下文Context,在Jamendo这个项目中,View类对应的是ListView,Context就使用ListView所在的Activity,因此,得出下面通用的封装了BaseAdapter的抽象类,其中getView函数留给子类来实现。

public abstract class ArrayListAdapter<T> extends BaseAdapter {

	protected ArrayList<T> mList; // 数据容器

	protected Activity mContext; // Android上下文环境

	protected ListView mListView; // 使用这个Adapter的ListView

	public ArrayListAdapter(Activity context) {
		mContext = context;
	}

	@Override
	public int getCount() {
		if (mList != null) {
			return mList.size();
		} else {
			return 0;
		}
	}

	@Override
	public Object getItem(int position) {
		return mList == null ? null : mList.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	// 留给子类实现,因为不同ListView的ListItem布局不一样
	@Override
	abstract public View getView(int position, View convertView,
			ViewGroup parent);

	// 改变了Adapter中的数据,需要刷新ListView
	public void setList(ArrayList<T> list) {
		this.mList = list;
		notifyDataSetChanged();
	}

	public void setList(T[] list) {
		ArrayList<T> arrayList = new ArrayList<T>(list.length);
		for (T t : list) {
			arrayList.add(t);
		}
		setList(arrayList);
	}

	public ArrayList<T> getList() {
		return mList;
	}

	public ListView getListView() {
		return mListView;
	}

	public void setListView(ListView listView) {
		mListView = listView;
	}

}


二 Jamendo中Adapter的体系及实现




ArrayListAdapter的子类都是用于ListView的,各个子类实现父类的抽象函数getView,在该函数中实现列表项的布局,并返回列表项对应的视图。本文就不细讲每个Adapter对应的视图,后面分析各个界面时会涉及到,这里只以DownloadJobAdapter作为例子说明ArrayListAdapter的子类的通用实现方式。

如下图所示是下载管理页面,列表中第一项是下载完成后的视图,第二项是正在下载视图,从中可以看出我们的DownloadJobAdapter的getView函数返回的View中起码包含:显示歌曲名称的TextView、显示专辑名称的TextView、表示下载进度的TextView以及一个进度条。




看DownloadJobAdapter类中定义的静态内部类ViewHolder可以看出,下载页面的列表项确实就是上述四个控件组成:

	static class ViewHolder {
		TextView songName;
		TextView songArtistAlbum;
		TextView songProgressText;
		ProgressBar progressBar;
	}

getView函数一般都会使用ViewHolder模式来优化性能,避免进行不必要的重复工作。由于Android为我们缓存了convertView,所以首先判断入参ConvertView是否为空,为空说明从来没有创建过,需要创建这个View;否则说明这个是之前构建的View的缓存,由于布局资源不变,对应的控件资源可以复用。而这些控件资源就是使用ViewHolder来存储的,方便使用convertView的setTag函数将它作为一个整体存储在convertView中。

可以注意到,setTag函数还有一个重载的函数,这两个函数定义如下:

public void setTag(final Object tag) {
    mTag = tag;
}

public void setTag(int key, final Object tag) {
    // If the package id is 0x00 or 0x01, it's either an undefined package
    // or a framework id
    if ((key >>> 24) < 2) {
        throw new IllegalArgumentException("The key must be an application-specific "
                + "resource id.");
    }

    setTagInternal(this, key, tag);
}

private static void setTagInternal(View view, int key, Object tag) {
    SparseArray<Object> tags = null;
    synchronized (sTagsLock) {
        if (sTags == null) {
            sTags = new WeakHashMap<View, SparseArray<Object>>();
        } else {
            tags = sTags.get(view);
        }
    }

    if (tags == null) {
        tags = new SparseArray<Object>(2);
        synchronized (sTagsLock) {
            sTags.put(view, tags);
        }
    }

    tags.put(key, tag);
}

可以看到,setTag(Object)将对象存在View类的成员变量mTag中;而setTag(int, Object)则使用WeakHashMap来建立View和要存储的对象之间的弱引用关系。如果需要在View中存放多个对象时,可以使用第二个重载函数,需要注意一点时,setTag(int, Object)中的int类型的参数必须定义在res/values/strings.xml文件中,格式如下:

<resources>
    <item type="id" name="wen" />
   <item type="id" name="asce" />
</resources>

在代码中引用如下:

			// 设置tag时
			convertView.setTag(R.id.wen, "wen1885");
			convertView.setTag(R.id.asce, "asce1885");
			...
			// 获取tag时
			String wen = convertView.getTag(R.id.wen);
			String asce = convertView.getTag(R.id.asce);

最后,看下DownloadJobAdapter类的getView函数关键代码如下:

		ViewHolder holder = null;
		if (convertView == null) {
			LayoutInflater inflater = mContext.getLayoutInflater();
			convertView = inflater.inflate(R.layout.download_row, null);

			holder = new ViewHolder();
			holder.songName = (TextView) convertView.findViewById(R.id.TrackRowName);
			holder.songArtistAlbum = (TextView) convertView.findViewById(R.id.TrackRowArtistAlbum);
			holder.songProgressText = (TextView) convertView.findViewById(R.id.TrackRowProgress);
			holder.progressBar = (ProgressBar) convertView.findViewById(R.id.ProgressBar);
			
			// 将控件资源作为整体存放在convertView中
			convertView.setTag(holder);

			mContext.registerForContextMenu(convertView);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值