Android核心基础(十一)

1、Android的状态栏通知(Notification)

通知用于在状态栏显示消息,消息到来时以图标方式表示,如下:

//获取通知管理器

NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

int icon = android.R.drawable.stat_notify_chat;

long when = System.currentTimeMillis();

//新建一个通知,指定其图标和标题

Notification notification = new Notification(icon, null, when);//第一个参数为图标,第二个参数为短暂提示标题,第三个为通知时间

notification.defaults = Notification.DEFAULT_SOUND;//发出默认声音

Intent openintent = new Intent(this, OtherActivity.class);

PendingIntent contentIntent = PendingIntent.getActivity(this, 0, openintent, 0);//当点击消息时就会向系统发送openintent意图

notification.setLatestEventInfo(this, “标题”, “我是内容", contentIntent);

mNotificationManager.notify(0, notification);//第一个参数为自定义的通知唯一标识

2、对话框通知(Dialog Notification)

当你的应用需要显示一个进度条或需要用户对信息进行确认时,可以使用对话框来完成。

下面代码将打开一个如右图所示的对话框:

new AlertDialog.Builder(context)

.setTitle("java培训")

.setCancelable(false) //设置不能通过“后退”按钮关闭对话框

.setMessage("浏览传智播客网站?")

.setPositiveButton("确认",

new DialogInterface.OnClickListener(){

public void onClick(DialogInterface dialoginterface, int i){

Uri uri = Uri.parse("http://www.itcast.cn/");//打开链接

Intent intent = new Intent(Intent.ACTION_VIEW, uri);

startActivity(intent);

}

})

.setNegativeButton("取消", new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int id) {

dialog.cancel();

}

})

.show();//显示对话框

上面代码采用的是一个链式调用,像setTitle()、setMessage()这些方法,他们的返回值都是当前对话框对象。

3、创建带单选项列表的对话框

下面代码将打开一个如右上图所示的选项列表对话框:

final String[] items = {"java", ".net", "php"};

new AlertDialog.Builder(SenderNotificationActivity.this).setTitle("选择语言")

.setItems(items, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int item) {

Toast.makeText(getApplicationContext(), items[item],

Toast.LENGTH_SHORT).show();

}

}).show();//显示对话框

下面代码将打开一个如右下图所示的带单选框的列表对话框:

final String[] items = {"java", ".net", "php"};

new AlertDialog.Builder(SenderNotificationActivity.this).setTitle("选择语言")

.setSingleChoiceItems(items, 1, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int item) {

Toast.makeText(getApplicationContext(), items[item],

Toast.LENGTH_SHORT).show();

dialog.cancel();

}

}).show();//显示对话框

setSingleChoiceItems()的第二个参数是设置默认选项,

开始,-1代表不选择任何选项。

4、创建带多选项列表的对话框

下面代码将打开一个如右下图所示的多选项列表对话框:

final String[] items = {"java", ".net", "php"};

new AlertDialog.Builder(SenderNotificationActivity.this).setCancelable(false)

.setTitle("选择语言")

.setMultiChoiceItems(items, new boolean[]{false,true,false}, new DialogInterface.OnMultiChoiceClickListener() {

@Override

public void onClick(DialogInterface dialog, int which, boolean isChecked) {

if(isChecked){

Toast.makeText(getApplicationContext(), items[which],

Toast.LENGTH_SHORT).show();

}

}

})

.setPositiveButton("确认",

new DialogInterface.OnClickListener(){

public void onClick(DialogInterface dialoginterface, int i){

dialoginterface.dismiss();

}

})

.show();//显示对话框

5、进度对话框(ProgressDialog)

使用代码ProgressDialog.show(ProgressDialogActivity.this, "请稍等", "数据正在加载中...", true);创建并显示一个进度对话框。

调用setProgressStyle()方法设置进度对话框风格。有两种风格:

ProgressDialog.STYLE_SPINNER 旋体进度条风格 (为默认风格)

ProgressDialog.STYLE_HORIZONTAL 横向进度条风格

public class ProgressDialogActivity extends Activity {

private ProgressDialog progressDialog;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.menu);

//开始一条专门处理耗时工作的线程

new Thread(new Runnable(){

@Override

public void run() {

try {

秒才能完成

progressDialog.dismiss();//关闭进程对话框

//runOnUiThread(finishDialog);//要求运行在UI线程

} catch (InterruptedException e) {}

}

}).start();

progressDialog = ProgressDialog.show(ProgressDialogActivity.this, "请稍等", "数据正在加载中...", true);

}

private Runnable finishDialog = new Runnable() {

@Override

public void run() {

progressDialog.dismiss();

}

};

}

6、单选框(RadioButton)

要完成单选框显示,我们需要使用到RadioGroup和RadioButton(单选框),RadioGroup用于对单选框进行分组,相同组内的单选框只有一个单选框能被选中。(例子代码请见下方备注栏)

RadioGroup.check(R.id.dotNet);将id名为dotNet的单选框设置成选中状态。

(RadioButton) findViewById(radioGroup.getCheckedRadioButtonId());//获取被选中的单选框。

RadioButton.getText();//获取单选框的值

调用setOnCheckedChangeListener()方法,处理单选框被选择事件,把RadioGroup.OnCheckedChangeListener实例作为参数传入

界面设计:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<RadioGroup android:id="@+id/radioGroup"

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

<RadioButton android:id="@+id/java"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="java" />

<RadioButton android:id="@+id/dotNet"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="dotNet" />

<RadioButton android:id="@+id/php"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="PHP" />

</RadioGroup>

</LinearLayout>

处理程序:

public void onCreate(Bundle savedInstanceState) {

......

RadioGroup radioGroup = (RadioGroup) findViewById(R.id.radioGroup);

radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {

public void onCheckedChanged(RadioGroup group, int checkedId) {

RadioButton radioButton = (RadioButton) findViewById(checkedId);

Log.i(TAG, String.valueOf(radioButton.getText()));

}

});

}

7、多选框(CheckBox)

每个多选框都是独立的,可以通过迭代所有多选框,然后根据其状态是否被选中再获取其值。

CheckBox.setChecked(true);//设置成选中状态。

CheckBox.getText();//获取多选框的值

调用setOnCheckedChangeListener()方法,处理多选框被选择事件,把CompoundButton.OnCheckedChangeListener实例作为参数传入

界面设计:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="wrap_content"

android:layout_height="fill_parent">

<CheckBox android:id="@+id/checkboxjava"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="java" />

<CheckBox android:id="@+id/checkboxdotNet"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="dotNet" />

<CheckBox android:id="@+id/checkboxphp"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="PHP" />

<Button android:id="@+id/checkboxButton"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="获取值" />

</LinearLayout>

代码处理:

public class CheckBoxActivity extends Activity {

private static final String TAG = "CheckBoxActivity";

private List<CheckBox> checkboxs = new ArrayList<CheckBox>();

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.checkbox);

checkboxs.add((CheckBox) findViewById(R.id.checkboxdotNet));

checkboxs.add((CheckBox) findViewById(R.id.checkboxjava));

checkboxs.add((CheckBox) findViewById(R.id.checkboxphp));

checkboxs.get(1).setChecked(true);//设置成选中状态

for(CheckBox box : checkboxs){

box.setOnCheckedChangeListener(listener);

}

Button button = (Button)findViewById(R.id.checkboxButton);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

List<String> values = new ArrayList<String>();

for(CheckBox box : checkboxs){

if(box.isChecked()){

values.add(box.getText().toString());

}

}

Toast.makeText(CheckBoxActivity.this, values.toString(), 1).show();

}

});

}

CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() {@Override

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

CheckBox checkBox = (CheckBox) buttonView;

Log.i(TAG, "isChecked="+ isChecked +",value="+ checkBox.getText());//输出单选框的值

}

};

}

8、下拉列表框(Spinner)

Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值

调用setOnItemSelectedListener()方法,处理下拉列表框被选择事件,把AdapterView.OnItemSelectedListener实例作为参数传入

界面设计:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content">

<Spinner android:id="@+id/spinner"

android:layout_height="wrap_content"

android:layout_width="fill_parent"/>

</LinearLayout>

代码处理:

public class SpinnerActivity extends Activity {

private static final String TAG = "SpinnerActivity";

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.spinner);

//第二个参数为下拉列表框每一项的界面样式,该界面样式由Android系统提供,当然您也可以自定义

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);

adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

adapter.add("java");

adapter.add("dotNet");

adapter.add("php");

Spinner spinner = (Spinner) findViewById(R.id.spinner);

spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

@Override

public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {

Spinner spinner = (Spinner)adapterView;

String itemContent = (String)adapterView.getItemAtPosition(position);

}

@Override

public void onNothingSelected(AdapterView<?> view) {

Log.i(TAG,  view.getClass().getName());

}

});

}

}

9、下拉列表框—采用javabean作为Adapter元素

很多时候显示在下拉列表框的值并不是希望得到的值,如果要做一个联系人下拉列表框,列表框列出的是联系人的姓名,因为姓名有可能相同,所以我们希望得到的值应该为该联系人的id,要实现这种需求我们需要自定义Adapter,当然自定义Adapter需要我们编写一小段代码,如果我们不想编写Adapter,又能实现我们的需求,那是最好不过的了。通过观察ArrayAdapter中getView(int position, View convertView, ViewGroup parent)的内部代码发现,如果为ArrayAdapter指定的实际泛型参数类型没有实现CharSequence(字符串)接口,将会调用该类型对象的toString()向下拉列表框输出显示值。利用这个特点我们可以重写javaBean的toString()向下拉列表框提供显示值。

界面设计:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content">

<Spinner android:id="@+id/spinner"

android:layout_height="wrap_content"

android:layout_width="fill_parent"/>

</LinearLayout>

代码处理:

public class SpinnerActivity extends Activity {

private static final String TAG = "SpinnerActivity";

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.spinner);

ArrayAdapter<Person> adapter = new ArrayAdapter<Person>(this, android.R.layout.simple_spinner_item);

adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

adapter.add(new Person(12, "李明"));

adapter.add(new Person(100, "李明"));

adapter.add(new Person(62, "张天"));

Spinner spinner = (Spinner) findViewById(R.id.spinner);

spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

@Override

public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {

Spinner spinner = (Spinner)adapterView;

Person person = (Person)adapterView.getItemAtPosition(position);

}

@Override

public void onNothingSelected(AdapterView<?> view) {

Log.i(TAG,  view.getClass().getName());

}

});

}

}

Person.java:

public class Person {

private Integer id;

private String name;

public Person(Integer id, String name) {

this.id = id;

this.name = name;

}

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return name;

}

}

10、下拉列表框--自定义选项界面样式

Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值

调用setOnItemSelectedListener()方法,处理下拉列表框被选择事件,把AdapterView.OnItemSelectedListener实例作为参数传入

主界面设计:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content">

<Spinner android:id="@+id/spinner"

android:layout_height="wrap_content"

android:layout_width="fill_parent"/>

</LinearLayout>

下拉列表框每一项的界面样式:stylespinner.xml

<?xml version="1.0" encoding="utf-8"?>

<TextView xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/contentTextView"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:background="#F4FDFF"

/>

代码处理:

public class SpinnerActivity extends Activity {

private static final String TAG = "SpinnerActivity";

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.spinner);

//第二个参数为layout文件在R文件的id,第三个参数为TextView在layout文件的id

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.stylespinner, R.id.contentTextView);

adapter.add("java");

adapter.add("dotNet");

adapter.add("php");

Spinner spinner = (Spinner) findViewById(R.id.spinner);

spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

@Override

public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {

Spinner spinner = (Spinner)adapterView;

String itemContent = (String)adapterView.getItemAtPosition(position);

}

@Override

public void onNothingSelected(AdapterView<?> view) {

Log.i(TAG,  view.getClass().getName());

}

});

}

}

11、拖动条(SeekBar)

SeekBar.getProgress()获取拖动条当前值

调用setOnSeekBarChangeListener()方法,处理拖动条值变化事件,把SeekBar.OnSeekBarChangeListener实例作为参数传入

主界面设计:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical">

<SeekBar

android:id="@+id/seekBar"

android:layout_height="wrap_content"

android:layout_width="fill_parent"/>

<Button android:id="@+id/seekBarButton"

android:layout_height="wrap_content"

android:layout_width="wrap_content"

android:text="获取值"

/>

</LinearLayout>

代码处理:

public class SeekBarActivity extends Activity {

private SeekBar seekBar;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.seekbar);

seekBar = (SeekBar) findViewById(R.id.seekBar);

seekBar.setMax(100);//设置最大刻度

seekBar.setProgress(30);//设置当前刻度

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

@Override

public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {

Log.v("onProgressChanged()", String.valueOf(progress) + ", " + String.valueOf(fromTouch));

}

@Override

public void onStartTrackingTouch(SeekBar seekBar) {//开始拖动

Log.v("onStartTrackingTouch()", String.valueOf(seekBar.getProgress()));

}

@Override

public void onStopTrackingTouch(SeekBar seekBar) {//结束拖动

Log.v("onStopTrackingTouch()", String.valueOf(seekBar.getProgress()));

}

});

Button button = (Button)this.findViewById(R.id.seekBarButton);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Toast.makeText(SeekBarActivity.this, String.valueOf(seekBar.getProgress()), 1).show();

}

});

}

}

12、菜单(Menu)

重写Activity的onCreateOptionsMenu(Menu menu)方法,该方法用于创建选项菜单,在用户按下手机的“Menu”按钮时就会显示创建好的菜单,在onCreateOptionsMenu(Menu menu)方法内部可以调用Menu.add()方法实现菜单的添加。

重写Activity的onMenuItemSelected()方法,该方法用于处理菜单被选择事件

通过手机上提供的“MENU”按钮可以打开菜单,如果希望通过代码打开菜单,可以调用Activity的openOptionsMenu()方法。

public class MenuActivity extends Activity {

private static final String TAG = "MenuActivity";

private static final int MENU_ADD = Menu.FIRST;

private static final int MENU_UPDATE = Menu.FIRST + 1;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.menu);

}

@Override

public boolean onCreateOptionsMenu(Menu menu) {

menu.add(Menu.NONE, MENU_ADD, Menu.NONE, "添加");

menu.add(Menu.NONE, MENU_UPDATE, Menu.NONE, "更新");

return super.onCreateOptionsMenu(menu);

}

@Override

public boolean onMenuItemSelected(int featureId, MenuItem item) {

switch (item.getItemId()) {

case MENU_ADD:

Log.i(TAG, "add was selected");

return true;

case MENU_UPDATE:

Log.i(TAG, "update was selected");

return true;

default:

return super.onMenuItemSelected(featureId, item);

}

}

}

13、进度条(ProgressBar)

在布局xml文件中添加进度条代码:

<ProgressBar

android:layout_width="fill_parent"

android:layout_height="20px"

style="?android:attr/progressBarStyleHorizontal"

android:id="@+id/downloadbar"/>

在代码中操作进度条:

ProgressBar.setMax(100);//设置最大刻度

,当前刻度为50,进度条将进行到一半。

14、输入内容自动完成文本框(AutoCompleteTextView )

AutoCompleteTextView和EditText组件类似,都可以输入文本。

但AutoCompleteTextView组件可以和一个字符串数组或List对象

绑定,当用户输入两个及以上字符时,系统将在

AutoCompleteTextView组件下方列出字符串数组中所有以输入

字符开头的字符串,这一点和www.google.com的搜索框非常相似,

当输入某一个要查找的字符串时,google搜索框就会列出以这个

字符串开头的最热门的搜索字符串列表。

<AutoCompleteTextView

android:layout_width="fill_parent“  android:layout_height="wrap_content“

<!– completionThreshold 指定至少输入几个字符后才会出现自动提示功能 à

android:completionThreshold="1“

android:id="@+id/name" />

public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);

setContentView(R.layout.main);

String[] names = {"老张", "老方", "老毕", "李明" , "李丽", "陈江", "abc", "acc"};

AutoCompleteTextView nameText = (AutoCompleteTextView)this.findViewById(R.id.name);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, names);

nameText.setAdapter(adapter);

}

15、多次输入-内容自动完成文本框(MultiAutoCompleteTextView)

除了AutoCompleteTextView控件外,我们还可以使用MultiAutoCompleteTextView控件来完成连续输入的功能。也就是说,当输入完一个字符串后,在该字符串后面输入一个逗号(,),在逗号前后可以有任意多个空格,然后再输入一个字符串,仍然会显示自动提示列表。

使用MultiAutoCompleteTextView时,需要为它的setTokenizer方法指定MultiAutoCompleteTextView.CommaTokenizer类对象实例,

该对象表示采用逗号作为输入多个字符串的分隔符。

< MultiAutoCompleteTextView

android:layout_width="fill_parent“  android:layout_height="wrap_content“

<!– completionThreshold 指定至少输入几个字符后才会出现自动提示功能à

android:completionThreshold="1“

android:id="@+id/name" />

public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);

setContentView(R.layout.main);

String[] names = {"老张", "老方", "老毕", "李明" , "李丽", "陈江", "abc", "acc"};

MultiAutoCompleteTextView nameText = (MultiAutoCompleteTextView)this.findViewById(R.id.name);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line,    names);

nameText.setAdapter(adapter);

nameText.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());}

16、android样式和主题(style&theme)

android中的样式和CSS样式作用相似,都是用于为界面元素定义显示风格,它是一个包含一个或者多个view控件属性的集合。如:需要定义字体的颜色和大小。

在CSS中是这样定义的:

<style>

.itcast{COLOR:#0000CC;font-size:18px;}

</style>

可以像这样使用上面的css样式:<div class="itcast">传智播客</div>

在Android中可以这样定义样式:

在res/values/styles.xml文件中添加以下内容

<?xml version="1.0" encoding="utf-8"?>

<resources>

<style name=“itcast”> <!-- 为样式定义一个全局唯一的名字-->

<item name=“android:textSize”>18px</item> <!-- name属性的值为使用了该样式的View控件的属性 -->

<item name="android:textColor">#0000CC</item>

</style>

</resources>

在layout文件中可以像下面这样使用上面的android样式:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ....>

<TextView style="@style/itcast"

.....  />

</LinearLayout>

<style>元素中有一个parent属性。这个属性可以让当前样式继承一个父样式,并且具有父样式的值。当然,如果父样式的值不符合你的需求,你也可以对它进行修改,如下:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<style name="itcast">

<item name="android:textSize">18px</item> <!-- name属性为样式要用在的View控件持有的属性 -->

<item name="android:textColor">#0000CC</item>

</style>

<style name="subitcast" parent="@style/itcast">

<item name="android:textColor">#FF0000</item>

</style>

</resources>

android中主题也是用于为应用定义显示风格,它的定义和样式的定义相同,如下:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<style name=“itcastTheme">

<item name=“android:windowNoTitle”>true</item> <!– 没标题 à

<item name=“android:windowFullscreen”>?android:windowNoTitle</item> <!– 全屏显示 à

</style>

</resources>

上面“?android:windowNoTitle”中的问号用于引用在当前主题中定义过的资源的值。下面代码显示在AndroidManifest.xml中如何为应用设置上面定义的主题:

<application android:icon="@drawable/icon" android:label="@string/app_name"

android:theme="@style/itcastTheme">

......

</application>

除了可以在AndroidManifest.xml中设置主题,同样也可以在代码中设置主题,如下:

setTheme(R.style.itcastTheme);

尽管在定义上,样式和主题基本相同,但是它们使用的地方不同。样式用在单独的View,如:EditText、TextView等;主题通过AndroidManifest.xml中的<application>和<activity>用在整个应用或者某个 Activity,主题对整个应用或某个Activity进行全局性影响。如果一个应用使用了主题,同时应用下的view也使用了样式,那么当主题和样式属性发生冲突时,样式的优先级高于主题。

另外android系统也定义了一些主题,例如:<activity android:theme=“@android:style/Theme.Dialog”>,该主题可以让Activity看起来像一个对话框,还有透明主题:@android:style/Theme.Translucent 。如果需要查阅这些主题,可以在文档的referenceàandroid-->R.style 中查看。

17、编码实现软件界面

Android除了可以使用xml实现软件界面,还可以通过编码方式实现软件的界面,而且在某种情况下只能采用编码方式实现软件的界面,例如:软件运行时需要根据运算结果决定显示某些内容。如果不是必须,建议使用xml,因为这样可以使应用遵守mvc设计模式,具有良好的软件分层结构。下面代码实现了如HelloWorld项目一样的软件界面:

public class MainActivity extends Activity {

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

LinearLayout linearLayout = new LinearLayout(this);

LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(

ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT);

TextView textView = new TextView(this);

textView.setText(R.string.hello);

textView.setId(34);

LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(

ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

linearLayout.addView(textView, textParams);

setContentView(linearLayout, layoutParams);

}

}

18、使用网页开发软件界面

因为android软件开发分工目前还没有细化,程序员往往需要负责软件界面的开发,虽然软件的界面图片已经由美工设计好了,但如果使用layout技术把软件做成如图片所示的界面确实很困难,而且也比较耗时。Android通过WebView实现了JS代码与Java代码互相通信的功能,使的android软件的界面开发也可以采用HTML网页技术,这样,广大网页美工可以参与进android软件的界面开发工作,从而让程序员从中解脱出来。

在项目的assets目录放入index.html文件

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Insert title here</title>

<style type="text/css">

A {

COLOR: #FFFFFF; TEXT-DECORATION: none

}

</style>

<script type="text/javascript">

function show(jsondata){

var jsonobjs = eval(jsondata);

var table = document.getElementById("personTable");

for(var y=0; y<jsonobjs.length; y++){

var tr = table.insertRow(table.rows.length); //添加一行

//添加三列

var td1 = tr.insertCell(0);

var td2 = tr.insertCell(1);

td2.align = "center";

var td3 = tr.insertCell(2);

//设置列内容和属性

td1.innerHTML = jsonobjs[y].id;

td2.innerHTML = "<a href='javascript:itcast.call(\"5554\")'>"+ jsonobjs[y].name + "</a>";

td3.innerHTML = jsonobjs[y].phone;

}

}

</script>

</head>

<body bgcolor="#000000" text="#FFFFFF" style="margin:0 0 0 0" onload="javascript:itcast.personlist()">

<table border="0" width="100%" id="personTable" cellspacing="0">

<tr>

<td width="15%">编号</td><td align="center">姓名</td><td width="15%">电话</td>

</tr>

</table>

<a href="javascript:window.location.reload()">刷新</a>

</body>

</html>

public class HtmlActivity extends Activity {

private WebView webView;

private Handler handler = new Handler();

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

webView = (WebView)this.findViewById(R.id.webView);

webView.getSettings().setJavaScriptEnabled(true);

webView.getSettings().setSaveFormData(false);

webView.getSettings().setSavePassword(false);

webView.getSettings().setSupportZoom(false);

webView.addJavascriptInterface(new ItcastJavaScript(), “itcast”);//addJavascriptInterface方法中要绑定的Java对象

webView.setWebChromeClient(new ItcastWebClient());

webView.loadUrl("file:///android_asset/index.html");

}

private final class ItcastJavaScript{

public void personlist(){

webview.loadUrl("javascript:contactlist('"+ getPersonJson() + "')");

}

public void call(final String phone){

startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+ phone)));

}

public static String getPersonJson() {//生成json字符串

try {

JSONObject jsonObject = new JSONObject();

jsonObject.put("id", 56);

try {

JSONObject jsonObject = new JSONObject();

jsonObject.put("id", 56);

jsonObject.put("name", "老张");

jsonObject.put("phone", "5556");

JSONObject jsonObject2 = new JSONObject();

jsonObject2.put("id", 89);

jsonObject2.put("name", "老方");

jsonObject2.put("phone", "5558");

JSONArray jsonArray = new JSONArray();

jsonArray.put(jsonObject);

jsonArray.put(jsonObject2);

return jsonArray.toString();

} catch (JSONException e) {

e.printStackTrace();

}

return "";

}

}

private final class ItcastWebClient extends WebChromeClient{

@Override

public boolean onJsAlert(WebView view, String url, String message, JsResult result) {

new AlertDialog.Builder(HtmlActivity.this)

.setTitle("提示信息")

.setMessage(message)

.setPositiveButton("确定", new DialogInterface.OnClickListener(){

public void onClick(DialogInterface dialoginterface, int i){}

}).show();

return true;

}

}

}

19、动画(Animation)

种动画:

种类型:

> Frame动画,即顺序播放事先做好的图像,跟放胶片电影类似。开发步骤:

)把准备好的图片放进项目res/ drawable下。

)在项目的res目录下创建文件夹anim,然后在anim文件夹下面定义动画XML文件,文件名称可以自定义。当然也可以采用编码方式定义动画效果(使用AnimationDrawable类)。

)为View控件绑定动画效果。调用代表动画的AnimationDrawable的start()方法开始动画。

本例要实现对ImageView对象进行渐变尺寸缩放动画效果

1> 在项目的res目录下创建文件夹anim,然后在anim文件夹下面定义动画XML文件,文件名称可以自定义,如:scale.xml,内容如下:

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android">

<scale android:interpolator="@android:anim/accelerate_decelerate_interpolator"

android:fromXScale="0.0"

android:fromYScale="0.0"

android:toXScale="5"

android:toYScale="5"

android:pivotX="50%"

android:pivotY="50%"

android:fillAfter="false"

android:duration="5000"

/>

</set>

动画的进度使用interpolator控制,android提供了几个Interpolator 子类,实现了不同的速度曲线,如LinearInterpolator实现了匀速效果、Accelerateinterpolator实现了加速效果、DecelerateInterpolator实现了减速效果等。还可以定义自己的Interpolator子类,实现抛物线、*落体等物理效果。

fromXScale(浮点型) 属性为动画起始时X坐标上的缩放尺寸

fromYScale(浮点型) 属性为动画起始时Y坐标上的缩放尺寸

toXScale(浮点型)   属性为动画结束时X坐标上的缩放尺寸

toYScale(浮点型)   属性为动画结束时Y坐标上的缩放尺寸

说明: 以上四种属性值

0.0表示收缩到没有

1.0表示正常无缩放

值小于1.0表示收缩

值大于1.0表示放大

pivotX(浮点型)     属性为动画相对于物件的X坐标的开始位置

pivotY(浮点型)     属性为动画相对于物件的Y坐标的开始位置

说明:

以上两个属性值 从0%-100%中取值

50%为物件的X或Y方向坐标上的中点位置

duration(长整型)属性为动画持续时间 。说明:   时间以毫秒为单位

2> 在layout文件添加<ImageView>节点:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<ImageView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:src="@drawable/icon"

android:id="@+id/imageView"

/>

</LinearLayout>

说明:除了可以对<ImageView>实现动画效果,其实也可以对其他View实现动画效果,如:<TextView>

3>在Activity里对ImageView使用前面定义好的动画效果:

public class AnimationActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ImageView imageView = (ImageView)this.findViewById(R.id.imageView);

//加载动画XML文件,生成动画指令

Animation animation = AnimationUtils.loadAnimation(this, R.anim.scale);

//开始执行动画

imageView.startAnimation(animation);

}

}

备注:上面采用的是xml文件定义动画效果,作为代替,也可以采用编码方式实现。下面采用编码方式实现上述例子同样的效果:

public class AnimationActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ImageView imageView = (ImageView)this.findViewById(R.id.imageView);

ScaleAnimation animation = new ScaleAnimation(0.0f, 5f, 0.0f, 5f,

Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

imageView.startAnimation(animation);

}

}

其他动画效果定义例子:

=================渐变透明度动画效果======================

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android">

<alpha

android:fromAlpha="0.1"

android:toAlpha="1.0"

android:duration="3000"

/>

</set>

编码实现透明度动画效果:

public class AnimationActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ImageView imageView = (ImageView)this.findViewById(R.id.imageView);

AlphaAnimation animation = new AlphaAnimation(0.1, 1.0);

imageView.startAnimation(animation);

}

}

=================画面位置移动动画效果======================

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android">

<translate

android:repeatCount="2"

android:fromXDelta="0"

android:fromYDelta="0"

android:toXDelta="120"

android:toYDelta="120"

android:duration="3000"

/>

<!-- fromXDelta fromYDelta 为动画起始时 X和Y坐标上的位置

toXDelta toYDelta为动画结束起始时  X和Y坐标上的位置

-->

</set>

编码实现位置移动动画效果:

public class AnimationActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ImageView imageView = (ImageView)this.findViewById(R.id.imageView);

TranslateAnimation animation = new TranslateAnimation(0, 120, 0, 120);

imageView.startAnimation(animation);

=================画面旋转动画效果======================

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android">

<rotate

android:interpolator="@android:anim/accelerate_interpolator"

android:repeatCount="2"

android:fromDegrees="0"

android:toDegrees="+360"

android:pivotX="50%"

android:pivotY="50%"

android:duration="3000"

/>

<!--

repeatCount 重复次数

fromDegrees为动画起始时物件的角度:

当角度为负数——表示逆时针旋转

当角度为正数——表示顺时针旋转

(负数fromDegrees——toDegrees正数:顺时针旋转)

(负数fromDegrees——toDegrees负数:逆时针旋转)

(正数fromDegrees——toDegrees正数:顺时针旋转)

(正数fromDegrees——toDegrees负数:逆时针旋转)

pivotX,pivotY  为动画相对于物件的X、Y坐标的开始位.说明:以上两个属性值 从0%-100%中取值,50%为物件的X或Y方向坐标上的中点位置

-->

</set>

编码实现:

RotateAnimation animation = new RotateAnimation(0, -90, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

animation.setDuration(500);

imageView.startAnimation(animation);

=====================  Frame动画例子  ===============================

)把准备好的图片放进项目res/ drawable下。

图片有:girl_1.gif, girl_2.gif, girl_3.gif

)在项目的res目录下创建文件夹anim,然后在anim文件夹下面定义动画XML文件,文件名称可以自定义,如:frame.xml。

<?xml version="1.0" encoding="utf-8"?>

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"

android:oneshot="false">

<item android:drawable="@drawable/girl_1" android:duration="200" />

<item android:drawable="@drawable/girl_2" android:duration="200" />

<item android:drawable="@drawable/girl_3" android:duration="200" />

</animation-list>

帧动画,3帧动画中分别应用了drawable中的3张图片:girl_1.gif, girl_2.gif, girl_3.gif,每帧动画持续200毫秒。android:oneshot属性如果为true,表示动画只播放一次停止在最后一帧上,如果设置为false表示动画循环播放。

)为View控件绑定动画效果,调用代表动画的AnimationDrawable的start()方法开始动画。

public class FrameActivity extends Activity {

private AnimationDrawable animationDrawable;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ImageView imageView = (ImageView)this.findViewById(R.id.imageView);

imageView.setBackgroundResource(R.anim.frame);

animationDrawable = (AnimationDrawable) imageView.getBackground();

}

@Override

public boolean onTouchEvent(MotionEvent event) {

if (event.getAction() == MotionEvent.ACTION_DOWN) {//按下

animationDrawable.start();

return true;

}

return super.onTouchEvent(event);

}

}

有一点需要强调的是:启动Frame动画的代码animationDrawable.start();不能应用在OnCreate()方法中,因为在OnCreate()中 AnimationDrawable还没有完全的与ImageView绑定。在OnCreate()中启动动画,只能看到第一张图片。这里在触摸事件中实现的。

20、传感器的使用

传感器类型:方向、加速度(重力)、光线、磁场、距离(临近性)、温度等。

(1)方向传感器:   Sensor.TYPE_ORIENTATION

)加速度(重力)传感器: Sensor.TYPE_ACCELEROMETER

)光线传感器:    Sensor.TYPE_LIGHT

)磁场传感器:   Sensor.TYPE_MAGNETIC_FIELD

)距离(临近性)传感器: Sensor.TYPE_PROXIMITY

)温度传感器:   Sensor.TYPE_TEMPERATURE

//获取某种类型的感应器

Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

//注册监听,获取传感器变化值

sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);

上面第三个参数为采样率:最快、游戏、普通、用户界面。当应用程序请求特定的采样率时,其实只是对传感器子系统的一个建议,不保证特定的采样率可用。

最快: SensorManager.SENSOR_DELAY_FASTEST

最低延迟,一般不是特别敏感的处理不推荐使用,该种模式可能造成手机电力大量消耗,由于传递的为原始数据,算法不处理好将会影响游戏逻辑和UI的性能。

游戏: SensorManager.SENSOR_DELAY_GAME

游戏延迟,一般绝大多数的实时性较高的游戏都使用该级别。

普通: SensorManager.SENSOR_DELAY_NORMAL

标准延迟,对于一般的益智类或EASY级别的游戏可以使用,但过低的采样率可能对一些赛车类游戏有跳帧现象。

用户界面: SensorManager.SENSOR_DELAY_UI

一般对于屏幕方向自动旋转使用,相对节省电能和逻辑处理,一般游戏开发中我们不使用。

下面介绍如何获取加速度(重力)传感器和方向传感器的测量值:

public class MainActivity extends Activity {

private TextView accelerometer;

private TextView orientation;

private SensorManager sensorManager;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

//获取感应器管理器

sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

accelerometer = (TextView) findViewById(R.id.accelerometer);

orientation = (TextView) findViewById(R.id.orientation);

}

@Override

protected void onResume() {

Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);//获取重力加速度传感器

sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);

Sensor sensor1 = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);//获取方向传感器

sensorManager.registerListener(listener, sensor1, SensorManager.SENSOR_DELAY_GAME);

super.onResume();

}

@Override

protected void onPause() {

sensorManager.unregisterListener(listener);//注消所有传感器监听

super.onPause();

}

private SensorEventListener listener = new SensorEventListener() {

@Override

public void onSensorChanged(SensorEvent event) {//当传感器的值发生变化

float x = event.values[SensorManager.DATA_X];

float y = event.values[SensorManager.DATA_Y];

float z = event.values[SensorManager.DATA_Z];

switch (event.sensor.getType()) {

case Sensor.TYPE_ACCELEROMETER:

accelerometer.setText("Accelerometer Sensor: " + x + ", " + y + ", " + z);

break;

case Sensor.TYPE_ORIENTATION:

代表北(North);90代表东(East);180代表南(South);270代表西(West)

个值之一,并且手机是水平放置,手机的顶部对准的方向就是该值代表的方向。

(由于很少有桌子是绝对水平的,

,但一般都是-5和5之间的某个值)。这时从手机顶部开始抬起,直到将手机沿X轴旋转180度(屏幕向下水平放在桌面上)。

到-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,

度,这时y值会在0到180之间变化。

。可以利用y值和z值来测量桌子等物体的倾斜度。

(由于很少有桌子是绝对水平的,

,但一般都是-5和5之间的某个值)。这时从手机顶部开始抬起,直到将手机沿X轴旋转180度(屏幕向下水平放在桌面上)。

到-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,

度,这时y值会在0到180之间变化。

。可以利用y值和z值来测量桌子等物体的倾斜度。

,但一般都是-5和5之间的某个值)。这时从手机顶部开始抬起,直到将手机沿X轴旋转180度(屏幕向下水平放在桌面上)。

到-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,

度,这时y值会在0到180之间变化。

。可以利用y值和z值来测量桌子等物体的倾斜度。

。将手机左侧逐渐抬起时,

z值逐渐变小,直到手机垂直于桌面放置,这时z值是-90。将手机右侧逐渐抬起时,z值逐渐增大,

。在垂直位置时继续向右或向左滚动,z值会继续在-90至90之间变化。

*/

orientation.setText("Orientation Sensor: " + x + ", " + y + ", " + z);

break;

}

}

@Override

public void onAccuracyChanged(Sensor sensor, int accuracy) {//当传感器的精度变化时

}

};

}

21、NinePatch图片

NinePatch是一种很有用的PNG图片格式,它可以在特定区域随文字大小进行缩放。如下:

从上图可以看到,背景图片的中间区域会随着文字的大小进行缩放。背景图片是一张NinePatch图片。 NinePatch图片可以使用android自带的draw9patch工具来制作,该工具在SDK安装路径的tools目录下。执行该工具,然后点击“File”->“open 9-path”打开一张用于制作NinePatch图片的原来图片。在画布的上方和左方的边上画线指定缩放区域,

勾选“Show patches”可显示画定的区域,绿色

为固定大小区域,红色为缩放区域,文字会摆放在红色

区域。制作完后,点击“File”à “save 9-path”保存

图片,draw9patch工具会自动为图片加上*.9.png后缀。

把制作好的图片拷贝进项目的res/drawable目录,然后

编写代码。如下:

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="退出" android:textColor="#330000"

android:background="@drawable/button"/>

22、关闭应用

当应用不再使用时,通常需要关闭应用,可以使用以下三种方法关闭android应用:

第一种方法:首先获取当前进程的id,然后杀死该进程。

android.os.Process.killProcess(android.os.Process.myPid())

第二种方法:终止当前正在运行的Java虚拟机,导致程序终止

System.exit(0);

第三种方法:强制关闭与该包有关联的一切执行

ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

manager.restartPackage(getPackageName());

<uses-permission android:name="android.permission.RESTART_PACKAGES" />

23、判断SIM卡属于哪个移动运营商

在文件AndroidManifest.xml中添加权限

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

第一种方法:

获取手机的IMSI码,并判断是中国移动\中国联通\中国电信

TelephonyManager telManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

/** 获取SIM卡的IMSI码

* SIM卡唯一标识:IMSI 国际移动用户识别码(IMSI:International Mobile Subscriber Identification Number)是区别移动用户的标志,

位数字组成,

;MNC为网络id,由2位数字组成,

,中国联通为01,中国电信为03;MSIN为移动客户识别码,采用等长11位数字构成。

* 唯一地识别国内GSM移动通信网中移动客户。所以要区分是移动还是联通,只需取得SIM卡中的MNC字段即可

*/

String imsi = telManager.getSubscriberId();

if(imsi!=null){

下的IMSI已经用完,所以虚拟了一个46002编号,134/159号段使用了此编号

//中国移动

}else if(imsi.startsWith("46001")){

//中国联通

}else if(imsi.startsWith("46003")){

//中国电信

}

}

第二种方法

TelephonyManager telManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

String operator = telManager.getSimOperator();

if(operator!=null){

if(operator.equals("46000") || operator.equals("46002")){

//中国移动

}else if(operator.equals("46001")){

//中国联通

}else if(operator.equals("46003")){

//中国电信

}

}

24、从SIM卡中获取联系人信息

Uri uri = Uri.parse("content://icc/adn");

String[] projection = {"_id", "name", "number"};

Cursor cursor = managedQuery(uri, projection, null, null, "name");

if(cursor!=null){

while(cursor.moveToNext()){

String name = cursor.getString(cursor.getColumnIndex("name"));

String phone = cursor.getString(cursor.getColumnIndex("number"));

}

}

在文件AndroidManifest.xml中添加权限

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

Android系统内部通过Contentprovider对外共享Sim卡存放的联系人等信息,你可以通过操作Contentprovider来实现Sim卡信息的添删改查操作。

package com.android.internal.telephony;

import android.content.ContentProvider;

import android.content.UriMatcher;

import android.content.ContentValues;

import com.android.internal.database.ArrayListCursor;

import android.database.Cursor;

import android.net.Uri;

import android.os.SystemProperties;

import android.os.RemoteException;

import android.os.ServiceManager;

import android.text.TextUtils;

import android.util.Log;

import java.util.ArrayList;

import java.util.List;

import com.android.internal.telephony.IccConstants;

import com.android.internal.telephony.AdnRecord;

import com.android.internal.telephony.IIccPhoneBook;

public class IccProvider extends ContentProvider {

private static final String TAG = "IccProvider";

private static final boolean DBG = false;

private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] {

"name",

"number"

};

private static final int ADN = 1;

private static final int FDN = 2;

private static final int SDN = 3;

private static final String STR_TAG = "tag";

private static final String STR_NUMBER = "number";

private static final String STR_PIN2 = "pin2";

private static final UriMatcher URL_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);

static {

URL_MATCHER.addURI("icc", "adn", ADN);

URL_MATCHER.addURI("icc", "fdn", FDN);

URL_MATCHER.addURI("icc", "sdn", SDN);

}

private boolean mSimulator;

@Override

public boolean onCreate() {

String device = SystemProperties.get("ro.product.device");

if (!TextUtils.isEmpty(device)) {

mSimulator = false;

} else {

// simulator

mSimulator = true;

}

return true;

}

@Override

public Cursor query(Uri url, String[] projection, String selection,

String[] selectionArgs, String sort) {

ArrayList<ArrayList> results;

if (!mSimulator) {

switch (URL_MATCHER.match(url)) {

case ADN:

results = loadFromEf(IccConstants.EF_ADN);

break;

case FDN:

results = loadFromEf(IccConstants.EF_FDN);

break;

case SDN:

results = loadFromEf(IccConstants.EF_SDN);

break;

default:

throw new IllegalArgumentException("Unknown URL " + url);

}

} else {

// Fake up some data for the simulator

results = new ArrayList<ArrayList>(4);

ArrayList<String> contact;

contact = new ArrayList<String>();

contact.add("Ron Stevens/H");

contact.add("512-555-5038");

results.add(contact);

contact = new ArrayList<String>();

contact.add("Ron Stevens/M");

contact.add("512-555-8305");

results.add(contact);

results = new ArrayList<ArrayList>(4);

ArrayList<String> contact;

contact = new ArrayList<String>();

contact.add("Ron Stevens/H");

contact.add("512-555-5038");

results.add(contact);

contact = new ArrayList<String>();

contact.add("Ron Stevens/M");

contact.add("512-555-8305");

results.add(contact);

contact.add("512-555-5038");

results.add(contact);

contact = new ArrayList<String>();

contact.add("Ron Stevens/M");

contact.add("512-555-8305");

results.add(contact);

contact = new ArrayList<String>();

contact.add("Melissa Owens");

contact.add("512-555-8305");

results.add(contact);

contact = new ArrayList<String>();

contact.add("Directory Assistence");

contact.add("411");

results.add(contact);

}

return new ArrayListCursor(ADDRESS_BOOK_COLUMN_NAMES, results);

}

@Override

public String getType(Uri url) {

switch (URL_MATCHER.match(url)) {

case ADN:

case FDN:

case SDN:

return "vnd.android.cursor.dir/sim-contact";

default:

throw new IllegalArgumentException("Unknown URL " + url);

}

}

@Override

public Uri insert(Uri url, ContentValues initialValues) {

Uri resultUri;

int efType;

String pin2 = null;

if (DBG) log("insert");

int match = URL_MATCHER.match(url);

switch (match) {

case ADN:

efType = IccConstants.EF_ADN;

break;

case FDN:

efType = IccConstants.EF_FDN;

pin2 = initialValues.getAsString("pin2");

break;

default:

throw new UnsupportedOperationException(

"Cannot insert into URL: " + url);

}

String tag = initialValues.getAsString("tag");

String number = initialValues.getAsString("number");

boolean success = addIccRecordToEf(efType, tag, number, pin2);

if (!success) {

return null;

}

StringBuilder buf = new StringBuilder("content://im/");

switch (match) {

case ADN:

buf.append("adn/");

break;

case FDN:

buf.append("fdn/");

break;

}

// TODO: we need to find out the rowId for the newly added record

buf.append(0);

resultUri = Uri.parse(buf.toString());

/*

// notify interested parties that an insertion happened

getContext().getContentResolver().notifyInsert(

resultUri, rowID, null);

*/

return resultUri;

}

private String normalizeValue(String inVal) {

int len = inVal.length();

String retVal = inVal;

if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') {

retVal = inVal.substring(1, len-1);

}

return retVal;

}

@Override

public int delete(Uri url, String where, String[] whereArgs) {

int efType;

if (DBG) log("delete");

int match = URL_MATCHER.match(url);

switch (match) {

case ADN:

efType = IccConstants.EF_ADN;

break;

case FDN:

efType = IccConstants.EF_FDN;

break;

default:

throw new UnsupportedOperationException(

"Cannot insert into URL: " + url);

}

// parse where clause

String tag = null;

String number = null;

String pin2 = null;

String[] tokens = where.split("AND");

int n = tokens.length;

while (--n >= 0) {

String param = tokens[n];

if (DBG) log("parsing '" + param + "'");

String[] pair = param.split("=");

if (pair.length != 2) {

Log.e(TAG, "resolve: bad whereClause parameter: " + param);

continue;

}

String key = pair[0].trim();

String val = pair[1].trim();

if (STR_TAG.equals(key)) {

tag = normalizeValue(val);

} else if (STR_NUMBER.equals(key)) {

number = normalizeValue(val);

} else if (STR_PIN2.equals(key)) {

pin2 = normalizeValue(val);

}

}

if (TextUtils.isEmpty(tag)) {

return 0;

}

if (efType == FDN && TextUtils.isEmpty(pin2)) {

return 0;

}

boolean success = deleteIccRecordFromEf(efType, tag, number, pin2);

if (!success) {

return 0;

}

return 1;

}

@Override

public int update(Uri url, ContentValues values, String where, String[] whereArgs) {

int efType;

String pin2 = null;

if (DBG) log("update");

int match = URL_MATCHER.match(url);

switch (match) {

case ADN:

efType = IccConstants.EF_ADN;

break;

case FDN:

efType = IccConstants.EF_FDN;

pin2 = values.getAsString("pin2");

break;

default:

throw new UnsupportedOperationException(

"Cannot insert into URL: " + url);

}

String tag = values.getAsString("tag");

String number = values.getAsString("number");

String newTag = values.getAsString("newTag");

String newNumber = values.getAsString("newNumber");

boolean success = updateIccRecordInEf(efType, tag, number,

newTag, newNumber, pin2);

if (!success) {

return 0;

}

return 1;

}

private ArrayList<ArrayList> loadFromEf(int efType) {

ArrayList<ArrayList> results = new ArrayList<ArrayList>();

List<AdnRecord> adnRecords = null;

if (DBG) log("loadFromEf: efType=" + efType);

try {

IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(

ServiceManager.getService("simphonebook"));

if (iccIpb != null) {

adnRecords = iccIpb.getAdnRecordsInEf(efType);

}

} catch (RemoteException ex) {

// ignore it

} catch (SecurityException ex) {

if (DBG) log(ex.toString());

}

if (adnRecords != null) {

// Load the results

int N = adnRecords.size();

if (DBG) log("adnRecords.size=" + N);

for (int i = 0; i < N ; i++) {

loadRecord(adnRecords.get(i), results);

}

} else {

// No results to load

Log.w(TAG, "Cannot load ADN records");

results.clear();

}

if (DBG) log("loadFromEf: return results");

return results;

}

private boolean

addIccRecordToEf(int efType, String name, String number, String pin2) {

if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name +

", number=" + number);

boolean success = false;

// TODO: do we need to call getAdnRecordsInEf() before calling

// updateAdnRecordsInEfBySearch()? In any case, we will leave

// the UI level logic to fill that prereq if necessary. But

// hopefully, we can remove this requirement.

try {

IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(

ServiceManager.getService("simphonebook"));

if (iccIpb != null) {

success = iccIpb.updateAdnRecordsInEfBySearch(efType, "", "",

name, number, pin2);

}

} catch (RemoteException ex) {

// ignore it

} catch (SecurityException ex) {

if (DBG) log(ex.toString());

}

if (DBG) log("addIccRecordToEf: " + success);

return success;

}

private boolean

updateIccRecordInEf(int efType, String oldName, String oldNumber,

String newName, String newNumber,String pin2) {

if (DBG) log("updateIccRecordInEf: efType=" + efType +

", oldname=" + oldName + ", oldnumber=" + oldNumber +

", newname=" + newName + ", newnumber=" + newNumber);

boolean success = false;

try {

IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(

ServiceManager.getService("simphonebook"));

if (iccIpb != null) {

success = iccIpb.updateAdnRecordsInEfBySearch(efType,

oldName, oldNumber, newName, newNumber, pin2);

}

} catch (RemoteException ex) {

// ignore it

} catch (SecurityException ex) {

if (DBG) log(ex.toString());

}

if (DBG) log("updateIccRecordInEf: " + success);

return success;

}

private boolean deleteIccRecordFromEf(int efType, String name, String number, String pin2) {

if (DBG) log("deleteIccRecordFromEf: efType=" + efType +

", name=" + name + ", number=" + number + ", pin2=" + pin2);

boolean success = false;

try {

IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(

ServiceManager.getService("simphonebook"));

if (iccIpb != null) {

success = iccIpb.updateAdnRecordsInEfBySearch(efType,

name, number, "", "", pin2);

}

} catch (RemoteException ex) {

// ignore it

} catch (SecurityException ex) {

if (DBG) log(ex.toString());

}

if (DBG) log("deleteIccRecordFromEf: " + success);

return success;

}

name, number, "", "", pin2);

}

} catch (RemoteException ex) {

// ignore it

} catch (SecurityException ex) {

if (DBG) log(ex.toString());

}

if (DBG) log("deleteIccRecordFromEf: " + success);

return success;

}

name, number, "", "", pin2);

}

} catch (RemoteException ex) {

// ignore it

} catch (SecurityException ex) {

if (DBG) log(ex.toString());

}

if (DBG) log("deleteIccRecordFromEf: " + success);

return success;

}

25、删除呼叫记录

在文件AndroidManifest.xml中添加权限

<uses-permission android:name="android.permission.READ_CONTACTS" />

<uses-permission android:name="android.permission.WRITE_CONTACTS" />

负责存放呼叫记录的内容提供者源码在ContactsProvider项目下:

源码路径:

com\android\providers\contacts\CallLogProvider.java

使用到的数据库在:

/data/data/com.android.providers.contacts/databases/contacts2.db

表名:calls

呼叫记录有三种类型:

删除指定号码的来电或未接呼叫记录:

IncomingCallLogContentObserver observer = new IncomingCallLogContentObserver(new Handler());

observer.setNumber("5554");

getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, observer);

private class IncomingCallLogContentObserver extends ContentObserver {

private String number;

public IncomingCallLogContentObserver(Handler handler){

super(handler);

}

public void setNumber(String number){

this.number = number;

}

public void onChange(boolean paramBoolean){

ContentResolver contentResolver = getContentResolver();

if(number!=null){

Uri localUri = CallLog.Calls.CONTENT_URI;

Cursor cursor = contentResolver.query(localUri, new String[]{"_id"}, "number=? AND (type=1 OR type=3)",

new String[]{number}, "_id desc limit 1");

if(cursor.moveToFirst()){

contentResolver.delete(localUri, "_id=?", new String[]{cursor.getString(0)});

}

cursor.close();

}

contentResolver.unregisterContentObserver(this);

}

}

26、在应用中安装其他程序

首先需要AndroidManifest.xml中加入安装程序权限:

<!-- 安装程序权限 -->

<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>

第二步把安装程序添加进SDCard。如把文件名为” sogouinput_android_1.40_sweb.apk.zip”的sogou拼音输入法安装文件放进SDCard。可以点击下面按钮:

第三步在程序中添加以下代码:

Intent intent = new Intent();

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

intent.setAction(android.content.Intent.ACTION_VIEW);

intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "sogouinput_android_1.40_sweb.apk.zip")),"application/vnd.android.package-archive");

startActivity(intent);

27、如何反编绎APK文件

安装ApkTool工具,该工具可以解码得到资源文件,但不能得到Java源文件。

安装环境:需要安装JRE1.6

1> 到http://code.google.com/p/android-apktool/下载apktool1.3.2.tar.bz2 和apktool-install-windows-2.2_r01-3.tar.bz2 文件。解压两个文件,然后把解压后的文件放在一起,如:c:\apktool

2> 在系统变量PATH中添加进aapt.exe,如:;c:\apktool\aapt.exe

3> 在DOS窗口下进入apktool.jar所在目录。执行DOS命令:apktool d -s c:\soft\xxx.apk c:\soft\source。

命令格式:apktool d [opts] <file.apk> [dir]  中的d代表解码,[opts]代表选项,-s选项代表不解码源文件。

Apktool工具只能反编译成smali的中间代码文件,这里需要借助另外一个开源工具Dex2Jar,该工具可以把dex文件转换成jar文件。这个工具不能直接翻译成java文件,但是可以把dex文件转换成jar文件

下载地址:http://code.google.com/p/dex2jar/。

1> 把APK安装包中的classes.dex解压到某个目录下,如:c:\soft

2> 在DOS窗口下进入dex2jar.bat所在目录,执行DOS命令:dex2jar.bat c:\soft\source\classes.dex c:\soft\source,命令生成classes.dex.dex2jar.jar文件。

安装jd-gui工具,该工具可以把jar文件反编译成Java源文件

下载地址:http://java.decompiler.free.fr/jd-gui/downloads/jd-gui-0.3.3.windows.zip。

运行该软件,直接打开classes.dex.dex2jar.jar文件即可看到java源代码。

28、如何防止我们的代码被反编译

由于apk是Android虚拟机加载的,它有一定的规范,加密apk后Dalvik无法

识别apk了。完全避免是不可能的,总有人能够破解你的代码。但是有几种

方式来提高被反编译取代码的难度。

1 关键代码使用jni调用本地代码,用c或者c++编写,因此相对比较难于反

编译

2 混淆java代码。混淆是不改变代码逻辑的情况下,增加无用代码,或者重

命名,使反编译后的源代码难于看懂。

网上开源的java代码混淆工具较多,一般是用ant的方式来编译的

上一篇:25个Java机器学习工具和库


下一篇:Android核心基础(十)