android学习笔记2

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);// 第一个参数为自定义的通知唯一标识


对话框通知(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() 这些方法,他们的返回值都是当前对话框对象。



创建带单选项列表的对话框
下面代码将打开一个如右上图所示的选项列表对话框:
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() 的第二个参数是设置默认选项,
选项索引从 0 开始, -1 代表不选择任何选项。





创建带多选项列表的对话框
下面代码将打开一个如右下图所示的多选项列表对话框:
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();//显示对话框



进度对话框(ProgressDialog)
效果图 :
l 使用代码 ProgressDialog.show(ProgressDialogActivity.this, " 请稍等 ", " 数据正在加载中 ...", true); 创建并显示一个 进度对话框。 
l 调用 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 {
Thread.sleep(5*1000);// 假设这项工作需要 5 秒才能完成
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();
        }
   };
}


单选框(RadioButton)

效果图:
要完成单选框显示,我们需要使用到RadioGroupRadioButton(单选框)RadioGroup用于对单选框进行分组,相同组内的单选框只有一个单选框能被选中。(例子代码请见下方备注栏)
l RadioGroup.check(R.id.dotNet);id名为dotNet的单选框设置成选中状态。
l(RadioButton) findViewById(radioGroup.getCheckedRadioButtonId());//获取被选中的单选框。
lRadioButton.getText();//获取单选框的值
l调用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()));
            }
        });
}

多选框(CheckBox)
效果图 :
每个多选框都是独立的,可以通过迭代所有多选框,然后根据其状态是否被选中再获取其值。
l   CheckBox.setChecked(true);// 设置成选中状态。
l   CheckBox.getText();// 获取多选框的值
l   调用 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());// 输出单选框的值
}
    };
}



下拉列表框(Spinner)
效果图:
l Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值
l 调用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());
}
        });
    }
}


下拉列表框采用javabean作为Adapter元素
效果图 :
l   很多时候显示在下拉列表框的值并不是希望得到的值,如果要做一个联系人下拉列表框,列表框列出的是联系 人的姓名,因为姓名有可能相同,所以我们希望得到的值应该为该联系人的 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;
}
}

下拉列表框--自定义选项界面样式
效果图:
l Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值
l 调用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());
}
        });
    }
}


拖动条(SeekBar)

效果图:
l SeekBar.getProgress()获取拖动条当前值
l 调用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();
}
         });
    }
}


菜单(Menu)
效果图 :
l 重写 Activity onCreateOptionsMenu(Menu menu) 方法,该方法用于创建选项菜单,在用户按下手机的 Menu” 按钮时就会显示创建好的菜单,在 onCreateOptionsMenu(Menu menu) 方法内部可以调用 Menu.add() 方法实现菜 单的添加。
l 重写 Activity onMenuItemSelected() 方法,该方法用于处理菜单被选择事件
l
通过手机上提供的 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);
  }
    }
}

进度条(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 ,进度条将 进行到一半。



输入内容自动完成文本框(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);
}



多次输入-内容自动完成文本框(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());}



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  中查看。


编码实现软件界面

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);      
    }

使用网页开发软件界面

因为 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" οnlοad="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);
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;
}
    }
}




动画(Animation
动画的类型
Xml定义动画使用的配置节点
编码定义动画使用的类
渐变透明度动画效果
<alpha/>
AlphaAnimation
渐变尺寸缩放动画效果
<scale/>
ScaleAnimation
画面位置移动动画效果
<translate/>
TranslateAnimation
画面旋转动画效果
<rotate/>
RotateAnimation

Android 提供了 2 种动画:
1> Tween 动画 ,通过对 View  的内容进行一系列的图形变换 ( 包括平移、缩放、旋转、改变透明度 ) 来实现动画效 果。动画效果的定义可以采用 XML 来做也可以采用编码来做。 Tween 动画有 4 种类型:
2> Frame 动画 ,即顺序播放事先做好的图像,跟放胶片电影类似。开发步骤:
1 )把准备好的图片放进项目 res/ drawable 下。
2 )在项目的 res 目录下创建文件夹 anim ,然后在 anim 文件夹下面定义动画 XML 文件,文件名称可以自定义。当 然也可以采用编码方式定义动画效果(使用 AnimationDrawable 类)。
3 )为 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 (长整型) 属性为动画持续时间 。说明 :    时间以毫秒为单位
fillAfter (布尔型) 属性当设置为 true ,该动画转化在动画结束后被应用
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);
animation.setDuration(5000); // 设置持续时间 5
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);
animation.setDuration(5000); // 设置持续时间 5
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);
animation.setDuration(5000); // 设置持续时间 5
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 负数 : 逆时针旋转 )
toDegrees 属性为动画结束时物件旋转的角度 可以大于 360
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 动画例子    ===============================
1 )把准备好的图片放进项目 res/ drawable 下。
    图片有: girl_1.gif, girl_2.gif, girl_3.gif
2 )在项目的 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>
上面的 XML 就定义了一个 Frame 动画,其包含 3 帧动画, 3 帧动画中分别应用了 drawable 中的 3 张图片: girl_1.gif, girl_2.gif, girl_3.gif ,每帧动画持续 200 秒。 android:oneshot 属性如果为 true ,表示动画只播放一次停止在最后一帧上,如果设置为 false 表示动画循环播放。
3 )为 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() 中启动动画,只能看到第一张图片。这里在触摸事件中实现的。

传感器的使用
传感器类型:方向、加速度 ( 重力 ) 、光线、磁场、距离 ( 临近性 ) 、温度等。
方向传感器:     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:
/*x该值表示方位,0代表北(North);90代表东(East);180表南(South);270代表西(West
  如果x值正好是这4个值之一,并且手机是水平放置,手机的顶部对准的方向就是该值代表的方向。
y值表示倾斜度,或手机翘起的程度。当手机绕着X轴倾斜时该值发生变化。y值的取值范围是-180y180
假设将手机屏幕朝上水平放在桌子上,这时如果桌子是完全水平的,y值应该是0(由于很少有桌子是绝对水平的,
因此,该值很可能不为0,但一般都是-55之间的某个值)。这时从手机顶部开始抬起,直到将手机沿X轴旋转180度(屏幕向下水平放在桌面上)。
在这个旋转过程中,y值会在0-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,
直到等于-180。如果从手机底部开始抬起,直到将手机沿X轴旋转180度,这时y值会在0180之间变化。
也就是y值会逐渐增大,直到等于180。可以利用y值和z值来测量桌子等物体的倾斜度。
z值表示手机沿着Y轴的滚动角度。表示手机沿着Y轴的滚动角度。取值范围是-90z值≤90
假设将手机屏幕朝上水平放在桌面上,这时如果桌面是平的,z应为0。将手机左侧逐渐抬起时,
z值逐渐变小,直到手机垂直于桌面放置,这时z值是-90。将手机右侧逐渐抬起时,z值逐渐增大,
直到手机垂直于桌面放置,这时z值是90。在垂直位置时继续向右或向左滚动,z值会继续在-9090之间变化。
*/
orientation.setText("Orientation Sensor: " + x + ", " + y + ", " + z);
break;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {//当传感器的精度变化时
}
};
}

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" />




关闭应用

当应用不再使用时,通常需要关闭应用,可以使用以下三种方法关闭 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" />


判断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 )是区别移动用户的标志,
          储存在 SIM 卡中,可用于区别移动用户的有效信息。 IMSI MCC MNC MSIN 组成,其中 MCC 为移动国家号码,由 3 位数字组成,
          唯一地识别移动客户所属的国家,我国为 460 MNC 为网络 id ,由 2 位数字组成,
          用于识别移动客户所归属的移动网络,中国移动为 00 ,中国联通为 01, 中国电信为 03 MSIN 移动客户识别码,采用等长 11 位数字构成。
          唯一地识别国内 GSM 移动通信网中移动客户。所以要区分是移动还是联通,只需取得 SIM 卡中 MNC 字段即可
         * /
        String imsi = telManager.getSubscriberId();
 if(imsi!=null){
        if(imsi.startsWith("46000") || imsi.startsWith("46002")){// 因为移动网络编号 46000 下的 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")){
         // 中国电信
         }
}

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);
            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;
    }
    /**
     * Loads an AdnRecord into an ArrayList. Must be called with mLock held.
     *
     * @param record the ADN record to load from
     * @param results the array list to put the results in
     */
    private void loadRecord(AdnRecord record,
            ArrayList<ArrayList> results) {
        if (!record.isEmpty()) {
            ArrayList<String> contact = new ArrayList<String>(2);
            String alphaTag = record.getAlphaTag();
            String number = record.getNumber();
            if (DBG) log("loadRecord: " + alphaTag + ", " + number);
            contact.add(alphaTag);
            contact.add(number);
            results.add(contact);
        }
    }
    private void log(String msg) {
        Log.d(TAG, "[IccProvider] " + msg);
    }
}


删除呼叫记录
在文件 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
呼叫记录有三种类型:
来电: CallLog.Calls. INCOMING_TYPE  (常量值: 1
外拔: CallLog.Calls. OUTGOING_TYPE (常量值: 2
未接: CallLog.Calls. MISSED_TYPE (常量值: 3

删除指定号码的来电或未接呼叫记录:
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);
        }
}


在应用中安装其他程序

 

如何反编绎APK文件

l 安装 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 选项代表不解码源文件。
l 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 文件。
l 安装 jd-gui 工具,该工具可以把 jar 文件反编译成 Java 源文件
下载地址: http://java.decompiler.free.fr/jd-gui/downloads/jd-gui-0.3.3.windows.zip
运行该软件,直接打开 classes.dex.dex2jar.jar 文件即可看到 java 源代码。

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值