安卓知识回顾

1.获取版本信息

public static String getVersionName(Context context) {
String versionName;
// 获取包管理器
PackageManager packageManager = context.getPackageManager();
try {
    PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);

    versionName = packageInfo.versionName;
} catch (NameNotFoundException e) {
    versionName = "获取版本信息错误";
    e.printStackTrace();
}
return versionName;
}

2.获取服务器版本信息

class GetServerVisionRunnable implements Runnable {
        @Override
        public void run() {
            InputStream is = null;
            BufferedReader br = null;
            try {
                // 创建连接
                HttpURLConnection connection = (HttpURLConnection) new URL(
                        "http://192.168.129.102:8080/version.json").openConnection();
                // 设置连接超时时间
                connection.setConnectTimeout(5000);
                // 设置读取超时时间
                connection.setReadTimeout(5000);
                // 连接
                connection.connect();
                String result = "";
                if (connection.getResponseCode() == 200) {

                    is = connection.getInputStream();
                    br = new BufferedReader(new InputStreamReader(is));
                    String readlline;
                    while ((readlline = br.readLine()) != null) {
                        result += readlline;
                    }
                    JSONObject json = new JSONObject(result);
                    int serverVersionCode = json.optInt("versioncode");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                CommonUtils.CloseStream(is);
                CommonUtils.CloseStream(br);
            }
        }
    }

3.下载、安装apk

class DownloadRunnable implements Runnable {
    private InputStream inputStream;
    private FileOutputStream fos;
    private ProgressDialog pd;

    public DownloadRunnable(ProgressDialog pd) {
        this.pd = pd;
    }

    @Override
    public void run() {
        try {
            // 初始化
            HttpURLConnection connection =
                    (HttpURLConnection) new URL(downUrl).openConnection();
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(5000);
            connection.connect();
            // 连接成功
            if (connection.getResponseCode() == 200) {
                // 创建文件
                File file = new File(Environment.getExternalStorageDirectory(), "test.apk");
                inputStream = connection.getInputStream();
                // 获取文件总长度
                int totalLength = connection.getContentLength();
                // 设置进度条的总大小
                pd.setMax(totalLength);
                fos = new FileOutputStream(file);
                int len;
                byte[] buffer = new byte[1024];
                int current = 0;
                while ((len = inputStream.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                    current += len;
                    // 设置进度条的进度
                    pd.setProgress(current);
                }
                // 隐藏进度条
                pd.dismiss();
                // 安装应用
                Intent intent = new Intent();
                intent.setAction("android.intent.action.VIEW");
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.addCategory("android.intent.category.DEFAULT");              
                intent.setDataAndType(Uri.parse("file:" + file.getAbsolutePath()),
                        "application/vnd.android.package-archive");
                startActivityForResult(intent, REQ_CODE_INSTALL_APP);

            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CommonUtils.CloseStream(inputStream);
            CommonUtils.CloseStream(fos);
        }
    }
}

4.动画

//沿Y轴旋转的属性动画
ObjectAnimator animator = ObjectAnimator.ofFloat(iv_icon, "rotationY", 0, 45, 90, 135, 180, 225, 270, 315, 360);
// 时常
animator.setDuration(2000);
// 重复次数
animator.setRepeatCount(ObjectAnimator.INFINITE);
// 重复模式
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.start();

5.按钮按压效果选择器

  • 在res/drawable/目录下新建一个正常状态下的shape

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" >
    <!-- oval: 椭圆 -->
    <!-- 半径 -->
    <corners android:radius="16dp" />
    <!-- 宽高 -->
    <size
    android:height="32dp"
    android:width="32dp" />
    <!-- 背景色 -->
    <solid android:color="#99ffffff" />
    </shape>
  • 在res/drawable/目录下新建一个按压状态下的shape

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" >
    <!-- 半径 -->
    <corners android:radius="16dp" />
    <!-- 宽高 -->
    <size
    android:height="32dp"
    android:width="32dp" />
    <!-- 背景色 -->
    <solid android:color="#33ffffff" />
    </shape>
  • 在res/drawable/目录下新建一个selector

    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="300" android:exitFadeDuration="300">
    <!-- 按压状态使用的背景图 -->
    <item android:drawable="@drawable/setting_pressed" android:state_pressed="true"/>
    <!-- 默认状态使用的背景图 -->
    <item android:drawable="@drawable/setting_normal"/>
    <!-- 代码执行的时候会从上向下匹配背景图,一旦有一行执行了,下面的就不会再执行.所以一定要将按压状态的背景图放在默认状态的背景图上方,否则背景图永远都是默认状态的 -->
    </selector>
  • 为setting按钮设置 android:background属性,值为新建的selector

6.自定义属性

  • 声明自定义属性,参考sdk/platforms/res/values/attrs

       <attr name="sivText" format="reference|string" />
    </declare-styleable>
    
  • 布局文件使用自定义属性

    添加命名空间

    xmlns:baidu="http://schemas.android.com/apk/res/com.baidu.demo"
    使用
    <com.baidu.demo.view.SettingItemView 
        android:layout_width="match_parent"
        baidu:siv_text="设置1"
        android:layout_height="wrap_content"/>
    
  • 接收属性值

    TypedArray ta = context.obtainStyledAttributes(set, R.styleable.SettingItemView);

    //获取属性值
    String sivText = ta.getString(R.styleable.SettingItemView_siv_text);
    //赋值
    mTvSettingItem = (TextView) findViewById(R.id.tv_setting_item);
    mTvSettingItem.setText(sivText);
    

7.自定义其他属性

  • 设置item背景的自定义属性:枚举类型,参考TextView ellipsize属性

    1
    2
    3
    4
    5
    <attr name="sivBackground">
    <enum name="start" value="0" />
    <enum name="middle" value="1" />
    <enum name="end" value="2" />
    </attr>
  • 获取枚举类型属性:参考TextView源码读取ellipsize属性,a.getInt()

    1
    int sivBackground = ta.getInt(R.styleable.SettingItemView_sivBackground,SETTING_ITEM_BACKGROUND_START);
  • 自定义控件什么常量,对应枚举的value值

    1
    2
    3
    private final static int SETTING_ITEM_BACKGROUND_START = 0;
    private final static int SETTING_ITEM_BACKGROUND_MIDDLE = 1;
    private final static int SETTING_ITEM_BACKGROUND_END = 2;
  • 赋值

    switch (sivBackground) {

    case SETTING_ITEM_BACKGROUND_START:
        mRlItemView.setBackgroundResource(R.drawable.setting_item_first_selector);
        break;
    case SETTING_ITEM_BACKGROUND_MIDDLE:
        mRlItemView.setBackgroundResource(R.drawable.setting_item_middle_selector);
        break;
    case SETTING_ITEM_BACKGROUND_END:
        mRlItemView.setBackgroundResource(R.drawable.setting_item_last_selector);
        break;
    }
    

8.自定义Dialog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//填充自定义布局
View view = View.inflate(this, R.layout.activity_setting_setting_pwd, null);

final EditText etPwd = (EditText) view.findViewById(R.id.et_pwd);

final EditText etComfirmPwd = (EditText) view.findViewById(R.id.et_comfirm_pwd);

Button btnOk = (Button) view.findViewById(R.id.btn_ok);

Button btnCancel = (Button) view.findViewById(R.id.btn_cancel);

builder.setView(view);

final AlertDialog dialog = builder.show();

9.Activity界面间的跳转动画(需要在基类中设置Activity进出的动画)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
设置Activity跳转:公共style中添加onClick方法(clickNext、clickPre),对应Activity实现方法
在res文件夹下建立anim文件夹,再建立XML文件如下:next_step_enter.xml、next_step_exit.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromXDelta="100%p"
android:toXDelta="0"/>

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromXDelta="-100%p"
android:toXDelta="0"/>


<!-- 下一步 -->
<style name="btnNextStep" parent="@style/btnOkNarmal">
<item name="android:drawableRight">@drawable/next</item>
<item name="android:onClick">clickNextStep</item>
</style>

<!-- 上一步 -->
<style name="btnPreStep" parent="@style/btnOkNarmal">
<item name="android:drawableRight">@drawable/pre</item>
<item name="android:onClick">clickPreStep</item>
</style>

//点击事件的触发
public void clickNextStep(View v){
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.next_enter_anim, R.anim.next_exit_anim);
finish();
}

public void clickPreStep(View v) {
Intent intent = new Intent(this, MainActivity2.class);
startActivity(intent);
overridePendingTransition(R.anim.pre_enter_anim, R.anim.pre_exit_anim);
finish();
}

10.手势识别器滑动效果的实现

    //手势识别器
GestureDetector mDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener(){
        /**手势滑动e1:起点、e2:终点、velocityX:X轴速率、velocityY:Y轴速率*/
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2,float velocityX, float velocityY) {

            float x1 = e1.getRawX();
            float x2 = e2.getRawX();
            float y1 = e1.getRawY();
            float y2 = e2.getRawY();

            //速率判断
            if(Math.abs(velocityX) < 200){
                Log.d(TAG, "速率低");
                return true;
            }
            //垂直方向判断
            if(Math.abs(y2-y1) > Math.abs(x2-x1)){
                Log.d(TAG, "垂直方向");
                return true;
            }
            if(x1 > x2){//向左滑动:下一步,x1>x2
                doNext();
                LogUtils.d("下一步");
            }else{//向右滑动:上一步,x1<x2
                doPre();
                LogUtils.d("上一步");
            }

            Log.d(TAG, "x2-x1="+(x2-x1));            
            return super.onFling(e1, e2, velocityX, velocityY);
        }

    });

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //必须调用手势监听
        mDetector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

11.获取手机sim卡信息

1
2
3
4
5
6
7
8
9
//获取sim卡信息
private String getSim(){
TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
manager.getLine1Number();//手机号,运营商出厂sim卡,有时候把手机号烧到sim卡上,有时候不烧,不一定,可能为空
return manager.getSimSerialNumber();//不为空,唯一
}

//权限6.0以上要动态申请权限
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

12.手机联系人

  • 内容解析者:ContentResolver

  • 需求:一个联系人可能有多个号码,比如有2个,就有2个item,对应2个联系人

  • Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI

  • 名称:ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME

  • 号码:ContactsContract.CommonDataKinds.Phone.NUMBER

  • 联系人id:ContactsContract.CommonDataKinds.Phone.CONTACT_ID

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ContentResolver resolver = context.getContentResolver();
    Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
    String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
    ContactsContract.CommonDataKinds.Phone.NUMBER,
    ContactsContract.CommonDataKinds.Phone.CONTACT_ID}; //查询内容
    String selection = null; //查询条件
    String[] selectionArgs = null;//查询条件对应参数
    String sortOrder = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME+" desc"; //排序
    Cursor cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder);
    if(cursor != null){
    while(cursor.moveToNext()){
    Contact contact = new Contact();
    contact.name = cursor.getString(0);
    contact.number = cursor.getString(1);
    contact.id = cursor.getLong(2);
    list.add(contact);
    }
    cursor.close();
    }
    return list;

  • 通过id获取联系人头像

  • 单独封装一个方法,通过id获取头像bitmap

  • ContactsContract:联系人协议

  • content://contacts 所有联系人uri,content://contacts/id 指定联系人id的uri ContentResolver cr = context.getContentResolver();

    1
    2
    3
    4
    5
    // content://contacts/id 对应id联系人uri
    Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,
    String.valueOf(contactId));
    InputStream stream = ContactsContract.Contacts.openContactPhotoInputStream(cr, contactUri );
    return BitmapFactory.decodeStream(stream);

13.监控手机卡的变化

  • 创建广播接收者监听sim卡变化

  • 清单文件注册上述广播

    1
    2
    3
    4
    5
    6
    <receiver android:name="com.baidu.BootCompleteReceive" >
    <intent-filter android:priority="1000" >
    <!-- 手机重启 -->
    <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
    </receiver>
  • 添加权限:android.permission.RECEIVE_BOOT_COMPLETED

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class BootCompleteReceive extends BroadcastReceiver{······
    @Override
    public void onReceive(Context context, Intent intent) {
    LogUtils.d("手机重启...");
    String newSim = PhoneUtils.getSimSerialNumber(context) + "123";
    String sim = SharePreferenceUtils.getString(context, SharePreferenceUtils.KEY_BIND_SIM);

    if(newSim.equals(sim)){
    LogUtils.d("手机sim卡没变...");
    return;
    }
    boolean protecting = SharePreferenceUtils.getBoolean(context, SharePreferenceUtils.KEY_SAFE_PROTECTED, false);
    if(!protecting){
    return;
    }
    LogUtils.d("手机丢失了,sim卡变了...");
    //安全号码
    String number = SharePreferenceUtils.getString(context, SharePreferenceUtils.KEY_SAFE_NUMBER);
    //发送报警短信
    SmsManager manager = SmsManager.getDefault();
    //需要添加发送短信权限
    manager.sendTextMessage(number, null, "手机已丢失!", null, null);
    LogUtils.d("发送报警短信...");
    }

14.屏蔽短信

  • 创建新广播接收者

  • 清单文件注册上述广播,权限android.provider.Telephony.SMS_RECEIVED

    1
    2
    3
    4
    5
    6
    <receiver android:name=".receiver.SmsBroadcastReceiver">
    <!-- 接收短信 -->
    <intent-filter android:priority="1000">
    <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
    </intent-filter>
    </receiver>
  • 注意:上述action在高版本被谷歌隐藏,真实情况该api还可以使用,为了使用可先降低版本,添加action在还原高版本

  • onReceive接收短信

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class SmsBroadcastReceiver extends BroadcastReceiver{·······

    Object[] objs = intent.getExtras.get("pdus");
    for(Object obj : objs){
    SmsMessage sms = SmsMessage.createFromPdu((byte[]) obj);
    //String sender = sms.getOriginatingAddress();//发送者
    String content = sms.getMessageBody();//短信内容
    //判断内容
    if("广告短信".equals(content)){
    abortBroadcast();//屏蔽,不让用户看到该条短信
    }
    }

15.手机播放音乐

  • 存放本地的res/raw目录下

  • 代码:

    1
    2
    3
    4
    MediaPlayer player = MediaPlayer.create(context, R.raw.music);
    player.setLooping(true);//无限播放
    player.setVolume(1f, 1f);//设置最大声音
    player.start();//开始播放

16.手机定位

1
2
3
4
5
1.getSystemService获取LocationManager
2.manager请求位置requestLocationUpdates
3.监听定位回调onLocationChanged
4.退出取消定位:mLocManager.removeUpdates(mListener);
5.添加GPS定位权限android.permission.ACCESS_FINE_LOCATION

17.火星座标系转真是座标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//火星坐标系转成正式数据:收费
//开源工具:不能使用在商业项目,违法
try {
InputStream is = getAssets().open("axisoffset.dat");
ModifyOffset modifyOffset = ModifyOffset.getInstance(is);

PointDouble pt = new PointDouble(longitude, latitude);//火星坐标对象

PointDouble result = modifyOffset.s2c(pt);//真实坐标系,防盗谷歌地图,确认是否显示真实位置
Log.d(TAG, result.toString());

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}

18.设备管理员(源码DeviceAdminReceiver )

使用的一般步骤

1
2
3
4
5
1.新建广播接受者:继承DeviceAdminReceiver
2.清单文件:注册设备管理员,和BroadcastReceiver一样,参考上面打开的文档
3.设备管理员权限申请:通过一个xml文件申请(保存在res/xml目录下),具体参考文档
4.使用api:DevicePolicyManager,manager.lockNow()锁屏,activity.finish();
5.用户激活设备管理员:设置-安全-设备管理器-激活应用

避免手机防盗的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
DevicePolicyManager mDevPolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName mDeviceAdminSample = new ComponentName(this, ScreenLockReceiver.class);
boolean iaActive = mDevPolicyManager.isAdminActive(mDeviceAdminSample);
if(!iaActive){//没有激活过,激活
//开启
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdminSample);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
getString(R.string.add_admin_extra_app_text));
startActivityForResult(intent, REQUEST_CODE_ENABLE_ADMIN);

}else{
lock(mDevPolicyManager);
}

private void lock(DevicePolicyManager mDevPolicyManager) {
mDevPolicyManager.lockNow();
finish();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_CODE_ENABLE_ADMIN && resultCode == Activity.RESULT_OK){
lock(mDevPolicyManager);
}else{
finish();
}
}