这里首先显示我们的主界面图片共大家理解。
一步步走到了我们的主界面,首先我们看看主界面的布局文件main.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2009 Teleca Poland Sp. z o.o. <android@teleca.com>
Licensed under the Apache License, Version 2.0 (the "License"); you
may not use this file except in compliance with the License. You may
obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 Unless required by
applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for
the specific language governing permissions and limitations under the
License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="#ffffff">
<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>
<android.gesture.GestureOverlayView
xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/gestures"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:gestureStrokeType="multiple"
android:eventsInterceptionEnabled="false" android:orientation="vertical">
<ListView android:id="@+id/HomeListView"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:divider="#000" />
</android.gesture.GestureOverlayView>
</LinearLayout>
我们可以看到在LinearLayout中嵌套了2栏,第一个是com.teleca.jamendo.util.FixedViewFlipper,第二个是手势视图android.gesture.GestureOverlayView,。默认是垂直显示分栏。
首先来看看com.teleca.jamendo.util.FixedViewFlipper,这是一个自定义控件,继承之ViewFlipper,是作为切换视图所用,一次只能显示一个View内容。
FixedViewFlipper代码如下:
public class FixedViewFlipper extends ViewFlipper {
public FixedViewFlipper(Context context) {
super(context);
}
public FixedViewFlipper(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDetachedFromWindow() {
int apiLevel = Build.VERSION.SDK_INT;
if (apiLevel >= 7) {
try {
super.onDetachedFromWindow();
} catch (IllegalArgumentException e) {
Log.w("Jamendo", "Android project issue 6191 workaround.");
/* Quick catch and continue on api level 7, the Eclair 2.1 */
} finally {
super.stopFlipping();
}
} else {
super.onDetachedFromWindow();
}
}
}
我们看到它基本没有什么特殊之处,只是重写了onDetachedFromWindow方法。该方法的作用是当view离开窗口时调用。这里判断了SDK版本,同时停止切换View.
我们已经知道ViewFlipper类似于FrameLayout,一次只显示一个View的内容。知道这一点后,我们再看看我们的布局文件中com.teleca.jamendo.util.FixedViewFlipper内部布局:
我们可以看到,里面共有三部分内容:Loading,Gallery,Failure。分别是加载中,显示Gallery图像列表,加载失败界面。
刚进来的时候当然默认显示加载界面,加载Gallery数据时,但是由于网络太快,基本上一闪而过,如果网络太慢,它就显示出来了,如果加载失败,就会显示加载失败界面。
我们怎么知道它是显示哪部分界面的呢?
我们先来看看HomeActivity的类结构以及继承关系吧。
它实现了OnAlbumClickListener接口,还有2个内部任务类。一个是获取最新专辑,一个是获取最近前100手歌曲。
好了现在看看onCreate方法:
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
mHomeListView = (ListView)findViewById(R.id.HomeListView);
mGallery = (Gallery)findViewById(R.id.Gallery);
mProgressBar = (ProgressBar)findViewById(R.id.ProgressBar);
mFailureBar = (FailureBar)findViewById(R.id.FailureBar);
mViewFlipper = (ViewFlipper)findViewById(R.id.ViewFlipper);
mGestureOverlayView = (GestureOverlayView) findViewById(R.id.gestures);
mGestureOverlayView.addOnGesturePerformedListener(JamendoApplication
.getInstance().getPlayerGestureHandler());
new NewsTask().execute((Void)null);
}
方法中主要是实例化布局文件main.xml中的控件,当然最重要的有2点,第1点:实例化手势图层,并添加手势感知监听。第2点:执行NewsTask任务获取专辑列表。
这里首先讲解NewsTask任务类,因为这里决定我们显示的是加载还是Gallery列表界面或者是失败界面。代码如:
/**
* Executes news download, JamendoGet2Api.getPopularAlbumsWeek
*
* @author Lukasz Wisniewski
*/
private class NewsTask extends AsyncTask<Void, WSError, Album[]> {
@Override
public void onPreExecute() {
mViewFlipper.setDisplayedChild(0);
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);
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);
}
NewsTask继承AsyncTask,异步任务,通常我们获取后台数据就是通过继承这个任务类,在任务中获取后台数据,而不是新建个Thread,然后再结合Handler,然后通过Handler与主UI线程交互显示数据,这样比较麻烦,使用AsyncTask简单多了,虽然它内部也是由Thread等等封装的。
我们先看看执行任务前做了什么,
mViewFlipper.setDisplayedChild(0);
mProgressBar.setText(R.string.loading_news);
执行前?默认显示ViewFlipper中第一个界面,同时设置进度条文字。
执行中呢?
@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);
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);
}
原来获取到专辑列表数据后,显示ViewFlipper中第二个界面,也就是Gallery列表,当然显示Gallery列表是通过Adpater设配器的,我们还看到如果获取不到数据或者获取数据失败,那么它就会显示ViewFlipper中第三个界面,就是我们的失败界面,同时提供方法让它可以再次执行任务来获取服务端数据。
逻辑非常清晰。
接下来就来看看下一部分内容。