打开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; } }
之后我们只需要一步步返回,便完成了从服务器端获取最近一个火爆专辑信息的获取。