文章目录
- 一、认识Android中的Activity组件
- 二、Activity生命周期
- 三、在Activity之间传递参数
- 四、Android中Activity启动模式
- 五、在Android中Intent的概念及应用
- 六、Android中Context的理解与使用
- 七、认识 Android Service
- 八、Android中Service通信
- 九、Android 中 AIDL(Android Interface Definition Language) 的理解与使用(出现问题,未解决)
- 十、Android 广播接收器 BroadcastReceiver
- 十一、Android日志系统
- 十二、Android权限系统
- 第三大模块:用户界面优化
- 第七章 数据存储全方案,详解持久化技术
贰:从这里正式慢慢学习
一、认识Android中的Activity组件
1、setContentView(R.layout.activity_main):指定一个视图用来呈现内容
2、R.java文件:自动生成的(由资源文件动态生成),用来定义Android程序中所有各类型的资源的索引
3、startActivity(new Intent( ,目标Activity)):来启动另一个Activity
4、startActivity(new Intent(Intent.ACTION_VIEW,Uri.parse(“http:\baidu.com”)):可以跳转到百度的首页
二、Activity生命周期
1、查看官方文档:
https://developer.android.google.cn/reference/android/app/Activity
2、
3、Activity在跳转过程中的生命周期:增加了一个Activity,测试的theme修改了一下,进行测试
三、在Activity之间传递参数
1、传递简单数据
2、传递数据包:Bundle
Bundle可以嵌入Bundle;
第二种方式:
```python
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnStartAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, theAty.class);
//i.putExtra("data", "Hello args");
//传递数据包Bundle
Bundle b = new Bundle();
b.putString("name", "zrf");
b.putInt("age", 18);
i.putExtra("data",b);//第二种方式
startActivity(i);
}
});
}
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_the_aty);
Intent i = getIntent();
Bundle data = i.getBundleExtra("data");//第二种方式
tv = (TextView)findViewById(R.id.tv);
tv.setText(String.format("name=%s,age=%d",data.getString("name"),data.getInt("age")));
}
3、传递值对象
在多个Activity之间传递值对象;
值对象:自定义的有数据类型的对象
第一种方式:实现Serializable接口,特点:操作简单,速度慢
public class User implements Serializable {
private String name;
private int age;
public User(String name, int age){
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnStartAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, theAty.class);
i.putExtra("user", new User("zrf", 18));
startActivity(i);
}
});
}
}
public class theAty extends AppCompatActivity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_the_aty);
Intent i = getIntent();
tv = (TextView)findViewById(R.id.tv);
User user = (User)i.getSerializableExtra("user");
tv.setText(String.format("User info(name=%s,age=%d)", user.getName(),user.getAge()));
}
}
第二种方式:专门面向移动化的序列接口:Android平台提供的Parcelable序列化接口Parcelable,特点:操作复杂,速度快
public class User implements Parcelable {
private String name;
private int age;
public User(String name, int age){
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(getName());
dest.writeInt(getAge());
}
public static final Creator<User> CREATOR = new Creator<User>(){
@Override
public User createFromParcel(Parcel source) {
return new User(source.readString(),source.readInt());
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnStartAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, theAty.class);
i.putExtra("user", new User("zrf", 18));
startActivity(i);
}
});
}
}
public class theAty extends AppCompatActivity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_the_aty);
Intent i = getIntent();
tv = (TextView)findViewById(R.id.tv);
User user =(User)i.getParcelableExtra("user");//注意这里和上面函数不一样
tv.setText(String.format("User info(name=%s,age=%d)", user.getName(),user.getAge()));
}
}
4、获取Activity返回的参数
布局文件:
activity-main.xml:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnStartAty"
android:text="启动另一个Activity"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView"
/>
activity_the_aty.xml:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/editText"
android:text="I am here"
/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send back" />
值对象:
public class User implements Parcelable {
private String name;
private int age;
public User(String name, int age){
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(getName());
dest.writeInt(getAge());
}
public static final Creator<User> CREATOR = new Creator<User>(){
@Override
public User createFromParcel(Parcel source) {
return new User(source.readString(),source.readInt());
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
findViewById(R.id.btnStartAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, theAty.class);
i.putExtra("user", new User("zrf", 18));
startActivityForResult(i, 0);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
textView.setText("另一个Activity返回的参数是:"+data.getStringExtra("data"));
}
}
theAty.java:
public class theAty extends AppCompatActivity {
private TextView tv;
private EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_the_aty);
tv = (TextView)findViewById(R.id.tv);
editText = (EditText) findViewById(R.id.editText);
Intent i = getIntent();
User user =(User)i.getParcelableExtra("user");
tv.setText(String.format("User info(name=%s,age=%d)", user.getName(),user.getAge()));
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i1 = new Intent();
i1.putExtra("data",editText.getText().toString());
setResult(1, i1);
finish();
}
});
}
}
四、Android中Activity启动模式
1、标准启动模式:同一个任务栈里面创建不同的实例
栈:先入后出
布局文件:
<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"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnStartSelf"
android:text="启动MainActivity"
/>
</LinearLayout>
MainActivity.java文件:
public class MainActivity extends AppCompatActivity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
tv.setText(String.format("TaskID:%d\nCurrentActivity:%s",getTaskId(),toString()));
findViewById(R.id.btnStartSelf).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, MainActivity.class));
}
});
}
}
2、singleTop模式
在同一个栈里面,如果指明Activity的启动模式为singleTop,如果此时Acitivity处于栈顶,那么只能创建一个实例,如果当前不处于栈顶,会创建新的实例。
布局文件:
activity_main.xml和activity_baty.xml(布局文件一样):
<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"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnMainAty"
android:text="启动MainActivity"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnStartBAty"
android:text="启动BAty"
/>
</LinearLayout>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
tv.setText(String.format("TaskID:%d\nCurrentActivity:%s",getTaskId(),toString()));
findViewById(R.id.btnMainAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, MainActivity.class));
}
});
findViewById(R.id.btnStartBAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, BAty.class));
}
});
}
}
BAty.java:
public class BAty extends AppCompatActivity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_baty);
tv = (TextView) findViewById(R.id.tv);
tv.setText(String.format("TaskID:%d\nCurrentActivity:%s",getTaskId(),toString()));
findViewById(R.id.btnStartBAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(BAty.this, BAty.class));
}
});
findViewById(R.id.btnMainBAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(BAty.this, MainActivity.class));
}
});
}
}
3、singleTask模式和singleInstance模式
singleTask模式:有一个任务栈,它的ID是80,这个任务栈里面有一个mainActivity,然后启动BAty,此时若再次启动mainActivity,会把BAty弹出,弹回到第一个Activity实例,此时再按后退键,程序就会退出。
<activity android:name=".BAty"></activity>
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
>
singleInstance模式:一个任务栈只包括一个Activity,只有一个实例
<activity android:name=".BAty"></activity>
<activity
android:name=".MainActivity"
android:launchMode="singleInstance"
>
(其他代码和singleTop模式所用代码同,只需修改android:launchMode="singleInstance"来进行测试看效果)
五、在Android中Intent的概念及应用
1、显式Intent:直接指明了要启动的类的定义
public void onClick(View v) {
startActivity(new Intent(BAty.this, BAty.class));
}
2、隐式Intent:就是我们创建一个Intent,并不指定要启动的Activity类的定义;
那么怎么启动?常用的启动方法:给它配置一个ACTION
<activity android:name=".MyAty">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="com.example.learnintent.intent.action.MyAty"/>
</intent-filter>
</activity>
public class MyAty extends Activity {
public static final String ACTION="com.example.learnintent.intent.action.MyAty";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myaty);
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnStartMyaty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MyAty.ACTION));
}
});
}
}
隐式Intent还可以:在另一个应用打开一个应用的Activity
(另一个APP的mainActivity.java):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnStartMyAty).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent("com.example.learnintent.intent.action.MyAty"));
}
});
}
一个Activity值只允许在该应用被访问:
加入:android:exported="false"即可,
<activity android:name=".MyAty" android:exported="false">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="com.example.learnintent.intent.action.MyAty"/>
</intent-filter>
</activity>
3、Intent过滤器相关选项
<activity android:name=".MyAty1">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="com.example.learnintent.intent.action.MyAty"/>
<data android:scheme="app"/>
</intent-filter>
</activity>
4、通过浏览器链接启动本地 Activity
关键代码:
<activity android:name=".LocalAppAty">
<intent-filter>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="app"/>
</intent-filter>
</activity>
Uri url = getIntent().getData();
System.out.println(url);
六、Android中Context的理解与使用
1、Android 中 Context 的理解及使用
context用来访问全局信息的;
Activity继承自Context,也就是Acitivity有访问全局信息的能力;
测试所用代码:
public class MainActivity extends AppCompatActivity {
//private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//tv = new TextView(MainActivity.this);//Activity继承自Context,有访问全局资源的能力
//tv.setText(R.string.hello_world);
//setContentView(tv);
//System.out.println(getResources().getText(R.string.hello_world));
ImageView iv = new ImageView(this);
iv.setImageResource(R.mipmap.ic_launcher);
setContentView(iv);
}
}
2、Application的用途
Context可以作为全局信息共享的桥梁,所以,使用context可以实现组件信息的共享;
main1.xml和main2.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="New Text"
android:id="@+id/textView"
/>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/editText"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="保存"
android:id="@+id/btnSaveData"
/>
</LinearLayout>
App.java:
public class App extends Application {
private String textData = "default";
public String getTextData() {
return textData;
}
public void setTextData(String textData) {
this.textData = textData;
}
}
main1.java和main2.java:
private TextView textView;
private EditText editText;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
textView = (TextView) findViewById(R.id.textView);
editText = (EditText) findViewById(R.id.editText);
textView.setText("共享的数据是:"+((App)getApplicationContext()).getTextData());
findViewById(R.id.btnSaveData).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((App)getApplicationContext()).setTextData(editText.getText().toString());
textView.setText("共享的数据是:"+editText.getText().toString());
}
});
AndroidManifest.xml文件的配置:
<application
android:name="App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity" android:label="main1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".main2" android:label="main2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
3、Application生命周期
几个生命周期函数:
@Override
public void onCreate() {
super.onCreate();
System.out.println("APP onCreate");
}
@Override
public void onTerminate() {
super.onTerminate();
}
@Override
public void onLowMemory() {
super.onLowMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
}
七、认识 Android Service
1、使用Service
Service:后台运行
先创建一个Service,下面是AndroidManifest.xml里面 Service的配置:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
启动服务:
findViewById(R.id.btnStartService).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(new Intent(MainActivity.this, MyService.class));
}
});
关闭服务:
findViewById(R.id.btnStopService).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService(new Intent(MainActivity.this, MyService.class));
}
});
2、绑定Service
绑定服务:
bindService(intent, this, Context.BIND_AUTO_CREATE);
解除绑定服务:
unbindService(this);
3、Service生命周期
如果同时执行了启动服务和绑定服务,那么就要同时执行解除绑定服务和停止服务,服务才会停止;
八、Android中Service通信
1、启动Service并且传递数据
private EditText edData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edData = (EditText)findViewById(R.id.edData);
findViewById(R.id.btnStartService).setOnClickListener(this);
findViewById(R.id.btnStopService).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnStartService:
Intent i = new Intent(this, MyService.class);
i.putExtra("data", edData.getText().toString());
startService(i);
break;
case R.id.btnStopService:
stopService(new Intent(this, MyService.class));
break;
}
}
MyService.java:
public class MyService extends Service {
private boolean running = false;
private String data;
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
data = intent.getStringExtra("data");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
super.onCreate();
new Thread(){
@Override
public void run() {
super.run();
running = true;
while(running){
System.out.println(data);
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
@Override
public void onDestroy() {
super.onDestroy();
running = false;
System.out.println("Service Destroy");
}
}
2、绑定Service进行通信
(1)比起直接startService()方法,通过binder方式,更高效;
关键代码:
MyService.java:
public IBinder onBind(Intent intent) {
return new Binder();
}
class Binder extends android.os.Binder{
public void setData(String data){
MyService.this.data = data;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
data = intent.getStringExtra("data");
return super.onStartCommand(intent, flags, startId);
}
MainActivity.java:
private MyService.Binder binder;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edData = (EditText)findViewById(R.id.edData);
findViewById(R.id.btnSyncData).setOnClickListener(this);//同步数据
}
case R.id.btnSyncData:
if(binder!=null){
binder.setData(edData.getText().toString());
}
break;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = (MyService.Binder)service;
}
(2)如何侦听服务的内部状态
内部发生改变,如何通知给外界代码(回调机制)
完整代码:
package com.example.connectservice;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {
private EditText edData;
private MyService.Binder binder;
private TextView tvOut;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edData = (EditText)findViewById(R.id.edData);
tvOut = (TextView) findViewById(R.id.tvOut);
findViewById(R.id.btnStartService).setOnClickListener(this);
findViewById(R.id.btnStopService).setOnClickListener(this);
findViewById(R.id.btnBindService).setOnClickListener(this);
findViewById(R.id.btnUnbindService).setOnClickListener(this);
findViewById(R.id.btnSyncData).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnStartService:
Intent i = new Intent(this, MyService.class);
i.putExtra("data", edData.getText().toString());
startService(i);
break;
case R.id.btnStopService:
stopService(new Intent(this, MyService.class));
break;
case R.id.btnBindService:
bindService(new Intent(this,MyService.class),this, Context.BIND_AUTO_CREATE);
break;
case R.id.btnUnbindService:
unbindService(this);
break;
case R.id.btnSyncData:
if(binder!=null){
binder.setData(edData.getText().toString());
}
break;
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = (MyService.Binder)service;
binder.getService().setCallback(new MyService.Callback() {
@Override
public void onDataChange(String data) {
Message msg = new Message();
Bundle b = new Bundle();
b.putString("data", data);
msg.setData(b);
handler.sendMessage(msg);
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
tvOut.setText(msg.getData().getString("data"));
}
};
}
package com.example.connectservice;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class MyService extends Service {
private boolean running = false;
private String data="这是默认信息";
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return new Binder();
}
class Binder extends android.os.Binder{
public void setData(String data){
MyService.this.data = data;
}
public MyService getService(){
return MyService.this;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
data = intent.getStringExtra("data");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
super.onCreate();
new Thread(){
@Override
public void run() {
super.run();
running = true;
int i=0;
while(running){
i++;
String str = i+":"+data;
System.out.println(str);
if(callback!=null){
callback.onDataChange(str);
}
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
@Override
public void onDestroy() {
super.onDestroy();
running = false;
System.out.println("Service Destroy");
}
private Callback callback = null;
public Callback getCallback() {
return callback;
}
public void setCallback(Callback callback) {
this.callback = callback;
}
public static interface Callback{
void onDataChange(String data);
}
}
<?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"
tools:context=".MainActivity">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/tvOut"
android:text="hhh"
/>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="默认信息"
android:id="@+id/edData"
/>
<Button
android:id="@+id/btnStartService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动服务"
/>
<Button
android:id="@+id/btnStopService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止服务"
/>
<Button
android:id="@+id/btnBindService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="绑定服务"
/>
<Button
android:id="@+id/btnUnbindService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="解除绑定服务"
/>
<Button
android:id="@+id/btnSyncData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="同步数据"
/>
</LinearLayout>
九、Android 中 AIDL(Android Interface Definition Language) 的理解与使用(出现问题,未解决)
1、跨应用启动Service
必须使用显示Intent,不能使用隐式Intent(ACTION)
可参考链接:https://www.jianshu.com/p/f2db0f58d47f
启动Service:
startService(new Intent(this, AppService.class));
关闭Service:
stopService(new Intent(this, AppService.class));
2、跨应用绑定Service
3、跨应用绑定Service并通信
十、Android 广播接收器 BroadcastReceiver
1、使用 BroadcastReceiver
首先创建一个BroadcastReceive
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("收到消息,消息的内容是:"+intent.getStringExtra("data"));
}
}
然后在布局文件里面加一个Button,来进行测试发送消息;
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.btnSendMsg).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnSendMsg:
Intent i = new Intent(this, MyReceiver.class);
i.putExtra("data","hello,I am msg");
sendBroadcast(i);
break;
}
}
}
2、动态注册和注销BroadcastReceive
关键代码:
switch (v.getId()){
case R.id.btnSendMsg:
Intent i = new Intent(MyReceiver.ACTION);
i.putExtra("data","hello,I am msg");
sendBroadcast(i);
break;
case R.id.btnReg:
if(myReceiver==null){
myReceiver = new MyReceiver();
registerReceiver(myReceiver, new IntentFilter(MyReceiver.ACTION));
}
break;
case R.id.btnUnReg:
if(myReceiver!=null){
unregisterReceiver(myReceiver);
}
break;
}
3、BroadcastReceive的优先级(出现了问题,已解决)
出现的错误:接收不到隐式广播
问题原因:Android9.0的版本对隐式广播有限制
问题解决:i.addFlags(0x01000000);
问题解决的原文链接:https://blog.csdn.net/zhgeliang/article/details/83184072
测试代码:
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnSendMsg).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent("com.example.learnbroadcastreceiver.intent.action.myService");
i.addFlags(0x01000000);
sendBroadcast(i);
}
});
}
}
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("MyReceive 接收到消息");
}
}
public class MyReceiver1 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("MyReceive 1 收到了消息");
}
}
<receiver
android:name=".MyReceiver1"
android:enabled="true"
android:exported="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.example.learnbroadcastreceiver.intent.action.myService" />
</intent-filter>
</receiver>
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.example.learnbroadcastreceiver.intent.action.myService" />
</intent-filter>
</receiver>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送消息"
android:id="@+id/btnSendMsg"
/>
十一、Android日志系统
1、使用日志API
private static String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//java自带
System.out.println("普通信息Info");
System.err.println("错误信息Warn");
//Android的日志API,下面的级别由高到低
Log.e(TAG,"错误信息");
Log.w(TAG,"警告信息");
Log.i(TAG,"普通信息");
Log.d(TAG,"调试信息");
Log.v(TAG,"无用信息");
}
可以调看到信息的级别:
2、日志分类
在 Android Studio 中对日志进行分类呈现,便于开发调试;
可以在下面进行设置:
3、使用DDMS查看日志(未找到)
十二、Android权限系统
1、请求权限实例(出现了问题,已解决)
WebView控件:呈现网页
我的AndroidStudio版本:3.5.2
SDK版本:Android 9
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/wv"
/>
添加权限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
public class MainActivity extends AppCompatActivity {
private WebView wv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wv = (WebView)findViewById(R.id.wv);
wv.loadUrl("http://www.baidu.com");
}
}
出现的问题:
添加了访问权限还是无法访问网页
解决:参见https://blog.csdn.net/qq_43509222/article/details/102987037
2、为代码添加权限检查
<permission android:name="com.example.checkpermissionincode.permission.SAY_HELLO"/>
<uses-permission android:name="com.example.checkpermissionincode.permission.SAY_HELLO"/>
public class Hello {
public static final String PERMISSION_SAY_HELLO = "com.example.checkpermissionincode.permission.SAY_HELLO";
public static void sayHello(Context context){
int CheckResult = context.checkCallingOrSelfPermission(PERMISSION_SAY_HELLO);
if(CheckResult!= PackageManager.PERMISSION_GRANTED){
throw new SecurityException("没有权限");
}
System.out.println("Hello,拥有权限的人");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Hello.sayHello(this);
}
}
3、为基本组件添加权限检查(出现了问题,已解决)
代码在这个基础上: 隐式Intent这一节代码上;
被启动的:
<permission android:name="com.example.componentpermission.permission.MyAty"/>
<activity android:name=".MyAty"
android:permission="com.example.componentpermission.permission.MyAty">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="com.example.componentpermission.intent.action.MyAty"/>
</intent-filter>
</activity>
要去启动的:
<uses-permission android:name="com.example.componentpermission.permission.MyAty"/>
第三大模块:用户界面优化
一、Android Fragment
1、如何创建和使用 Fragment
在一个程序内部使用Fragment做页面之间的切换比较好,轻量级,灵活
布局文件:
<FrameLayout 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:id="@+id/container"
tools:context=".MainActivity">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="======================我是容器" />
</FrameLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".PlaceholdFragment">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动另一个Fragment"
android:id="@+id/btnStartAnotherFragment"
/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是另一个Fragment"
android:id="@+id/textView"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnBack"
android:text="后退"
/>
</LinearLayout>
java类文件:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState==null){
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholdFragment())
.commit();
}
}
}
public class PlaceholdFragment extends Fragment {
public PlaceholdFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
rootView.findViewById(R.id.btnStartAnotherFragment).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager().beginTransaction()
.addToBackStack(null)//后退到上一步
.replace(R.id.container, new AnotherFragment())
.commit();
}
});
return rootView;
}
}
public class AnotherFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_another, container, false);
root.findViewById(R.id.btnBack).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager().popBackStack();//主动后退
}
});
return root;
}
}
2、Fragment的生命周期
onCreate()
onCreateView()
onpause()
onDestroyView()
当一个Fragment去启动另一个Fragment的时候,会执行onDestroyView();
3、带侧边栏的 Activity
4、Tabbed Activity
二、Android基本布局
1、LinearLayout
子对象水平排开或垂直排开
比较有用的一个属性:比重
比重:android:layout_weight =“1”,分割父级容器的比例,不写比重这个属性表示不参与父级容器的分割,会以内容为主
简单小例子:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="前往"
/>
</LinearLayout>
<WebView
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
2、用代码控制子对象
用程序控制子对象的添加和删除
测试代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private LinearLayout root;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
root = new LinearLayout(this);//创建线性布局
root.setOrientation(LinearLayout.VERTICAL);//设置布局方式为垂直布局
setContentView(root);
for(int i=0;i<10;i++) {
btn = new Button(this);
btn.setText("remove me");
btn.setOnClickListener(this);
//root.addView(btn);
//root.addView(btn,300,200);//把子对象添加进来
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.weight = 1;//让其均匀分割父级容器
root.addView(btn,lp);
}
}
@Override
public void onClick(View v) {
root.removeView(v);
}
}
3、RelativeLayout(相对布局)
后添加的控件会叠在上面,Button的优先级比较高
4、FrameLayout
能调整的位置非常有限,它的功能用RelativeLayout是完全可以替代的,但相对于RelativeLayout比较轻量级,速度快;
测试代码:
布局文件:
<FrameLayout 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:id="@+id/root"
tools:context=".FrameLayoutAty">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/img1"
android:id="@+id/ivA"
/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/img2"
android:visibility="invisible"
android:id="@+id/ivB"
/>
java类文件:
public class FrameLayoutAty extends AppCompatActivity {
private FrameLayout fl;
private ImageView ivA,ivB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_frame_layout_aty);
fl = (FrameLayout) findViewById(R.id.root);
ivA = findViewById(R.id.ivA);
ivB = findViewById(R.id.ivB);
showA();
fl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(ivA.getVisibility()==View.VISIBLE){
showB();
}else {
showA();
}
}
});
}
public void showA(){
ivA.setVisibility(View.VISIBLE);
ivB.setVisibility(View.INVISIBLE);
}
public void showB(){
ivB.setVisibility(View.VISIBLE);
ivA.setVisibility(View.INVISIBLE);
}
}
三、Android RecyclerView
RecyclerView新的列表组件,用来替代传统所使用的ListView
1、使用RecyclerView
首先,要使用RecycleView,需要给工程添加support v7的RecycleView的支持
画了圈的地方有个下载的箭头,下载即可使用
更多方法参见:https://blog.csdn.net/tran_sient/article/details/101919083?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159122753419724848328452%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=159122753419724848328452&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogtop_click~default-1-101919083.pc_v2_rank_blog_default&utm_term=android+studio+recyclerview
出现如下图错误:
解决方式:
解释可见:https://blog.csdn.net/qq_36467463/article/details/90693607
测试代码:
public class MainActivity extends AppCompatActivity {
private RecyclerView rv;
private String[] data = new String[]{"hello","I am ErHa"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rv = new RecyclerView(this);//初始化
setContentView(rv);
rv.setLayoutManager(new LinearLayoutManager(this));//布局
//填充内容,创建Adapter
rv.setAdapter(new RecyclerView.Adapter() {
class viewHolder extends RecyclerView.ViewHolder{
private TextView tv;
public viewHolder(TextView itemView) {
super(itemView);
tv = itemView;
}
public TextView getTv() {
return tv;
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new viewHolder(new TextView(parent.getContext()));
}
//position这个参数是指索引
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
viewHolder vh = (viewHolder) holder;
vh.getTv().setText(data[position]);
}
@Override
public int getItemCount() {//获取RecyclerView子对象的数量
return data.length;
}
});
}
}
2、使用资源文件自定义列表项
3、RecycleView的布局样式
四、Android常用控件
1、下拉列表
如何创建下拉列表、如何为下拉列表适配数据源,以及如何侦听下拉列表的选择事件
布局文件:
<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">
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
java类文件:
public class MainActivity extends AppCompatActivity {
private Spinner s;
private String[] dataSource = new String[]{"lala","jjj","uuuu"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
s = (Spinner)findViewById(R.id.spinner);
//为下拉列表适配数据源
s.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_expandable_list_item_1,dataSource));
//侦听下拉列表的选择事件
s.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
System.out.println("用户选择的是:"+dataSource[position]);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
}
2、日期选择器
布局文件:
<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"
tools:context=".ChooseADate">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0000-00-00"
android:id="@+id/btnChooseDate"
/>
</LinearLayout>
java类文件:
public class ChooseADate extends AppCompatActivity {
private Button btnChooseDate;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_adate);
btnChooseDate = (Button)findViewById(R.id.btnChooseDate);
btnChooseDate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new DatePickerDialog(ChooseADate.this, new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
String theDate = String.format("%d-%d-%d",year,month+1,dayOfMonth);
btnChooseDate.setText(theDate);
System.out.println(theDate);
}
},2015,1,2).show();
}
});
}
}
3、时间选择器
关键代码:
btnChooseTime.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new TimePickerDialog(ChooseTime.this, new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
System.out.println("执行了");
String theTime = String.format("%d:%d",hourOfDay,minute);
System.out.println(theTime);
btnChooseTime.setText(theTime);
}
},0,0,true).show();
}
});
4、单项选择
<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"
tools:context=".SingleChoose">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="世界上最大的海洋是什么?"
/>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A.太平洋"
android:id="@+id/rA"
/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="B.大西洋"
android:id="@+id/rB"
/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="C.北海"
android:id="@+id/rC"
/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="D.南海"
android:id="@+id/rD"
/>
</RadioGroup>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="提交"
android:id="@+id/submit"
/>
</LinearLayout>
public class SingleChoose extends AppCompatActivity {
private Button btnSubmit;
private RadioButton rA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_choose);
btnSubmit = findViewById(R.id.submit);
rA = findViewById(R.id.rA);
btnSubmit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(rA.isChecked()){
Toast.makeText(SingleChoose.this,"所选是正确的",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(SingleChoose.this,"所选是错误的",Toast.LENGTH_SHORT).show();
}
}
});
}
}
5、多项选择
<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"
tools:context=".MulChoose">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你喜欢哪些水果?"
tools:layout_editor_absoluteX="94dp"
tools:layout_editor_absoluteY="25dp" />
<CheckBox
android:id="@+id/checkBox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="香蕉" />
<CheckBox
android:id="@+id/checkBox2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="苹果" />
<CheckBox
android:id="@+id/checkBox3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="樱桃" />
<CheckBox
android:id="@+id/checkBox4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="梨" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="Large Text"
android:id="@+id/result"
/>
</LinearLayout>
public class MulChoose extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
private CheckBox checkBox1,checkBox2,checkBox3,checkBox4;
private TextView result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mul_choose);
checkBox1 = findViewById(R.id.checkBox1);
checkBox2 = findViewById(R.id.checkBox2);
checkBox3 = findViewById(R.id.checkBox3);
checkBox4 = findViewById(R.id.checkBox4);
result = findViewById(R.id.result);
checkBox1.setOnCheckedChangeListener(this);
checkBox2.setOnCheckedChangeListener(this);
checkBox3.setOnCheckedChangeListener(this);
checkBox4.setOnCheckedChangeListener(this);
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String str = "你喜欢:";
if(checkBox1.isChecked()){
str = str+checkBox1.getText()+",";
}
if(checkBox2.isChecked()){
str = str+checkBox2.getText()+",";
}
if(checkBox3.isChecked()){
str = str+checkBox3.getText()+",";
}
if(checkBox4.isChecked()){
str = str+checkBox4.getText();
}
result.setText(str);
}
}
6、TextView、EditView、Button、ImageView
7、ProgressBar(进度条):用于在界面上显示一个进度条,表示程序正在加载一些数据
(1)android:visibility属性可以来设置进度的可见于不可见
(2)style可以来设置进度条的样式:圆形进度条或者水平进度条,默认是圆形进度条,下面的属性可以修改为水平进度条
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
(3)通过代码来控制进度条是否可见,以及动态的更改进度条的进度
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(progressBar.getVisibility()==View.VISIBLE){
progressBar.setVisibility(View.GONE);
}else{
progressBar.setVisibility(View.VISIBLE);
}
}
});
btnT.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
progressBar.setProgress(progressBar.getProgress()+10);
}
});
8、AlertDialog
AlertDialog可以在界面弹出一个对话框,这个对话框置顶于所有元素之上,能够屏蔽其他控件的交互能力,因此,AlertDialog一般用于提示非常重要的信息或警告信息;
测试代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_learn_alert_dialog);
btnCeshi = findViewById(R.id.btnCeshi);
btnCeshi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(LearnAlertDialog.this);
builder.setTitle("This ia a dialog")
.setMessage("you should think successfully")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
});
}
9.ListView
查看QQ聊天、翻阅微博消息的时候都在使用这个控件;
第七章 数据存储全方案,详解持久化技术
7.1持久化技术简介
数据持久化就是指将内存中的瞬时数据保存到存储设备中,保证即使在手机或计算机关机的情况下,这些数据仍然不会丢失;
Android系统中主要提供了3种方式用于简单的实现数据持久化功能:文件存储、SharedPreferences存储以及数据库存储;
7.2文件存储
Context类中提供了一个openFileOutput()方法,可以用于把数据存储到指定的文件中。这个方法接受两个参数:第一个参数是文件名,在文件创建的时候使用,注意,这里指定的文件名不可以包含路径,因为所有的文件都默认存储到/data/data/package name/files/目录下;第二个参数是文件的操作模式,主要有MODE_PRIVATE和MODE_APPEND两种模式可选(其他两种因为容易引起安全漏洞,在Android 4.2版本已被废弃),默认是MODE_PRIVATE,表示当指定相同文件名时,所写入内容将会覆盖原文件中的内容,而MODE_APPEDN则表示如果该文件已存在,就往文件里追加内容,不存在就创建新文件。
openFileOutput()方法返回的是一个FileOutputStream对象,得到这个对象以后就可以使用java流的方式将数据写入文件;
7.2.1从文件读取数据
Context类中提供了一个openFileInput()方法,用于从文件中读取数据,它只接收一个参数:要读取的文件名,系统会自动去加载这个文件,并返回一个FileInputStream对象,得到这个对象之后,再通过流的方式就可以将数据读取出来了;
文件存储用到的核心技术就是Context类中提供的openFileInput()和openFileOutput()方法,之后就是利用各种流来进行读写操作;
文件存储是Android中最基本的数据存储方式,它不对存储的内容进行任何格式化处理,所有数据都是原封不动的保存到文件当中,因而它适合存储一些简单的文本数据或二进制数据,它不适合用于保存一些较为复杂的结构型数据;
简单测试代码:
布局文件:
<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">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="这是要输入要保存的数据"
android:id="@+id/editText"
/>
</LinearLayout>
存储数据:
public class MainActivity extends AppCompatActivity {
private EditText editText;
private String str;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onDestroy() {
super.onDestroy();
editText = (EditText) findViewById(R.id.editText);
str = editText.getText().toString();
save(str);
}
private void save(String str) {
try{
FileOutputStream output = openFileOutput("data", Context.MODE_PRIVATE);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(output,"UTF-8"));
bw.write(str);
bw.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
在打开的时候让存储的数据显示:
public class ReadActivity extends AppCompatActivity {
private String str;
private EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = findViewById(R.id.editText);
try {
str = load();
} catch (IOException e) {
e.printStackTrace();
}
if(!str.isEmpty()){
editText.setText(str);
editText.setSelection(str.length());
Toast.makeText(this,"Restoring succeeded",Toast.LENGTH_SHORT).show();
}
}
private String load() throws IOException {
FileInputStream data = null;
try {
data = openFileInput("data");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BufferedReader reader = new BufferedReader(new InputStreamReader(data));
String str1 = reader.readLine();
return str1;
}
}
7.3 SharedPreferences存储
SharedPreferences是使用键值对的方式存储数据的。也就是说,当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以把相应的值取出来,而且SharedPreferences还支持多种不同的数据类型存储,如果存储的数据类型是整型,那么读取的数据也是整型的;
7.3.1 将数据存储到SharedPreferences中
要想使用SharedPreferences存储数据,首先需要获取SharedPreferences对象。Android主要提供了以下两种方法用于得到SharedPreferences对象。
1.Context类中的getSharedPreferences()方法
此方法接收两个参数:第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data/包名/shared_prefs/目录下的;第二个参数用于指定操作模式,目前只有默认的MODE_PRIVATE这一种模式可选,它和直接传入0的效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写,其他几种操作模式均已被废弃;
2.Activity类中的getPreferences()方法
这个方法和Context中的getSharedPreferences()方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前Activity的类名作为SharedPreferences的文件名;得 到了SharedPreferences对象之后,就可以开始向SharedPreferences文件中存储数据了,主要分3步实现:
(1)调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象;
(2)向SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用putBoolean()方法,添加一个字符串则使用putString()方法,以此类推;
(3)调用apply()方法将添加的数据提交,从而完成数据存储操作;
7.3.2 从SharedPreferences中读取数据
先通过getSharedPreferences()方法获得到SharedPreferences对象,然后分别调用它的getString,getInt()等方法,可以获取到存储的数据;可以看到使用
SharedPreferences存储数据非常简单,读取数据也非常简单,比文本存储方便简单许多,应用场景也多了不少;
简单的测试代码:
java类文件:
public class SharedPreferencesTest extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared_preferences_test);
textView = findViewById(R.id.textView);
findViewById(R.id.btnSave).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();//得到SharedPreferences.Editor
editor.putString("name","Tom");
editor.putInt("age",18);
editor.putBoolean("married",false);
editor.apply();
}
});
findViewById(R.id.btnRestore).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences getdatas = getSharedPreferences("data", Context.MODE_PRIVATE);
textView.setText(getdatas.getAll().toString());
}
});
}
}
布局文件:
<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"
tools:context=".SharedPreferencesTest">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnSave"
android:textAllCaps="false"
android:text="save data"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnRestore"
android:textAllCaps="false"
android:text="Restore data"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView"
android:hint="还原的数据会在这里"
/>
</LinearLayout>
简单小案例:记住密码功能的实现
java类文件:
public class LoginActivity extends AppCompatActivity {
private String account,password;
private Boolean isRemember;
private EditText accountEdit,passwordEdit;
private CheckBox rememberPass;
private SharedPreferences prefs;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
rememberPass = findViewById(R.id.rememberPass);
accountEdit = findViewById(R.id.accountEdit);
passwordEdit = findViewById(R.id.passwordEdit);
prefs = getPreferences(Context.MODE_PRIVATE);
isRemember = prefs.getBoolean("remember_password",false);
//System.out.println("这里是:"+isRemember);
if(isRemember){
account = prefs.getString("account","");
password = prefs.getString("password","");
accountEdit.setText(account);
passwordEdit.setText(password);
rememberPass.setChecked(true);
}
findViewById(R.id.btnLogin).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
account = accountEdit.getText().toString();
//System.out.println(account);
password = passwordEdit.getText().toString();
//System.out.println(password);
if(account.equals("admin") && password.equals("12345")){
System.out.println("执行了");
SharedPreferences.Editor editor = prefs.edit();
if(rememberPass.isChecked()){
editor.putBoolean("remember_password",true);
editor.putString("account",account);
editor.putString("password",password);
}else{
editor.clear();
}
editor.apply();
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}else
Toast.makeText(LoginActivity.this, "account or password is invalid", Toast.LENGTH_SHORT).show();
}
});
}
}
布局文件:
<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"
tools:context=".LoginActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Account:"
/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/accountEdit"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Password:"
/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textPassword"
android:id="@+id/passwordEdit"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/rememberPass"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Remember password"
/>
</LinearLayout>
<Button
android:id="@+id/btnLogin"
android:layout_width="214dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Login" />
</LinearLayout>
7.4 SQLite数据库存储
Android系统内置了数据库,SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,特别适合在移动设备上使用;SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务。SQLite比一般数据库简单,不用设置用户名和密码即可使用。
文件存储和SharedPreferencess存储只适用于保存一些简单的数据和键值对,存储数据量大、结构性复杂的数据库要用SQLite数据库;
7.4.1 创建数据库
Android为了让我们能够更加方便的管理数据库,专门提供了一个叫SQLiteOpenHelper帮助类,借助这个类可以非常简单地对数据库进行创建和升级;
SQLiteOpenHelper帮助类是一个抽象类;里面有两个抽象方法:onCreate()和onUpgrade(),我们必须在自己的帮助类里重写这两个方法,然后分别在这两个方法中实现创建和升级数据库的逻辑;
SQLiteOpenHelper还有两个非常重要的实例方法:getReadableDatabase()和getWritableDatabase();这两个方法都可以创建和打开一个现有的数据库(如果数据库已存在则直接打开,否则要创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间不足),getReadableDatabase()方法返回的对象将以只读的方式打开数据库,而getWritableDatabase()方法则将出现异常。
SQLiteOpenHelper中有两个构造方法可重写,一般使用参数少一点的那个构造方法即可。这个构造方法中接受四个参数:第一参数是Context,必须有它才能对数据库进行操作;第二个参数是数据库名,创建数据库时使用的就是这里指定的名称;第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般传入null即可;第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。构建出SQLiteOpenHelper的实例之后,再调用它的getReadableDatabase()方法或getWritableDatabase()方法就能够创建数据库了,数据库文件放在/data/data/包名/databases/目录下。此时,重写的onCreate()方法也会得到执行,所以通常会在这里处理一些创建表的逻辑。
public class MyDatabaseHelper extends SQLiteOpenHelper{
public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, "db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE user(" +
"id integer primary key autoincrement," +
"author text," +
"price real," +
"pages integer," +
"name text)");
System.out.println("create succeeded");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
7.4.2 升级数据库
onUpgrade()方法是用来升级数据库的,它在整个数据库的管理工作当中起着非常重要的作用;
新增表,运用SOLiteOpenHelper的升级功能(无需再卸载程序再新增)
public class MyDatabaseHelper extends SQLiteOpenHelper{
private String book = "CREATE TABLE user(" +
"id integer primary key autoincrement," +
"author text," +
"price real," +
"pages integer," +
"name text)";
private String book2 = "CREATE TABLE user2(" +
"id integer primary key autoincrement," +
"author text," +
"price real," +
"pages integer," +
"name text)";
private String category = "create table category(id integer primary key autoincrement," +
"catgory_name text," +
"category_code integer)";
public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(book);
db.execSQL(book2);
db.execSQL(category);
System.out.println("succeed");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists user");
db.execSQL("drop table if exists user2");
db.execSQL("drop table if exists category");
onCreate(db);
}
}
public class MainActivity1 extends Activity {
private MyDatabaseHelper db;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mydatebasehelper);
db = new MyDatabaseHelper(this,"BookStore.db",null,3);
findViewById(R.id.btnCreate).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
db.getWritableDatabase();
}
});
}
}
可以看到新增的两个表:
7.4.3 操作数据
对表中的数据进行操作4种:添加(create)、查询(retrieve)、更新(update)、删除(delete)
SQL语言:添加(create)insert、查询(retrieve)select、更新(update)update、删除(delete)delete;
Android提供了一系列的辅助方法,使得在Android中即使不用编写SQL语句,也能轻松完成所有的CRUD操作;
我们知道,调用SQLiteOpenHelper的getReadableDatabase()和getWritableDatabase()方法;是可以创建和升级数据库的,不仅如此,这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作;
(1)添加数据
SQLiteDatabase中提供了一个insert()方法,专门用于添加数据,它接受三个参数:第一个参数是要添加数据的表名,第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值null,一般我们用不到这个功能,直接传入null即可,第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可;
关键代码:
findViewById(R.id.btnAddData).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase sdb = db.getWritableDatabase();
ContentValues values1 = new ContentValues();
values1.put("name","The DAVI");
values1.put("author","chenzhongshi");
values1.put("pages",466);
values1.put("price",89);
sdb.insert("user",null,values1);
Log.i(TAG,"succeed");
}
});
先获取了SQLiteDatabase对象,然后使用ContentValues对要添加的数据进行组装,最后调用SQLiteDatabase的insert()方法把数据添加进去;
(2)更新数据
怎么修改表中已有数据?update()方法,用于对数据进行更新,它接受四个参数:第一个参数,要更新的表名,第二个参数,ContentValues对象,把要更新数据在这里组装进去,第三个参数、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认更新所有行;
findViewById(R.id.btnUpdate).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase sdb = db.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price",19);
sdb.update("user",values,"name=?", new String[]{"The DAVI"});
Log.i(TAG,"Update succeeded");
}
});
这里使用第三个参数、第四个参数来指定具体更新哪几行,第三个参数对应的是SQL语句的where部分,表示更新所有name=?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容;
3、删除数据
delete()方法,三个参数:第一参数表名,第二个和第三个参数用于约束删除某一行或某几行的数据,不指定的话默认删除所有行;
findViewById(R.id.btnDelete).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase sdb = db.getWritableDatabase();
sdb.delete("user","id=?",new String[]{"1"});
}
});
4.查询数据
SQLiteDatabase提供了一个query()方法用于对数据进行查询,这个方法的参数非常复杂,最短一个方法重载也需要传入7个参数;第一个参数表名,第二个参数用于指定查询哪几列,如果不指定则默认查询所有列,第三第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行,第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作,第六个参数用于对group by之后的数据进行进一步过滤,不指定则表示不进行过滤,第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的查询方式;
虽然query()方法的参数非常多,但是多数情况下只需传入几个参数即可完成查询操作;调用query()方法后会返回一个Cursor对象,查询到的所有数据都将从这个对象中取出;
findViewById(R.id.btnQuery).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase sdb = db.getWritableDatabase();
//查询user表中所有数据
Cursor cursor = sdb.query("user",null, null,null,null,null,null);
if(cursor.moveToFirst()){
do{
String name = cursor.getString(cursor.getColumnIndex("name"));
String author= cursor.getString(cursor.getColumnIndex("author"));
Integer pages = cursor.getInt(cursor.getColumnIndex("pages"));
Double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d(TAG,"book name is "+ name);
Log.d(TAG,"book author is "+author);
Log.d(TAG,"book pages is "+pages);
Log.d(TAG,"book price is "+price);
}while (cursor.moveToNext());
}
cursor.close();
}
});
5、使用SQL也可操作数据库
第九章 多媒体
9.2使用通知
创建通知渠道的详细步骤:
首先需要一个NotificationManager对通知进行管理,可以通过调用Context的getSystemService()方法获取。getSystemService()方法接受一个字符串参数用于确定获取系统的哪个服务,这里传入Context.NOTIFICATION_SERVICE。
接下来使用NotificationChannel类创建一个通知渠道,并调用NotificationManager的createNotificationChannel()方法完成创建。
创建一个通知渠道至少需要渠道ID、渠道名称及重要等级3个参数。其中,渠道ID可以随便定义,只要保证全唯一性即可。渠道名称是给用户看的,需要可以清楚的表达这个渠道的用途。通知的重要等级主要有IMPORTANCE_HIGH、IMPORTANCE_LOW、IMPORTANCE_MIN这几种,不同的重要等级会决定通知的不同行为。
9.2.2 通知的基本用法
通知的用法还是比较灵活的,可以在BroadcastReceive/Activity/Service里创建,在Activity里创建通知的场景还是比较少的,因为一般只有当程序进入后台的时候才需要使用通知。
AndroidX库中提供了一个NotificationCompat类,使用这个类的构造器创建Notification对象,就可以保证我们的程序再所有的Android系统版本上都能正常工作。
NotificationCompat.Builder的构造函数中接收两个参数:第一个参数是Context;第二个参数是渠道ID,需要和我们在创建通知渠道时指定的渠道ID相匹配才行。
9.3 调用摄像头和相册
9.3.1 调用摄像头拍照
public class MainActivity extends AppCompatActivity {
private static final int TAKE_PHOTO =1 ;
private Uri imageUri;
private File outputImage;
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.ImageView);
findViewById(R.id.btnTakePhoto).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建File对象,用于存储拍照后的图片
outputImage = new File(getExternalCacheDir(),"outputImage.jpg");
if(outputImage.exists()){
outputImage.delete();
}
try {
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);
}else{
imageUri = Uri.fromFile(outputImage);
}
//启动相机程序
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent,TAKE_PHOTO);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
try {
// 将拍摄的照片显示出来
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
imageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cameraalbumtest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<provider
android:authorities="com.example.cameraalbumtest.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<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"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnTakePhoto"
android:textAllCaps="false"
android:text="Take Photo"
/>
<ImageView
android:id="@+id/ImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="/"/>
</paths>
(未完,学习ing…)