Android - 方位传感器

1. 三维坐标系

在现实中确定方向需要一个由 X、Y、Z 轴 组成的三维坐标。安卓给我们返回的方向值就是一个长度为 3 的 flaot 数组,包含三个方向的值。

坐标轴参考释义

参考
X 轴 沿着屏幕水平方向从左到右
Y 轴 从屏幕的左下角开始沿着屏幕的的垂直方向指向屏幕的顶端
Z 轴 当设备水平放置时,指向天空的方向

2. 方向传感器数据值

传感器的回调方法 onSensorChanged 中的参数 SensorEvent event 是一个最多包含三个元素的 Float[] 数组,对应含义见下表:

数据下标 数据名称 含义
values[0] 方位角 手机绕着 Z 轴旋转的角度。
0 表示正北(North),90 表示正东(East),180表示正南(South),270表示正西(West)。
假如 values[0] 的值刚好是这四个值的话,并且手机沿水平放置的话,那么当前手机的正前方就是这四个方向,可以利用这一点来实现指南针
values[1] 倾斜角 手机翘起来的程度,当手机绕着 X 轴倾斜时该值会发生变化。取值范围是 [-180, 180] 之间。假如把手机放在桌面上,而桌面是完全水平的话,这个值应该为0,当然很少桌子是绝对水平的。从手机顶部开始抬起,直到手机沿着 X 轴旋转180°(此时屏幕向下水平放在桌面上)。在这个旋转过程中,这个值会在 [0, -180] 之间变化,即手机抬起时,这个值会逐渐变小,直到等于 -180°;而假如从手机底部开始抬起,直到手机沿着 X 轴旋转180°,这个值会在 [0, 180] 之间变化。可以利用这些特性结合 values[2] 来实现一个水平尺
values[2] 滚转角 沿着Y轴的滚动角度,取值范围为 [-90, 90],假设将手机屏幕朝上水平放在桌面上,这时如果桌面是平的,这个值应为 0。将手机从左侧逐渐抬起,这个值将逐渐减小,直到垂直于手机放置,此时这个值为 -90,相反,从右侧抬起则是 90;假如在垂直位置时继续向右或者向左滚动,这个值将会继续在 [-90, 90] 之间变化

好了,看了这么多文字想必都犯困了吧。那么接下来上代码

3. Demo:监听坐标轴变化

activity_main.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp"
tools:context=".MainActivity">


<TextView
android:id="@+id/val1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
android:text="方位角" />

<TextView
android:id="@+id/val2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textSize="20sp"
android:textStyle="bold"
android:text="倾斜角" />

<TextView
android:id="@+id/val3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textSize="20sp"
android:textStyle="bold"
android:text="滚转角" />
</LinearLayout>

MainActivity.java

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
public class MainActivity extends AppCompatActivity implements SensorEventListener {

private TextView val1, val2, val3;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI);

val1 = findViewById(R.id.val1);
val2 = findViewById(R.id.val2);
val3 = findViewById(R.id.val3);
}

@SuppressLint("SetTextI18n")
@Override
public void onSensorChanged(SensorEvent event) {
val1.setText("方位角:" + (float) Math.round(event.values[0] * 100) / 100);
val2.setText("倾斜角:" + (float) Math.round(event.values[1] * 100) / 100);
val3.setText("滚转角:" + (float) Math.round(event.values[2] * 100) / 100);
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}
}

TODO: Compass demo

Java 中各进制间的转换

实际上,不管是常用的10进制转2、8、16进制,还是10进制转R进制、R进制转10进制的方法,都被封装在 Integer 对象中

10 进制转 R 进制

R: 进制
N: 10进制数

目标进制 方法 返回值
2 进制 Integer.toBinaryString(N:int) 字符串
8 进制 Integer.toOctalString(N:int) 字符串
16 进制 Integer.toHexString(N:int) 字符串
R 进制 Integer.toString(N:int, R:int) 字符串

R 进制转 10 进制

R: 进制
N: R 进制数字符串

1
Integer.parseInt(N:String, R:int)

附:Demo

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BinaryConversionTest {
public static void main(String[] args) {
System.out.println("Just Monika");
int dec_num = 18;
System.out.println(Integer.toBinaryString(dec_num));
System.out.println(Integer.toOctalString(dec_num));
System.out.println(Integer.toHexString(dec_num));
System.out.println(Integer.toString(dec_num, 3));

String bin_num = "10010";
System.out.println(Integer.parseInt(bin_num, 2));
}
}

Output

1
2
3
4
5
6
Just Monika
10010
22
12
200
18

Android - PopupMenu 弹出式菜单

PopupMenu 可以很方便的在指定 View 下显示一个弹出菜单,而且 PopupMenu 的菜单选项可以来自于 Menu 资源。

示例代码

menu_pop.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/monika" android:title="Monika" />
<item android:id="@+id/sayori" android:title="Sayori" />
<item android:id="@+id/natsuki" android:title="Natsuki" />
<item android:id="@+id/yuri" android:title="Yuri" />
<item android:id="@+id/delete" android:title="DELETE THEM ALL" />
</menu>

Android - Date & Time 组件

1. TextClock 文本时钟

TextClock 可以以字符串格式显示当前的日期和时间

效果

用法非常简单,没有什么值得说的,android:format24Hour 属性的参数就是一个时间格式化字符串,之后可能会单独写一篇文章

示例代码

1
2
3
4
5
<TextClock
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:format24Hour="yyyy-MM-dd HH:mm:ss"
/>

Android - Date/TimePickerDialog 日期和时间选择器

DatePickerDialog 和 TimePickerDialog 可以供用户来选择日期和时间

示例代码

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
42
43
44
45
46
47
48
49
50
51
52
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

findViewById(R.id.btn_pick_date).setOnClickListener(this);
findViewById(R.id.btn_pick_time).setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_pick_date:
Calendar cale1 = Calendar.getInstance();
new DatePickerDialog(
MainActivity.this,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
String result = "";
// 这里获取到的月份需要 +1s
result += "你选择的日期是:" + year + "年" + (monthOfYear + 1) + "月" + dayOfMonth + "日";
Toast.makeText(getApplicationContext(), result, Toast.LENGTH_LONG).show();
}
},
cale1.get(Calendar.YEAR),
cale1.get(Calendar.MONTH),
cale1.get(Calendar.DAY_OF_MONTH)
).show();
break;
case R.id.btn_pick_time:
Calendar cale2 = Calendar.getInstance();
new TimePickerDialog(
MainActivity.this,
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
String result = "";
result += "你选择的时间是:" + hourOfDay + "时" + minute + "分";
Toast.makeText(getApplicationContext(), result, Toast.LENGTH_LONG).show();
}
},
cale2.get(Calendar.HOUR_OF_DAY),
cale2.get(Calendar.MINUTE),
true
).show();
break;
}
}
}

Android - ProgressDialog 进度对话框

创建进度对话框主要有两种方法

  • 1. 直接调用 ProgressDialog 提供的静态方法 show() 显示
  • 2. 创建 ProgressDialog 对象,再设置对话框的参数,最后调用 show() 显示

ProgressDialog 有 STYLE_HORIZONTAL 和 STYLE_SPINNER 两种样式

示例代码

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private ProgressDialog pd2 = null;

private final static int MAXVALUE = 100;
private int progressStart = 0;
private int add = 0;

@SuppressLint("HandlerLeak")
final Handler hand = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x114514) {
//设置进度条的当前值
pd2.setProgress(progressStart);
}
if (progressStart >= MAXVALUE) {
pd2.dismiss();
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

findViewById(R.id.btn1).setOnClickListener(this);
findViewById(R.id.btn2).setOnClickListener(this);
findViewById(R.id.btn3).setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1:
ProgressDialog.show(MainActivity.this, "Loading", "少女祈祷中...", false, true);
break;
case R.id.btn2:
ProgressDialog pd1 = new ProgressDialog(MainActivity.this);
// 依次设置标题,内容
pd1.setTitle("Loading");
pd1.setMessage("思考好玩的加载提示中...");
pd1.setCancelable(true);
// 这里是设置进度条的风格, HORIZONTAL 是水平进度条, SPINNER 是圆形进度条
pd1.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd1.setIndeterminate(true);
// 调用 show() 方法将ProgressDialog显示出来
pd1.show();
break;
case R.id.btn3:
progressStart = 0;
add = 0;
pd2 = new ProgressDialog(MainActivity.this);
pd2.setMax(MAXVALUE);
pd2.setTitle("Loading");
pd2.setMessage("正在挽救纱世里...");
pd2.setCancelable(false);
pd2.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
// 取消不确定性进度
pd2.setIndeterminate(false);
pd2.show();
// 新建一个线程更新进度
new Thread() {
public void run() {
while (progressStart < MAXVALUE) {
progressStart = 2 * takeTime();
hand.sendEmptyMessage(0x114514);
}
}
}.start();
break;
}
}

private int takeTime() {
add++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return add;
}
}

Android - Notification 通知

1. 基本使用流程

状态通知栏主要涉及到2个类:Notification 和 NotificationManager

Notification: 通知信息类,它里面对应了通知栏的各个属性
NotificationManager: 是状态栏通知的管理类,负责发通知、清除通知等操作。

基本使用流程:

Step 1. 获得 NotificationManager 对象:NotificationManager mNManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Step 2. 创建一个通知栏的 Builder 构造类:Notification.Builder mBuilder = new Notification.Builder(this);
Step 3. 对 Builder 进行相关的设置,比如标题,内容,图标,动作等
Step 4. 调用 Builder 的 build() 方法为 notification 赋值
Step 5. 调用 NotificationManager的 notify() 方法发送通知

PS. 还可以调用 NotificationManager 的 cancel() 方法取消通知

2. 常用方法

首先创建 Builder 构造类

1
Notification.Builder mBuilder = new Notification.Builder(this);

然后再调用下述的相关的方法进行设置

Android - Vibrator 设备震动控制

首先需要在 AndroidManifest.xml 中申明权限

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

实例化 Vibrator 类

1
Vibrator vibrator = (Vibrator) getApplicationContext().getSystemService(VIBRATOR_SERVICE);

单次震动

1
2
// 参数为震动时间,单位为 ms
vibrator.vibrate(1000);

循环震动

1
2
3
4
5
long[] patter = {50, 50, 50, 100};
// 第一个参数接受一个 long 型数组
// patter 的偶下标为静止时间,奇下标为震动时间
// 第二个参数为循环模式,指定从数组的哪个下标开始循环,-1为不循环
vibrator.vibrate(patter, 0);

停止/取消震动

1
vibrator.cancel();

CentOS 通过 CUPS 将本地打印机共享到网络

CUPS(Common UNIX Printing System,通用Unix打印系统)是Fedora Core3中支持的打印系统,它主要是使用IPP(Internet Printing Protocol)来管理打印工作及队列,但同时也支持”LPD”(Line Printer Daemon)和”SMB”(Server Message Block)以及AppSocket等通信协议。

安装 CUPS

1
yum install cups cups-libs

修改 CUPS 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 编辑 CUPS 配置文件
nano /etc/cups/cupsd.conf

# 在 DefaultAuthType Basic 后追加一行
DefaultEncryption Never

# 修改 <Location /> 部分
<Location />
# Allow remote access...
Order allow,deny
Allow all
</Location>

# 如果需要指定 Web 面板端口,修改第 4 行的 'Port 631' 中的端口号

Android 帧动画

  • 在 drawable 目录下新建 frames.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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/p1"
android:duration="50" />

<item
android:drawable="@drawable/p2"
android:duration="50" />

<item
android:drawable="@drawable/p3"
android:duration="50" />

<item
android:drawable="@drawable/p4"
android:duration="50" />

</animation-list>
  • 在页面中调用 frames.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<ImageView
android:id="@+id/iv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:srcCompat="@drawable/frames" />

</LinearLayout>
  • 解析并开启动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ImageView iv = findViewById(R.id.iv);

AnimationDrawable animationDrawable = (AnimationDrawable) iv.getDrawable();
if(!animationDrawable.isRunning()){
animationDrawable.start();
}
}
}

  • 下面是纯代码方式实现
1
2
3
4
5
6
7
8
9
AnimationDrawable animationDrawable1 = new AnimationDrawable();
int[] ids = {R.drawable.p1,R.drawable.p2,R.drawable.p3,R.drawable.p4};
for(int i = 0 ; i < 4 ; i ++){
Drawable frame = getResources().getDrawable(ids[i]);
animationDrawable1.addFrame(frame,50);
}
animationDrawable1.setOneShot(false);
iv.setBackground(animationDrawable1);
animationDrawable1.start();
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×