Android - HsuJv/Note GitHub Wiki
-
.gradle, .idea: 这两个目录下放置的都是Android Studio自动生成的一些文件, 无需关心
-
app: 项目中的代码, 资源等内容几乎全部都放置在这个目录下
-
build: 这个目录也不要过多关心, 它主要包含了一些在编译时自动生成的文件
-
gradle: 这个目录下包含了gradle wrapper的配置文件, 使用gradle wrapper的方式不需要提前将gradle下载好, 而是会自动根据本地的缓存情况决定是否需要联网下载gradle。Android Studio默认没有启动gradle wrapper的方式, 如果需要打开, 可以点击Android Studio导航栏→File→Setting→Build, Execution, Deployment→Gradle, 进行配置更改
-
.gitignore: 这个文件用来将指定的目录或者文件排除在版本控制之外
-
build.gradle: 这是项目全局的.gradle构建脚本, 通常这个文件的内容是不需要修改
-
gardle.properties: 这个文件是全局的.gradle配置文件, 在这里配置的属性将会影响到项目中所有的.gradle编译脚本
-
gradlew, gradlew.bat: 这两个文件是用来在命令行界面中执行gradle命令的, 其中gradlew是在linux或者Mac系统中使用的, gradlew.bat是在windows系统中使用的
-
MyApplication.iml: iml文件是所有IntelliJ IDEA都会自动生成的一个文件, (Android Studio是基于IntelliJ IDEA开发的), 用于标识是一个IntelliJ IDEA项目
-
local.properties: 这个文件用于指定本机中的Android SDK路径
-
setting.gradle: 这个文件用于指定项目中所有引入的模块, 通常情况下模块的引入都是自动完成
-
ViewGroup和View:
- ViewGroup相当于一个放置View的容器, 在写布局xml的时候, 会告诉容器(凡是以layout为开头的属性, 都是为用于告诉容器的), 宽度(layout_width), 高度(layout_height), 对齐方式(layout_gravity), margin等
- ViewGroup的职能为: 给childView计算出建议的宽和高和测量模式, 决定childView的位置
- View的职责: 根据测量模式和ViewGroup给出的建议的宽和高, 计算出自己的宽和高, 并且在ViewGroup为其指定的区域内绘制自己的形态
-
通过代码和脚本管理外观
- 脚本: 创建效率高, 减少业务逻辑代码和外观代码的耦合
- 代码: 通过findViewById找到控件, 通过setXXX来设置相应属性
-
资源管理
- 通过@索引资源
- 通过@+将控件增加到R.java的索引库
-
几种布局的对齐方式
- LinearLayout(线性布局): 提供了控件水平(vertical)垂直(horizonta)排列的模型, 同时可以通过设置子控件的weight布局参数控制各个控件在布局中的相对大小
fill-parent: 占满整个屏幕 wrap-content: 刚好适合控件内容的大小 对齐方式gravity取值: top: 不改变大小, 位置置于容器的顶部 bottom: 不改变大小, 位置置于容器的底部 left: 不改变大小, 位置置于容器的左边 right: 不改变大小, 位置置于容器的右边 center_vertical: 不改变大小, 位置置于容器的纵向中央部分 center_horizontal: 不改变大小, 位置置于容器的横向中央部分 center: 不改变大小, 位置置于容器的横向和纵向的中央部分 fill_vertical: 可能的话, 纵向延伸可以填满容器 fiil_horizontal: 可能的话, 横向延伸可以填满容器 fiil: 可能的话, 纵向和横向延伸填满容器
- AbsoluteLayout(坐标布局): 可以让子元素指定准确的x/y坐标值, 并显示在屏幕上, (0, 0)为左上角, 当向下或向右移动时, 坐标值将变大, AbsoluteLayout没有页边框, 允许元素之间互相重叠(尽管不推荐)
android:layout_x="40px" android:layout_y="56px" //确定控件位置
- RelativeLayout(相对布局): 允许子元素指定他们相对于其它元素或父元素的位置(通过ID指定), 因此, 可以左右对齐, 或上下, 或置于屏幕中央的形式来排列两个元素, 元素按顺序排列, 如果第一个元素在屏幕的中央, 那么相对于这个元素的其它元素将以屏幕中央的相对位置来排列。如果使用XML来指定这个layout, 定义它之前, 被关联的元素必须定义。
android:layout_centerInparent, 将当前控件放置于起父控件的横向和纵向的中央部分 android:layout_centerHorizontal, 使当前控件置于父控件横向的中央部分 android:layout_centerVertical, 使当前控件置于父控件纵向的中央部分 android:layout_alignParentBottom, 使当前控件的底端和父控件底端对齐 android:layout_alignParentLeft, 使当前控件的左端和父控件左端对齐 android:layout_alignParentRight, 使当前控件的右端和父控件右端对齐 android:layout_alignParentTop, 使当前控件的顶端和父控件顶端对齐 android:layout_alignParentBottom, 使当前控件的底端和父控件底端对齐 android:layout_above, 将该控件的底部至于给定ID的控件之上 android:layout_below, 将该控件的顶部至于给定ID的控件之下 android:layout_toLeftOf, 将该控件的右边缘和给定ID的控件的左边缘对齐 android:layout_toRightOf, 将该控件的左边缘和给定ID的控件的右边缘对齐 android:layout_alignBaseline, 该控件的baseline和给定ID的控件的baseline对齐 android:layout_alignBottom, 将该控件的底部边缘与给定ID控件的底部边缘 android:layout_alignLeft, 将该控件的左边缘与给定ID控件的左边缘对齐 android:layout_alignRight, 将该控件的右边缘与给定ID控件的右边缘对齐 android:layout_alignTop, 将给定控件的顶部边缘与给定ID控件的顶部对齐 Android:layout_marginBottom/layout_marginLeft/layout_marginRight/layout_marginTop=”n px”, 使当前控件底部/左边/右边/顶部空出相应像素空间
- FrameLayout(单帧布局): 是最简单的一个布局对象, 它被定制为屏幕上的一个空白备用区域, 之后可以在其中填充一个单一对象, 所有的子元素将会固定在屏幕的左上角, 不能为FrameLayout中的一个子元素指定一个位置, 后一个子元素将会直接在前一个子元素之上进行覆盖填充, 把它们部份或全部挡住(除非后一个子元素是透明的)
android:src=”@drawable/”, 属性指定所需图片的文件位置, 用ImageView显示图片时, 也应当用android:src指定要显示的图片
- TableLayout(表格布局): 以行列的形式管理子控件, 每一行为一个TableRow的对象, TableRow也可以添加子控件
android:collapseColumns=“n”, 隐藏TableLayout里面的TableRow的列n android:stretchColumns=“n”, 设置列n为可延伸的列 android:shrinkColumns=“n”, 设置列n为可收缩的列
-
事件处理器(event handler)
- 很多控件本身就带有事件处理器, 例如Button.onTouchEvent()
- 需要重新定义一个控件继承现有控件, 再重写原有控件的事件处理器
- 一般很少使用, 除非要引用一种非标准行为的控件
-
事件监听器(event listner)
- 创建一个类, 具有接收事件的功能
- 在类当中定义某种事件处理的方法
- 形式1:
XXXListner implements OnClickListner{ OnClick(){ .... } // 事件处理方法 } MyActivity extends Activity{ OnCreate(){ Button b = XXX b.setOnClickListner(new XXXListner) // 将指定的监听器和有事件 // 处理需求的控件关联起来 } }
- 形式2:
MyActivity extends Activity implements OnClickListner{ // 某个程序的界面 // 既是界面, 又是 // 事件监听器 OnCreate(){ Button b = XXX b.setOnClickListner(this) // 它自身就是一个监听器 } OnClick(){ .... } // 事件处理方法 }
- 形式3(常用):
MyActivity extends Activity{ OnCreate(){ Button b = XXX b.setOnClickListner(new OnClickListner(){ OnClick(){ ... } }) // 使用匿名内部类进行监听器的定义 } }
-
Intent
- Intent这个英语单词的本意是"目的, 意向, 意图", Android中提供了Intent机制来协助应用间的交互与通讯, 或者采用更准确的说法是, Intent不仅可用于应用程序之间, 也可用于应用程序内部的activity, service和broadcast receiver之间的交互
- Intent是一种运行时绑定(runtime binding)机制, 它能在程序运行的过程中连接两个不同的组件, 通过Intent, 程序可以向Android表达某种请求或者意愿, Android会根据意愿的内容选择适当的组件响应
-
Intent Filter
- 用于接收方
- 应用在Manifest文件中配置了Intent Filter, 告诉系统能够接收什么样的Intent
-
Intent应用
- 一个Activity请求另一个Activity: 使用startActivity()或startActiviForResult() (有返回)
- 启动一个Service: 使用startService() (异步启动一个Service)或bindService() (Activity和Service形成CS模式结构, Activity给Service发出请求, Service返回响应)
- 发出消息(Broadcast): 使用sendBroadcast()或sendOrderedBroadcast() (发送的Intent信息带有优先级)或sendStickyBroadcast() (所有定义了接收相关Intent接收者接收完后, 消息并不消失, 如果有新的接收者启动, 会立即收到消息)
-
Intent类型
- 显式(Explicit Intent): 在发出的Intent当中指明由哪个类处理
- 隐式(Implicit Intent): Intent当中仅包含想要什么, 由用户选择满足该Intent的应用
- SQLite DB
- means a Lite SQL, 体积小
- 开源, 兼容Standard SQL Language
- Single tier: 应用程序直接访问数据库, 无中间层
- No Server: 自包含, 数据仅存储在存储器上, 没有服务器端
- Loose Type: 每一行每一列的数据类型可以不同
- App Process: 访问数据库的代码就在应用程序的进程当中, 开销小
- Internal Storage
- 手机系统存储器的存储
- 一般, 安卓系统会在ROM中划分一部分专门存储系统文件以及app相关文件
- 另一部分称为external storage(包括sd存储卡, 如果安装)
- internal storage里的内容, 若非有root权限, 仅被创建它的app访问, 而external storage无限制
- External Storage
- 可以被任何应用程序访问
- Shared Preferences
- 保存一些程序偏好
- 存储的key-value中的value只能是java的8种原始类型(byte, int, short, long, boolean, char, float, double)
- Remote Storage
- net I/O
- Using SQLite Database for data storage
- 数据库位于
/data/data/<package-name>/databases
- 默认情况下为private
- Notice:
- While storing big files, a URL of it should be stored in the database instead of the bin file itself
- Appropriate design should be done for the database
- There should be a main key ordered 1, 2, 3....
- 数据库位于
- Creating a database
- Code
- android.database.sqlite.SQLiteDatabase
- directly use in your code
- eg:
SQLiteDatabase sqlDB = openOrCreateDatabase("test.db", SQLiteDatabase.CREATE_IF_NECESSARY, null); if (sqlDB != null){ tv.setText(getString(R.string.activity_main_db_created)); } sqlDB.execSQL("create table tb(id integer primary key, name text, phone long);"); sqlDB.close();
- android.database.sqlite.SQLiteOpenHelper
- 通过一个组件(自定义类)间接访问, 解耦
- eg:
public class DataHelper { SQLiteDatabase sqlDb; OpenHelper oh; String DBNAME; int primary_key_id; public DataHelper(Context context, String DBNAME){ this.DBNAME = DBNAME + ".db"; oh = new OpenHelper(context, DBNAME); sqlDb = oh.getWritableDatabase(); Cursor cursor = sqlDb.rawQuery("select count(id) from tb;", new String[0]); if (cursor.moveToFirst()){ primary_key_id = cursor.getInt(0); } else primary_key_id = 0; } public boolean insert (String name, String phone){ primary_key_id += 1; sqlDb.execSQL("insert into tb (id,name,phone) values (" + Integer.toString(primary_key_id) + ", '"+ name + "', '"+ phone + "');"); return true; } public boolean delete(int primary_key_id){ sqlDb.execSQL("delete from tb where id = " + Integer.toString(primary_key_id) + ";"); return true; } public boolean update (int id, String name, String phone){ sqlDb.execSQL("update tb set name = '" + name + "', phone = '" + phone + "' where id = " + Integer.toString(id) + ";"); return true; } private static class OpenHelper extends SQLiteOpenHelper{ private OpenHelper(Context context, String DBNAME){ super(context, DBNAME, null, 1, null); } @Override public void onCreate(SQLiteDatabase db){ db.execSQL("create table tb(id integer primary key, name text, phone long);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("drop table if exists tb;"); onCreate(db); } } } 调用: DataHelper dh = new DataHelper(MainActivity.this, "test2");
- android.database.sqlite.SQLiteDatabase
- Linux Shell
- 使用adb
- e.g.
> adb shell # cd /data/data/<package-name>/databases # sqlite3 test3.db > create table tb(id integer primary key, name text, phone long); > .quit # exit
- Code
- Storing / Retrieving
- Insert, Delete, Update
- 直接用数据库对象的execSQL方法执行相应的sql语句
- ContentValue
- 安卓提供个一个特殊的key-value数据结构(value只为8个基本类型)
- 常用于数据库记录的添加
- e.g.
ContentValues cv = new ContentValues(); cv.put(col, value); ...... sqlDB.insert(table, null, cv); // SQLiteDatabase sqlDB = openOrCreateDatabase("test.db", SQLiteDatabase.CREATE_IF_NECESSARY, null);
- Retrieve records from a table
- 主要通过query方法
- 返回一个访问指针(Cursor)
- Insert, Delete, Update
- Created to enable sharing of the app's data with other apps. To make other apps can access the database of this app.
- Custom Content Provider
- Content Provider(提供者)
- Create a custom content provider(a class extends ContentProvider)
- Specify the URI of a content provider
- Implement query handling methods(e.g. insert)
- Register a custom content provider: Manifest.xml(label
<provider>
)
- Customer(使用方)
- Declare an instance of ContentResolver class
uri = Uri.parse("content://<provider.authorities>/<table name>")
- Call ContentResolver.query() (wiht a cursor returned)
- Traversal with the returned cursor(Similar to SQLite)
- Content Provider(提供者)
- Native Content Provider
- All native content providers can be used wherever required
- the uri like
content://contacts/people/20
, means to get the 20th people in the contacts (specially, this app must have the READ_CONTACTS permission)
- 使用了java.IO类
- 一般位于
/data/data/<app-package>/files
- 属于APP所有, 其他应用一般无法访问
- 关键类或方法:
- 写:
openFileOutput(fileName, flag) FileOutputStream.write(buffer)
- 读:
openFileInput(fileName) FileInputStream.read(buffer)
- 静态资源文件
- 一般是一旦创建就不再修改的文件
- 此类文件一般在
res/raw
下 - 一般作为资源文件, 读取利用:
Resources obj = getResources() FileInputStream fis = obj.openRawResource(R.raw.fileName)
- 优点: 将APP外观, 文字等存入资源文件, 高效实现国际化
- 指在系统及应用程序之外的存储空间, Internal空间有限, 所以External一般用于存放大文件
- SD Card 属于External Data Storage, 一般挂载到
/mnt/sdcard
下 - External下的文件一般是公共的, 任何人可以读取
- 使用External存储之前, 需要用Environment类确认外存是否存在, 并返回外存挂载的路径
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED){
PrintWriter pw = new PrintWriter(new FileOutputStream(Environment.getExternalStorageDirectory() + "//" + filename));
pw.println(....);
}
- 一个APP如果要写数据到外存中, 需要在Manifest文件中声明
<uses-permission
权限 - 外存文件读取用FileInputStream和FileOutputStream
- 适合存储key-value对, 一般用于保存APP设置
- 有两种类型:
- Activity-level preference(一个activity一个)
- Application-level preference(可以存在多个)
- 只存放boolean, float, int, long, String
- 读取:
- Activity-level:
SharedPreferences pref = getPreferences(MODE_PRIVATE) // 'Cause it is unique for one activity
- Application-level:
SharedPreferences pref = getPreferences(file_name, MODE_WORLD_READABLE)
- Activity-level:
- 存储位置一般在
/data/data/<app-package>/shared_prefs
- pref文件格式形如:
<map>
<string name="pref1">test1</string>
<string name="pref2">test2</string>
</map>
- 修改:
SharedPreferences obj = getPreferences(...)
SharedPreferences.Editor eObj = obj.edit()
eObj.putValueType("name", value)
eObj.commit()
- 读取:
SharedPreferences obj = getPreferences(...)
obj.getValueType("name", defaultValue)
- APP想要联网, 需在 Manifest 中申请权限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
- 访问核心代码:
URL obj = new URL("...")
URLConnection objConn = obj.openConnection()
InputStream in = objConn.getInputStream()
BufferedInputStream bis = new BufferedInputStream(in)
char[] ch = new char[1]
while(bis.read(ch) != -1){...}
- Services是没有界面的运行体, 类似后台进程, 但一般Services存在于调用他的APP的进程中, 本事非进程或线程, 可以类比动态链接库
- Forms of Services
- Started: APP想让某些工作在后台做, 通过Context.startService()启动
- Bound: APP想让自己的功能给其他APP使用, 通过Context.bindService()启动
-
Lifecycle of a Service
- demo
// MyService.class
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
boolean pause = false;
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "I'm a toast", Toast.LENGTH_LONG).show();
pause = false;
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++){
if (!pause)
fun();
try{
Thread.sleep(2000);
}
catch (Exception e){
;
}
}
}
}).start();
}
public void fun(){
Log.e("Thread", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));}
@Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Destroy", Toast.LENGTH_LONG).show();
pause = true;
}
}
// MainActivity.class
Button btn1 = findViewById(R.id.button);
Button btn2 = findViewById(R.id.button2);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(new Intent(MainActivity.this, MyService.class));
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService(new Intent(MainActivity.this, MyService.class));
}
});
// Manifest.xml
<service android:name=".MyService"></service>
- 来源:
- System
- Application
- 类型: (Intent即消息)
- Normal: 使用Context.sendBroadcast(Intent, rcvPermission)来发送
- Ordered(接收者按序接收): 使用Context.sendOrderedBroadcast(Intent, rcvPermission)来发送
- Create a Broadcast Receiver
- Implement a class extends BroadcastReceiver
- register in manifest
- Notice:
On Android O, code like this no longer works the way that you expect:
sendBroadcast(new Intent("this.is.an.implicit.broadcast"));
Normally, this broadcast would be received by all receivers that are registered for that custom action string. Even on O, two sets of receivers will still receive the broadcast:
Those whose apps have targetSdkVersion of 25 or lower
Those that were registered via registerReceiver() of some already-running process
However, manifest-registered receivers of apps with a higher targetSdkVersion will not receive the broadcast. Instead, a message like this one will appear in LogCat:
04-11 14:12:36.340 753-763/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:com.commonsware.cwac.cam2.demo flg=0x4000010 (has extras) } to com.commonsware.android.sysevents.pkg/.OnPackageChangeReceiver
- Solution: As is shown in demo
- demo
// Sender.class
public class MainActivity extends AppCompatActivity {
final private String MY_ACT = "com.njupt.hsu.action.MY_ACT";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn1 = findViewById(R.id.button);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MY_ACT);
i.putExtra("count", 3);
sendImplicitBroadcast(MainActivity.this, i);
}
});
}
// Solution for Android O and newer
private static void sendImplicitBroadcast(Context ctxt, Intent i) {
PackageManager pm=ctxt.getPackageManager();
List<ResolveInfo> matches=pm.queryBroadcastReceivers(i, 0);
for (ResolveInfo resolveInfo : matches) {
Intent explicit=new Intent(i);
ComponentName cn=
new ComponentName(resolveInfo.activityInfo.applicationInfo.packageName,
resolveInfo.activityInfo.name);
explicit.setComponent(cn);
ctxt.sendBroadcast(explicit);
}
}
}
// Receiver
// Receiver.class
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int i = intent.getExtras().getInt("count");
Log.e("Broadcast", i + "");
}
}
// Manifest.xml
<receiver android:name=".MyReceiver" android:exported="true">
<intent-filter>
<action android:name="com.njupt.hsu.action.MY_ACT"/>
<category android:name="android.intent.category.APP_MESSAGING"/>
</intent-filter>
</receiver>
- Options menu
- 点按手机实体menu键后出现的6个menu, 如果超过6个, 则一层只显示5个, 加一个"more", 点more后出现的菜单叫Expanded menu
- 在项目
res/menu
文件夹下可以创建menu的外观脚本 - Inflating a menu(将外观脚本填充成对象, 便可以在界面中出现)
- Context menu
- 与PC右键类似, 任何控件(View)都可以有Context menu
- Sub menu
- 菜单的一下项中有三角形的右箭头, 点击后弹出的菜单叫子菜单
- Defining Styles
- Inheritance
- Style Properties
- Applying Style and Themes to the UI
- Apply a theme to an activity or application
- Select a theme based on platform version
- Applying build-in themes(Using platform styles and themes)
- More Info
- Creating Compound Views
- Android 中有些控件本身就是用这种技术创建的, 例如Spinner, AutoCompleteTextView
- Step1: 定义外观
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ... <EditText android:id="@+id/editText1" ... /> <Button ... > ... </Button> </LinearLayout>
- Step2: 定义代码并充气(将对象与资源关联) // 此控件包含一个 button, 当按下时, 可以将文本框中字符串两边加上 www. 和 .com
public class MyCompoundView extends LinearLayout{ EditText eT; Button Btn; String str; public MyCompoundView(Context context, AttributeSet Attr) { super(context, Attr); String service = Context.LAYOUT_INFLATER_SERVICE; LayoutInflater li = (LayoutInflater)getContext().getSystemService(service); li.inflate(R.layout.main1, this, true); eT = (EditText)findViewById(R.id. editText1); Btn = (Button)findViewById(R.id. add); AddUrl(); } private void AddUrl() { Btn.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { str= eT.getText().toString(); str="http://www."+str+".com"; eT.setText(str); } }); } }
- Step3: 声明新控件的存在:
// <merge> 见 http://developer.android.com/guide/topics/resources/layout-resource.html#merge-element <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <com.CompoundView.niit.MyCompoundView android:layout_width="match_parent" android:layout_height="wrap_content" /> </merge>
- Creating Custom Views
- step1: 定义控件:
public class MycustomButton extends View{ public MycustomButton(Context context){ super(context); } @Override protected void onDraw(Canvas canvas) { canvas.drawXXXX(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ 测量控件的大小(控件默认大小取所在容器大小),调用 setMeasuredDimension(int, int) 进行设置 } @Override public boolean onKeyUp(int keyCode, KeyEvent keyEvent){ ... // 让控件具有交互性 } }
- step2: 使用控件:
<view class="com.android.Custom.OutterClass$MyCustomView " // 若 MyCustomView 是以内部类定义的, 则须写出 外部类名字, 并用 $ 符号分隔 id="@+id/note" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:drawable/empty" android:padding="10dip" android:scrollbars="vertical" android:fadingEdge="vertical" />
- Toast
- Standard toast
- step 1: android.widget.Toast.makeToast(context, text, duration)
- step 2: Toast.makeToast(...).show
- step 3: Change the position (default is the bottom)
- toast.setGravity(Gravity.BOTTOM|Gravity.LEFT, 0, 0);
- Custom toast
- step 1: Create the view
<LinearLayout ... <TextView android:id="@+id/textView1" android:text=".." android:layout_width="wrap_content"> </TextView> <EditText android:text="" android:layout_height="wrap_content" ..> </EditText> <LinearLayout>
- step 2: Inflating
LayoutInflater inflater = getLayoutInflater(); View toastView = inflater.inflate(R.layout.ctoast, (ViewGroup)findViewById(R.id.cToast));
- step 3: Use the toast
Toast toast = new Toast(getApplicationContext()); // 设置文字、duration等 toast.setView(toastView); toast.show()
- Standard toast
- Notification area and drawer
- Note: the code has changed after several versions change. To get the newest api guide, go and see.
- 'cause service do not have UI interface, notification drawer is a applicable way for it to notify user.
- Two classes:
- Notification: the object which is show in notification drawer
- NotificationManager: call notify(int id, Notification-object) to display the notification. Note: if there are two notifications share the same id, the later one will cover the earlier one.
- step 1: Obtain the NotificationManager service
NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
- step 2: Create the instance of Notification
//The message to be shown on the notification drawer String msg = "Notification msg"; // Text long when = System.currentTimeMillis(); // When to display int icon = R.drawable.icon; // Icon Notification notification = new Notification(icon, ticker, when);
- step 3: Set the context which is to be show when drop down the bar
setLatestEventInfo(Context con, CharSequence title, CharSequence text, PendingIntent i); // 对notification window 中的项, 点击的话要做什么
- step 4: Sound
notification.defaults |= Notification.DEFAULT_SOUND; notification.sound = Uri.parse("file:///sdcard/alarm.mp3");
- step 5: Vibration
notification.defaults|= Notification.DEFAULT_VIBRATE; long[] vibrate = {0,100,200,300}; // {震动前等待,1st震动时长,第一次震动后等待时长,2nd震动时长} , 单位ms notification.vibrate = vibrate;
- step 6: Flash Lights
notification.defaults |= Notification.DEFAULT_LIGHTS; notification.ledARGB = 0xffff0000; // 颜色 notification.ledOnMS = 250; // 亮灯时长 ms notification.ledOffMS = 500; // 闭灯时长 notification.flags |= Notification.FLAG_SHOW_LIGHTS;
- Dialog box
- When alarm triggered -> Android send an Intent -> Automatically start corresponding broadcast
- Alarms will be deleted when the machine is shutdown.
- Alarm can wake the system up or just keep it sleeping when the system is sleeping.
- AlarmManager will hold a cpu lock which ensures the system sleep after handling this alarm.
- The essence of the alarm is: Independent services that can be used to fire intents at predetermined times.
- Use:
- step 1: Obtain AlarmManager
- References
AlarmManager alarms = (AlarmManager)getSystemService(Context. ALARM_SERVICE);
- step 2.1: Create one-time alarms
int Type = AlarmManager.ELAPSED_REALTIME_WAKEUP; long timeOfWait = 10000; // 单位ms String ALARM_ACTION = "ALARM_ACTION"; // Name Intent intentToFire = new Intent(ALARM_ACTION); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0); alarms.set(Type, timeOfWait, pendingIntent)
- step 2.2: Create repeating alarms
alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (5 * 1000), // triggerAtMillis 10 * 1000, //intervalMillis pendingIntent);
- step 3: Cease
alarms.cancel(pendingIntent);
- step 1: Obtain AlarmManager
- Three LBS technique: Global Positioning System (GPS); Cell tower triangulation; Public Wireless Fidelity (Wi-Fi) hotspots
- GPS:
- Trilateration 2D/3D, computes the distance to each satellite using the speed of light
- A-GPS (Assisted GPS): An expansion of GPS, using internet to accelerate the speed.
- Cell Tower Triangulation
- computing its location by identifying neighboring cells and their signal strengths.
- Public Wi-Fi Hotspots
- This technique asks the provider to refresh the change of Wi-Fi hotspots
- Working with Location-based Service
- Two significant classes: LocationManager, LocationProvider
- Accessing Location-based Services
- In order to prevent power consumption, privacy leaks, applications need to apply for permission to use LBS
- step 1: apply for permission
// for precise location <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> // for roughly location <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- step 2: Get Location Manager
String serviceString = Context.LOCATION_SERVICE; LocationManager lm = (LocationManager)getSystemService(serviceString);
- step 3: select a LocationProvider (see Select a location provider)
- demo:
requestPermissions(new String[] {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION }, 1
);
final LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
boolean gpsIsOk = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
if(gpsIsOk){
try{
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
Toast.makeText(MainActivity.this,"location updated", Toast
.LENGTH_SHORT).show();
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
});
}
catch (SecurityException e){
Log.e("Location:", e.toString());
}
}
Button bt = findViewById(R.id.button);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try{
Location loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
String locInfo = Double.toString(loc.getLongitude()) + " " + Double.toString
(loc.getLatitude()) +
" " + Double.toString(loc.getAltitude());
Toast.makeText(MainActivity.this, locInfo, Toast.LENGTH_LONG).show();
}
catch (SecurityException e){
Log.e("location: ", e.toString());
}
}
});
- To ways to use Google map: web, Google Map API
- Using the MapView Class
- MapView offers a wealth of map features such as zoom, marker position, etc., supports a number of view types (route, satellite, traffic flow, etc.)
- step 1: declaration ('cause it isn't in the standard library)
<uses-library android:name="com.google.android.maps" />
- step 2: Use MapView as simple widget
MapView mapView; mapView = (MapView)findViewById(R.id.map_view); mapView.setSatellite(true); mapView.setTraffic(true);
- step 3: Apply for a Google Map agreement with Google by a unique developer sign; and then obtain a Maps API key (Google will know who is using the Map service)
<com.google.android.maps.MapView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mapview" ... android:layout_height="fill_parent" android:clickable="true" android:apiKey="<generated key>" />
- step 4: Use MapController to handle zoom.
- demo:
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout. main);
mapView = (MapView) findViewById(R.id. mapView);
mapView.setBuiltInZoomControls(true);
mapView.setStreetView(true);
mapController = mapView.getController();
mapController.setZoom(14); // Zoom 1 is world view
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
String coordinates[] = {"1.352566007", "103.78921587"};
double lat = Double.parseDouble(coordinates[0]);
double lng = Double.parseDouble(coordinates[1]);
GeoPoint gp = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
mapController.animateTo(gp);
}
- Using Overlays