指定应用的包名
package="com.itheima.helloworld"
Android的四大组件在使用前全部需要在清单文件中配置
功能:用户输入一个号码,点击拨打按钮,启动系统打电话的应用把号码拨打出去
组件必须设置宽高,否则不能通过编译
android:layout_width="wrap_content"
android:layout_height="wrap_content"
如果要在java代码中操作某个组件,则组件需要设置id,这样才能在代码中通过id拿到这个组件
android:id="@+id/et_phone"
给按钮设置侦听
//通过id拿到按钮对象
Button bt_call = (Button) findViewById(R.id.bt_call);
//给按钮设置点击
bt_call.setOnClickListener(new MyListener());
//得到用户输入的号码,先拿到输入框组件
EditText et_phone = (EditText) findViewById(R.id.et_phone);
String phone = et_phone.getText().toString();
设置动作,通过意图告知系统
//把号码打出去
//先创建一个意图对象
Intent intent = new Intent();
//设置动作,打电话
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + phone));
//把意图告诉系统
startActivity(intent);
添加权限
定义一个MyListener实现onClickListener接口
Button bt1 = (Button) findViewById(R.id.bt1);
bt1.setOnClickListener(new MyListener());
定义一个匿名内部类实现onClickListener接口
Button bt2 = (Button) findViewById(R.id.bt2);
bt2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("第二种");
}
});
让当前activity实现onClickListener接口
Button bt3 = (Button) findViewById(R.id.bt3);
bt3.setOnClickListener(this);
给Button节点设置onClick属性,
android:onClick="click"
然后在activity中定义跟该属性值同名的方法
public void click(View v){
System.out.println("第四种");
}
功能:用户输入号码和短信内容,点击发送按钮,调用短信api把短信发送给指定号码
输入框的提示
android:hint="请输入号码"
EditText et_phone = (EditText) findViewById(R.id.et_phone);
EditText et_content = (EditText) findViewById(R.id.et_content);
String phone = et_phone.getText().toString();
String content = et_content.getText().toString();
//调用发送短信的api
SmsManager sm = SmsManager.getDefault();
//发送短信
sm.sendTextMessage(phone, null, content, null, null);
* 添加权限
* 如果短信过长,需要拆分
List smss = sm.divideMessage(content);
指定各个节点的排列方向
android:orientation="horizontal"
设置右对齐
android:layout_gravity="right"
线性布局非常重要的一个属性:权重
android:layout_weight="1"
设置组件在指定组件的右边
android:layout_toRightOf="@id/tv1"
设置在指定组件的下边
android:layout_below="@id/tv1"
设置右对齐父元素
android:layout_alignParentRight="true"
设置与指定组件右对齐
android:layout_alignRight="@id/tv1"
指定各个节点的排列方向
android:orientation="horizontal"
设置右对齐
android:layout_gravity="right"
线性布局非常重要的一个属性:权重
android:layout_weight="1"
可以设置上下左右对齐,水平竖直居中,设置方式与线性布局一样
android:layout_gravity="bottom"
表格布局中的节点可以不设置宽高,因为设置了也无效
根节点中可以设置以下属性,表示让第1列拉伸填满屏幕宽度的剩余空间
android:stretchColumns="1"
直接指定组件的x、y坐标
android:layout_x="144dp"
android:layout_y="154dp"
Android提供的日志输出api
Log.v(TAG, "加油吧,童鞋们");
Log.d(TAG, "加油吧,童鞋们");
Log.i(TAG, "加油吧,童鞋们");
Log.w(TAG, "加油吧,童鞋们");
Log.e(TAG, "加油吧,童鞋们");
小案例:用户输入账号密码,勾选“记住账号密码”,点击登录按钮,登录的同时持久化保存账号和密码
弹土司提示用户登录成功
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
判断用户是否勾选保存账号密码
CheckBox cb = (CheckBox) findViewById(R.id.cb);
if(cb.isChecked()){
}
直接开启文件输出流写数据
//持久化保存数据
File file = new File("data/data/com.itheima.rwinrom/info.txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write((name + "##" + pass).getBytes());
fos.close();
读取数据前先检测文件是否存在
if(file.exists())
读取保存的数据,也是直接开文件输入流读取
File file = new File("data/data/com.itheima.rwinrom/info.txt");
FileInputStream fis = new FileInputStream(file);
//把字节流转换成字符流
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String text = br.readLine();
String[] s = text.split("##");
读取到数据之后,回显至输入框
et_name.setText(s[0]);
et_pass.setText(s[1]);
getCacheDir()得到的file对象的路径是data/data/com.itheima.rwinrom2/cache
系统管理应用界面的清除缓存,会清除cache文件夹下的东西,清除数据,会清除整个包名目录下的东西
4.3开始,sd卡路径:storage/sdcard
最简单的打开sd卡的方式
File file = new File("sdcard/info.txt");
写sd卡需要权限
读sd卡,在4.0之前不需要权限,4.0之后可以设置为需要
使用api获得sd卡的真实路径,部分手机品牌会更改sd卡的路径
Environment.getExternalStorageDirectory()
判断sd卡是否准备就绪
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
查找“可用空间”得到
"可用空间"
查找”memory_available”,得到
查找”memory_sd_avail”,得到
//这个字符串就是sd卡剩余容量
formatSize(availableBlocks * blockSize) + readOnly
//这两个参数相乘,得到sd卡以字节为单位的剩余容量
availableBlocks * blockSize
存储设备会被分为若干个区块,每个区块有固定的大小
用SharedPreference存储账号密码
往SharedPreference里写数据
//拿到一个SharedPreference对象
SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE);
//拿到编辑器
Editor ed = sp.edit();
//写数据
ed.putBoolean("name", name);
ed.commit();
从SharedPreference里取数据
SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE);
//从SharedPreference里取数据
String name = sp.getBoolean("name", "");
把整个xml文件所有节点append到sb对象里
sb.append("");
//添加smss的开始节点
sb.append("");
.......
把sb写到输出流中
fos.write(sb.toString().getBytes());
得到xml序列化器对象
XmlSerializer xs = Xml.newSerializer();
给序列化器设置输出流
File file = new File(Environment.getExternalStorageDirectory(), "backupsms.xml");
FileOutputStream fos = new FileOutputStream(file);
//给序列化器指定好输出流
xs.setOutput(fos, "utf-8");
开始生成xml文件
xs.startDocument("utf-8", true);
xs.startTag(null, "smss");
......
InputStream is = getClassLoader().getResourceAsStream("weather.xml");
XmlPullParser xp = Xml.newPullParser();
拿到指针所在当前节点的事件类型
int type = xp.getEventType();
事件类型主要有五种
如果获取到的事件类型不是END_DOCUMENT,就说明解析还没有完成,如果是,解析完成,while循环结束
while(type != XmlPullParser.END_DOCUMENT)
当我们解析到不同节点时,需要进行不同的操作,所以判断一下当前节点的name
当解析到name开始节点时,获取下一个节点的文本内容,temp、pm也是一样
case XmlPullParser.START_TAG:
//获取当前节点的名字
if("weather".equals(xp.getName())){
citys = new ArrayList();
}
else if("city".equals(xp.getName())){
city = new City();
}
else if("name".equals(xp.getName())){
//获取当前节点的下一个节点的文本
String name = xp.nextText();
city.setName(name);
}
else if("temp".equals(xp.getName())){
String temp = xp.nextText();
city.setTemp(temp);
}
else if("pm".equals(xp.getName())){
String pm = xp.nextText();
city.setPm(pm);
}
break;
当解析到city的结束节点时,说明city的三个子节点已经全部解析完了,把city对象添加至list
case XmlPullParser.END_TAG:
if("city".equals(xp.getName())){
citys.add(city);
}
白盒测试
根据测试粒度
根据测试暴力程度
定义一个类继承AndroidTestCase,在类中定义方法,即可测试该方法
在指定指令集时,targetPackage指定你要测试的应用的包名
定义使用的类库
断言的作用,检测运行结果和预期是否一致
创建数据库需要使用的api:SQLiteOpenHelper
必须定义一个构造方法:
//arg1:数据库文件的名字
//arg2:游标工厂
//arg3:数据库版本
public MyOpenHelper(Context context, String name, CursorFactory factory, int version){
}
//创建OpenHelper对象
MyOpenHelper oh = new MyOpenHelper(getContext(), "person.db", null, 1);
//获得数据库对象,如果数据库不存在,先创建数据库,后获得,如果存在,则直接获得
SQLiteDatabase db = oh.getWritableDatabase();
在创建数据库时创建表
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("create table person (_id integer primary key autoincrement, name char(10), phone char(20), money integer(20))");
}
//插入
db.execSQL("insert into person (name, phone, money) values (?, ?, ?);", new Object[]{"张三", 15987461, 75000});
//查找
Cursor cs = db.rawQuery("select _id, name, money from person where name = ?;", new String[]{"张三"});
* 测试方法执行前会调用此方法
protected void setUp() throws Exception {
super.setUp();
// 获取虚拟上下文对象
oh = new MyOpenHelper(getContext(), "people.db", null, 1);
}
* 测试方法执行后会调用此方法:tearDown()
插入
//以键值对的形式保存要存入数据库的数据
ContentValues cv = new ContentValues();
cv.put("name", "刘能");
cv.put("phone", 1651646);
cv.put("money", 3500);
//返回值是改行的主键,如果出错返回-1
long i = db.insert("person", null, cv);
删除
//返回值是删除的行数
int i = db.delete("person", "_id = ? and name = ?", new String[]{"1", "张三"});
修改
ContentValues cv = new ContentValues();
cv.put("money", 25000);
int i = db.update("person", cv, "name = ?", new String[]{"赵四"});
查询
//arg1:要查询的字段
//arg2:查询条件
//arg3:填充查询条件的占位符
Cursor cs = db.query("person", new String[]{"name", "money"}, "name = ?", new String[]{"张三"}, null, null, null);
while(cs.moveToNext()){
// 获取指定列的索引值
String name = cs.getString(cs.getColumnIndex("name"));
String money = cs.getString(cs.getColumnIndex("money"));
System.out.println(name + ";" + money);
}
事务api
try {
//开启事务
db.beginTransaction();
...........
//设置事务执行成功
db.setTransactionSuccessful();
} finally{
//关闭事务
//如果此时已经设置事务执行成功,则sql语句生效,否则不生效
db.endTransaction();
}
任意插入一些数据
读取数据库的所有数据
Cursor cs = db.query(“person”, null, null, null, null, null, null);
while(cs.moveToNext()){
String name = cs.getString(cs.getColumnIndex(“name”));
String phone = cs.getString(cs.getColumnIndex(“phone”));
String money = cs.getString(cs.getColumnIndex(“money”));
//把读到的数据封装至Person对象
Person p = new Person(name, phone, money);
//把person对象保存至集合中
people.add(p);
}
把集合中的数据显示至屏幕
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
for(Person p : people){
//创建TextView,每条数据用一个文本框显示
TextView tv = new TextView(this);
tv.setText(p.toString());
//把文本框设置为ll的子节点
ll.addView(tv);
}
分页查询
Cursor cs = db.query(“person”, null, null, null, null, null, null, “0, 10”);
必须实现的两个方法
第一个
//系统调用此方法,用来获知模型层有多少条数据
@Override
public int getCount() {
return people.size();
}
第二个
//系统调用此方法,获取要显示至ListView的View对象
//position:是return的View对象所对应的数据在集合中的位置
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("getView方法调用" + position);
TextView tv = new TextView(MainActivity.this);
//拿到集合中的元素
Person p = people.get(position);
tv.setText(p.toString());
//把TextView的对象返回出去,它会变成ListView的条目
return tv;
}
在条目中显示一个字符串
String[] objects = new String[]{
"张三",
"李四",
"王五"
};
ListView lv = (ListView) findViewById(R.id.lv);
//arg1:指定要填充的布局文件
//arg2:指定文本显示至哪一个文本框内
lv.setAdapter(new ArrayAdapter(this, R.layout.item_array, R.id.tv_name, objects));
要显示的数据封装在List中,集合的每一个元素存放的是一个条目会显示的数据,因为可能会有多种数据,而集合的泛型只能指定一种数据,所以把数据先存放在Map中,在把Map放入List中
List
通过两个数组的下标对应指定数据存放入对应的控件中
lv.setAdapter(new SimpleAdapter(this, data, R.layout.item_array,
new String[]{“name”, “image”}, new int[]{R.id.tv_name, R.id.iv_photo}));
发送http请求
//1.使用网址构造一个URL对象
URL url = new URL(address);
//2.获取连接对象,并没有建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//3.设置一些属性
//设置连接和读取超时
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//设置请求方法,注意必须大写
conn.setRequestMethod(“GET”);
//建立连接,发送get请求
//conn.connect();
//建立连接,然后获取响应吗,200说明请求成功
conn.getResponseCode();
服务器的图片是以流的形式返回给浏览器的
//拿到服务器返回的输入流
InputStream is = conn.getInputStream();
//把流里的数据读取出来,并构造成图片
Bitmap bm = BitmapFactory.decodeStream(is);
把图片设置为ImageView的显示内容
ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageBitmap(bm);
主线程创建时,消息队列和轮询器对象就会被创建,但是消息处理器对象,需要使用时,自行创建
//消息队列
Handler handler = new Handler(){
//主线程中有一个消息轮询器looper,不断检测消息队列中是否有新消息,如果发现有新消息,自动调用此方法,注意此方法是在主线程中运行的
public void handleMessage(android.os.Message msg) {
}
};
在子线程中使用Handler对象往消息队列里发消息
//创建消息对象
Message msg = new Message();
//消息的obj属性可以赋值任何对象,通过这个属性可以携带数据
msg.obj = bm;
//what属性相当于一个标签,用于区分出不同的消息,从而运行不能的代码
msg.what = 1;
//发送消息
handler.sendMessage(msg);
通过switch语句区分不同的消息
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
//如果是1,说明属于请求成功的消息
case 1:
ImageView iv = (ImageView) findViewById(R.id.iv);
Bitmap bm = (Bitmap) msg.obj;
iv.setImageBitmap(bm);
break;
case 2:
Toast.makeText(MainActivity.this, "请求失败", 0).show();
break;
}
}
把服务器返回的流里的数据读取出来,然后通过文件输入流写至本地文件
//1.拿到服务器返回的输入流
InputStream is = conn.getInputStream();
//2.把流里的数据读取出来,并构造成图片
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len = 0;
while((len = is.read(b)) != -1){
fos.write(b, 0, len);
}
创建bitmap对象的代码改成
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
使用自定义组件时,标签名字要写包名
SmartImageView的使用
SmartImageView siv = (SmartImageView) findViewById(R.id.siv);
siv.setImageUrl("http://192.168.1.102:8080/dd.jpg");
发送GET请求
URL url = new URL(path);
//获取连接对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置连接属性
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//建立连接,获取响应吗
if(conn.getResponseCode() == 200){
}
获取服务器返回的流,从流中把html源码读取出来
byte[] b = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while((len = is.read(b)) != -1){
//把读到的字节先写入字节数组输出流中存起来
bos.write(b, 0, len);
}
//把字节数组输出流中的内容转换成字符串
//默认使用utf-8
text = new String(bos.toByteArray());
乱码的出现是因为服务器和客户端码表不一致导致
//手动指定码表
text = new String(bos.toByteArray(), "gb2312");
get方式提交的数据是直接拼接在url的末尾
final String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + name + "&pass=" + pass;
发送get请求,代码和之前一样
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
if(conn.getResponseCode() == 200){
}
浏览器在发送请求携带数据时会对数据进行URL编码,我们写代码时也需要为中文进行URL编码
String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass;
协议头中多了两个属性
Content-Length: 32,描述提交的数据的长度
//给请求头添加post多出来的两个属性
String data = "name=" + URLEncoder.encode(name) + "&pass=" + pass;
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", data.length() + "");
设置允许打开post请求的流
conn.setDoOutput(true);
获取连接对象的输出流,往流里写要提交给服务器的数据
OutputStream os = conn.getOutputStream();
os.write(data.getBytes());
创建一个客户端对象
HttpClient client = new DefaultHttpClient();
创建一个get请求对象
HttpGet hg = new HttpGet(path);
发送get请求,建立连接,返回响应头对象
HttpResponse hr = client.execute(hg);
获取状态行对象,获取状态码,如果为200则说明请求成功
if(hr.getStatusLine().getStatusCode() == 200){
//拿到服务器返回的输入流
InputStream is = hr.getEntity().getContent();
String text = Utils.getTextFromStream(is);
}
//创建一个客户端对象
HttpClient client = new DefaultHttpClient();
//创建一个post请求对象
HttpPost hp = new HttpPost(path);
往post对象里放入要提交给服务器的数据
//要提交的数据以键值对的形式存在BasicNameValuePair对象中
List parameters = new ArrayList();
BasicNameValuePair bnvp = new BasicNameValuePair("name", name);
BasicNameValuePair bnvp2 = new BasicNameValuePair("pass", pass);
parameters.add(bnvp);
parameters.add(bnvp2);
//创建实体对象,指定进行URL编码的码表
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "utf-8");
//为post请求设置实体
hp.setEntity(entity);
//创建异步的httpclient对象
AsyncHttpClient ahc = new AsyncHttpClient();
//发送get请求
ahc.get(path, new MyHandler());
* 注意AsyncHttpResponseHandler两个方法的调用时机
class MyHandler extends AsyncHttpResponseHandler{
//http请求成功,返回码为200,系统回调此方法
@Override
public void onSuccess(int statusCode, Header[] headers,
//responseBody的内容就是服务器返回的数据
byte[] responseBody) {
Toast.makeText(MainActivity.this, new String(responseBody), 0).show();
}
//http请求失败,返回码不为200,系统回调此方法
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
Toast.makeText(MainActivity.this, "返回码不为200", 0).show();
}
}
使用RequestParams对象封装要携带的数据
//创建异步httpclient对象
AsyncHttpClient ahc = new AsyncHttpClient();
//创建RequestParams封装要携带的数据
RequestParams rp = new RequestParams();
rp.add("name", name);
rp.add("pass", pass);
//发送post请求
ahc.post(path, rp, new MyHandler());
原理:服务器CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源
发送http请求至下载地址
String path = "http://192.168.1.102:8080/editplus.exe";
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
获取文件总长度,然后创建长度一致的临时文件
if(conn.getResponseCode() == 200){
//获得服务器流中数据的长度
int length = conn.getContentLength();
//创建一个临时文件存储下载的数据
RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd");
//设置临时文件的大小
raf.setLength(length);
raf.close();
确定线程下载多少数据
//计算每个线程下载多少数据
int blockSize = length / THREAD_COUNT;
for(int id = 1; id <= 3; id++){
//计算每个线程下载数据的开始位置和结束位置
int startIndex = (id - 1) * blockSize;
int endIndex = id * blockSize - 1;
if(id == THREAD_COUNT){
endIndex = length;
}
//开启线程,按照计算出来的开始结束位置开始下载数据
new DownLoadThread(startIndex, endIndex, id).start();
}
String path = "http://192.168.1.102:8080/editplus.exe";
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
//向服务器请求部分数据
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
conn.connect();
* 下载请求到的数据,存放至临时文件中
if(conn.getResponseCode() == 206){
InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd");
//指定从哪个位置开始存放数据
raf.seek(startIndex);
byte[] b = new byte[1024];
int len;
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
}
raf.close();
}
定义一个int变量记录每条线程下载的数据总长度,然后加上该线程的下载开始位置,得到的结果就是下次下载时,该线程的开始位置,把得到的结果存入缓存文件
//用来记录当前线程总的下载长度
int total = 0;
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
total += len;
//每次下载都把新的下载位置写入缓存文本文件
RandomAccessFile raf2 = new RandomAccessFile(threadId + ".txt", "rwd");
raf2.write((startIndex + total + "").getBytes());
raf2.close();
}
下次下载开始时,先读取缓存文件中的值,得到的值就是该线程新的开始位置
FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String text = br.readLine();
int newStartIndex = Integer.parseInt(text);
//把读到的值作为新的开始位置
startIndex = newStartIndex;
fis.close();
三条线程都下载完毕之后,删除缓存文件
RUNNING_THREAD--;
if(RUNNING_THREAD == 0){
for(int i = 0; i <= 3; i++){
File f = new File(i + ".txt");
f.delete();
}
}
拿到下载文件总长度时,设置进度条的最大值
//设置进度条的最大值
pb.setMax(length);
进度条需要显示三条线程的整体下载进度,所以三条线程每下载一次,就要把新下载的长度加入进度条
定义一个int全局变量,记录三条线程的总下载长度
int progress;
刷新进度条
while((len = is.read(b)) != -1){
raf.write(b, 0, len);
//把当前线程本次下载的长度加到进度条里
progress += len;
pb.setProgress(progress);
每次断点下载时,从新的开始位置开始下载,进度条也要从新的位置开始显示,在读取缓存文件获取新的下载开始位置时,也要处理进度条进度
FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String text = br.readLine();
int newStartIndex = Integer.parseInt(text);
//新开始位置减去原本的开始位置,得到已经下载的数据长度
int alreadyDownload = newStartIndex - startIndex;
//把已经下载的长度设置入进度条
progress += alreadyDownload;
tv.setText(progress * 100 / pb.getMax() + "%");
HttpUtils本身就支持多线程断点续传,使用起来非常的方便
* 创建HttpUtils对象
HttpUtils http = new HttpUtils();
* 下载文件
http.download(url, //下载请求的网址
target, //下载的数据保存路径和文件名
true, //是否开启断点续传
true, //如果服务器响应头中包含了文件名,那么下载完毕后自动重命名
new RequestCallBack() {//侦听下载状态
//下载成功此方法调用
@Override
public void onSuccess(ResponseInfo arg0) {
tv.setText("下载成功" + arg0.result.getPath());
}
//下载失败此方法调用,比如文件已经下载、没有网络权限、文件访问不到,方法传入一个字符串参数告知失败原因
@Override
public void onFailure(HttpException arg0, String arg1) {
tv.setText("下载失败" + arg1);
}
//在下载过程中不断的调用,用于刷新进度条
@Override
public void onLoading(long total, long current, boolean isUploading) {
super.onLoading(total, current, isUploading);
//设置进度条总长度
pb.setMax((int) total);
//设置进度条当前进度
pb.setProgress((int) current);
tv_progress.setText(current * 100 / total + "%");
}
});
标签中如果带有这个子节点,则会在系统中多创建一个快捷图标
activity的名称、图标可以和应用程序的名称、图标不相同
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
Activity的跳转需要创建Intent对象,通过设置intent对象的参数指定要跳转Activity
通过设置Activity的包名和类名实现跳转,称为显式意图
通过指定动作实现跳转,称为隐式意图
跳转至同一项目下的另一个Activity,直接指定该Activity的字节码即可
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
startActivity(intent);
跳转至其他应用中的Activity,需要指定该应用的包名和该Activity的类名
Intent intent = new Intent();
//启动系统自带的拨号器应用
intent.setClassName("com.android.dialer", "com.android.dialer.DialtactsActivity");
startActivity(intent);
隐式意图跳转至指定Activity
Intent intent = new Intent();
//启动系统自带的拨号器应用
intent.setAction(Intent.ACTION_DIAL);
startActivity(intent);
要让一个Activity可以被隐式启动,需要在清单文件的activity节点中设置intent-filter子节点
//获取启动此Activity的intent对象
Intent intent = getIntent();
Uri uri = intent.getData();
Activity通过Intent启动时,可以通过Intent对象携带数据到目标Activity
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("maleName", maleName);
intent.putExtra("femaleName", femaleName);
startActivity(intent);
在目标Activity中取出数据
Intent intent = getIntent();
String maleName = intent.getStringExtra("maleName");
String femaleName = intent.getStringExtra("femaleName");
onCreate–>onStart–>onResume–>onPause–>onStop–>onDestory
onStart–>onResume–>onPause–>onStop
onResume–>onPause
每个应用会有一个Activity任务栈,存放已启动的Activity
Activity的启动模式,修改任务栈的排列情况
singeTask 单一任务栈,在当前任务栈里面只能有一个实例存在
singleInstance启动模式非常特殊, activity会运行在自己的任务栈里面,并且这个任务栈里面只有一个实例存在
默认情况下 ,横竖屏切换, 销毁当前的activity,重新创建一个新的activity
快捷键ctrl+F11
在一些特殊的应用程序常见下,比如游戏,不希望横竖屏切换activity被销毁重新创建
需求:禁用掉横竖屏切换的生命周期
横竖屏写死
android:screenOrientation=”landscape”
android:screenOrientation=”portrait”
让系统的环境 不再去敏感横竖屏的切换。
android:configChanges="orientation|screenSize|keyboardHidden"
开启activity并且获取返回值
startActivityForResult(intent, 0);
在新开启的界面里面实现设置数据的逻辑
Intent data = new Intent();
data.putExtra("phone", phone);
//设置一个结果数据,数据会返回给调用者
setResult(0, data);
finish();//关闭掉当前的activity,才会返回数据
在开启者activity里面实现方法
//通过data获取返回的数据
onActivityResult(int requestCode, int resultCode, Intent data) {
}
原理:接收拨打电话的广播,修改广播内携带的电话号码
* 定义广播接收者接收打电话广播
public class CallReceiver extends BroadcastReceiver {
//当广播接收者接收到广播时,此方法会调用
@Override
public void onReceive(Context context, Intent intent) {
//拿到用户拨打的号码
String number = getResultData();
//修改广播内的号码
setResultData("17951" + number);
}
}
* 在清单文件中定义该广播接收者接收的广播类型
* 接收打电话广播需要权限
* 即使广播接收者的进程没有启动,当系统发送的广播可以被该接收者接收时,系统会自动启动该接收者所在的进程
系统收到短信时会产生一条广播,广播中包含了短信的号码和内容
定义广播接收者接收短信广播
public void onReceive(Context context, Intent intent) {
//拿到广播里携带的短信内容
Bundle bundle = intent.getExtras();
Object[] objects = (Object[]) bundle.get("pdus");
for(Object ob : objects ){
//通过object对象创建一个短信对象
SmsMessage sms = SmsMessage.createFromPdu((byte[])ob);
System.out.println(sms.getMessageBody());
System.out.println(sms.getOriginatingAddress());
}
}
清单文件中配置广播接收者接收的广播类型,注意要设置优先级属性,要保证优先级高于短信应用,才可以实现拦截
添加权限
4.0之后,广播接收者所在的应用必须启动过一次,才能生效
清单文件中定义广播接收者接收的类型,监听SD卡常见的三种状态,所以广播接收者需要接收三种广播
广播接收者的定义
public class SDCardReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 区分接收到的是哪个广播
String action = intent.getAction();
if(action.equals("android.intent.action.MEDIA_MOUNTED")){
System.out.println("sd卡就绪");
}
else if(action.equals("android.intent.action.MEDIA_UNMOUNTED")){
System.out.println("sd卡被移除");
}
else if(action.equals("android.intent.action.MEDIA_REMOVED")){
System.out.println("sd卡被拔出");
}
}
}
清单文件中配置接收开机广播
权限
定义广播接收者
@Override
public void onReceive(Context context, Intent intent) {
//开机的时候就启动勒索软件
Intent it = new Intent(context, MainActivity.class);
context.startActivity(it);
}
手动设置创建新任务栈的flag
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
原理:应用在安装卸载更新时,系统会发送广播,广播里会携带应用的包名
* 清单文件定义广播接收者接收的类型,因为要监听应用的三个动作,所以需要接收三种广播
* 广播接收者的定义
public void onReceive(Context context, Intent intent) {
//区分接收到的是哪种广播
String action = intent.getAction();
//获取广播中包含的应用包名
Uri uri = intent.getData();
if(action.equals("android.intent.action.PACKAGE_ADDED")){
System.out.println(uri + "被安装了");
}
else if(action.equals("android.intent.action.PACKAGE_REPLACED")){
System.out.println(uri + "被更新了");
}
else if(action.equals("android.intent.action.PACKAGE_REMOVED")){
System.out.println(uri + "被卸载了");
}
}
获取电话管理器,设置侦听
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
tm.listen(new MyPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE);
侦听对象的实现
class MyPhoneStateListener extends PhoneStateListener{
//当电话状态改变时,此方法调用
@Override
public void onCallStateChanged(int state, String incomingNumber) {
// TODO Auto-generated method stub
super.onCallStateChanged(state, incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE://空闲
if(recorder != null){
recorder.stop();
recorder.release();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK://摘机
if(recorder != null){
recorder.start();
}
break;
case TelephonyManager.CALL_STATE_RINGING://响铃
recorder = new MediaRecorder();
//设置声音来源
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置音频文件格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setOutputFile("sdcard/haha.3gp");
//设置音频文件编码
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
recorder.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
}
绑定服务
Intent intent = new Intent(this, BanZhengService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
定义这个实现类
class MyServiceconn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
zjr = (PublicBusiness) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
创建实现类对象
conn = new MyServiceconn();
在服务中定义一个类实现Ibinder接口,以在onBind方法中返回
class ZhongJianRen extends Binder implements PublicBusiness{
public void QianXian(){
//访问服务中的banZheng方法
BanZheng();
}
public void daMaJiang(){
}
}
电量改变、屏幕开关,必须使用代码注册
注册广播接收者
//创建广播接收者对象
receiver = new ScreenOnOffReceiver();
//通过IntentFilter对象指定广播接收者接收什么类型的广播
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
//注册广播接收者
registerReceiver(receiver, filter);
解除注册广播接收者
unregisterReceiver(receiver);
设置确定和取消按钮
builder.setPositiveButton("现在自宫", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "恭喜你自宫成功,现在程序退出", 0).show();
}
});
builder.setNegativeButton("下次再说", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "若不自宫,一定不成功", 0).show();
}
});
使用构建器创建出对话框对象
AlertDialog ad = builder.create();
ad.show();
AlertDialog.Builder builder = new Builder(this);
builder.setTitle("选择你的性别");
* 定义单选选项
*
final String[] items = new String[]{
“男”, “女”, “其他”
};
//-1表示没有默认选择
//点击侦听的导包要注意别导错
builder.setSingleChoiceItems(items, -1, new OnClickListener() {
//which表示点击的是哪一个选项
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "您选择了" + items[which], 0).show();
//对话框消失
dialog.dismiss();
}
});
builder.show();
AlertDialog.Builder builder = new Builder(this);
builder.setTitle("请选择你认为最帅的人");
* 定义多选的选项,因为可以多选,所以需要一个boolean数组来记录哪些选项被选了
*
final String[] items = new String[]{
“赵帅哥”,
“赵师哥”,
“赵老师”,
“侃哥”
};
//true表示对应位置的选项被选了
final boolean[] checkedItems = new boolean[]{
true,
false,
false,
false,
};
builder.setMultiChoiceItems(items, checkedItems, new OnMultiChoiceClickListener() {
//点击某个选项,如果该选项之前没被选择,那么此时isChecked的值为true
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
checkedItems[which] = isChecked;
}
});
builder.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
StringBuffer sb = new StringBuffer();
for(int i = 0;i < items.length; i++){
sb.append(checkedItems[i] ? items[i] + " " : "");
}
Toast.makeText(MainActivity.this, sb.toString(), 0).show();
}
});
builder.show();
图片大小 = 图片的总像素 * 每个像素占用的大小
Android系统以ARGB表示每个像素,所以每个像素占用4个字节,很容易内存溢出
获取屏幕宽高
Display dp = getWindowManager().getDefaultDisplay();
int screenWidth = dp.getWidth();
int screenHeight = dp.getHeight();
获取图片宽高
Options opts = new Options();
//请求图片属性但不申请内存
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
int imageWidth = opts.outWidth;
int imageHeight = opts.outHeight;
图片的宽高除以屏幕宽高,算出宽和高的缩放比例,取较大值作为图片的缩放比例
int scale = 1;
int scaleX = imageWidth / screenWidth;
int scaleY = imageHeight / screenHeight;
if(scaleX >= scaleY && scaleX > 1){
scale = scaleX;
}
else if(scaleY > scaleX && scaleY > 1){
scale = scaleY;
}
按缩放比例加载图片
//设置缩放比例
opts.inSampleSize = scale;
//为图片申请内存
opts.inJustDecodeBounds = false;
Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
iv.setImageBitmap(bm);
直接加载的bitmap对象是只读的,无法修改,要修改图片只能在内存中创建出一个一模一样的bitmap副本,然后修改副本
//加载原图
Bitmap srcBm = BitmapFactory.decodeFile("sdcard/photo3.jpg");
iv_src.setImageBitmap(srcBm);
//创建与原图大小一致的空白bitmap
Bitmap copyBm = Bitmap.createBitmap(srcBm.getWidth(), srcBm.getHeight(), srcBm.getConfig());
//定义画笔
Paint paint = new Paint();
//把纸铺在画版上
Canvas canvas = new Canvas(copyBm);
//把srcBm的内容绘制在copyBm上
canvas.drawBitmap(srcBm, new Matrix(), paint);
iv_copy.setImageBitmap(copyBm);
首先定义一个矩阵对象
Matrix mt = new Matrix();
缩放效果
//x轴缩放1倍,y轴缩放0.5倍
mt.setScale(1, 0.5f);
旋转效果
//以copyBm.getWidth() / 2, copyBm.getHeight() / 2点为轴点,顺时旋转30度
mt.setRotate(30, copyBm.getWidth() / 2, copyBm.getHeight() / 2);
平移
//x轴坐标+10,y轴坐标+20
mt.setTranslate(10, 20);
镜面
//把X坐标都变成负数
mt.setScale(-1, 1);
//图片整体向右移(要用post,不能用setTranslate,set会复位)
mt.postTranslate(copyBm.getWidth(), 0);
倒影
//把Y坐标都变成负数
mt.setScale(1, -1);
//图片整体向下移
mt.postTranslate(0, copyBm.getHeight());
记录用户触摸事件的XY坐标,绘制直线
* 给ImageView设置触摸侦听,得到用户的触摸事件,并获知用户触摸ImageView的坐标
iv.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
//触摸屏幕
case MotionEvent.ACTION_DOWN:
//得到触摸屏幕时手指的坐标
startX = (int) event.getX();
startY = (int) event.getY();
break;
//在屏幕上滑动
case MotionEvent.ACTION_MOVE:
//用户滑动手指,坐标不断的改变,获取最新坐标
int newX = (int) event.getX();
int newY = (int) event.getY();
//用上次onTouch方法得到的坐标和本次得到的坐标绘制直线
canvas.drawLine(startX, startY, newX, newY, paint);
iv.setImageBitmap(copyBm);
startX = newX;
startY = newY;
break;
}
return true;
}
});
* 刷子效果,加粗画笔
paint.setStrokeWidth(8);
* 调色板,改变画笔颜色
paint.setColor(Color.GREEN);
* 保存图片至SD卡
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("sdcard/dazuo.png"));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//保存图片
copyBm.compress(CompressFormat.PNG, 100, fos);
* 系统每次收到SD卡就绪广播时,都会去遍历sd卡的所有文件和文件夹,把遍历到的所有多媒体文件都在MediaStore数据库保存一个索引,这个索引包含多媒体文件的文件名、路径、大小
* 图库每次打开时,并不会去遍历sd卡获取图片,而是通过内容提供者从MediaStore数据库中获取图片的信息,然后读取该图片
* 系统开机或者点击加载sd卡按钮时,系统会发送sd卡就绪广播,我们也可以手动发送就绪广播
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
sendBroadcast(intent);
原理:把穿内衣和穿外衣的照片重叠显示,内衣照在下面,用户滑动屏幕时,触摸的是外衣照,把手指经过的像素都置为透明,内衣照就显示出来了
iv.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
int newX = (int) event.getX();
int newY = (int) event.getY();
//把指定的像素变成透明
copyBm.setPixel(newX, newY, Color.TRANSPARENT);
iv.setImageBitmap(copyBm);
break;
}
return true;
}
});
每次只设置一个像素点太慢,以触摸的像素为圆心,半径为5画圆,圆内的像素全部置为透明
for (int i = -5; i < 6; i++) {
for (int j = -5; j < 6; j++) {
if(Math.sqrt(i * i + j * j) <= 5)
copyBm.setPixel(newX + i, newY + j, Color.TRANSPARENT);
}
}
服务里定义play、stop、pause、continuePlay等方法
private void play() {
// TODO Auto-generated method stub
player.reset();
try {
player.setDataSource("sdcard/bzj.mp3");
player.prepare();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
player.start();
}
private void pause() {
player.pause();
}
private void stop() {
player.stop();
}
private void continuePlay() {
player.start();
}
先start启动MusicService,再bind
Intent intent = new Intent(this, MusicService.class);
startService(intent);
bindService(intent, conn, BIND_AUTO_CREATE);
获取当前的播放时间和当前音频的最长时间
int currentPosition = player.getCurrentPosition();
int duration = player.getDuration();
发消息至Handler,把播放进度放进Message对象中,在Handler中更新SeekBar的进度
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
int currentPosition = player.getCurrentPosition();
int duration = player.getDuration();
Message msg = Message.obtain();
//把播放进度存入Message中
Bundle data = new Bundle();
data.putInt("currentPosition", currentPosition);
data.putInt("duration", duration);
msg.setData(data);
MainActivity.handler.sendMessage(msg);
}
}, 5, 500);
在Activity中定义Handler
static Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
//取出消息携带的数据
Bundle data = msg.getData();
int currentPosition = data.getInt("currentPosition");
int duration = data.getInt("duration");
//设置播放进度
sb.setMax(duration);
sb.setProgress(currentPosition);
};
};
//给sb设置一个拖动侦听
sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
//停止拖动时调用
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
int progress = seekBar.getProgress();
mi.seekTo(progress);
}
//开始拖动时调用
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
//拖动的时候不断调用
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
}
});
播放视频也是用MediaPlayer,不过跟音频不同,要设置显示在哪个SurfaceView
SurfaceView sv = (SurfaceView) findViewById(R.id.sv);
SurfaceHolder sh = sv.getHolder();
MediaPlayer player = new MediaPlayer();
player.reset();
try {
player.setDataSource("sdcard/2.3gp");
player.setDisplay(sh);
player.prepare();
} catch (Exception e) {
e.printStackTrace();
}
player.start();
给SurfaceHolder设置CallBack,类似于侦听,可以知道SurfaceView的状态
sh.addCallback(new Callback() {
//SurfaceView销毁时调用
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
//SurfaceView创建时调用
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
启动系统提供的拍照程序
//隐式启动系统提供的拍照Activity
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//设置照片的保存路径
File file = new File(Environment.getExternalStorageDirectory(), "haha.jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
startActivityForResult(intent, 0);
启动系统提供的摄像程序
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
File file = new File(Environment.getExternalStorageDirectory(), "haha.3gp");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
//设置保存视频文件的质量
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
startActivityForResult(intent, 0);
自定义内容提供者,继承ContentProvider类,重写增删改查方法,在方法中写增删改查数据库的代码,举例增方法
@Override
public Uri insert(Uri uri, ContentValues values) {
db.insert("person", null, values);
return uri;
}
在清单文件中定义内容提供者的标签,注意必须要有authorities属性,这是内容提供者的主机名,功能类似地址
创建一个其他应用,访问自定义的内容提供者,实现对数据库的插入操作
public void click(View v){
//得到内容分解器对象
ContentResolver cr = getContentResolver();
ContentValues cv = new ContentValues();
cv.put("name", "小方");
cv.put("phone", 138856);
cv.put("money", 3000);
//url:内容提供者的主机名
cr.insert(Uri.parse("content://com.itheima.person"), cv);
}
添加匹配规则
//指定多条uri
um.addURI("com.itheima.person", "person", PERSON_CODE);
um.addURI("com.itheima.person", "company", COMPANY_CODE);
//#号可以代表任意数字
um.addURI("com.itheima.person", "person/#", QUERY_ONE_PERSON_CODE);
通过Uri匹配器可以实现操作不同的表
@Override
public Uri insert(Uri uri, ContentValues values) {
if(um.match(uri) == PERSON_CODE){
db.insert("person", null, values);
}
else if(um.match(uri) == COMPANY_CODE){
db.insert("company", null, values);
}
else{
throw new IllegalArgumentException();
}
return uri;
}
如果路径中带有数字,把数字提取出来的api
int id = (int) ContentUris.parseId(uri);
ContentResolver cr = getContentResolver();
Cursor c = cr.query(Uri.parse("content://sms"), new String[]{"body", "date", "address", "type"}, null, null, null);
while(c.moveToNext()){
String body = c.getString(0);
String date = c.getString(1);
String address = c.getString(2);
String type = c.getString(3);
System.out.println(body+";" + date + ";" + address + ";" + type);
}
ContentResolver cr = getContentResolver();
ContentValues cv = new ContentValues();
cv.put("body", "您尾号为XXXX的招行储蓄卡收到转账1,000,000人民币");
cv.put("address", 95555);
cv.put("type", 1);
cv.put("date", System.currentTimeMillis());
cr.insert(Uri.parse("content://sms"), cv);
* 插入查询系统短信需要注册权限
先查询raw_contacts表拿到联系人id
Cursor cursor = cr.query(Uri.parse("content://com.android.contacts/raw_contacts"), new String[]{"contact_id"}, null, null, null);
然后拿着联系人id去data表查询属于该联系人的信息
Cursor c = cr.query(Uri.parse("content://com.android.contacts/data"), new String[]{"data1", "mimetype"}, "raw_contact_id = ?", new String[]{contactId}, null);
得到data1字段的值,就是联系人的信息,通过mimetype判断是什么类型的信息
while(c.moveToNext()){
String data1 = c.getString(0);
String mimetype = c.getString(1);
if("vnd.android.cursor.item/email_v2".equals(mimetype)){
contact.setEmail(data1);
}
else if("vnd.android.cursor.item/name".equals(mimetype)){
contact.setName(data1);
}
else if("vnd.android.cursor.item/phone_v2".equals(mimetype)){
contact.setPhone(data1);
}
}
把确定的联系人id插入raw_contacts表
cv.put("contact_id", _id);
cr.insert(Uri.parse("content://com.android.contacts/raw_contacts"), cv);
在data表插入数据
插3个字段:data1、mimetype、raw_contact_id
cv = new ContentValues();
cv.put("data1", "赵六");
cv.put("mimetype", "vnd.android.cursor.item/name");
cv.put("raw_contact_id", _id);
cr.insert(Uri.parse("content://com.android.contacts/data"), cv);
cv = new ContentValues();
cv.put("data1", "1596874");
cv.put("mimetype", "vnd.android.cursor.item/phone_v2");
cv.put("raw_contact_id", _id);
cr.insert(Uri.parse("content://com.android.contacts/data"), cv);
当数据库数据改变时,内容提供者会发出通知,在内容提供者的uri上注册一个内容观察者,就可以收到数据改变的通知
cr.registerContentObserver(Uri.parse("content://sms"), true, new MyObserver(new Handler()));
class MyObserver extends ContentObserver{
public MyObserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
//内容观察者收到数据库发生改变的通知时,会调用此方法
@Override
public void onChange(boolean selfChange) {
}
}
在内容提供者中发通知的代码
ContentResolver cr = getContext().getContentResolver();
//发出通知,所有注册在这个uri上的内容观察者都可以收到通知
cr.notifyChange(uri, null);
定义布局文件作为Fragment的显示内容
//此方法返回的View就会被显示在Fragment上
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
//用布局文件填充成一个View对象,返回出去,那么就显示在Fragment上了
View v = inflater.inflate(R.layout.fragment01, null);
return v;
}
把Fragment显示至指定ViewGroup中
//把fragment显示至界面
//new出fragment对象
Fragment01 fg = new Fragment01();
FragmentManager fm = getFragmentManager();
//开启事务
FragmentTransaction ft = fm.beginTransaction();
//把fragment对象显示到指定资源id的组件里面
ft.replace(R.id.fl, fg);
ft.commit();
一张张图片不断的切换,形成动画效果
在drawable目录下定义xml文件,子节点为animation-list,在这里定义要显示的图片和每张图片的显示时长
在屏幕上播放帧动画
ImageView iv = (ImageView) findViewById(R.id.iv);
//把动画文件设置为imageView的背景
iv.setBackgroundResource(R.drawable.animations);
AnimationDrawable ad = (AnimationDrawable) iv.getBackground();
//播放动画
ad.start();
参数150指的是X的终点坐标,它的值是imageview的 真实X + 150
//创建为位移动画对象,设置动画的初始位置和结束位置
TranslateAnimation ta = new TranslateAnimation(10, 150, 20, 140);
y坐标的终点位置,如果传入2,那么终点坐标就是 真实Y + 2 * iv高度
TranslateAnimation ta = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 2);
动画播放相关的设置
//设置动画持续时间
ta.setDuration(2000);
//动画重复播放的次数
ta.setRepeatCount(1);
//动画重复播放的模式
ta.setRepeatMode(Animation.REVERSE);
//动画播放完毕后,组件停留在动画结束的位置上
ta.setFillAfter(true);
//播放动画
iv.startAnimation(ta);
缩放的中心点在iv左上角
ScaleAnimation sa = new ScaleAnimation(0.1f, 4, 0.1f, 4);
中心点Y坐标 = 真实Y + 0.5 * iv高度
ScaleAnimation sa = new ScaleAnimation(0.1f, 4, 0.1f, 4, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
0为完全透明,1为完全不透明
AlphaAnimation aa = new AlphaAnimation(0, 0.5f);
默认旋转的圆心在iv左上角
RotateAnimation ra = new RotateAnimation(20, 360);
圆心的Y坐标:真实Y + iv高度 * 0.5
RotateAnimation ra = new RotateAnimation(20, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
//创建动画集合
AnimationSet set = new AnimationSet(false);
//往集合中添加动画
set.addAnimation(aa);
set.addAnimation(sa);
set.addAnimation(ra);
iv.startAnimation(set);
传入100,代表x终点坐标:当前x + 100
//具有get、set方法的成员变量就称为属性
ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "translationX", 0, 100) ;
2是到原本高度的2倍结束
ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "scaleY", 0.1f, 2);
透明度,0是完全透明,1是完全不透明
ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "alpha", 0.1f, 1);
270是结束角度
ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "rotation", 20, 270);
属性指定为rotationY是水平翻转
ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "rotationY", 20, 180);
第三个参数可变参数可以传入多个参数,可以实现往回位移(旋转、缩放、透明)
ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "translationX", 0, 70, 30, 100) ;
//创建动画师集合
AnimatorSet set = new AnimatorSet();
//设置要播放动画的组件
set.setTarget(bt);
//所有动画有先后顺序的播放
//set.playSequentially(oa, oa2, oa3, oa4);
//所有动画一起播放
set.playTogether(oa, oa2, oa3, oa4);
set.start();