本页文章导读:
▪ExpandableListActivity的施用 ExpandableListActivity的使用
所用到的布局文件min.xml(ExpandableListActivity布局文件)注意事项:ExpandableListActivity的布局文件中必须包含一个ExpandableListView,并且id必须为="@id/android:list"。还可以.........
▪ Activity生命周期的学习以及Logcat的施用(转载) Activity生命周期的学习以及Logcat的使用(转载)
原创地址:http://android.blog.51cto.com/268543/322518
Activities是由Activity
stack管理的。当一个新的Activity被启动,它就会处于stack的top位置,成为当前运.........
▪ 【Titanium】经过回调和事件来降低耦合度 【Titanium】通过回调和事件来降低耦合度
【官方地址】https://wiki.appcelerator.org/display/guides/Callbacks+and+Events+for+Loose+Coupling我们在阅读了Appcelerator wiki中的“JavaScript最佳实践”之后都应该知道.........
[1]ExpandableListActivity的施用
来源: 互联网 发布时间: 2014-02-18
ExpandableListActivity的使用
所用到的布局文件
min.xml(ExpandableListActivity布局文件)
注意事项:
ExpandableListActivity的布局文件中必须包含一个ExpandableListView,并且id必须为="@id/android:list"。还可以包含一个id为empty的TextView,在ExpandableListActivity中没有数据的时候显示该控件的text值。
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ExpandableListView android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false">
</ExpandableListView>
<TextView
android:id="@id/android:empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="没有数据">
</TextView>
</LinearLayout>
group.xml(一级条目布局文件,样式外观可根据需要自由发挥)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="@+id/groupTo"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="60dip"
android:paddingTop="10dip"
android:paddingBottom="10dip"
android:text="No Date"
android:textSize="20sp">
</TextView>
</LinearLayout>
child.xml(二级条目布局文件,样式外观可根据需要自由发挥)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="@+id/childTo"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="50dip"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:textSize="20sp"
android:text="No Date">
</TextView>
</LinearLayout>
JAVA代码
package com.test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.ExpandableListActivity;
import android.os.Bundle;
import android.widget.SimpleExpandableListAdapter;
public class Sample_ExpandableListActivityActivity extends ExpandableListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//一级条目
List<Map<String, String>>groups=new ArrayList<Map<String,String>>();
Map<String, String> group1=new HashMap<String, String>();
group1.put("group","第一组");
Map<String, String> group2=new HashMap<String, String>();
group2.put("group", "第二组");
groups.add(group1);
groups.add(group2);
//二组条目
List<List<Map<String, String>>> childs=new ArrayList<List<Map<String,String>>>();
//第一组二级科目数据
List<Map<String, String>> child1=new ArrayList<Map<String,String>>();
Map<String, String> item1=new HashMap<String, String>();
item1.put("child","科目-1");
Map<String, String> item2=new HashMap<String, String>();
item2.put("child","科目-2");
child1.add(item1);
child1.add(item2);
//第二组二级科目数据
List<Map<String, String>> child2=new ArrayList<Map<String,String>>();
Map<String, String> item3=new HashMap<String, String>();
item3.put("child","科目-3");
Map<String, String> item4=new HashMap<String, String>();
item4.put("child","科目-4");
Map<String, String> item5=new HashMap<String, String>();
item5.put("child","科目-5");
child2.add(item3);
child2.add(item4);
child2.add(item5);
childs.add(child1);
childs.add(child2);
//SimpleExpandableListAdapter构造函数参数
//1.content
//2.一级条目数据
//3.一级条目布局文件
//4.一级条目Key
//5.一级条目显示信息控件id
//6.二级条目数据
//7.二级条目布局文件
//8.二级条目Key
//9.二级条目显示信息控件id
SimpleExpandableListAdapter adapter=new SimpleExpandableListAdapter(this,groups,R.layout.group,
new String[]{"group"},new int[]{R.id.groupTo}, childs, R.layout.child, new String[]{"child"},
new int[]{R.id.childTo});
setListAdapter(adapter);
}
}
转:http://blog.163.com/xygzx@126/blog/static/237809502011102010100331/
所用到的布局文件
min.xml(ExpandableListActivity布局文件)
注意事项:
ExpandableListActivity的布局文件中必须包含一个ExpandableListView,并且id必须为="@id/android:list"。还可以包含一个id为empty的TextView,在ExpandableListActivity中没有数据的时候显示该控件的text值。
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ExpandableListView android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false">
</ExpandableListView>
<TextView
android:id="@id/android:empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="没有数据">
</TextView>
</LinearLayout>
group.xml(一级条目布局文件,样式外观可根据需要自由发挥)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="@+id/groupTo"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="60dip"
android:paddingTop="10dip"
android:paddingBottom="10dip"
android:text="No Date"
android:textSize="20sp">
</TextView>
</LinearLayout>
child.xml(二级条目布局文件,样式外观可根据需要自由发挥)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="@+id/childTo"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="50dip"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:textSize="20sp"
android:text="No Date">
</TextView>
</LinearLayout>
JAVA代码
package com.test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.ExpandableListActivity;
import android.os.Bundle;
import android.widget.SimpleExpandableListAdapter;
public class Sample_ExpandableListActivityActivity extends ExpandableListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//一级条目
List<Map<String, String>>groups=new ArrayList<Map<String,String>>();
Map<String, String> group1=new HashMap<String, String>();
group1.put("group","第一组");
Map<String, String> group2=new HashMap<String, String>();
group2.put("group", "第二组");
groups.add(group1);
groups.add(group2);
//二组条目
List<List<Map<String, String>>> childs=new ArrayList<List<Map<String,String>>>();
//第一组二级科目数据
List<Map<String, String>> child1=new ArrayList<Map<String,String>>();
Map<String, String> item1=new HashMap<String, String>();
item1.put("child","科目-1");
Map<String, String> item2=new HashMap<String, String>();
item2.put("child","科目-2");
child1.add(item1);
child1.add(item2);
//第二组二级科目数据
List<Map<String, String>> child2=new ArrayList<Map<String,String>>();
Map<String, String> item3=new HashMap<String, String>();
item3.put("child","科目-3");
Map<String, String> item4=new HashMap<String, String>();
item4.put("child","科目-4");
Map<String, String> item5=new HashMap<String, String>();
item5.put("child","科目-5");
child2.add(item3);
child2.add(item4);
child2.add(item5);
childs.add(child1);
childs.add(child2);
//SimpleExpandableListAdapter构造函数参数
//1.content
//2.一级条目数据
//3.一级条目布局文件
//4.一级条目Key
//5.一级条目显示信息控件id
//6.二级条目数据
//7.二级条目布局文件
//8.二级条目Key
//9.二级条目显示信息控件id
SimpleExpandableListAdapter adapter=new SimpleExpandableListAdapter(this,groups,R.layout.group,
new String[]{"group"},new int[]{R.id.groupTo}, childs, R.layout.child, new String[]{"child"},
new int[]{R.id.childTo});
setListAdapter(adapter);
}
}
转:http://blog.163.com/xygzx@126/blog/static/237809502011102010100331/
[2] Activity生命周期的学习以及Logcat的施用(转载)
来源: 互联网 发布时间: 2014-02-18
Activity生命周期的学习以及Logcat的使用(转载)
2. 一个activity的 可视生命周期 自 onStart() 调用开始直到相应的 onStop()调用。在此期间,用户可以在屏幕上看到此activity,尽管它也许并不是位于前台或者正在与用户做交互。在这两个方法中,你可以管控 用来向用户显示这个activity的资源。比如说,你可以在onStart() 中注册一个BroadcastReceiver 来监控会影响到你UI的改变,而在onStop() 中来取消注册,这时用户是无法看到你的程序显示的内容的。onStart() 和 onStop() 方法可以随着应用程序是否为用户可见而被多次调用。
3. 一个activity的 前台生命周期 自 onResume() 调用起,至相应的 onPause()调用为止。在此期间,activity位于前台最上面并与用户进行交互。activity会经常在暂停和恢复之间进行状态转换──比如 说当设备转入休眠状态或有新的activity启动时,将调用onPause() 方法。当activity获得结果或者接收到新的intent的时候会调用onResume() 方法。因此,在这两个方法中的代码应当是轻量级的。
原创地址:http://android.blog.51cto.com/268543/322518
Activities是由Activity stack管理的。当一个新的Activity被启动,它就会处于stack的top位置,成为当前运行的Activity。而前一个Activity依 然保留在stack里面,当需要调用这个Activity时就会回到stack的top位置成为当前运行的Activity。
一个Activity有4个基本状态:
1. active / running 状态,当Activity处于当前屏幕时;
2. paused
状态,当Activity失去焦点但对用户依然可见时;即是,一个非全屏或者透明的Activity在该Activity的屏幕上面,并成为了当前的焦
点。而paused的Activity依然是alive状态的,它保留着所有的状态和成员信息并连接至窗口管理器,但当系统处于极低内存的情况下,仍然可
以杀死这个Activity。
3. stopped 状态,当Activity完全被另一个Activity覆盖时;它仍然保留所有的状态和成员信息。但它不再被用户可见,所以它的窗口将被隐藏,当其它地方需要内存,则系统经常会杀死这个Activity。
4. 当Activity是paused或者stopped状态时,系统可以通过要求它结束(调用它的finish()方法)或直接杀死它的进程来将它驱出内存。当它再次为用户可见的时候,它只能完全重新启动并恢复至以前的状态。
怎么来理解Activity的状态及其生命周期呢?引用网友的解释:
“由于手机应用的一些特殊性,所以我们需要更多的去关注各个Android
Component的运行时生命周期模型。(所谓手机应用的特殊性主要是指这样2点: 1.
手机应用的大多数情况下我们只能在手机上看到一个程序的一个界面
,用户除了通过程序界面上的功能按钮来在不同的窗体间切换,还可以通过Back键和Home键来返回上一个窗口,而用户使用Back或者Home的时机是
非常不确定的,任何时候用户都可以使用Home或Back来强行切换当前的界面。 2.
往往手机上一些特殊的事件发生也会强制的改变当前用户所处的操作状态,例如无论任何情况,在手机来电时,系统都会优先显示电话接听界面。)了解这些
Component的生命周期模型一方面是让我们对软件在手机中的运行情况做到心中有数,更重要的,对于程序开发来说,生命周期中的每一个关键事件都会有
我们可以覆写于各种Component对应基类型的事件处理方法,了解各Component的生命周期就是让我们在开发程序时明白我们该怎样去编写各种事
件的处理代码。”
“由于前面已经说到的手机应用的特殊性,一个Activity很可能被强制交换到后台
(交换到后台就是指该窗体不再对用户
可见,但实际上又还是存在于某个Task中的,比如一个新的Activity压入了当前的Task从而“遮盖”住了当前的Activity,或者用户按了
Home键回到桌面,又或者其他重要事件发生导致新的Activity出现在当前Activity之上,比如来电界面),而如果此后用户在一段时间内没有重新查看该窗体
(Android
通过长按Home键可以选择最近运行的6个程序,或者用户直接再次点击程序的运行图标,如果窗体所在的Task和进程没有被系统销毁,则不用重新加载
Process, Task和Task中的Activity, 直接重新显示Task顶部的Activity,
这就称之为重新查看某个程序的窗体),该窗体连同其所在的Task和Process则可能已经被系统自动销毁了,此时如果再次查看该窗体,则要重新执行
onCreate事件初始化窗体。而这个时候我们可能希望用户继续上次打开该窗体时的操作状态进行操作,而不是一切从头开始。例如用户在编辑短信时突然来
电,接完电话后用户又去做了一些其他的事情,比如保存来电号码到联系人,而没有立即回到短信编辑界面,导致了短信编辑界面被销毁,当用户重新进入短信程序
时他可能希望继续上次的编辑。这种情况我们就可以覆写Activity的void onSaveInstanceState(Bundle
outState)事件,通过向outState中写入一些我们需要在窗体销毁前保存的状态或信息,这样在窗体重新执行onCreate的时候,则会通过
savedInstanceState将之前保存的信息传递进来,此时我们就可以有选择的利用这些信息来初始化窗体,而不是一切从头开始。”
下面官方提供的这幅图,描述了Activity生命周期的整个过程:
可以看到,以上有3个关键的生命周期循环:
1. 一个activity 完整的生命周期
自第一次调用
onCreate(Bundle)开始,直至调用onDestroy()为止。activity在onCreate()中设置所有"全局"状态以完成初始
化,而在onDestroy()中释放所有系统资源。比如说,如果activity有一个线程在后台运行以从网络上下载数据,它会以
onCreate()创建那个线程,而以 onDestroy()销毁那个线程。
2. 一个activity的 可视生命周期 自 onStart() 调用开始直到相应的 onStop()调用。在此期间,用户可以在屏幕上看到此activity,尽管它也许并不是位于前台或者正在与用户做交互。在这两个方法中,你可以管控 用来向用户显示这个activity的资源。比如说,你可以在onStart() 中注册一个BroadcastReceiver 来监控会影响到你UI的改变,而在onStop() 中来取消注册,这时用户是无法看到你的程序显示的内容的。onStart() 和 onStop() 方法可以随着应用程序是否为用户可见而被多次调用。
3. 一个activity的 前台生命周期 自 onResume() 调用起,至相应的 onPause()调用为止。在此期间,activity位于前台最上面并与用户进行交互。activity会经常在暂停和恢复之间进行状态转换──比如 说当设备转入休眠状态或有新的activity启动时,将调用onPause() 方法。当activity获得结果或者接收到新的intent的时候会调用onResume() 方法。因此,在这两个方法中的代码应当是轻量级的。
当一个activity从这个状态转变到另一个状态时,它被以下列protected方法所通知:
public class Activity extends ApplicationContext { protected void onCreate(Bundle savedInstanceState); protected void onStart(); protected void onRestart(); protected void onResume(); protected void onPause(); protected void onStop(); protected void onDestroy(); }
要注意的是,onPause(),onStop(),onDestory()这3个方法是可以被系统直接kill的,当系统内存不足的时候。而
平常从一个activity转向/回到另一个activity时,当新activity是full
screen(弹出窗口,例如AlertDialog是不算的)的时候就会call 前一个activity的onPause(),然后call
onStop(),而无论onPause或者onStop,都有可能被kill,所以一般在onPause就会执行savedata操作将所有持久性数据
(比如用户的编辑结果)写入存储之中。
现在我们来看看两个activity在同一个进程内的调用情况:
1. 调用当前activity的 onPause() 方法。
2. 接着调用新启动activity的onCreate()、 onStart()和onResume()方法。
3. 然后,如果启动的activity不再于屏幕上可见,则调用它的onStop()方法。
以下我使用Logcat记录下来的Activity调用过程中的方法调用顺序:
1. 点击按钮去启动 Activity1,就会看到
05-08 09:39:48.389: DEBUG/Activity1(313): onCreate Activity 1 05-08 09:39:48.399: DEBUG/Activity1(313): onStart Activity 1 05-08 09:39:48.399: DEBUG/Activity1(313): onResume Activity 1
2. 点击back返回键后
05-08 09:40:04.129: DEBUG/Activity1(313): onPause Activity 1 05-08 09:40:04.628: DEBUG/Activity1(313): onStop Activity 1 05-08 09:40:04.659: DEBUG/Activity1(313): onDestory Activity 1
退出当前Activity时,onPause -> onStop -> onDestory
3. 再次启动Activity
05-08 09:40:18.249: DEBUG/Activity1(313): onCreate Activity 1 05-08 09:40:18.249: DEBUG/Activity1(313): onStart Activity 1 05-08 09:40:18.259: DEBUG/Activity1(313): onResume Activity 1
和一般启动的顺序是一样的
4. 从Activity1启动Acitivity2
05-08 09:40:25.477: DEBUG/Activity1(313): onPause Activity 1 05-08 09:40:25.687: DEBUG/Activity2(313): onCreate Activity 2 05-08 09:40:25.687: DEBUG/Activity2(313): onStart Activity 2 05-08 09:40:25.719: DEBUG/Activity2(313): onResume Activity 2 05-08 09:40:26.277: DEBUG/Activity1(313): onStop Activity 1
Activity1.onPause -> Activity2.onCreate -> Activity2.onStart -> Activity2.onResume ->Activity1.onStop
5. 点击Home键回到桌面
05-08 09:40:31.777: DEBUG/Activity2(313): onPause Activity 2 05-08 09:40:32.658: DEBUG/Activity2(313): onStop Activity 2
Activity2.onPause - > Activity2.onStop
6. 回到原来的程序
05-08 09:40:50.429: INFO/ActivityManager(58): Starting activity: Intent ... 05-08 09:40:50.649: DEBUG/Activity2(313): onRestart Activity 2 05-08 09:40:50.649: DEBUG/Activity2(313): onStart Activity 2 05-08 09:40:50.769: DEBUG/Activity2(313): onResume Activity 2
Activity2.onRestart -> Activity2.onStart -> Activity2.onResume
7. 点击comfirm, setResult(RESULT_OK),Activity2.finish(),返回到Activity1
05-08 09:41:04.928: DEBUG/Activity2(313): onPause Activity 2 05-08 09:41:04.988: DEBUG/Activity1(313): onRestart Activity 1 05-08 09:41:04.998: DEBUG/Activity1(313): onStart Activity 1 05-08 09:41:04.998: DEBUG/Activity1(313): onResume Activity 1 05-08 09:41:05.419: DEBUG/Activity2(313): onStop Activity 2 05-08 09:41:05.469: DEBUG/Activity2(313): onDestory Activity 2
Activity2.onPause - > Activity1.onRestart - > Activity1.onStart - > Activity1.onResume - > Activity2.onStop
8. 点击back返回键后
05-08 09:41:51.868: DEBUG/Activity1(313): onPause Activity 1 05-08 09:41:52.428: DEBUG/Activity1(313): onStop Activity 1 05-08 09:41:52.468: DEBUG/Activity1(313): onDestory Activity 1
Activity1 退出:onPause -> onStop -> onDestory
保存activity状态
保存activity状态,是为了方便用户重新打开程序时,能够回到上次离开时的状态。这里面涉及到的方法有:
protected void onSaveInstanceState (Bundle outState)
protected void onRestoreInstanceState (Bundle savedInstanceState)
当一个Activity被kill之前,它可以调用onSaveInstanceState()来保存当前activity的状态信息,它会将一个以名称-值对方式记录了activity动态状态的Bundle
对
象传递给该方法。当activity再次启动时,这个Bundle会传递给onCreate()方法和随着onStart()方法调用的
onRestoreInstanceState()。这两个方法的内容一般是把要临时保存的数据放到Bundle里面,或者从里面拿出来。
要注意的是,onSaveInstanceState() 和 onRestoreInstanceState()
并不是生命周期方法。它们并不是总会被调用。比如说,Android会在activity易于被系统销毁之前调用
onSaveInstanceState(),但用户动作(比如按下了BACK键)造成的销毁则不调用。在这种情况下,用户没打算再次回到这个
activity,所以没有保存状态的必要。因为onSaveInstanceState()不是总被调用,所以你应该只用它来为activity保存一
些临时的状态,而不能用来保存持久性数据。而是应该用onPause()来达到这个目的:�在onPause()里面调用自定义命名的函数saveState(),在saveState里面保存数据到数据库。
Logcat的使用
1. import android.util.Log;
2. private static final String TAG = "Activity1"; 设置TAG
3. Log.d(TAG, message); log记录信息
其中Log.v() : VERBOSE
Log.d() : DEBUG
Log.i() : INFO
Log.w() : WARN
Log.e() : ERROR
以上log的级别依次升高,Debug信息应当只存在于开发中,INFO、 WARN、ERROR这3种log将出现在发布版本中。
4. 在Eclipse查看Log:
在菜单Window-> Show
View选择other,在弹出的窗口中选择Android里面的LogCat,ok,就会看到LogCat窗口,在Filter那里可以输入过滤的词
语,一般是输入TAG的内容,这样子可以直接看到有关这个TAG的Log。
[3] 【Titanium】经过回调和事件来降低耦合度
来源: 互联网 发布时间: 2014-02-18
【Titanium】通过回调和事件来降低耦合度
【官方地址】https://wiki.appcelerator.org/display/guides/Callbacks+and+Events+for+Loose+Coupling
我们在阅读了Appcelerator wiki中的“JavaScript最佳实践”之后都应该知道,使用全局变量或者破坏全局命名空间是编写高质量,可维护代码的禁忌。然而我们也知道旧的习惯是很难改的,我们在写代码时需要“this”来设值?只能创建一个全局变量来使用了。
通过这种方法解决问题最终他还会为我们带来麻烦。我们暂时忘记全局变量,将会有另外一种方法来得到同样的信息。
这里需要我们写在黄色便签上,让我们重视的有三点:
1、我们将要编写的是模块化代码(至少从现在开始)
2、基于事件的编程是很好的
3、只有在不得已的时候再使用全局变量
获取地理信息
这里,首先问题是什么?获取用户的当前地理位置信息。
这个处理会被异步执行,我们要更新的UI控件在另外一个文件中,当然,这是一个模块话的代码。
我们都知道通过调用API Titanium.Geolocation.getCurrentPosition使用回调函数来让我们知道什么时候它找到了地理位置信息。
把地理位置信息放在有用的地方
这里我们可以在回调函数的外边创建一个全局变量,来使用它获取信息。
但是这仅仅是帮助了我们,如果需要更新的UI控件跟回调处于同一个JS文件的情况。这不足以是我们写出模块化的代码。我们绝不能将UI代码和获取地理位置信息的逻辑代码混淆在一起。
通过事件监听把地理位置信息放在更好的地方
那么我们下来该做什么?首先是要摆脱全局变量,激发一个事件来指示地理位置信息已经成功被取到。
这样,在应用的任何我们需要获取地理位置信息的地方,我们都可以创建一个监听来获得坐标信息。这样很棒,不再需要全局变量了!
也有另外一种方法:在回调中使用回调
我们在设备获取地理位置信息完成后调用一个函数。和激发一个事件一样,我们将调用一个函数,把我们从设备上获取到的地理位置信息坐标作为参数传给它。
在我们代码的其他地方,我们需要获得信息后把信息更新到UI控件上。
Kevi说模块化是最好的
Kevin Whinnery: Write Better JavaScript: video| slides
好,我们现在已经在包含文件中有了GPS代码,它和UI代码分离,我们可以在任何需要获取地理位置信息的地方调用它,或者是一个事件,或者是一个回调函数。但是,我们还是没有做到-模块化CommonJS,还有“requires”的一切。
我们想做一个好的Appcelerator市民,编写出比Kitchen-sink更强大的代码。KitchenSink 毕竟只是一个演示API功能的,它并不是最好的例子。
这里让我们把我们的地理位置信息文件转化成一个模块。只需要在函数名之前加一个“exports”,并保证其公有。
在我们的ui.js文件的创建窗口的位置,我们需要加载这个含有地理位置信息取得的模块。完整的例子,我们把文件命名为“maps.js”。我们需要添加一下语句:
记住:在使用requie时不需要包含".js"扩展名,他不需要。
当你调用方法时:
这里是完整的ui.js:
总结
使用回调和事件对你的代码是有益的。它减少耦合,更好的分离业务逻辑和表现层。从现在开始,我们将在应用开发中更加强化模块化代码,这将放映在不久以后我们带给你所有的内容之中。
Keep coding strong!
代码
CallbacksAreYourFriend on Github
【官方地址】https://wiki.appcelerator.org/display/guides/Callbacks+and+Events+for+Loose+Coupling
我们在阅读了Appcelerator wiki中的“JavaScript最佳实践”之后都应该知道,使用全局变量或者破坏全局命名空间是编写高质量,可维护代码的禁忌。然而我们也知道旧的习惯是很难改的,我们在写代码时需要“this”来设值?只能创建一个全局变量来使用了。
通过这种方法解决问题最终他还会为我们带来麻烦。我们暂时忘记全局变量,将会有另外一种方法来得到同样的信息。
这里需要我们写在黄色便签上,让我们重视的有三点:
1、我们将要编写的是模块化代码(至少从现在开始)
2、基于事件的编程是很好的
3、只有在不得已的时候再使用全局变量
获取地理信息
这里,首先问题是什么?获取用户的当前地理位置信息。
这个处理会被异步执行,我们要更新的UI控件在另外一个文件中,当然,这是一个模块话的代码。
我们都知道通过调用API Titanium.Geolocation.getCurrentPosition使用回调函数来让我们知道什么时候它找到了地理位置信息。
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST; Titanium.Geolocation.distanceFilter = .25; Ti.Geolocation.purpose = "Callbacks Are Your Friend"; // 调用API Ti.Geolocation.getCurrentPosition(function(e) { // 当获取位置信息出错时 if (e.error) { Ti.API.error('geo - current position' + e.error); return; } // 获取地理位置信息 Ti.App.info('got a location ',JSON.stringify(e)); });
把地理位置信息放在有用的地方
这里我们可以在回调函数的外边创建一个全局变量,来使用它获取信息。
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST; Titanium.Geolocation.distanceFilter = .25; Ti.Geolocation.purpose = "Callbacks Are Your Friend"; // 在回调中保存的坐标信息(全局) var global_var_coords; // 调用API Ti.Geolocation.getCurrentPosition(function(e) { // 当获取位置信息出错时 if (e.error) { Ti.API.error('geo - current position' + e.error); return; } // 获取地理位置信息 Ti.App.info('got a location ',JSON.stringify(e)); // 给全局变量赋值 global_var_coords = e.coords });
但是这仅仅是帮助了我们,如果需要更新的UI控件跟回调处于同一个JS文件的情况。这不足以是我们写出模块化的代码。我们绝不能将UI代码和获取地理位置信息的逻辑代码混淆在一起。
通过事件监听把地理位置信息放在更好的地方
那么我们下来该做什么?首先是要摆脱全局变量,激发一个事件来指示地理位置信息已经成功被取到。
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST; Titanium.Geolocation.distanceFilter = .25; Ti.Geolocation.purpose = "Callbacks Are Your Friend"; // 全局监听 Ti.App.addEventListener('location.updated',function(coords){ Ti.API.debug(JSON.stringify(coords)); } // 调用API Ti.Geolocation.getCurrentPosition(function(e) { // 当获取位置信息出错时 if (e.error) { Ti.API.error('geo - current position' + e.error); return; } // 获取地理位置信息 Ti.App.info('got a location ',JSON.stringify(e)); // 激发含有地理位置信息的事件 Ti.App.fireEvent('location.updated',e.coords); });
这样,在应用的任何我们需要获取地理位置信息的地方,我们都可以创建一个监听来获得坐标信息。这样很棒,不再需要全局变量了!
也有另外一种方法:在回调中使用回调
我们在设备获取地理位置信息完成后调用一个函数。和激发一个事件一样,我们将调用一个函数,把我们从设备上获取到的地理位置信息坐标作为参数传给它。
// // file_with_location.js // Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST; Titanium.Geolocation.distanceFilter = .25; Ti.Geolocation.purpose = "Callbacks Are Your Friend"; /** * @param {Object} _callback 地理位置信息查询完成后调用 */ function currentLocation(_callback) { // 调用API Ti.Geolocation.getCurrentPosition(function(e) { // 当获取位置信息出错时 if (e.error) { Ti.API.error('geo - current position' + e.error); // to keep it simple, just returning null, could be // error information if (_callback) { _callback(null); } return; } // 获取地理位置信息 Ti.App.info('got a location ',JSON.stringify(e)); // 激发含有地理位置信息的事件 Ti.App.fireEvent('location.updated',e.coords); // 用坐标调用回调函数 if (_callback) { _callback(e.coords); } }); }
在我们代码的其他地方,我们需要获得信息后把信息更新到UI控件上。
Ti.include('file_with_location.js'); // 打开一个窗口 var window = Ti.UI.createWindow({ backgroundColor:'white' }); var coordsLbl = Titanium.UI.createLabel({ text:"NO LOCATION", left: 10, width: 240, height: 'auto', top: 20, textAlign: 'left', }); window.add(coordsLbl); window.open(); // 执行回调获取地理位置信息, gpsCallback方法被传入获取地理位置信息的方法 // 他将在获取到地理位置信息伙子出错的时候被调用 currentLocation(gpsCallback); /** * @param {Object} _coords 地理位置信息的经纬度值 */ function gpsCallback(_coords) { if (_coords) { coordsLbl.text = String.format("lon: %s\n lat: %s ", _coords.longitude + "", _coords.latitude + ""); } else { coordsLbl.text = "NO LOCATION"; } }
Kevi说模块化是最好的
Kevin Whinnery: Write Better JavaScript: video| slides
好,我们现在已经在包含文件中有了GPS代码,它和UI代码分离,我们可以在任何需要获取地理位置信息的地方调用它,或者是一个事件,或者是一个回调函数。但是,我们还是没有做到-模块化CommonJS,还有“requires”的一切。
我们想做一个好的Appcelerator市民,编写出比Kitchen-sink更强大的代码。KitchenSink 毕竟只是一个演示API功能的,它并不是最好的例子。
这里让我们把我们的地理位置信息文件转化成一个模块。只需要在函数名之前加一个“exports”,并保证其公有。
// Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST; Titanium.Geolocation.distanceFilter = .25; Ti.Geolocation.purpose = "Callbacks Are Your Friend"; // PUBLIC FUNCTION /** * @param {Object} _callback 地理位置信息查询完成后调用 */ exports.currentLocation = function(_callback) { Titanium.Geolocation.getCurrentPosition(function(e) { if(e.error) { alert('error ' + JSON.stringify(e.error)); // 为了保持一致,返回null if(_callback) { _callback(null); } return; } Ti.App.fireEvent('location.updated', e.coords); if(_callback) { _callback(e.coords); } }); };
在我们的ui.js文件的创建窗口的位置,我们需要加载这个含有地理位置信息取得的模块。完整的例子,我们把文件命名为“maps.js”。我们需要添加一下语句:
引用
require
记住:在使用requie时不需要包含".js"扩展名,他不需要。
var maps = require('lib/maps');
当你调用方法时:
// do the callback to get current location maps.currentLocation(gpsCallback);
这里是完整的ui.js:
// private vars var mainWindow, callback_label_coords; var styles = require('styles'); var maps = require('lib/maps'); /** * @param {Object} args properties for the window */ exports.AppWindow = function(args) { var instance, event_label, callback_label; // create window instance = Ti.UI.createWindow(args); // make callback label callback_label = Ti.UI.createLabel(styles.callback_label); instance.add(callback_label); callback_label_coords = Ti.UI.createLabel(styles.coords_label); instance.add(callback_label_coords); // make event listener label event_label = Ti.UI.createLabel(styles.event_label); instance.add(event_label); event_label_coords = Ti.UI.createLabel(styles.coords_label); instance.add(event_label_coords); // create event Listener Ti.App.addEventListener('location.updated', function(_coords) { event_label_coords.text = String.format("longitude: %s\n latitude: %s ", _coords.longitude + "", _coords.latitude + ""); }); // do the callback to get current location maps.currentLocation(gpsCallback); // save the window mainWindow = instance; return instance; }; /** * @param {Object} _coords lat, lon values from locationo */ function gpsCallback(_coords) { callback_label_coords.text = String.format("longitude: %s\n latitude: %s ", _coords.longitude + "", _coords.latitude + ""); }
总结
使用回调和事件对你的代码是有益的。它减少耦合,更好的分离业务逻辑和表现层。从现在开始,我们将在应用开发中更加强化模块化代码,这将放映在不久以后我们带给你所有的内容之中。
Keep coding strong!
代码
CallbacksAreYourFriend on Github
最新技术文章: