Android核心基础(十一)
1、Android的状态栏通知(Notification)
通知用于在状态栏显示消息,消息到来时以图标方式表示,如下:
//获取通知管理器
NotificationManagermNotificationManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
inticon=android.R.drawable.stat_notify_chat;
longwhen=System.currentTimeMillis();
//新建一个通知,指定其图标和标题
Notificationnotification=newNotification(icon,null,when);//第一个参数为图标,第二个参数为短暂提示标题,第三个为通知时间
notification.defaults=Notification.DEFAULT_SOUND;//发出默认声音
Intentopenintent=newIntent(this,OtherActivity.class);
PendingIntentcontentIntent=PendingIntent.getActivity(this,0,openintent,0);//当点击消息时就会向系统发送openintent意图
notification.setLatestEventInfo(this,“标题”,“我是内容",contentIntent);
mNotificationManager.notify(0,notification);//第一个参数为自定义的通知唯一标识
2、对话框通知(DialogNotification)
当你的应用需要显示一个进度条或需要用户对信息进行确认时,可以使用对话框来完成。
下面代码将打开一个如右图所示的对话框:
newAlertDialog.Builder(context)
.setTitle("java培训")
.setCancelable(false)//设置不能通过“后退”按钮关闭对话框
.setMessage("浏览传智播客网站?")
.setPositiveButton("确认",
newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialoginterface,inti){
Uriuri=Uri.parse("http://www.itcast.cn/");//打开链接
Intentintent=newIntent(Intent.ACTION_VIEW,uri);
startActivity(intent);
}
})
.setNegativeButton("取消",newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialog,intid){
dialog.cancel();
}
})
.show();//显示对话框
上面代码采用的是一个链式调用,像setTitle()、setMessage()这些方法,他们的返回值都是当前对话框对象。
3、创建带单选项列表的对话框
下面代码将打开一个如右上图所示的选项列表对话框:
finalString[]items={"java",".net","php"};
newAlertDialog.Builder(SenderNotificationActivity.this).setTitle("选择语言")
.setItems(items,newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialog,intitem){
Toast.makeText(getApplicationContext(),items[item],
Toast.LENGTH_SHORT).show();
}
}).show();//显示对话框
下面代码将打开一个如右下图所示的带单选框的列表对话框:
finalString[]items={"java",".net","php"};
newAlertDialog.Builder(SenderNotificationActivity.this).setTitle("选择语言")
.setSingleChoiceItems(items,1,newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialog,intitem){
Toast.makeText(getApplicationContext(),items[item],
Toast.LENGTH_SHORT).show();
dialog.cancel();
}
}).show();//显示对话框
setSingleChoiceItems()的第二个参数是设置默认选项,
选项索引从0开始,-1代表不选择任何选项。
4、创建带多选项列表的对话框
下面代码将打开一个如右下图所示的多选项列表对话框:
finalString[]items={"java",".net","php"};
newAlertDialog.Builder(SenderNotificationActivity.this).setCancelable(false)
.setTitle("选择语言")
.setMultiChoiceItems(items,newboolean[]{false,true,false},newDialogInterface.OnMultiChoiceClickListener(){
@Override
publicvoidonClick(DialogInterfacedialog,intwhich,booleanisChecked){
if(isChecked){
Toast.makeText(getApplicationContext(),items[which],
Toast.LENGTH_SHORT).show();
}
}
})
.setPositiveButton("确认",
newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialoginterface,inti){
dialoginterface.dismiss();
}
})
.show();//显示对话框
5、进度对话框(ProgressDialog)
使用代码ProgressDialog.show(ProgressDialogActivity.this,"请稍等","数据正在加载中...",true);创建并显示一个进度对话框。
调用setProgressStyle()方法设置进度对话框风格。有两种风格:
ProgressDialog.STYLE_SPINNER旋体进度条风格(为默认风格)
ProgressDialog.STYLE_HORIZONTAL横向进度条风格
publicclassProgressDialogActivityextendsActivity{
privateProgressDialogprogressDialog;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.menu);
//开始一条专门处理耗时工作的线程
newThread(newRunnable(){
@Override
publicvoidrun(){
try{
Thread.sleep(5*1000);//假设这项工作需要5秒才能完成
progressDialog.dismiss();//关闭进程对话框
//runOnUiThread(finishDialog);//要求运行在UI线程
}catch(InterruptedExceptione){}
}
}).start();
progressDialog=ProgressDialog.show(ProgressDialogActivity.this,"请稍等","数据正在加载中...",true);
}
privateRunnablefinishDialog=newRunnable(){
@Override
publicvoidrun(){
progressDialog.dismiss();
}
};
}
6、单选框(RadioButton)
要完成单选框显示,我们需要使用到RadioGroup和RadioButton(单选框),RadioGroup用于对单选框进行分组,相同组内的单选框只有一个单选框能被选中。(例子代码请见下方备注栏)
RadioGroup.check(R.id.dotNet);将id名为dotNet的单选框设置成选中状态。
(RadioButton)findViewById(radioGroup.getCheckedRadioButtonId());//获取被选中的单选框。
RadioButton.getText();//获取单选框的值
调用setOnCheckedChangeListener()方法,处理单选框被选择事件,把RadioGroup.OnCheckedChangeListener实例作为参数传入
界面设计:
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RadioGroupandroid:id="@+id/radioGroup"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButtonandroid:id="@+id/java"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="java"/>
<RadioButtonandroid:id="@+id/dotNet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="dotNet"/>
<RadioButtonandroid:id="@+id/php"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PHP"/>
</RadioGroup>
</LinearLayout>
处理程序:
publicvoidonCreate(BundlesavedInstanceState){
......
RadioGroupradioGroup=(RadioGroup)findViewById(R.id.radioGroup);
radioGroup.setOnCheckedChangeListener(newRadioGroup.OnCheckedChangeListener(){
publicvoidonCheckedChanged(RadioGroupgroup,intcheckedId){
RadioButtonradioButton=(RadioButton)findViewById(checkedId);
Log.i(TAG,String.valueOf(radioButton.getText()));
}
});
}
7、多选框(CheckBox)
每个多选框都是独立的,可以通过迭代所有多选框,然后根据其状态是否被选中再获取其值。
CheckBox.setChecked(true);//设置成选中状态。
CheckBox.getText();//获取多选框的值
调用setOnCheckedChangeListener()方法,处理多选框被选择事件,把CompoundButton.OnCheckedChangeListener实例作为参数传入
界面设计:
<?xmlversion="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">
<CheckBoxandroid:id="@+id/checkboxjava"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="java"/>
<CheckBoxandroid:id="@+id/checkboxdotNet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="dotNet"/>
<CheckBoxandroid:id="@+id/checkboxphp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PHP"/>
<Buttonandroid:id="@+id/checkboxButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="获取值"/>
</LinearLayout>
代码处理:
publicclassCheckBoxActivityextendsActivity{
privatestaticfinalStringTAG="CheckBoxActivity";
privateList<CheckBox>checkboxs=newArrayList<CheckBox>();
@Override
publicvoidonCreate(BundlesavedInstanceState){
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(CheckBoxbox:checkboxs){
box.setOnCheckedChangeListener(listener);
}
Buttonbutton=(Button)findViewById(R.id.checkboxButton);
button.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
List<String>values=newArrayList<String>();
for(CheckBoxbox:checkboxs){
if(box.isChecked()){
values.add(box.getText().toString());
}
}
Toast.makeText(CheckBoxActivity.this,values.toString(),1).show();
}
});
}
CompoundButton.OnCheckedChangeListenerlistener=newCompoundButton.OnCheckedChangeListener(){@Override
publicvoidonCheckedChanged(CompoundButtonbuttonView,booleanisChecked){
CheckBoxcheckBox=(CheckBox)buttonView;
Log.i(TAG,"isChecked="+isChecked+",value="+checkBox.getText());//输出单选框的值
}
};
}
8、下拉列表框(Spinner)
Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值
调用setOnItemSelectedListener()方法,处理下拉列表框被选择事件,把AdapterView.OnItemSelectedListener实例作为参数传入
界面设计:
<?xmlversion="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">
<Spinnerandroid:id="@+id/spinner"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
</LinearLayout>
代码处理:
publicclassSpinnerActivityextendsActivity{
privatestaticfinalStringTAG="SpinnerActivity";
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.spinner);
//第二个参数为下拉列表框每一项的界面样式,该界面样式由Android系统提供,当然您也可以自定义
ArrayAdapter<String>adapter=newArrayAdapter<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");
Spinnerspinner=(Spinner)findViewById(R.id.spinner);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(newAdapterView.OnItemSelectedListener(){
@Override
publicvoidonItemSelected(AdapterView<?>adapterView,Viewview,intposition,longid){
Spinnerspinner=(Spinner)adapterView;
StringitemContent=(String)adapterView.getItemAtPosition(position);
}
@Override
publicvoidonNothingSelected(AdapterView<?>view){
Log.i(TAG,view.getClass().getName());
}
});
}
}
9、下拉列表框—采用javabean作为Adapter元素
很多时候显示在下拉列表框的值并不是希望得到的值,如果要做一个联系人下拉列表框,列表框列出的是联系人的姓名,因为姓名有可能相同,所以我们希望得到的值应该为该联系人的id,要实现这种需求我们需要自定义Adapter,当然自定义Adapter需要我们编写一小段代码,如果我们不想编写Adapter,又能实现我们的需求,那是最好不过的了。通过观察ArrayAdapter中getView(intposition,ViewconvertView,ViewGroupparent)的内部代码发现,如果为ArrayAdapter指定的实际泛型参数类型没有实现CharSequence(字符串)接口,将会调用该类型对象的toString()向下拉列表框输出显示值。利用这个特点我们可以重写javaBean的toString()向下拉列表框提供显示值。
界面设计:
<?xmlversion="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">
<Spinnerandroid:id="@+id/spinner"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
</LinearLayout>
代码处理:
publicclassSpinnerActivityextendsActivity{
privatestaticfinalStringTAG="SpinnerActivity";
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.spinner);
ArrayAdapter<Person>adapter=newArrayAdapter<Person>(this,android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
adapter.add(newPerson(12,"李明"));
adapter.add(newPerson(100,"李明"));
adapter.add(newPerson(62,"张天"));
Spinnerspinner=(Spinner)findViewById(R.id.spinner);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(newAdapterView.OnItemSelectedListener(){
@Override
publicvoidonItemSelected(AdapterView<?>adapterView,Viewview,intposition,longid){
Spinnerspinner=(Spinner)adapterView;
Personperson=(Person)adapterView.getItemAtPosition(position);
}
@Override
publicvoidonNothingSelected(AdapterView<?>view){
Log.i(TAG,view.getClass().getName());
}
});
}
}
Person.java:
publicclassPerson{
privateIntegerid;
privateStringname;
publicPerson(Integerid,Stringname){
this.id=id;
this.name=name;
}
publicIntegergetId(){
returnid;
}
publicvoidsetId(Integerid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
@Override
publicStringtoString(){
returnname;
}
}
10、下拉列表框--自定义选项界面样式
Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值
调用setOnItemSelectedListener()方法,处理下拉列表框被选择事件,把AdapterView.OnItemSelectedListener实例作为参数传入
主界面设计:
<?xmlversion="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">
<Spinnerandroid:id="@+id/spinner"
android:layout_height="wrap_content"
android:layout_width="fill_parent"/>
</LinearLayout>
下拉列表框每一项的界面样式:stylespinner.xml
<?xmlversion="1.0"encoding="utf-8"?>
<TextViewxmlns: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"
/>
代码处理:
publicclassSpinnerActivityextendsActivity{
privatestaticfinalStringTAG="SpinnerActivity";
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.spinner);
//第二个参数为layout文件在R文件的id,第三个参数为TextView在layout文件的id
ArrayAdapter<String>adapter=newArrayAdapter<String>(this,R.layout.stylespinner,R.id.contentTextView);
adapter.add("java");
adapter.add("dotNet");
adapter.add("php");
Spinnerspinner=(Spinner)findViewById(R.id.spinner);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(newAdapterView.OnItemSelectedListener(){
@Override
publicvoidonItemSelected(AdapterView<?>adapterView,Viewview,intposition,longid){
Spinnerspinner=(Spinner)adapterView;
StringitemContent=(String)adapterView.getItemAtPosition(position);
}
@Override
publicvoidonNothingSelected(AdapterView<?>view){
Log.i(TAG,view.getClass().getName());
}
});
}
}
11、拖动条(SeekBar)
SeekBar.getProgress()获取拖动条当前值
调用setOnSeekBarChangeListener()方法,处理拖动条值变化事件,把SeekBar.OnSeekBarChangeListener实例作为参数传入
主界面设计:
<?xmlversion="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"/>
<Buttonandroid:id="@+id/seekBarButton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="获取值"
/>
</LinearLayout>
代码处理:
publicclassSeekBarActivityextendsActivity{
privateSeekBarseekBar;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.seekbar);
seekBar=(SeekBar)findViewById(R.id.seekBar);
seekBar.setMax(100);//设置最大刻度
seekBar.setProgress(30);//设置当前刻度
seekBar.setOnSeekBarChangeListener(newSeekBar.OnSeekBarChangeListener(){
@Override
publicvoidonProgressChanged(SeekBarseekBar,intprogress,booleanfromTouch){
Log.v("onProgressChanged()",String.valueOf(progress)+","+String.valueOf(fromTouch));
}
@Override
publicvoidonStartTrackingTouch(SeekBarseekBar){//开始拖动
Log.v("onStartTrackingTouch()",String.valueOf(seekBar.getProgress()));
}
@Override
publicvoidonStopTrackingTouch(SeekBarseekBar){//结束拖动
Log.v("onStopTrackingTouch()",String.valueOf(seekBar.getProgress()));
}
});
Buttonbutton=(Button)this.findViewById(R.id.seekBarButton);
button.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
Toast.makeText(SeekBarActivity.this,String.valueOf(seekBar.getProgress()),1).show();
}
});
}
}
12、菜单(Menu)
重写Activity的onCreateOptionsMenu(Menumenu)方法,该方法用于创建选项菜单,在用户按下手机的“Menu”按钮时就会显示创建好的菜单,在onCreateOptionsMenu(Menumenu)方法内部可以调用Menu.add()方法实现菜单的添加。
重写Activity的onMenuItemSelected()方法,该方法用于处理菜单被选择事件
通过手机上提供的“MENU”按钮可以打开菜单,如果希望通过代码打开菜单,可以调用Activity的openOptionsMenu()方法。
publicclassMenuActivityextendsActivity{
privatestaticfinalStringTAG="MenuActivity";
privatestaticfinalintMENU_ADD=Menu.FIRST;
privatestaticfinalintMENU_UPDATE=Menu.FIRST+1;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.menu);
}
@Override
publicbooleanonCreateOptionsMenu(Menumenu){
menu.add(Menu.NONE,MENU_ADD,Menu.NONE,"添加");
menu.add(Menu.NONE,MENU_UPDATE,Menu.NONE,"更新");
returnsuper.onCreateOptionsMenu(menu);
}
@Override
publicbooleanonMenuItemSelected(intfeatureId,MenuItemitem){
switch(item.getItemId()){
caseMENU_ADD:
Log.i(TAG,"addwasselected");
returntrue;
caseMENU_UPDATE:
Log.i(TAG,"updatewasselected");
returntrue;
default:
returnsuper.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);//设置最大刻度
ProgressBar.setProgress(0);//设置进度条的当前刻度,如果进度条的最大刻度为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"/>
publicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String[]names={"老张","老方","老毕","李明","李丽","陈江","abc","acc"};
AutoCompleteTextViewnameText=(AutoCompleteTextView)this.findViewById(R.id.name);
ArrayAdapter<String>adapter=newArrayAdapter<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"/>
publicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String[]names={"老张","老方","老毕","李明","李丽","陈江","abc","acc"};
MultiAutoCompleteTextViewnameText=(MultiAutoCompleteTextView)this.findViewById(R.id.name);
ArrayAdapter<String>adapter=newArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,names);
nameText.setAdapter(adapter);
nameText.setTokenizer(newMultiAutoCompleteTextView.CommaTokenizer());}
16、android样式和主题(style&theme)
android中的样式和CSS样式作用相似,都是用于为界面元素定义显示风格,它是一个包含一个或者多个view控件属性的集合。如:需要定义字体的颜色和大小。
在CSS中是这样定义的:
<style>
.itcast{COLOR:#0000CC;font-size:18px;}
</style>
可以像这样使用上面的css样式:<divclass="itcast">传智播客</div>
在Android中可以这样定义样式:
在res/values/styles.xml文件中添加以下内容
<?xmlversion="1.0"encoding="utf-8"?>
<resources>
<stylename=“itcast”><!--为样式定义一个全局唯一的名字-->
<itemname=“android:textSize”>18px</item><!--name属性的值为使用了该样式的View控件的属性-->
<itemname="android:textColor">#0000CC</item>
</style>
</resources>
在layout文件中可以像下面这样使用上面的android样式:
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"....>
<TextViewstyle="@style/itcast"
...../>
</LinearLayout>
<style>元素中有一个parent属性。这个属性可以让当前样式继承一个父样式,并且具有父样式的值。当然,如果父样式的值不符合你的需求,你也可以对它进行修改,如下:
<?xmlversion="1.0"encoding="utf-8"?>
<resources>
<stylename="itcast">
<itemname="android:textSize">18px</item><!--name属性为样式要用在的View控件持有的属性-->
<itemname="android:textColor">#0000CC</item>
</style>
<stylename="subitcast"parent="@style/itcast">
<itemname="android:textColor">#FF0000</item>
</style>
</resources>
android中主题也是用于为应用定义显示风格,它的定义和样式的定义相同,如下:
<?xmlversion="1.0"encoding="utf-8"?>
<resources>
<stylename=“itcastTheme">
<itemname=“android:windowNoTitle”>true</item><!–没标题à
<itemname=“android:windowFullscreen”>?android:windowNoTitle</item><!–全屏显示à
</style>
</resources>
上面“?android:windowNoTitle”中的问号用于引用在当前主题中定义过的资源的值。下面代码显示在AndroidManifest.xml中如何为应用设置上面定义的主题:
<applicationandroid: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系统也定义了一些主题,例如:<activityandroid:theme=“@android:style/Theme.Dialog”>,该主题可以让Activity看起来像一个对话框,还有透明主题:@android:style/Theme.Translucent。如果需要查阅这些主题,可以在文档的referenceàandroid-->R.style中查看。
17、编码实现软件界面
Android除了可以使用xml实现软件界面,还可以通过编码方式实现软件的界面,而且在某种情况下只能采用编码方式实现软件的界面,例如:软件运行时需要根据运算结果决定显示某些内容。如果不是必须,建议使用xml,因为这样可以使应用遵守mvc设计模式,具有良好的软件分层结构。下面代码实现了如HelloWorld项目一样的软件界面:
publicclassMainActivityextendsActivity{
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
LinearLayoutlinearLayout=newLinearLayout(this);
LinearLayout.LayoutParamslayoutParams=newLinearLayout.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.FILL_PARENT);
TextViewtextView=newTextView(this);
textView.setText(R.string.hello);
textView.setId(34);
LinearLayout.LayoutParamstextParams=newLinearLayout.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文件
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDHTML4.01Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">
<title>Inserttitlehere</title>
<styletype="text/css">
A{
COLOR:#FFFFFF;TEXT-DECORATION:none
}
</style>
<scripttype="text/javascript">
functionshow(jsondata){
varjsonobjs=eval(jsondata);
vartable=document.getElementById("personTable");
for(vary=0;y<jsonobjs.length;y++){
vartr=table.insertRow(table.rows.length);//添加一行
//添加三列
vartd1=tr.insertCell(0);
vartd2=tr.insertCell(1);
td2.align="center";
vartd3=tr.insertCell(2);
//设置列内容和属性
td1.innerHTML=jsonobjs[y].id;
td2.innerHTML="<ahref='javascript:itcast.call(\"5554\")'>"+jsonobjs[y].name+"</a>";
td3.innerHTML=jsonobjs[y].phone;
}
}
</script>
</head>
<bodybgcolor="#000000"text="#FFFFFF"style="margin:0000"onload="javascript:itcast.personlist()">
<tableborder="0"width="100%"id="personTable"cellspacing="0">
<tr>
<tdwidth="15%">编号</td><tdalign="center">姓名</td><tdwidth="15%">电话</td>
</tr>
</table>
<ahref="javascript:window.location.reload()">刷新</a>
</body>
</html>
publicclassHtmlActivityextendsActivity{
privateWebViewwebView;
privateHandlerhandler=newHandler();
@Override
publicvoidonCreate(BundlesavedInstanceState){
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(newItcastJavaScript(),“itcast”);//addJavascriptInterface方法中要绑定的Java对象
webView.setWebChromeClient(newItcastWebClient());
webView.loadUrl("file:///android_asset/index.html");
}
privatefinalclassItcastJavaScript{
publicvoidpersonlist(){
webview.loadUrl("javascript:contactlist('"+getPersonJson()+"')");
}
publicvoidcall(finalStringphone){
startActivity(newIntent(Intent.ACTION_CALL,Uri.parse("tel:"+phone)));
}
publicstaticStringgetPersonJson(){//生成json字符串
try{
JSONObjectjsonObject=newJSONObject();
jsonObject.put("id",56);
try{
JSONObjectjsonObject=newJSONObject();
jsonObject.put("id",56);
jsonObject.put("name","老张");
jsonObject.put("phone","5556");
JSONObjectjsonObject2=newJSONObject();
jsonObject2.put("id",89);
jsonObject2.put("name","老方");
jsonObject2.put("phone","5558");
JSONArrayjsonArray=newJSONArray();
jsonArray.put(jsonObject);
jsonArray.put(jsonObject2);
returnjsonArray.toString();
}catch(JSONExceptione){
e.printStackTrace();
}
return"";
}
}
privatefinalclassItcastWebClientextendsWebChromeClient{
@Override
publicbooleanonJsAlert(WebViewview,Stringurl,Stringmessage,JsResultresult){
newAlertDialog.Builder(HtmlActivity.this)
.setTitle("提示信息")
.setMessage(message)
.setPositiveButton("确定",newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialoginterface,inti){}
}).show();
returntrue;
}
}
}
19、动画(Animation)
Android提供了2种动画:
1>Tween动画,通过对View的内容进行一系列的图形变换(包括平移、缩放、旋转、改变透明度)来实现动画效果。动画效果的定义可以采用XML来做也可以采用编码来做。Tween动画有4种类型:
>Frame动画,即顺序播放事先做好的图像,跟放胶片电影类似。开发步骤:
(1)把准备好的图片放进项目res/drawable下。
(2)在项目的res目录下创建文件夹anim,然后在anim文件夹下面定义动画XML文件,文件名称可以自定义。当然也可以采用编码方式定义动画效果(使用AnimationDrawable类)。
(3)为View控件绑定动画效果。调用代表动画的AnimationDrawable的start()方法开始动画。
本例要实现对ImageView对象进行渐变尺寸缩放动画效果
1>在项目的res目录下创建文件夹anim,然后在anim文件夹下面定义动画XML文件,文件名称可以自定义,如:scale.xml,内容如下:
<?xmlversion="1.0"encoding="utf-8"?>
<setxmlns:android="http://schemas.android.com/apk/res/android">
<scaleandroid: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>节点:
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns: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使用前面定义好的动画效果:
publicclassAnimationActivityextendsActivity{
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageViewimageView=(ImageView)this.findViewById(R.id.imageView);
//加载动画XML文件,生成动画指令
Animationanimation=AnimationUtils.loadAnimation(this,R.anim.scale);
//开始执行动画
imageView.startAnimation(animation);
}
}
备注:上面采用的是xml文件定义动画效果,作为代替,也可以采用编码方式实现。下面采用编码方式实现上述例子同样的效果:
publicclassAnimationActivityextendsActivity{
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageViewimageView=(ImageView)this.findViewById(R.id.imageView);
ScaleAnimationanimation=newScaleAnimation(0.0f,5f,0.0f,5f,
Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animation.setDuration(5000);//设置持续时间5秒
imageView.startAnimation(animation);
}
}
其他动画效果定义例子:
=================渐变透明度动画效果======================
<?xmlversion="1.0"encoding="utf-8"?>
<setxmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:fromAlpha="0.1"
android:toAlpha="1.0"
android:duration="3000"
/>
</set>
编码实现透明度动画效果:
publicclassAnimationActivityextendsActivity{
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageViewimageView=(ImageView)this.findViewById(R.id.imageView);
AlphaAnimationanimation=newAlphaAnimation(0.1,1.0);
animation.setDuration(5000);//设置持续时间5秒
imageView.startAnimation(animation);
}
}
=================画面位置移动动画效果======================
<?xmlversion="1.0"encoding="utf-8"?>
<setxmlns: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"
/>
<!--fromXDeltafromYDelta为动画起始时X和Y坐标上的位置
toXDeltatoYDelta为动画结束起始时X和Y坐标上的位置
-->
</set>
编码实现位置移动动画效果:
publicclassAnimationActivityextendsActivity{
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageViewimageView=(ImageView)this.findViewById(R.id.imageView);
TranslateAnimationanimation=newTranslateAnimation(0,120,0,120);
animation.setDuration(5000);//设置持续时间5秒
imageView.startAnimation(animation);
=================画面旋转动画效果======================
<?xmlversion="1.0"encoding="utf-8"?>
<setxmlns: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负数:逆时针旋转)
toDegrees属性为动画结束时物件旋转的角度可以大于360度
pivotX,pivotY为动画相对于物件的X、Y坐标的开始位.说明:以上两个属性值从0%-100%中取值,50%为物件的X或Y方向坐标上的中点位置
-->
</set>
编码实现:
RotateAnimationanimation=newRotateAnimation(0,-90,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animation.setDuration(500);
imageView.startAnimation(animation);
=====================Frame动画例子===============================
(1)把准备好的图片放进项目res/drawable下。
图片有:girl_1.gif,girl_2.gif,girl_3.gif
(2)在项目的res目录下创建文件夹anim,然后在anim文件夹下面定义动画XML文件,文件名称可以自定义,如:frame.xml。
<?xmlversion="1.0"encoding="utf-8"?>
<animation-listxmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<itemandroid:drawable="@drawable/girl_1"android:duration="200"/>
<itemandroid:drawable="@drawable/girl_2"android:duration="200"/>
<itemandroid:drawable="@drawable/girl_3"android:duration="200"/>
</animation-list>
上面的XML就定义了一个Frame动画,其包含3帧动画,3帧动画中分别应用了drawable中的3张图片:girl_1.gif,girl_2.gif,girl_3.gif,每帧动画持续200毫秒。android:oneshot属性如果为true,表示动画只播放一次停止在最后一帧上,如果设置为false表示动画循环播放。
(3)为View控件绑定动画效果,调用代表动画的AnimationDrawable的start()方法开始动画。
publicclassFrameActivityextendsActivity{
privateAnimationDrawableanimationDrawable;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageViewimageView=(ImageView)this.findViewById(R.id.imageView);
imageView.setBackgroundResource(R.anim.frame);
animationDrawable=(AnimationDrawable)imageView.getBackground();
}
@Override
publicbooleanonTouchEvent(MotionEventevent){
if(event.getAction()==MotionEvent.ACTION_DOWN){//按下
animationDrawable.start();
returntrue;
}
returnsuper.onTouchEvent(event);
}
}
有一点需要强调的是:启动Frame动画的代码animationDrawable.start();不能应用在OnCreate()方法中,因为在OnCreate()中AnimationDrawable还没有完全的与ImageView绑定。在OnCreate()中启动动画,只能看到第一张图片。这里在触摸事件中实现的。
20、传感器的使用
传感器类型:方向、加速度(重力)、光线、磁场、距离(临近性)、温度等。
(1)方向传感器:Sensor.TYPE_ORIENTATION
(2)加速度(重力)传感器:Sensor.TYPE_ACCELEROMETER
(3)光线传感器:Sensor.TYPE_LIGHT
(4)磁场传感器:Sensor.TYPE_MAGNETIC_FIELD
(5)距离(临近性)传感器:Sensor.TYPE_PROXIMITY
(6)温度传感器:Sensor.TYPE_TEMPERATURE
//获取某种类型的感应器
Sensorsensor=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
一般对于屏幕方向自动旋转使用,相对节省电能和逻辑处理,一般游戏开发中我们不使用。
下面介绍如何获取加速度(重力)传感器和方向传感器的测量值:
publicclassMainActivityextendsActivity{
privateTextViewaccelerometer;
privateTextVieworientation;
privateSensorManagersensorManager;
@Override
publicvoidonCreate(BundlesavedInstanceState){
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
protectedvoidonResume(){
Sensorsensor=sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);//获取重力加速度传感器
sensorManager.registerListener(listener,sensor,SensorManager.SENSOR_DELAY_GAME);
Sensorsensor1=sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);//获取方向传感器
sensorManager.registerListener(listener,sensor1,SensorManager.SENSOR_DELAY_GAME);
super.onResume();
}
@Override
protectedvoidonPause(){
sensorManager.unregisterListener(listener);//注消所有传感器监听
super.onPause();
}
privateSensorEventListenerlistener=newSensorEventListener(){
@Override
publicvoidonSensorChanged(SensorEventevent){//当传感器的值发生变化
floatx=event.values[SensorManager.DATA_X];
floaty=event.values[SensorManager.DATA_Y];
floatz=event.values[SensorManager.DATA_Z];
switch(event.sensor.getType()){
caseSensor.TYPE_ACCELEROMETER:
accelerometer.setText("AccelerometerSensor:"+x+","+y+","+z);
break;
caseSensor.TYPE_ORIENTATION:
/*x该值表示方位,0代表北(North);90代表东(East);180代表南(South);270代表西(West)
如果x值正好是这4个值之一,并且手机是水平放置,手机的顶部对准的方向就是该值代表的方向。
y值表示倾斜度,或手机翘起的程度。当手机绕着X轴倾斜时该值发生变化。y值的取值范围是-180≤y值≤180。
假设将手机屏幕朝上水平放在桌子上,这时如果桌子是完全水平的,y值应该是0(由于很少有桌子是绝对水平的,
因此,该值很可能不为0,但一般都是-5和5之间的某个值)。这时从手机顶部开始抬起,直到将手机沿X轴旋转180度(屏幕向下水平放在桌面上)。
在这个旋转过程中,y值会在0到-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,
直到等于-180。如果从手机底部开始抬起,直到将手机沿X轴旋转180度,这时y值会在0到180之间变化。
也就是y值会逐渐增大,直到等于180。可以利用y值和z值来测量桌子等物体的倾斜度。
y值表示倾斜度,或手机翘起的程度。当手机绕着X轴倾斜时该值发生变化。y值的取值范围是-180≤y值≤180。
假设将手机屏幕朝上水平放在桌子上,这时如果桌子是完全水平的,y值应该是0(由于很少有桌子是绝对水平的,
因此,该值很可能不为0,但一般都是-5和5之间的某个值)。这时从手机顶部开始抬起,直到将手机沿X轴旋转180度(屏幕向下水平放在桌面上)。
在这个旋转过程中,y值会在0到-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,
直到等于-180。如果从手机底部开始抬起,直到将手机沿X轴旋转180度,这时y值会在0到180之间变化。
也就是y值会逐渐增大,直到等于180。可以利用y值和z值来测量桌子等物体的倾斜度。
因此,该值很可能不为0,但一般都是-5和5之间的某个值)。这时从手机顶部开始抬起,直到将手机沿X轴旋转180度(屏幕向下水平放在桌面上)。
在这个旋转过程中,y值会在0到-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,
直到等于-180。如果从手机底部开始抬起,直到将手机沿X轴旋转180度,这时y值会在0到180之间变化。
也就是y值会逐渐增大,直到等于180。可以利用y值和z值来测量桌子等物体的倾斜度。
z值表示手机沿着Y轴的滚动角度。表示手机沿着Y轴的滚动角度。取值范围是-90≤z值≤90。
假设将手机屏幕朝上水平放在桌面上,这时如果桌面是平的,z值应为0。将手机左侧逐渐抬起时,
z值逐渐变小,直到手机垂直于桌面放置,这时z值是-90。将手机右侧逐渐抬起时,z值逐渐增大,
直到手机垂直于桌面放置,这时z值是90。在垂直位置时继续向右或向左滚动,z值会继续在-90至90之间变化。
*/
orientation.setText("OrientationSensor:"+x+","+y+","+z);
break;
}
}
@Override
publicvoidonAccuracyChanged(Sensorsensor,intaccuracy){//当传感器的精度变化时
}
};
}
21、NinePatch图片
NinePatch是一种很有用的PNG图片格式,它可以在特定区域随文字大小进行缩放。如下:
从上图可以看到,背景图片的中间区域会随着文字的大小进行缩放。背景图片是一张NinePatch图片。NinePatch图片可以使用android自带的draw9patch工具来制作,该工具在SDK安装路径的tools目录下。执行该工具,然后点击“File”->“open9-path”打开一张用于制作NinePatch图片的原来图片。在画布的上方和左方的边上画线指定缩放区域,
勾选“Showpatches”可显示画定的区域,绿色
为固定大小区域,红色为缩放区域,文字会摆放在红色
区域。制作完后,点击“File”à“save9-path”保存
图片,draw9patch工具会自动为图片加上*.9.png后缀。
把制作好的图片拷贝进项目的res/drawable目录,然后
编写代码。如下:
<TextViewandroid: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);
第三种方法:强制关闭与该包有关联的一切执行
ActivityManagermanager=(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
manager.restartPackage(getPackageName());
<uses-permissionandroid:name="android.permission.RESTART_PACKAGES"/>
23、判断SIM卡属于哪个移动运营商
在文件AndroidManifest.xml中添加权限
<uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>
第一种方法:
获取手机的IMSI码,并判断是中国移动\中国联通\中国电信
TelephonyManagertelManager=(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
/**获取SIM卡的IMSI码
*SIM卡唯一标识:IMSI国际移动用户识别码(IMSI:InternationalMobileSubscriberIdentificationNumber)是区别移动用户的标志,
*储存在SIM卡中,可用于区别移动用户的有效信息。IMSI由MCC、MNC、MSIN组成,其中MCC为移动国家号码,由3位数字组成,
*唯一地识别移动客户所属的国家,我国为460;MNC为网络id,由2位数字组成,
*用于识别移动客户所归属的移动网络,中国移动为00,中国联通为01,中国电信为03;MSIN为移动客户识别码,采用等长11位数字构成。
*唯一地识别国内GSM移动通信网中移动客户。所以要区分是移动还是联通,只需取得SIM卡中的MNC字段即可
*/
Stringimsi=telManager.getSubscriberId();
if(imsi!=null){
if(imsi.startsWith("46000")||imsi.startsWith("46002")){//因为移动网络编号46000下的IMSI已经用完,所以虚拟了一个46002编号,134/159号段使用了此编号
//中国移动
}elseif(imsi.startsWith("46001")){
//中国联通
}elseif(imsi.startsWith("46003")){
//中国电信
}
}
第二种方法
TelephonyManagertelManager=(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
Stringoperator=telManager.getSimOperator();
if(operator!=null){
if(operator.equals("46000")||operator.equals("46002")){
//中国移动
}elseif(operator.equals("46001")){
//中国联通
}elseif(operator.equals("46003")){
//中国电信
}
}
24、从SIM卡中获取联系人信息
Uriuri=Uri.parse("content://icc/adn");
String[]projection={"_id","name","number"};
Cursorcursor=managedQuery(uri,projection,null,null,"name");
if(cursor!=null){
while(cursor.moveToNext()){
Stringname=cursor.getString(cursor.getColumnIndex("name"));
Stringphone=cursor.getString(cursor.getColumnIndex("number"));
}
}
在文件AndroidManifest.xml中添加权限
<uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>
Android系统内部通过Contentprovider对外共享Sim卡存放的联系人等信息,你可以通过操作Contentprovider来实现Sim卡信息的添删改查操作。
packagecom.android.internal.telephony;
importandroid.content.ContentProvider;
importandroid.content.UriMatcher;
importandroid.content.ContentValues;
importcom.android.internal.database.ArrayListCursor;
importandroid.database.Cursor;
importandroid.net.Uri;
importandroid.os.SystemProperties;
importandroid.os.RemoteException;
importandroid.os.ServiceManager;
importandroid.text.TextUtils;
importandroid.util.Log;
importjava.util.ArrayList;
importjava.util.List;
importcom.android.internal.telephony.IccConstants;
importcom.android.internal.telephony.AdnRecord;
importcom.android.internal.telephony.IIccPhoneBook;
publicclassIccProviderextendsContentProvider{
privatestaticfinalStringTAG="IccProvider";
privatestaticfinalbooleanDBG=false;
privatestaticfinalString[]ADDRESS_BOOK_COLUMN_NAMES=newString[]{
"name",
"number"
};
privatestaticfinalintADN=1;
privatestaticfinalintFDN=2;
privatestaticfinalintSDN=3;
privatestaticfinalStringSTR_TAG="tag";
privatestaticfinalStringSTR_NUMBER="number";
privatestaticfinalStringSTR_PIN2="pin2";
privatestaticfinalUriMatcherURL_MATCHER=newUriMatcher(UriMatcher.NO_MATCH);
static{
URL_MATCHER.addURI("icc","adn",ADN);
URL_MATCHER.addURI("icc","fdn",FDN);
URL_MATCHER.addURI("icc","sdn",SDN);
}
privatebooleanmSimulator;
@Override
publicbooleanonCreate(){
Stringdevice=SystemProperties.get("ro.product.device");
if(!TextUtils.isEmpty(device)){
mSimulator=false;
}else{
//simulator
mSimulator=true;
}
returntrue;
}
@Override
publicCursorquery(Uriurl,String[]projection,Stringselection,
String[]selectionArgs,Stringsort){
ArrayList<ArrayList>results;
if(!mSimulator){
switch(URL_MATCHER.match(url)){
caseADN:
results=loadFromEf(IccConstants.EF_ADN);
break;
caseFDN:
results=loadFromEf(IccConstants.EF_FDN);
break;
caseSDN:
results=loadFromEf(IccConstants.EF_SDN);
break;
default:
thrownewIllegalArgumentException("UnknownURL"+url);
}
}else{
//Fakeupsomedataforthesimulator
results=newArrayList<ArrayList>(4);
ArrayList<String>contact;
contact=newArrayList<String>();
contact.add("RonStevens/H");
contact.add("512-555-5038");
results.add(contact);
contact=newArrayList<String>();
contact.add("RonStevens/M");
contact.add("512-555-8305");
results.add(contact);
results=newArrayList<ArrayList>(4);
ArrayList<String>contact;
contact=newArrayList<String>();
contact.add("RonStevens/H");
contact.add("512-555-5038");
results.add(contact);
contact=newArrayList<String>();
contact.add("RonStevens/M");
contact.add("512-555-8305");
results.add(contact);
contact.add("512-555-5038");
results.add(contact);
contact=newArrayList<String>();
contact.add("RonStevens/M");
contact.add("512-555-8305");
results.add(contact);
contact=newArrayList<String>();
contact.add("MelissaOwens");
contact.add("512-555-8305");
results.add(contact);
contact=newArrayList<String>();
contact.add("DirectoryAssistence");
contact.add("411");
results.add(contact);
}
returnnewArrayListCursor(ADDRESS_BOOK_COLUMN_NAMES,results);
}
@Override
publicStringgetType(Uriurl){
switch(URL_MATCHER.match(url)){
caseADN:
caseFDN:
caseSDN:
return"vnd.android.cursor.dir/sim-contact";
default:
thrownewIllegalArgumentException("UnknownURL"+url);
}
}
@Override
publicUriinsert(Uriurl,ContentValuesinitialValues){
UriresultUri;
intefType;
Stringpin2=null;
if(DBG)log("insert");
intmatch=URL_MATCHER.match(url);
switch(match){
caseADN:
efType=IccConstants.EF_ADN;
break;
caseFDN:
efType=IccConstants.EF_FDN;
pin2=initialValues.getAsString("pin2");
break;
default:
thrownewUnsupportedOperationException(
"CannotinsertintoURL:"+url);
}
Stringtag=initialValues.getAsString("tag");
Stringnumber=initialValues.getAsString("number");
booleansuccess=addIccRecordToEf(efType,tag,number,pin2);
if(!success){
returnnull;
}
StringBuilderbuf=newStringBuilder("content://im/");
switch(match){
caseADN:
buf.append("adn/");
break;
caseFDN:
buf.append("fdn/");
break;
}
//TODO:weneedtofindouttherowIdforthenewlyaddedrecord
buf.append(0);
resultUri=Uri.parse(buf.toString());
/*
//notifyinterestedpartiesthataninsertionhappened
getContext().getContentResolver().notifyInsert(
resultUri,rowID,null);
*/
returnresultUri;
}
privateStringnormalizeValue(StringinVal){
intlen=inVal.length();
StringretVal=inVal;
if(inVal.charAt(0)=='\''&&inVal.charAt(len-1)=='\''){
retVal=inVal.substring(1,len-1);
}
returnretVal;
}
@Override
publicintdelete(Uriurl,Stringwhere,String[]whereArgs){
intefType;
if(DBG)log("delete");
intmatch=URL_MATCHER.match(url);
switch(match){
caseADN:
efType=IccConstants.EF_ADN;
break;
caseFDN:
efType=IccConstants.EF_FDN;
break;
default:
thrownewUnsupportedOperationException(
"CannotinsertintoURL:"+url);
}
//parsewhereclause
Stringtag=null;
Stringnumber=null;
Stringpin2=null;
String[]tokens=where.split("AND");
intn=tokens.length;
while(--n>=0){
Stringparam=tokens[n];
if(DBG)log("parsing'"+param+"'");
String[]pair=param.split("=");
if(pair.length!=2){
Log.e(TAG,"resolve:badwhereClauseparameter:"+param);
continue;
}
Stringkey=pair[0].trim();
Stringval=pair[1].trim();
if(STR_TAG.equals(key)){
tag=normalizeValue(val);
}elseif(STR_NUMBER.equals(key)){
number=normalizeValue(val);
}elseif(STR_PIN2.equals(key)){
pin2=normalizeValue(val);
}
}
if(TextUtils.isEmpty(tag)){
return0;
}
if(efType==FDN&&TextUtils.isEmpty(pin2)){
return0;
}
booleansuccess=deleteIccRecordFromEf(efType,tag,number,pin2);
if(!success){
return0;
}
return1;
}
@Override
publicintupdate(Uriurl,ContentValuesvalues,Stringwhere,String[]whereArgs){
intefType;
Stringpin2=null;
if(DBG)log("update");
intmatch=URL_MATCHER.match(url);
switch(match){
caseADN:
efType=IccConstants.EF_ADN;
break;
caseFDN:
efType=IccConstants.EF_FDN;
pin2=values.getAsString("pin2");
break;
default:
thrownewUnsupportedOperationException(
"CannotinsertintoURL:"+url);
}
Stringtag=values.getAsString("tag");
Stringnumber=values.getAsString("number");
StringnewTag=values.getAsString("newTag");
StringnewNumber=values.getAsString("newNumber");
booleansuccess=updateIccRecordInEf(efType,tag,number,
newTag,newNumber,pin2);
if(!success){
return0;
}
return1;
}
privateArrayList<ArrayList>loadFromEf(intefType){
ArrayList<ArrayList>results=newArrayList<ArrayList>();
List<AdnRecord>adnRecords=null;
if(DBG)log("loadFromEf:efType="+efType);
try{
IIccPhoneBookiccIpb=IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if(iccIpb!=null){
adnRecords=iccIpb.getAdnRecordsInEf(efType);
}
}catch(RemoteExceptionex){
//ignoreit
}catch(SecurityExceptionex){
if(DBG)log(ex.toString());
}
if(adnRecords!=null){
//Loadtheresults
intN=adnRecords.size();
if(DBG)log("adnRecords.size="+N);
for(inti=0;i<N;i++){
loadRecord(adnRecords.get(i),results);
}
}else{
//Noresultstoload
Log.w(TAG,"CannotloadADNrecords");
results.clear();
}
if(DBG)log("loadFromEf:returnresults");
returnresults;
}
privateboolean
addIccRecordToEf(intefType,Stringname,Stringnumber,Stringpin2){
if(DBG)log("addIccRecordToEf:efType="+efType+",name="+name+
",number="+number);
booleansuccess=false;
//TODO:doweneedtocallgetAdnRecordsInEf()beforecalling
//updateAdnRecordsInEfBySearch()?Inanycase,wewillleave
//theUIlevellogictofillthatprereqifnecessary.But
//hopefully,wecanremovethisrequirement.
try{
IIccPhoneBookiccIpb=IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if(iccIpb!=null){
success=iccIpb.updateAdnRecordsInEfBySearch(efType,"","",
name,number,pin2);
}
}catch(RemoteExceptionex){
//ignoreit
}catch(SecurityExceptionex){
if(DBG)log(ex.toString());
}
if(DBG)log("addIccRecordToEf:"+success);
returnsuccess;
}
privateboolean
updateIccRecordInEf(intefType,StringoldName,StringoldNumber,
StringnewName,StringnewNumber,Stringpin2){
if(DBG)log("updateIccRecordInEf:efType="+efType+
",oldname="+oldName+",oldnumber="+oldNumber+
",newname="+newName+",newnumber="+newNumber);
booleansuccess=false;
try{
IIccPhoneBookiccIpb=IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if(iccIpb!=null){
success=iccIpb.updateAdnRecordsInEfBySearch(efType,
oldName,oldNumber,newName,newNumber,pin2);
}
}catch(RemoteExceptionex){
//ignoreit
}catch(SecurityExceptionex){
if(DBG)log(ex.toString());
}
if(DBG)log("updateIccRecordInEf:"+success);
returnsuccess;
}
privatebooleandeleteIccRecordFromEf(intefType,Stringname,Stringnumber,Stringpin2){
if(DBG)log("deleteIccRecordFromEf:efType="+efType+
",name="+name+",number="+number+",pin2="+pin2);
booleansuccess=false;
try{
IIccPhoneBookiccIpb=IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if(iccIpb!=null){
success=iccIpb.updateAdnRecordsInEfBySearch(efType,
name,number,"","",pin2);
}
}catch(RemoteExceptionex){
//ignoreit
}catch(SecurityExceptionex){
if(DBG)log(ex.toString());
}
if(DBG)log("deleteIccRecordFromEf:"+success);
returnsuccess;
}
name,number,"","",pin2);
}
}catch(RemoteExceptionex){
//ignoreit
}catch(SecurityExceptionex){
if(DBG)log(ex.toString());
}
if(DBG)log("deleteIccRecordFromEf:"+success);
returnsuccess;
}
name,number,"","",pin2);
}
}catch(RemoteExceptionex){
//ignoreit
}catch(SecurityExceptionex){
if(DBG)log(ex.toString());
}
if(DBG)log("deleteIccRecordFromEf:"+success);
returnsuccess;
}
25、删除呼叫记录
在文件AndroidManifest.xml中添加权限
<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>
<uses-permissionandroid:name="android.permission.WRITE_CONTACTS"/>
负责存放呼叫记录的内容提供者源码在ContactsProvider项目下:
源码路径:
com\android\providers\contacts\CallLogProvider.java
使用到的数据库在:
/data/data/com.android.providers.contacts/databases/contacts2.db
表名:calls
呼叫记录有三种类型:
来电:CallLog.Calls.INCOMING_TYPE(常量值:1)
外拔:CallLog.Calls.OUTGOING_TYPE(常量值:2)
未接:CallLog.Calls.MISSED_TYPE(常量值:3)
删除指定号码的来电或未接呼叫记录:
IncomingCallLogContentObserverobserver=newIncomingCallLogContentObserver(newHandler());
observer.setNumber("5554");
getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI,true,observer);
privateclassIncomingCallLogContentObserverextendsContentObserver{
privateStringnumber;
publicIncomingCallLogContentObserver(Handlerhandler){
super(handler);
}
publicvoidsetNumber(Stringnumber){
this.number=number;
}
publicvoidonChange(booleanparamBoolean){
ContentResolvercontentResolver=getContentResolver();
if(number!=null){
UrilocalUri=CallLog.Calls.CONTENT_URI;
Cursorcursor=contentResolver.query(localUri,newString[]{"_id"},"number=?AND(type=1ORtype=3)",
newString[]{number},"_iddesclimit1");
if(cursor.moveToFirst()){
contentResolver.delete(localUri,"_id=?",newString[]{cursor.getString(0)});
}
cursor.close();
}
contentResolver.unregisterContentObserver(this);
}
}
26、在应用中安装其他程序
首先需要AndroidManifest.xml中加入安装程序权限:
<!--安装程序权限-->
<uses-permissionandroid:name="android.permission.INSTALL_PACKAGES"/>
第二步把安装程序添加进SDCard。如把文件名为”sogouinput_android_1.40_sweb.apk.zip”的sogou拼音输入法安装文件放进SDCard。可以点击下面按钮:
第三步在程序中添加以下代码:
Intentintent=newIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(newFile(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命令:apktoold-sc:\soft\xxx.apkc:\soft\source。
命令格式:apktoold[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.batc:\soft\source\classes.dexc:\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的方式来编译的
更多相关文章
- Android(安卓)通过WebView 调用Js sqlite数据库
- 初学Android,五大布局对象(六)
- Test
- Android(安卓)UI设计的三种常见布局(LinearLayout、RelativeLayou
- Android(安卓)11适配指南之Toast解析
- android 全屏 webview 加载的h5的输入框,被键盘遮挡的解决
- Android——GSON解析JSON
- android APP隐私政策弹框的实现代码实例
- android手机打电话代码分析