Android核心基础(十一)

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

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

 

//获取通知管理器

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

int icon = android.R.drawable.stat_notify_chat;

long when = System.currentTimeMillis();

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

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

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

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

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

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

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

 

2、对话框通知(Dialog Notification)

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

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

new AlertDialog.Builder(context)

.setTitle("java培训")

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

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

.setPositiveButton("确认",

new DialogInterface.OnClickListener(){

public void onClick(DialogInterface dialoginterface, int i){

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

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

        startActivity(intent);

    }

})

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

           public void onClick(DialogInterface dialog, int id) {

                dialog.cancel();

           }

    })

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

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

 

 

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

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

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

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

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

    public void onClick(DialogInterface dialog, int item) {

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

Toast.LENGTH_SHORT).show();

    }

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

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

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

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

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

 public void onClick(DialogInterface dialog, int item) {

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

Toast.LENGTH_SHORT).show();

        dialog.cancel();

  }

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

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

选项索引从0开始,-1代表不选择任何选项。

 

 

 

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

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

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

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

.setTitle("选择语言")

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

@Override

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

if(isChecked){

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

Toast.LENGTH_SHORT).show();

}

}

})

.setPositiveButton("确认",

new DialogInterface.OnClickListener(){

public void onClick(DialogInterface dialoginterface, int i){

dialoginterface.dismiss();

 }

})

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

 

 

 

5进度对话框(ProgressDialog)

 

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

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

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

     ProgressDialog.STYLE_HORIZONTAL 横向进度条风格

 

public class ProgressDialogActivity extends Activity {

private ProgressDialog progressDialog;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.menu); 

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

        new Thread(new Runnable(){

        @Override

        public void run() {

           try {

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

        }

   };

}

6、单选框(RadioButton)

 

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

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

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

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

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

 

界面设计:

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

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

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    >

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

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

  android:layout_width="wrap_content"

  android:layout_height="wrap_content">

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

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="java" />

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

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="dotNet" />

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

  android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="PHP" />

</RadioGroup>

</LinearLayout>

处理程序:

public void onCreate(Bundle savedInstanceState) {

       ......

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

        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {

            public void onCheckedChanged(RadioGroup group, int checkedId) {

                RadioButton radioButton = (RadioButton) findViewById(checkedId);

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

            }

      });

}

7、多选框(CheckBox)

 

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

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

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

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

 

界面设计:

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

<LinearLayout

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

  android:layout_width="wrap_content"

  android:layout_height="fill_parent">

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

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:text="java" />

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

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:text="dotNet" />

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

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:text="PHP" />

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

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:text="获取值" />

</LinearLayout>

代码处理:

public class CheckBoxActivity extends Activity {

private static final String TAG = "CheckBoxActivity";

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

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.checkbox); 

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

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

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

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

        for(CheckBox box : checkboxs){

        box.setOnCheckedChangeListener(listener);

        }

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

        button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

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

 for(CheckBox box : checkboxs){

if(box.isChecked()){

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

}

 }

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

}

});

    }

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

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

CheckBox checkBox = (CheckBox) buttonView;

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

}

    };

}

8、下拉列表框(Spinner)

 

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

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

界面设计:

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

<LinearLayout

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

  android:layout_width="fill_parent"

  android:layout_height="wrap_content">

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>

</LinearLayout>

代码处理:

public class SpinnerActivity extends Activity {

    private static final String TAG = "SpinnerActivity";

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.spinner); 

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

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

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

        adapter.add("java");

        adapter.add("dotNet");

        adapter.add("php");

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

        spinner.setAdapter(adapter);

        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

@Override

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

Spinner spinner = (Spinner)adapterView;

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

}

@Override

public void onNothingSelected(AdapterView<?> view) {

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

}

        });

    }

}

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

 

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

界面设计:

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

<LinearLayout

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

  android:layout_width="fill_parent"

  android:layout_height="wrap_content">

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>

</LinearLayout>

代码处理:

public class SpinnerActivity extends Activity {

    private static final String TAG = "SpinnerActivity";

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.spinner); 

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

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

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

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

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

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

        spinner.setAdapter(adapter);

        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

@Override

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

Spinner spinner = (Spinner)adapterView;

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

}

@Override

public void onNothingSelected(AdapterView<?> view) {

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

}

        });

    }

}

Person.java:

public class Person {

private Integer id;

private String name;

public Person(Integer id, String name) {

this.id = id;

this.name = name;

}

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return name;

}

}

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

 

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

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

主界面设计:

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

<LinearLayout

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

  android:layout_width="fill_parent"

  android:layout_height="wrap_content">

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>

</LinearLayout>

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

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

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

   android:id="@+id/contentTextView"

    android:layout_width="fill_parent" 

    android:layout_height="wrap_content" 

    android:background="#F4FDFF"

/>

代码处理:

public class SpinnerActivity extends Activity {

    private static final String TAG = "SpinnerActivity";

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.spinner); 

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

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

        adapter.add("java");

        adapter.add("dotNet");

        adapter.add("php");

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

        spinner.setAdapter(adapter);

        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

@Override

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

Spinner spinner = (Spinner)adapterView;

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

}

@Override

public void onNothingSelected(AdapterView<?> view) {

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

}

        });

    }

}

11、拖动条(SeekBar)

     

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

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

主界面设计:

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

<LinearLayout

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

  android:layout_width="fill_parent"

  android:layout_height="fill_parent"

  android:orientation="vertical">

  <SeekBar

    android:id="@+id/seekBar"

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>

    

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

    android:layout_height="wrap_content"

    android:layout_width="wrap_content"

    android:text="获取值"

    />

</LinearLayout>

代码处理:

public class SeekBarActivity extends Activity {

    private SeekBar seekBar;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.seekbar);

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

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

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

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override

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

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

            }

            @Override

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

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

            }

  @Override

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

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

            }

        });

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

        button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

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

}

         });

    }

}

12、菜单(Menu)

 

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

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

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

 

public class MenuActivity extends Activity {

private static final String TAG = "MenuActivity";

private static final int MENU_ADD = Menu.FIRST;

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

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.menu); 

    }

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

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

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

return super.onCreateOptionsMenu(menu);

    }

    @Override

    public boolean onMenuItemSelected(int featureId, MenuItem item) {

switch (item.getItemId()) {

  case MENU_ADD:

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

    return true;

  case MENU_UPDATE:

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

       return true;

  default:

              return super.onMenuItemSelected(featureId, item);

  }

    }

}

13、进度条(ProgressBar)

 

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

<ProgressBar 

    android:layout_width="fill_parent" 

    android:layout_height="20px"

    style="?android:attr/progressBarStyleHorizontal"

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

在代码中操作进度条:

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

ProgressBar.setProgress(0);//设置进度条的当前刻度,如果进度条的最大刻度为100,当前刻度为50,进度条将进行到一半。

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

AutoCompleteTextViewEditText组件类似,都可以输入文本。

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

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

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

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

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

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

<AutoCompleteTextView

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

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

   android:completionThreshold="1“  

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

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

setContentView(R.layout.main);

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

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

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

nameText.setAdapter(adapter);

}

 

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

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

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

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

< MultiAutoCompleteTextView

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

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

   android:completionThreshold="1“  

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

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

setContentView(R.layout.main);

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

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

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

     nameText.setAdapter(adapter);

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

 

 

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

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

CSS中是这样定义的:

<style>

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

</style>

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

Android中可以这样定义样式:

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

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

<resources>

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

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

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

    </style>

</resources>

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

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

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

    <TextView style="@style/itcast"

        .....  />

</LinearLayout>

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

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

<resources>

    <style name="itcast">

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

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

    </style>

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

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

    </style>

</resources>

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

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

<resources>

<style name=itcastTheme">

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

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

</style>

</resources>

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

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

     android:theme="@style/itcastTheme">

   ......

</application>

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

setTheme(R.style.itcastTheme);

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

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

17、编码实现软件界面

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

public class MainActivity extends Activity {

public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        LinearLayout linearLayout = new LinearLayout(this);

        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(

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

        TextView textView = new TextView(this);

        textView.setText(R.string.hello);

        textView.setId(34);

        LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(

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

        linearLayout.addView(textView, textParams);

        setContentView(linearLayout, layoutParams);       

    }

}

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

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

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

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

<html>

<head>

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

<title>Insert title here</title>

<style type="text/css">

A {

COLOR: #FFFFFF; TEXT-DECORATION: none

}

</style>

<script type="text/javascript">

function show(jsondata){

        var jsonobjs = eval(jsondata);

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

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

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

        //添加三列

        var td1 = tr.insertCell(0);

        var td2 = tr.insertCell(1);

        td2.align = "center";

        var td3 = tr.insertCell(2);

        //设置列内容和属性

        td1.innerHTML = jsonobjs[y].id; 

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

        td3.innerHTML = jsonobjs[y].phone;

}

}

</script>

</head>

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

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

<tr>

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

</tr>

</table>

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

</body>

</html>

public class HtmlActivity extends Activity {

private WebView webView;

private Handler handler = new Handler();

 

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        

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

        webView.getSettings().setJavaScriptEnabled(true);

        webView.getSettings().setSaveFormData(false);

        webView.getSettings().setSavePassword(false);

        webView.getSettings().setSupportZoom(false);

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

        webView.setWebChromeClient(new ItcastWebClient());

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

    }

    

    private final class ItcastJavaScript{

    public void personlist(){

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

}

    

    public void call(final String phone){

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

    }

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

   try {

JSONObject jsonObject = new JSONObject();

jsonObject.put("id", 56);

   try {

JSONObject jsonObject = new JSONObject();

jsonObject.put("id", 56);

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

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

JSONObject jsonObject2 = new JSONObject();

jsonObject2.put("id", 89);

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

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

JSONArray jsonArray = new JSONArray();

jsonArray.put(jsonObject);

jsonArray.put(jsonObject2);    

return jsonArray.toString();

     } catch (JSONException e) {

e.printStackTrace();

     }

   return "";

}

    }

    private final class ItcastWebClient extends WebChromeClient{

@Override

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

    new AlertDialog.Builder(HtmlActivity.this) 

.setTitle("提示信息")       

.setMessage(message)           

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

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

}).show();

return true;

}

}

}

19、动画(Animation

Android提供了2种动画:

1> Tween动画,通过对 View 的内容进行一系列的图形变换 (包括平移、缩放、旋转、改变透明度)来实现动画效果。动画效果的定义可以采用XML来做也可以采用编码来做。Tween动画有4种类型:

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

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

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

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

本例要实现对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%为物件的XY方向坐标上的中点位置

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

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

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

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

    android:orientation="horizontal"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    >

<ImageView

   android:layout_width="wrap_content"

   android:layout_height="wrap_content"

   android:src="@drawable/icon"

   android:id="@+id/imageView"

   />

</LinearLayout>

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

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

public class AnimationActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

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

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

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

//开始执行动画

imageView.startAnimation(animation);

}

}

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

public class AnimationActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

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

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

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

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 为动画起始时 XY坐标上的位置

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

 -->

</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  为动画相对于物件的XY坐标的开始位.说明:以上两个属性值 从0%-100%中取值,50%为物件的XY方向坐标上的中点位置

 -->

</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控件绑定动画效果,调用代表动画的AnimationDrawablestart()方法开始动画。

public class FrameActivity extends Activity {

private AnimationDrawable animationDrawable;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

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

imageView.setBackgroundResource(R.anim.frame);

animationDrawable = (AnimationDrawable) imageView.getBackground();

}

@Override

public boolean onTouchEvent(MotionEvent event) {

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

  animationDrawable.start();

  return true;

  }

  return super.onTouchEvent(event);

}

}

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

20、传感器的使用

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

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

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

3光线传感器:    Sensor.TYPE_LIGHT

4磁场传感器:   Sensor.TYPE_MAGNETIC_FIELD

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

6温度传感器:   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值的取值范围是-180y值 ≤180

假设将手机屏幕朝上水平放在桌子上,这时如果桌子是完全水平的,y值应该是0(由于很少有桌子是绝对水平的,

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

在这个旋转过程中,y值会在0-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,

直到等于-180。如果从手机底部开始抬起,直到将手机沿X轴旋转180度,这时y值会在0180之间变化。

也就是y值会逐渐增大,直到等于180。可以利用y值和z值来测量桌子等物体的倾斜度。

y值表示倾斜度,或手机翘起的程度。当手机绕着X轴倾斜时该值发生变化。y值的取值范围是-180y值 ≤180

假设将手机屏幕朝上水平放在桌子上,这时如果桌子是完全水平的,y值应该是0(由于很少有桌子是绝对水平的,

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

在这个旋转过程中,y值会在0-180之间变化,也就是说,从手机顶部抬起时,y的值会逐渐变小,

直到等于-180。如果从手机底部开始抬起,直到将手机沿X轴旋转180度,这时y值会在0180之间变化。

也就是y值会逐渐增大,直到等于180。可以利用y值和z值来测量桌子等物体的倾斜度。

因此,该值很可能不为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) {//当传感器的精度变化时

}

};

}

 

21、NinePatch图片

 

 

 

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

 

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

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

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

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

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

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

编写代码。如下:

<TextView android:layout_width="wrap_content" 

    android:layout_height="wrap_content"

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

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

 

22、关闭应用

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

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

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

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

System.exit(0);

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

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

manager.restartPackage(getPackageName());

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

 

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

在文件AndroidManifest.xml中添加权限

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

第一种方法:

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

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

        /** 获取SIM卡的IMSI

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

         * 储存在SIM卡中,可用于区别移动用户的有效信息。IMSIMCCMNCMSIN组成,其中MCC为移动国家号码,由3位数字组成,

         * 唯一地识别移动客户所属的国家,我国为460MNC为网络id,由2位数字组成,

         * 用于识别移动客户所归属的移动网络,中国移动为00,中国联通为01,中国电信为03MSIN为移动客户识别码,采用等长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")){

         //中国电信

        }

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

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

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

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

if(cursor!=null){

      while(cursor.moveToNext()){

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

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

        }

}

在文件AndroidManifest.xml中添加权限

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

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

package com.android.internal.telephony;

import android.content.ContentProvider;

import android.content.UriMatcher;

import android.content.ContentValues;

import com.android.internal.database.ArrayListCursor;

import android.database.Cursor;

import android.net.Uri;

import android.os.SystemProperties;

import android.os.RemoteException;

import android.os.ServiceManager;

import android.text.TextUtils;

import android.util.Log;

import java.util.ArrayList;

import java.util.List;

import com.android.internal.telephony.IccConstants;

import com.android.internal.telephony.AdnRecord;

import com.android.internal.telephony.IIccPhoneBook;

public class IccProvider extends ContentProvider {

    private static final String TAG = "IccProvider";

    private static final boolean DBG = false;

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

        "name",

        "number"

    };

    private static final int ADN = 1;

    private static final int FDN = 2;

    private static final int SDN = 3;

private static final String STR_TAG = "tag";

    private static final String STR_NUMBER = "number";

    private static final String STR_PIN2 = "pin2";

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

    static {

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

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

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

    }

    private boolean mSimulator;

    @Override

    public boolean onCreate() {

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

        if (!TextUtils.isEmpty(device)) {

            mSimulator = false;

        } else {

            // simulator

            mSimulator = true;

        }

        return true;

    }

    @Override

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

            String[] selectionArgs, String sort) {

        ArrayList<ArrayList> results;

  if (!mSimulator) {

            switch (URL_MATCHER.match(url)) {

                case ADN:

                    results = loadFromEf(IccConstants.EF_ADN);

                    break;

                case FDN:

                    results = loadFromEf(IccConstants.EF_FDN);

                    break;

                case SDN:

                    results = loadFromEf(IccConstants.EF_SDN);

                    break;

                default:

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

            }

        } else {

            // Fake up some data for the simulator

            results = new ArrayList<ArrayList>(4);

            ArrayList<String> contact;

            contact = new ArrayList<String>();

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

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

            results.add(contact);

            contact = new ArrayList<String>();

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

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

            results.add(contact);

            results = new ArrayList<ArrayList>(4);

            ArrayList<String> contact;

            contact = new ArrayList<String>();

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

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

            results.add(contact);

            contact = new ArrayList<String>();

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

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

            results.add(contact);

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

            results.add(contact);

            contact = new ArrayList<String>();

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

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

            results.add(contact);

   contact = new ArrayList<String>();

            contact.add("Melissa Owens");

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

            results.add(contact);

            contact = new ArrayList<String>();

            contact.add("Directory Assistence");

            contact.add("411");

            results.add(contact);

        }

        return new ArrayListCursor(ADDRESS_BOOK_COLUMN_NAMES, results);

    }

    @Override

    public String getType(Uri url) {

        switch (URL_MATCHER.match(url)) {

            case ADN:

            case FDN:

            case SDN:

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

            default:

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

        }

   }

    @Override

    public Uri insert(Uri url, ContentValues initialValues) {

        Uri resultUri;

        int efType;

        String pin2 = null;

        if (DBG) log("insert");

        int match = URL_MATCHER.match(url);

        switch (match) {

            case ADN:

                efType = IccConstants.EF_ADN;

                break;

            case FDN:

                efType = IccConstants.EF_FDN;

                pin2 = initialValues.getAsString("pin2");

                break;

            default:

                throw new UnsupportedOperationException(

                        "Cannot insert into URL: " + url);

        }

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

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

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

    if (!success) {

            return null;

        }

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

        switch (match) {

            case ADN:

                buf.append("adn/");

                break;

            case FDN:

                buf.append("fdn/");

                break;

        }

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

        buf.append(0);

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

        /*

        // notify interested parties that an insertion happened

        getContext().getContentResolver().notifyInsert(

                resultUri, rowID, null);

        */

        return resultUri;

    }

  private String normalizeValue(String inVal) {

        int len = inVal.length();

        String retVal = inVal;

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

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

        }

        return retVal;

    }

    @Override

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

        int efType;

        if (DBG) log("delete");

        int match = URL_MATCHER.match(url);

        switch (match) {

            case ADN:

                efType = IccConstants.EF_ADN;

                break;

            case FDN:

                efType = IccConstants.EF_FDN;

                break;

            default:

                throw new UnsupportedOperationException(

                        "Cannot insert into URL: " + url);

        }

           

 // parse where clause

        String tag = null;

        String number = null;

        String pin2 = null;

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

        int n = tokens.length;

        while (--n >= 0) {

            String param = tokens[n];

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

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

            if (pair.length != 2) {

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

                continue;

            }

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

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

            if (STR_TAG.equals(key)) {

                tag = normalizeValue(val);

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

                number = normalizeValue(val);

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

                pin2 = normalizeValue(val);

            }

        }

     if (TextUtils.isEmpty(tag)) {

            return 0;

        }

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

            return 0;

        }

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

        if (!success) {

            return 0;

        }

        return 1;

    }

    @Override

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

        int efType;

        String pin2 = null;

        if (DBG) log("update");

        int match = URL_MATCHER.match(url);

        switch (match) {

            case ADN:

                efType = IccConstants.EF_ADN;

                break;

            case FDN:

                efType = IccConstants.EF_FDN;

                pin2 = values.getAsString("pin2");

       break;

            default:

                throw new UnsupportedOperationException(

                        "Cannot insert into URL: " + url);

        }

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

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

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

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

        boolean success = updateIccRecordInEf(efType, tag, number,

                newTag, newNumber, pin2);

        if (!success) {

            return 0;

        }

        return 1;

    }

    private ArrayList<ArrayList> loadFromEf(int efType) {

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

        List<AdnRecord> adnRecords = null;

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

        try {

            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(

                    ServiceManager.getService("simphonebook"));

            if (iccIpb != null) {

                adnRecords = iccIpb.getAdnRecordsInEf(efType);

           }

        } catch (RemoteException ex) {

            // ignore it

        } catch (SecurityException ex) {

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

        }

        if (adnRecords != null) {

            // Load the results

            int N = adnRecords.size();

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

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

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

            }

        } else {

            // No results to load

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

            results.clear();

        }

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

        return results;

    }

    private boolean

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

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

                ", number=" + number);

        boolean success = false;

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

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

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

   // hopefully, we can remove this requirement.

        try {

            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(

                    ServiceManager.getService("simphonebook"));

            if (iccIpb != null) {

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

                        name, number, pin2);

            }

        } catch (RemoteException ex) {

            // ignore it

        } catch (SecurityException ex) {

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

        }

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

        return success;

    }

    private boolean

    updateIccRecordInEf(int efType, String oldName, String oldNumber,

            String newName, String newNumber,String pin2) {

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

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

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

        boolean success = false;

        try {

            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(

                    ServiceManager.getService("simphonebook"));

            if (iccIpb != null) {

                success = iccIpb.updateAdnRecordsInEfBySearch(efType,

                        oldName, oldNumber, newName, newNumber, pin2);

            }

 } catch (RemoteException ex) {

            // ignore it

        } catch (SecurityException ex) {

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

        }

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

        return success;

    }

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

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

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

        boolean success = false;

        try {

            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(

                    ServiceManager.getService("simphonebook"));

            if (iccIpb != null) {

                success = iccIpb.updateAdnRecordsInEfBySearch(efType,

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

            }

        } catch (RemoteException ex) {

            // ignore it

        } catch (SecurityException ex) {

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

        }

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

        return success;

    }

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

            }

        } catch (RemoteException ex) {

            // ignore it

        } catch (SecurityException ex) {

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

        }

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

        return success;

    }

  

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

            }

        } catch (RemoteException ex) {

            // ignore it

        } catch (SecurityException ex) {

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

        }

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

        return success;

    }

  25删除呼叫记录

在文件AndroidManifest.xml中添加权限

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

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

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

源码路径:

com\android\providers\contacts\CallLogProvider.java

使用到的数据库在:

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

表名:calls

呼叫记录有三种类型:

来电: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);

        }

}

 

26在应用中安装其他程序

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

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

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

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

 

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

Intent intent = new Intent();

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

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

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

startActivity(intent);

 

 

 

 

27、如何反编绎APK文件

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

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

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

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

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

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

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

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

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

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

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

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

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

 

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

由于apkAndroid虚拟机加载的,它有一定的规范,加密apkDalvik无法

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

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

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

   编译

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

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

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

 

你可能感兴趣的:(android)