打开Jammendo进入到首页之后,会看到这样一个界面。可以看到下左效果,我们可以看到,他是上部分的ViewFlipper模块和下半部分的listview模块构成的,今天就简单的说一下JammendoViewFlipper是如何加载实际的图片的
我们可以看到上面的ViewFlipper模块中有很多图片,这个模块第一次初始化的时候,实际作用可以理解为是——获取这个星期内最流行的专辑,并在ViewFlipper模块中展示
主界面功能在Jammendo中是在HomeActivity中实现的,可以看下他的布局文件
main.xml文件
<com.teleca.jamendo.util.FixedViewFlipper
android:orientation="vertical" android:id="@+id/ViewFlipper"
android:layout_width="fill_parent" android:layout_height="75dip"
android:background="@drawable/gradient_dark_purple"> <!-- (0) Loading -->
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_marginLeft="15dip" android:gravity="left|center_vertical">
<com.teleca.jamendo.widget.ProgressBar
android:id="@+id/ProgressBar" android:layout_width="wrap_content"
android:layout_height="wrap_content">
</com.teleca.jamendo.widget.ProgressBar>
</LinearLayout> <!-- (1) Gallery -->
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:gravity="center">
<Gallery android:id="@+id/Gallery" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:spacing="0px" />
</LinearLayout> <!-- (2) Failure -->
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_marginLeft="15dip" android:gravity="left|center_vertical">
<com.teleca.jamendo.widget.FailureBar
android:id="@+id/FailureBar" android:layout_width="wrap_content"
android:layout_height="wrap_content">
</com.teleca.jamendo.widget.FailureBar>
</LinearLayout>
</com.teleca.jamendo.util.FixedViewFlipper>
在Jammendo中,他自定义了一个继承于ViewFlipper的FixedViewFlipper控件,并且在代码中重写了onDetachedFromWindow方法。在此,他重写这个方法的目的是为了防止一个bug的出现,可以参考下面这篇文章 http://blog.sina.com.cn/s/blog_74c22b2101012urv.html
我们可以看到在FixedViewFlipper控件中又包含了三个LinerLayerout布局,它们的作用分别如下:
(1)Loading:当图片加载比较慢的时候,会在ViewFlipper显示Loading字样
(2)Gallery:Gallery是一个滑动图片展示控件,如何客户端可以成功从服务器获取数据,会将在这个控件中展示获取专辑内容
(3)Fialure:客户端信息获取失败时,会提示加载失败
让我们会到HomeActivity文件,在HomeActivity的OnCreate()方法中存在我们可以看到这样一段代码
new NewsTask().execute((Void)null);
这里的NewsTask是继承于AsyncTask的一个类,执行异步操作。异步操作的意义在于我们可以将一些比较费时的操作放在这里执行,提高整体工作效率。在这里,我们将客户端请求服务器端数据,并将数据显示到FixedViewFlipper这样一个过程放在这里执行。
首先简单分析一下AsyncTask中一些基本方法的意义:
(1)onPreExecute():在执行doingBackground方法前会执行这个方法,可以显示一些进度条之类的
(2)doinBackground():耗费时间的动作主要在这个方法中执行
(3)publicProgress():用来更新任务进度
(4)onProgressUpdate():当publicProgress方法被调用时,UI线程将调用这个方法在界面上展示任务的进展情况
(5)onPostExecute():任务执行完成,返回结果
private class NewsTask extends AsyncTask<Void, WSError, Album[]> { @Override
public void onPreExecute() {
mViewFlipper.setDisplayedChild(0); // 默认显示ViewFlipper中第一个界面
mProgressBar.setText(R.string.loading_news); //进度条文字
super.onPreExecute();
} @Override
/*
* 获取专辑数据
*
*/
public Album[] doInBackground(Void... params) {
JamendoGet2Api server = new JamendoGet2ApiImpl();
Album[] albums = null;
try {
albums = server.getPopularAlbumsWeek();
} catch (JSONException e) {
e.printStackTrace();
} catch (WSError e){
publishProgress(e);
}
return albums;
} @Override
public void onPostExecute(Album[] albums) { if(albums != null && albums.length > 0){
mViewFlipper.setDisplayedChild(1); // 显示ViewFlipper中第二个界面
ImageAdapter albumsAdapter = new ImageAdapter(HomeActivity.this);
albumsAdapter.setList(albums);
mGallery.setAdapter(albumsAdapter);
mGallery.setOnItemClickListener(mGalleryListener);
mGallery.setSelection(albums.length/2, true); // animate to center } else {
mViewFlipper.setDisplayedChild(2);
mFailureBar.setOnRetryListener(new OnClickListener(){ @Override
public void onClick(View v) {
new NewsTask().execute((Void)null);
} });
mFailureBar.setText(R.string.connection_fail);
}
super.onPostExecute(albums);
} @Override
protected void onProgressUpdate(WSError... values) {
Toast.makeText(HomeActivity.this, values[0].getMessage(), Toast.LENGTH_LONG).show();
super.onProgressUpdate(values);
} }
在onPreExecute()方法中,我们可以看到,他这里执行了一个让FixedViewFlipper显示loading界面的功能
doinBackground()方法主要完成了从服务器获取专辑信息的动作
onPostExecute(),将当前获取数据绑定到Gallery控件之上,完成专辑信息的显示
onProgressUpdate()如果发生异常,会弹出一个对话框提示
下面我们主要分析一下doinBackground()函数到底干了些什么?
public Album[] doInBackground(Void... params) {
JamendoGet2Api server = new JamendoGet2ApiImpl();
Album[] albums = null;
try {
albums = server.getPopularAlbumsWeek();
} catch (JSONException e) {
e.printStackTrace();
} catch (WSError e){
publishProgress(e);
}
return albums;
}
首先创建了一个JamendoGet2Api对象,然后定义了一个Album[]数组,在这里的Album[]其实存放的就是我们将要从服务器上获取的专辑信息
下面调用server中的getPopularAlbumsWeek()获取专辑信息
跳转到getPopularAlbumsWeek()方法
private String doGet(String query) throws WSError{
return Caller.doGet(GET_API + query);
} @Override
public Album[] getPopularAlbumsWeek() throws JSONException, WSError { String jsonString = doGet("id+name+url+image+rating+artist_name/album/json/?n=20&order=ratingweek_desc");
if (jsonString == null)
return null; try {
JSONArray jsonArrayAlbums = new JSONArray(jsonString);
return AlbumFunctions.getAlbums(jsonArrayAlbums);
} catch (NullPointerException e) {
e.printStackTrace();
throw new JSONException(e.getLocalizedMessage());
}
}
getPopularAlbumsWeek中定义了一个jsonString来保存从从服务器上获取的数据,调用的doGet方法是在Caller类中定义,在这里只是传递了后面一些的参数信息
跳转到Caller类中的doGet()方法中我们可以发现,他就是通常的json从服务器端获取数据的一个封装,值得我们注意的是他这里的Cache机制,当他收到当前url信息时,会先遍历本地的cache数据,如果本地存在,则不再去访问服务器获取数据。如果不存在于本地,则从服务端获取数据,并数据转换为String类型。
String data = null;
if(requestCache != null){
data = requestCache.get(url);
if(data != null){
Log.d(JamendoApplication.TAG, "Caller.doGet [cached] "+url);
return data;
}
}
之后我们只需要一步步返回,便完成了从服务器端获取最近一个火爆专辑信息的获取。