在大学期间,我的Android跌跌撞撞的学了一年。我上学时代对于知识的整理还是蛮痴迷的,可惜当时不会使用markdown(富文本让我头昏脑胀)。如今在利用晚上睡觉、工作之余把大学的Android用markdown整理了一遍。供学弟学妹们参考书使用。
超长篇!!大量小白(学生)操作!!高手止步!!
当然,后续笔记中,我会补充很多企业级写法、常用框架、底层代码、flutter、RN等,以供大家参考!
第一节 基础布局
- 0.常用属性
layout_width[宽]、layout_height[高] 其属性包含(match_parent填充窗体、wrap_content包裹内容)
layout_marginTop上外边距
layout_weight(设置权重(要实施权重的方向不能设置为填充))
- 1.线性布局(LinearLayout)
它主要以水平与垂直的方式来显示界面控件。当控件水平排列时(显示从左到右),当控件垂直排列时,显示顺序从上到下。
线性布局中有一个非常重要的属性orientation。用于控制排列方向。(两个值horizontal[水平]于vertical[垂直])
如何让线形布局中的控件居中呢?
layout_gravity属性只有根布局是LinearLayout的情况下才能使用。表示子布局在父布局中的显示方式:
layout_gravity有以下几种值:
center:居中(横向和纵向均居中)
center_horizontal:水平居中
center_vertical:垂直居中
- 2.相对布局(RelateiveLayout)
相对布局是通过相对定位的方式指定控件位置,在设计的时候要遵循控件之间的依赖关系。其内部的属性较多,比较常用的是以下几个
layout_centerInParent位于父布局中央位置
layout_centerVertical位于父布局垂直居中位置
layout_centerHorizontal位于父布局水平居中位置
layout_above当前控件位于某控件上方
layout_below当前控件位于某控件下方
layout_toLeftOf当前控件位于某控左侧
layout_toRightOf当前控件位于某控右侧
layout_alignBottom和某控件的底部对齐
layout_alignParentBottom当前控件是否与父控件底部对齐
- 3.帧布局(frameLayout)
帧布局是最简单的一种布局模式,该布局为每一个控件都创造一个空白区域。安装先后顺序重叠摆放(先放入的显示在最底层)
foreground属性设置容器前景(始终在所有子控件至上)
- 4.表格布局(TableLayout)
表格布局是以表格形式排列控件的,表格布局需要tableRow配合使用,每一行都由tableRow决定
-
5.绝对布局(AbsoluteLayout)「已经废弃」
-
6.网格布局(GridLayout)「android4.0出现」
-
7.约束布局(ConstraintLayout)「维护太费劲」
第二节 Android常用控件
- 1.TextView 用于显示文字信息
gravity="center" 居中显示
- 2.EditText 继承于TextView进行可编辑操作(文本框)
hint="提示" //用于文本框提示
maxLines="1" //设置最大行数
backgroud="@null" //不设置会出现一道蜜汁下划线
- 3.Button 按钮 响应点击事件
/*
*三种实现方式(java8以后lambda表达式也成为第四种大家较为常用的方式之一)
*/
//1. 用onClick="click"标签 在对应的activity中实现该方法
Button myBtn_one = (Button)findViewById(R.id.bt_one);
public void click(View view){//参数很重要
myBtn_one.setText(按钮已被点击);
}
//2. 用匿名内部类实现
myBtn_one.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
myBtn_one.setText(按钮已被点击);
}
})
//3. 在当前Activity实现OnClickListener接口
myBtn_one.setOnClickListener(this);
public void onClick(View v){
switch(v.getId){
case R.id.bt_one:
break;
default:
break;
}
}
- 4.RadioButton单选按钮
//RadioButton必须放到RadioGroup中去
RadioGroup rg=(RadioGroup)findViewById(R.id.bt_two);
rg.setOnClickedChangedListener(new RadioGroup.OnClickedChangedListener{
public void onCheckedChanged(RadioGroup rg,int checkedId){
if(checkedId == R.id.rbtn){
//具体操作
}
}
})
- 5.ImageView 视图控件
可以从各种来源加载图像,并提供缩放、裁剪、着色等功能。
backgroud="@drawable/bg"//拉伸
src="@drawable/bg"//不拉伸
第三节 常见对话框
对话框是程序与用户交互的一种方式,通常用于显示当前程序提示信息以及相关说明,以小窗口形式展现。常见有以下几种:
- 1.普通对话框
链式调用:
AlertDialog ad=new AlertDialog.Builder(this)
.setTitle("Dialog对话框")//设置标题
.setMessage("是否确定退出")//设置提示信息
.setIcon(R.mipmap.ic_launcher)//设置图标
.setPositiveButton("确定",null)//添加确定按钮
.setNegetiveButton("取消",null)//设置取消按钮
.create();
ad.show();
2.单选对话框
AlertDialog ad=new AlertDialog.Builder(this)
.setTitle("请选择性别")//设置标题
.setIcon(R.mipmap.ic_launcher)//设置图标
.setPositiveButton("确定",null)//添加确定按钮
.setSingleChoiceItems(new String[]{"男","女"},0,new DialogInterFace.OnClickListener(){
/*参数0表示默认选择第一个,-1可表示没有默认选中*/
public void onClick(DialogInterFace Dialog,int which){
}
})
.create();
ad.show();
3.多选对话框
AlertDialog ad=new AlertDialog.Builder(this)
.setTitle("请选择兴趣爱好")//设置标题
.setIcon(R.mipmap.ic_launcher)//设置图标
.setPositiveButton("确定",null)//添加确定按钮
.setMultiChoiceItems(new String[]{"旅游","美食","汽车","购物"},null,null)
.create();
ad.show();
4.进度条对话框
ProgressDialog dialog=new ProgressDialog(this);
dialog.setTitle("进度条对话框");
dialog.setIcon(R.mipmap.ic_launcher);
dialog.setMessage("正在下载请稍后")
dialog.setprogressStyle(ProgressDialog.STYLE_HORIZONTAL);//设置样式
dialog.show;
5.消息对话框
Toast.makeText(this,"提示信息",Toast.LENGTH.LONG).show();
6.自定义对话框
自己做一个对话框my_dialog.xml,建一个类 继承系统的 DiaLlog.
class MyDialog extends Dialog{
private TextView tvMsg;
private String dialogName;
private Button btnOK;//确认按钮
private Button btnCancel;//取消按钮
public MyDialog(Context context,String dialogName){
super(context);
this.DialogName=dialogName;
}
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
requestWindFeature(Windos.FEATURE_NO_TITLE);//去处标题
setContentView(R.layout_mydialog);//引入自定义的对话框布局文件
tvMsg=(TextView)findViewByIdViewById(R.id.tv_msg);
Button btnOK =(Button)findViewByIdViewById(R.id.tv_ok);
Button btnCancel =(Button)findViewByIdViewById(R.id.tv_Cancel);
vMsg.setText(dialogName);
btnOK.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
}
});
btnCancel.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
dismiss();
}
});
}
}
调用:
MyDialog myDialog=new MyDialog(this,"我是要提示的字");
myDialog.show;
第四节 view其他细节
一、样式(一般作用在控件上)
在res-->values-->styles.xml中:
<style name="textStyle"><!--也可以继承(parent="")-->
<item name="android:layout_width">match_parent</item>
</style>
使用的时候
style="@style/textStyle"
二、主题(一般作用在activity或者application上)
在res-->values-->styles.xml中
内容与样式相同
使用的时候: 在清单文件AndroidManifest.xml
<activity android:name=".MainActivity" android:theme="@style/textStyle">//加入该行
三、国际化(别名 i18n)
在res-->values-->创建string.xml文件、选择locale 选择相应国家然后编写属性(可以加多个国家)
四、单元测试
在项目创建之时,软件已经为我们创建了androidTest包与aoolication类,所有测试功能模块写入此类即可
五、LogCat的使用
六、debug调试
F8走到下一行 绿色播放键 走到下一个断点, F7走入方法内部
七、其他
android焦点的概念 就相当于windows中的光标
像你做登录程序的话,密码输入错误,焦点是不是要聚焦密码框好点,这样就不用鼠标去点密码框然后再输入密码,这样有利于用户体验。
比如在一个界面上你点击按钮,那么焦点就聚集在这个按钮上。
八、两种得到上下文的方法
1.因为activity继承了activity所以可以用this
2.直接用getApplicationContext()
第五节 Android中的动画
一.帧动画(Drawable Animation)【2.3不兼容(认为是耗时操作)】
就是加载一系列的图片资源,在drawable下建立一个xml文件[例:dome.xml]
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true"> <!--是否执行一次 -->
<item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
<item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
<item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>
在显示页面
ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.dome);//设置背景
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();//加载类型
rocketAnimation.start();//开启动画
二.view动画(也叫补间动画)
- 【1】透明AlphaAnim
ImageView Image = (ImageView) findViewById(R.id.rocket_image);
//1表示完全不透明,0表示完全透明
AlphaAnimation aa = new AlphaAnimation(1.0f,0.0f)
aa.setDuration(2000);//执行时间
aa.setRepeatCount(1);//重复次数(不重复为0)
aa.setRepeatMode(Animation.REVERSE);//动画效果
Image.startAnimation(aa);
- 【2】旋转rotateAnim
//RotateAnimation ra=new RotateAnimation(0,360)
RotateAnimation ra=new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,RELATIVE_TO_SELF,0.5f);
ra.setDuration(2000);//执行时间
ra.setRepeatCount(1);//重复次数(不重复为0)
ra.setRepeatMode(Animation.REVERSE);//动画效果
Image.startAnimation(ra);
- 【3】缩放scaleAnim
ScaleAnimation sa=new ScaleAnimation(1f,2f,1f,2f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f)//放大(前四个参数)
sa.setDuration(2000);//执行时间
sa.setRepeatCount(1);//重复次数(不重复为0)
sa.setRepeatMode(Animation.REVERSE);//动画效果
Image.startAnimation(as);
- 【4】位移translateAnim(不会改变真实位置)
- 【5】一起用
AnimationSet set =new AnimationSet(true)
xml方式 定义补间动画
res目录下创建anim文件夹,建立alpha文件
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0f"
android:toAlpha="0.0f"
android:duration="3000"
android:repeatCount="1"
android:repeatMode="restart" />
Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim);
Image.startAnimation(animation);
三.属性动画(真实的改变控件)
ObjectAnimator oa=ObjectAnimator.ofFloat(iv."scaleY",0.1f,2,1,2);
oa.setDuration(2000);
oa.start();
第六节 Android中Activity
一个activity管理一个页面
setContentView(R.layout.factivity);决定加载哪个布局
一.activity的创建:
(五个状态、六个方法)
启动、运行、暂停、停止、销毁
onCreate()创建时调用、onStart()即将可见时调用、onResum()获取焦点与用户交互时调用
onPause()被覆盖或锁屏时调用、onStop()对用户不开见时调用、onRestart()从停止再启动时调用、onDestroy()销毁时调用
二.android的任务栈
一个应用对应一个任务栈(点开应用就会建立)
当页面启动时,页面对应的activity进栈。
按后退键,页面对应的activity就出栈
用户操作的页面永远是栈顶页面
三.activity的四种启动模式
在清单文件中。每一个activity都对应一个<activity>标签、其中的android:launchMode声明了启动方式
<activity android:name=".MainActivity" android:launchMode="standard">
默认为standard(即标准启动):每一次启动activity就在任务栈顶创建一个新的实例。
singleTop:创建检查任务栈栈顶,如果有实例就会复用(覆盖)该实例,不会创建新实例(浏览器书签)
singleTask:创建检查整个任务栈,如果栈内出现相同activity即复用该实例,并把该activity之上的实例全部清空(保证只有实例存在,浏览器页面)
singleInstance:会为实例单独创建任务栈(系统电话来电)
四.意图(Intent)
主要用于activity、server、发送广播
根据开启组件的方式不同,意图被分为两种(显式意图、隐式意图)
- 显式意图:可以通过名称开启、指定目标组件
Intent intent=new Intent(this,MainActivity.class);
//参数:上下文+目标activity的字节码文件(指定具体类名)
startActivity(intent);
- 隐式意图:通过指定action和category等属性,系统根据这些属性进行分析后寻找activity清单文件的意图过滤器:(action表示执行动作,category表示条件)
<intent-filter>
<action android:name="cn.yijiajia.hh" />
<category android:name="android.intent.category.LAUNCHER"/>(.LAUNCHER表示启动页面;.DELAULT表示普通)
</intent-filter>
Intent intent=new Intent();
intent.setAction("cn.yijiajia.hh");
intent.addCategory("android.intent.category.DELAULT");
startActivity(intent);
使用时间(显示意图更安全)
1.开启自己写的界面用显示意图
2.开启其他应用的界面(大多数指系统应用)用隐式意图
第七节 Activity操作
一、Activity之间的跳转
二、Activity之间数据传递
1.打开网页:
intent.setDate(Uri:parse("http://www.baidu.com"));
startActivity(intent);
2.传递信息:
Intent intent=new Intent(this,MainActivity.class);
intent.putExtra("date","你好");//背地里用map进行封装
startActivity(intent);
Intent intent=getIntent();
String date =intent.getStringExtra("date");// String date data=intent.getStringExtra("date","默认");
3.数据的回传
- 页面一
Intent intent=new Intent(this,Activity2.class);
startActivityForResult(intent,1);
//使用startActivityForResult方法开启一个新的activity,第一个参数是intent对象,第二个参数是请求码,用于判断来源
- 页面二
Intent intent=new Intent();
intent.putExtra("date","你好");
setResult(1,intent);
- 页面一
protected void onActivityResult(int requestCode,int resultCode,Intent date){
//本页面开启的页面关闭的时候执行(做数据回传的时候调用)[resultCode判断数据从哪来]
super.onActivityResult(requestCode,resultCode,date);
if(requestCode==1){
if(resultCode==1){
String string=data.getStringExtra("data")
}
}
}
4.关闭页面 finish();
四、Activity的启动模式
五、常见activity
1.拨打电话
Intent intent = new Inetent();
intent.setAction(Intent.ACTION_CALL);
intent.setDate(Uri.paese("tel:"+119));
startActivity(intent);
2.发短信
SmsManager smsManager=SmsManager.getDefault();
//1.一起发
smsManager.sendTextMessage("电话号码",null,"短信内容",null,null);
//2.分条发
String text="短信内容";
ArrayList<String> divide=smsManager.divideMessage(text);
for (String s:divide){
smsManager.sendTextMessage("电话号码",null,s,null,null);
}
第八节 二种数据存储格式
一、XML解析
- 1.DOM解析
将XML文件中所有内容以DOM数形式存放在内存中,支持删除、修改等功能,缺点是消耗内存大。
- 2.SAX解析
逐行扫描XML文件,读取文件的同时即可以进行解析处理,不必等文件加载结束。缺点是无法进行增删改查
- 3.PULL解析
一个开源的JAVA项目既可以用于AndRoid应用,也可以用于JavaEE程序。Android中已经继承了PULL解析器
- 天气预报
<?xml version="1.0" encoding="utf-8"?>
<infos>
<city id="sh">
<temp>20℃/30℃</temp>
<weather>晴天多云</weather>
<name>上海</name>
<pm>80</pm>
<wind>1级</wind>
</city>
<city id="bj">
<temp>26℃/32℃</temp>
<weather>晴天</weather>
<name>北京</name>
<pm>98</pm>
<wind>3级</wind>
</city>
</infos>
</xml>
public static List<WeatherBean>getInfromXml(InputStream is)throws Exception{
List<WeatherBean> weatherInfos=null;
WeatherBean weatherBean=null;
//获取pull解析器
XmlPullParser parser=Xml.newPullParser();
//告诉要解析的xml文件
parser.setInput(is,"utf-8");
//得到当前事件类型
int type =parser.getEventType();
//不到文件结尾 就一直解析
while(type!=XmlPullParser.END_DOCUMENT){
switch(type){
case XmlPullParser.START_TAG ://解析的标签是开始标签
if("infos".equals(parser.getName())){
weatherInfos =new ArrayList<WeatherBean>();
}else if("city".equals(parser.getName())){
weatherBean=new weatherBean();
String id= parser.getAttributeValue(0);
weatherBean.setId(id);
}else if("temp".equals(parser.getName())){
String temp= parser.getAttributeValue(0);
weatherBean.settemp(temp)
}
break;
case XmlPullParser.END_TAG :
if("city".equals(parser.getName())){
weatherInfos.add(weatherBean);
}
break;
}
type=parser.next();
}
return weatherInfos;
}
二、JSON数据
JSON即JavaScript Object Notation (对象表示法),是一种轻量级的数据交换格式
JSON是基于纯文本的数据格式、可以传播String、Number、Boolean类型的数据,也可以传输数组,或者Object对象
JSON的扩展名为.json
JSON分为JSON对象和JSON数组两种数据结构
例子
{"name":"zhangsan" "address":{"city":"bejing" "postcode":100096}}
{"name":"zhangsan" "hobby":["羽毛球" "篮球"]}
JSON解析
{"name":"zhangsan","age":27,"married":true}
[16,2,26]
//解析对象
JSONObject jsonObj = new JSONObject(json1);
String name= jsonObj.optString("name");
int age =jsonObj.optInt("age");
//解析数组
JSONObject jsonObj = new JSONObject(json1);
for(int i=0;i<jsonArray.length();i++){
int age =jsonArray.optInt(i);
}
//注意:optXXX()比getXXX方法更安全,不会抛出异常,得不到返回null或者0
2.用Gson库解析对象
//对象
Gson gson=new Gson();
Person person =gson.fromJson(json1,Person.class);
//数组
Gson gson=new Gson();
Type listType = new TypeToken<List<Integer>>(){}.getType();
List<Integer>ages =gson.fromJson(json2,listType);
················
第九节 Android中的五种数据存储方式
Android中有五种数据存储方式,它们分别是:
1.文件存储
2.SharedPreferences 存储简单配置信息(xml文件)
3.SQLiet数据库
4.ContentProvider(内容提供者) 可以将数据共享给其他应用
5.网络存储
一、文件存储的简介
- 文件存储是Android中最基本的一种存储方式,它分成两种存储类型 内部存储、外部存储
内部存储写入
String fileName="data.txt";
String content="helloworld";
FileOutput fos;
try{
fos =openFileOutput(fileName,MODE_PRIVATE);
fos.write(content.getBytes());
fos.close;
}catch(Exception e){
e.printStackTrace();
}
外部存储
String state=Environment.getExternalStorageState();
if(stare.equals(Environment.MEDIA_MOUNTED)){//判断sd卡是否可用
File SDPath=Environment.getExternalStorageStateDirectory();
File file=new File(SDPath,"date.text");
String date="HelloWorld";
FileOutputStream fos;
try{
fos= new FileOutputStream(file);
fos.write(data.getBytes());
fos.close;
}catch(Exception e){
e.printStackTrace();
}
- QQ保存实战
//1.得到控件的值
String number=etText.getTex().toString().trim();//trim可以去除前后空格
//2.检查用户名密码是否为空
if(TextUtils.isEmpty(number)){}//为空返回true
//3.文件保存重新一个类
public class FileSaveQQ{
//写入
public static boolean save(Context context,String number,String password){
try{
//首先得到数据流
FileOutputStream fos=content.openFileOutput("data.txt",content.MODE_PRIVATE);
//把数据写到文件中
fos.write((number+":"+password).getBytes());
fos.close;
return true;
}catch(){}
}
//从文件中读取
public static Map<String,String> get(Context context){
try{
//首先得到数据流
FileOutputStream fis=content.openFileInput("data.txt");
byte[] buffer=new byte[fis.available];
fis.resd(buffer);
Map<String,String> userMap =new HashMap<>();
String content=new String(buffer);
String[] info=content.split(":");
userMap.put("number",info[0]);
userMap.put("password",info[1]);
fis.close;
}catch(){}
}
}
//保存密码的数据回显
Map<String,String> userInfo = FileSaveQQ.get(this);
if(userInfo!=null){
etNumber.setText(userInfo.get("number"));
etpassword.setText(userInfo.get("password"));
}
二、SharedPreferences
SharedPreferences是Android平台一个轻量级的存储类
用于存储配置参数、如:用户名、密码等
通过key/value(键值对)的形式将数据保存在Xml文件中
value的值,只能是float、int、long、boolean、String StringSet类型数据
使用方法
保存:
SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);【保存成data.xml】
SharedPreferences.editor editor=sp.edit();
editor.putString("name","创智博客");
editor.putInt("age",8);
editor.commit();
获取:
SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);
String date =sp.getString("name","");
三、SQLite数据库的操作与事务
-
1.相关简介
SQLiet是一个轻量级数据库,占用资源非常低。在内存中只需要占用几百KB的存储空间。
SQLiet是遵循ACID的关系型数据库管理系统,ACID是指数据库事务正确执行的四个基本要素 (原子性、一致性、隔离性、持久性)
SQLiet保存数据时,支持NULL(零)、INTEGER(整数)、REAL(浮点数字)、TEXT(字符串文本)、BLOB(二进制对象)五种类型 -
2.创建数据库的方法
只需要继承SQLiteOpenHelper即可
public class MyHelper extends SQLiteOpenHelper{
public MyHelper (Context context){
super(context,"itcast.db",null,2);//上下文、数据库文件名、游标工厂、版本
}
//第一次创建时候调用
public void onCreate(SQLietDatebase db){
db.execSQL("CREATE TABLE information(id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20),price INTEGER)");
}
//当实况版本号增加时候调用
public void onUpgrade(SQLietDatebase db,int oldVersion,int newVersion){
}
}
添加数据
public void intsert(String name,String price){
SQLiteDatebase db=MyHelper.getWritableDatabase();
ContentValues values= new ContentVables();
values.put("name",name);
values.put("price",price);
long id =db.insert("information",null,values);//表名、sql语句不允许插入空行、表示values(固定类,底层用map实现)
db.close();
}
修改数据
public int update(String name,String price){
SQLiteDatebase db=MyHelper.getWritableDatabase();
ContentVables values = new ContentValues();
values.put("price",price);
int number =db.update("information",value,"name=?",new String[]{name});
db.close();
return number;
}
删除方法
public int delete(long id){
SQLiteDatebase db= MyHelper.getWritableDatabas();
int number =db.delete("information","id=?",new String[]{id+""});
db.close;
return number;
}
查询方法
//cursor是一个bean类
public boolean find(long id){
SQLiteDatebase db=MyHelper.getReadableDatebase();
Cursor cursor=db.query("information",null,"id=?",new String[]{id+""},null,null,null);
boolean result = cursor.moreToNext();
cursor.close();
db.close();
return result;
}
查询所有
public boolean find(long id){
SQLiteDatebase db=MyHelper.getReadableDatebase();
Cursor cursor=db.query("information",null,null,null,null,null);
if(cursor.getCount()==0){
//就是说没有数据
Toast.makeText(this,"什么都没有",Toast.LENGTH_LONG).show;
}else{
cursor.moveToFrist();//把第一行数据取出来
Toast.makeText(this,cursor.getString(1),Toast.LENGTH_LONG).show;
}
//当然你可以这样子把所有数据都拿出来
while(cursor.moreToNext()){
mTvShow.append("\n"+"name"+cursor.getString(1))
}
cursor.close;
db.close;
}
- 3.事务处理
PersonSQLiteOpenHelper helper= new PersonSQLiteOpenHelper(getContext());
SQLietDatebase db =helper.getWritableDatabas();
db.beginTransaction();
db.beginTransaction();
try{
db.execSQL("update person set account =account -1000 where name = ?",new Object[]{"zhangsan"});
db.execSQL("update person set account =account +1000 where name = ?",new Object[]{"lisi"});
db.setTrabsactionSuccessful();
}catch(Exception E){
Log.i("事务处理失败",e.toString);
}finally{
db.endTransaction();
db.close;
}
第十节 android中的Broadcast
一、Broadcast Receiver(广播接收者)
- 1.广播接受者介绍:
1.Android系统中内置了很多广播(手机开发完成、电池电量过低都会发送)
2.为了监听来自系统或者程序的广播时间,Android通过了Broadcast Receiver组件
3.一个广播事件,可以有多个接收者处理
- 2.BroadcastReceiver的使用
1.创建BroadcastReceiver对象
2.创建好以后记得注册
3.静态注册(既是以后不开应用,系统都会帮你创建广播)
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
</receiver>
动态注册(只有应用打开的时候才接受,注意需要注销)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化广播
myReceiver= new MyReceiver();
//实例化过滤器,并设置要接收的广播
String action="android.provider.Telephony.SMS_RECEIVED";
//创建意图过滤器对象
IntentFilter intentFilter=new IntentFilter(action);
registerReceiver(myReceiver,intentFilter);
}
protected void onDestroy() {
super.onDestroy();
//注销
unregisterReceiver(myReceiver);
}
练习题.电话拦截器——保存电话:
SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);
SharedPreferences.Editor editor=sp.edit();
editor.putString("name","你好");
editor.commit();
获取:
SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);
String date =sp.getString("name","");
新的API
getResultDate();//得到电话
setResultDate(null);//修改电话
修改电话,需要在清单文件中声明
<receiver
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL"></action>
</intent-filter>
</receiver>
修改电话,涉及到打电话权限
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
二、自定义广播
1.当自定义广播发送消息时,会存储到公共消息区中,而公共消息区如果存在对应的广播接受者,就会及时接受到这条消息
2.拯救史迪仔
Intent intent=new Intent();
//定义广播的事件类型
intent.setAction("help_Stitch");
//发送广播
sendBroadcast(intent)
在控制台输出代码
Log.i("BroadcastReceiver","我收到了");
当然接受者要写相应的拦截器
<intent-filter>
<action android:name="help_Stitch"></action>
</intent-filter>
三、广播的类型
1.无序广播:完全异步执行,发送广播时所有监听该广播的接收者会同时收到消息
Intent intent=new Intent();
intent.setAction("help_Stitch");
intent.putExtra("data","鬼子进村了");
sendBroadcast(intent);
2.有序广播:安装接收者的优先级,只有一个广播接收后,执行完相应逻辑,才会继续传播。(可以被拦截)
优先级设定:
<receiver>
<intent-filter
android:name=""
android:priority="" //值越大,优先级越高(相同级别,先创建先收到)
>
</receiver>
发送广播
Intent intent=new Intent();
//定义广播的事件类型
intent.setAction("help_Stitch");
//发送广播
sendOrderedBroadcast(intent,null);//第二个是权限,不需要则设置为null
接收广播
- 拦截
abortBroadcast();
- 修改
setResultData("上面发了500斤大米");
- 得到数据
getResultData()
3.写入一定会被某个接收者的广播
sendOrderedBroadcast(intent,null,new final(),null,0,"发1000斤大米",null);
//myReceiver即使等级很低,即使被拦截,依然会被final接收(final不需要配置)
四、广播打开activity
Intent intent=new Intent(this,Activity2.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
content.startActivity(intent);
第十一节 android中的server
startService一旦开启就要手动关闭,而bingService()与Actity组件绑定 不求同年同月同日但求同年同月同日死
一、服务的创建
- 1.服务可以看做是一个没有界面的Activity。比如音乐的播放
特点:在后台长期运行
- 2.服务的创建
二、服务的声明周期
- 1.和activity一样创建与销毁
- 2.服务的开启方式有两种
(1)通过startService()方式启动
通过startService()方式启动,需要自身调用stopSelf()方法或其他组件调用stopService()方法服务才能停止
onCreate()-》onStartCommand()-》Service running-》上面-》onDestroy()
(2)通过bindService()方式启动
通过bindService()方式启动,需要调用onUnbind方法解除绑定才能销毁
onCreate()-》onBind()-》Service running-》上面-》onDestroy()
三、服务的启动方式
案例1.
通过startService:特点:与开启者没有关系。既是启动组件被销毁,服务依然运行。
onStartCommand()可以执行多次
需要在Service中写onCreate()、onStartCommand()、onDestroy()方法; onBind()不用,返回null
//开启:
Intent intent =new Intent(this,MyServer.class);
startService(intent);
//关闭:
Intent intent =new Intent(this,MyServer.class);
stopService(intent);
案例2.
通过bingService()
需要在Service中写onCreate()、onUnbind()方法;||onBind()方法绑定(返回一个IBinder数据类型)、onUnbind()解绑 onBind()不能多次执行
需一个内部类继承Binder(已经实现了IBinder接口)
//通过该对象可以间接的访问服务(在里面可以调用该服务的方法)
class MyBinder extends Binder{
}
//IBinder是夸进程访问
开启:
bindServer(intent,conn,flags);||//意图(指明要开启哪一个),ServiceConnection用于监听调用者与service的连接状态,指明连接时是否自动创建service
成员变量MyConn myconn;
在某个方法内
{
if(myconn==null){
myconn=new MyConn();
}
Intent intent =new Intent(this,MyServer.class);
bindServer(intent,myconn,BIND_ABOVE_CLIENT);
}
//在Activity中创建一个内部类
private class MyConn implements ServiceConnection{
//当服务连接的时候,调用该方法,
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
MyService.MyBinder myBinder=(MyService.MyBinder)iBinder;
log.i("MainActivity","服务成功绑定")
}
//当服务失去连接的时候调用该方法
public void onServiceDisconnected(ComponentName componentName) {
}
//关闭:用onUnbind
if(myconn!=null){
unbindService(myconn);
myconn==null
}
四、通信方式
本地通信(运行在自己应用的服务)
远程服务通信(运行在其他应用的服务)【AIDL】
android interface Definition language;
- 1.先把interface 文件后缀改成aidl[Stub使用进程间通信IPC]
- 2.服务类中间人mybind直接继承stub
- 3.在本地创建一个一模一样的包 一模一样的.aidl文件
- 4.在获取的时候不使用iservice=(Iservice)iBinder;而是 iservice=stub.asInterface(server);
五、音乐播放器
MediaPlayer mediaPlayer=new MediaPlayer();
//指定音频文件
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource("dizhi");
mediaPlayer.prepare();//准备播
mediaPlayer.start();//开始播
if(mediaPlayer.isplaying())//判断在不在播放
mediaPlayer.stop;
mediaPlayer.release();
mediaPlayer=null;
六、进程概念【一个进程含有多个线程】
四大组件都运行在主线程中(换句话说 四大组件都运行在主线程中)
系统尽可能长时间的维持进程的操作,
五种进程的优先级
- 1.前台进程(正在调用onResum()方法)
- 2.可视进程(用户可见,影响用户看得见)
- 3.服务进程(通过通过startService()方式启动)【一般不会杀到这里】
- 4.后台进程(activity进行了onstop()方法)
- 5.空进程(不维持任何激活组件)[有时不杀死,提高速度]
七、通过特定的接口 暴露想暴露的方法
- 1.定义一个接口 把自己想暴露的方法暴露在接口中
- 2.定义的中间人实现接口
- 3.在获取中间人对象时mybinder
八、混合方式开启服务(既想长期运行 又想调用里面的方法)
startserver让服务运行
在服务类中创建中间人mybind继承Binder
mybind方法实现接口类
服务类onBind方法返回刚才创建的类
在actity中创建myconn实现ServiceConnection类 然后new出来
然后bindService(intent,myconn,BIND_AUTO_CREATE);
调用unbindserver解绑服务
第十二节 android中的内容提供者contentProvider
一、内容提供者(contentProvider)
内容提供者是android的四大组件之一,它是不同应用程序之间进行数据共享的标准API
ContentResolver类可以访问ContentPrivider中共享数据
他们主要是通过ContentProvider 与ContentResolver两个组件
- 1.ContentResolver提供了一系列的增删改查方法对数据进行操作,并将这些方法以uri的形式对外通过数据
- 2.uri为内容提供者中的数据建立了唯一标识符。主要由3部分构成
1.scheme部分:content://是标准前缀,不能被修改
2.authority部分:创建内容提供者指定的authorities属性值,通常采用程序包名的方式命名
3.path部分:/person 代表资源或数据,可以动态改变
例:content://cn.itcast.mycontentprovider/person
二、内容提供者的创建{在日志中搜索Pub}
创建:
new->Other->Content Provider
URI Authorities 通过哪一个uri可以访问该内容提供者 (一般用包名 cn.itcast.mycontentprovider)
Exported:是否暴露给别人
Enabled:是否允许系统自动创建
其中几个方法
getType:获取媒体类型
需要在清单文件中声明
例子:(这里以查询为例)
public class MyContentProvider extends ContentProvider {
private static final UriMatcher sUriMatcher=new UriMatcher(UriMatcher.NO_MATCH);//不匹配返回-1
private static final int QUERYSUCCSEE=0;
private static final int INSERTSUCCSEE=1;
private MyHelper myHelper;
public MyContentProvider() {
sUriMatcher.addURI("henan.com.provider","query",QUERYSUCCSEE);
sUriMatcher.addURI("henan.com.provider","insert",INSERTSUCCSEE);
}
@Override
public boolean onCreate() {
myHelper = new MyHelper(getContext());
return false;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
int code = sUriMatcher.match(uri);
if (code==INSERTSUCCSEE){
SQLiteDatabase myDatabase = myHelper.getReadableDatabase();
long insert=myDatabase.insert("info",null,values);
Uri uri1=Uri.parse("com.henan.insert/"+insert);
return uri1;
}else {
return null;
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
int code = sUriMatcher.match(uri);
if (code==QUERYSUCCSEE){
SQLiteDatabase myDatabase = myHelper.getReadableDatabase();
Cursor query = myDatabase.query("info", projection, selection, selectionArgs, null, null, sortOrder);
myDatabase.close()
//这里cursor不用关,但是数据库要关
return query;
}else {
return null;
}
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
三、内容解析者
查询
Uri uri = Uri.parse("content://henan.com.provider/query");
ContentResolver contentResolver= getContentResolver();//获取ContentResolver对象
Cursor cursor=contentResolver.query(uri,new String[]{"name","age"},null,null,null);//条件、条件参数、排序
while (cursor.moveToNext()){
String name=cursor.getString(1);
int age=cursor.getInt(2);
}
cursor.close();
插入
Uri uri = Uri.parse("content://henan.com.provider/insert");
ContentValues contentValues=new ContentValues();
contentValues.put("name","张三");
contentValues.put("password","123");
ContentResolver contentResolver= getContentResolver();//获取ContentResolver对象
Uri uri=contentResolver.insert(uri,contentValues);
四、查看短信案例
五、内容观察者ContentObserver
1.内容观察者是用来指定Uri所代表的数据。当ContentObserver观察到指定Uri代表的数据发生变化时,就会触发onChange()方法,在该方法中使用ContentProvider可查询数据变化
2.要使用ContentObserver观察数据变化,就必须在ContentProvider的delete()、insert()、update()方法中调用ContentResover的notifyChange()方法;
假设B程序调用A程序
/*
*A程序:ContentProvider暴露数据并调用ContentResolver的notifyChange()方法
* 当程序发生变化是,A向信息中心发生消息
*B程序:使用ContentResolver操作A程序的数据
*C程序:注册ContentObserver
* 观察消息中心的消息,通过消息观察A程序的数据变化
* 当观察到变化的数据触发onChange()方法
*/
//ContentObserver的两个常用方法:
public ContentObserver(Handler handler){}
//ContentObserver的派生类都需要调用该构造方法,参数可以是主线程Handler,也可以是其他handler对象
public void onChange(boolean selfChange){}
//当观察者Uri代表的数据发生变化时,会触发该方法。在该方法中使用ContentResovler可以查询到变化数据
Uri uri = Uri.parse("content://cn.itcast.mycontentprovider/person");//一定要注意首字母大写其余小写
ContentResolver contentResolver= getContentResolver();//获取ContentResolver对象
contentResolver.registerContentObserver(uri,true,MyObserver(new Handler()));//第二个参数:是否精确匹配uri true只匹配该uri false匹配其派生类 第三个参数:ContentObserver实例
六、常用的几个系统表
data表:datal存所有人联系的信息 mimetype_id区分是什么信息 raw_contact_id:存放有多少条信息
raw_contacts:mimetype表
第十三节 初步http协议
一、http协议:
超文本传输协议
二、HttpURLConnection的基本用法
URL url=new URL("http://www.itcast.cn");
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);//连接超时时间
InputStream is=conn.getInputStream();
conn.disconnect();
三、GET和POST请求方式
String path = "http://xx.xxx.xx.xxx:80/web/LoginServlet?username="+URLEncoder.encode("zhangsan")+"&password="+URLEncoder.encode("123");
URL url=new URL(path);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
第十四节 多线程初步
一、多线程编程
android的主线程不能进行耗时操作(4.0以后强制,主线程也没这个义务)
打印线程名称方法:
Thread.currentThread().getName();(Android的主线程就是UI线程,只有UI线程才可以更新ui)
开启多线程的方法一:
第一步:new Thread(){}
第二步:new Thread(){}.stare()
第三步:new Thread(){public void run(){};}.stare()
开启多线程的方法二:(基本上用第一种,第二种是在特殊方法中使用)
new Runnable(){}
new Runnable(){public void run(){};}
二、handle机制
主线程 含handler,可以告诉系统更新ui
handler的作用是发消息和处理消息
looper是从消息队列里取消息
(该模型十分重要)
1.以下是更新ui的方法(该方法小白使用,存在内存泄漏风险)
Handler handler=new Handler(){
//该方法在UI线程执行
@Override
public void handleMessage(Message msg) {
String s=(String)msg.obj;
view.setText(s);
}
}
new Thread(){
@Override
public void run() {
Message msg=new Message();
String content="内容";
msg.obj=content;
handler.sendMessage(msg);//发送一条消息,数据放到msg里(子线程)handleMessage方法便会执行
}
}.start();
图片查看器
new Thread(){
try{
//获取访问图片路径
String path=et_view.getText().toString().trim();
//创建URL路径
URL url=new URL(path);
//获取httpurlconnection
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
//设置请求方式
conn.setRequestMethod("GET");
//设置超时时间
conn.setConnectTimeout(5000)
//获取服务器返回状态码
int code=conn.getResponseCode();
if(code == 200){//200代表成功 404代表没有找到
//获取图片数据(基本上都是以流的形式接收)
InputStream in=conn.getInputStream();
//通过位图工厂获取位图
Bitmap bitmap=BitmapFactory.decodeStream(in);
//把位图显示到iv上
Message msg= Message.obtain();//使用msg的静态方法 可以减少对对象的创建
msg.obj=bitmap;
handler.sendMessage(msg);
}
}
}.start();
三、缓存机制
//缓存图片,谷歌给我们提供了一个缓存目录getCacheDir() //创建缓存目录[类似于 getFilesDir()//创建file目录]
File file=new File(getCacheDir(),"Beautiful.png")
if(file.exists()&&file.length()>0){
Bitmap cacheBitmap=BitmapFactory.decodeFile(file.getAbsolutePath());//
Message msg=Message.obtain();
msg.obj=cacheBitmap;
handler.sendMessage(msg)
}else{
//第一次
}
四、cache目录和filedir目录区别
cache可以被用户手动清除掉缓存(用户可以清楚)
file可以被用户手动清除掉data
五、runOnUiThread();
作用:在主线程中会立即执行 在子线程中会交给主线程执行
runOnUiThread(new Runnable(){
@Override
public void run() {
view.setText("xxx");
}
});
六、常见的API
下面的方法不能更新ui(当然可以用runOnUiThread())【大部分在企业中是没办法用的,有兴趣的同学可以学习线程池】
第一个:handler
//2秒后执行run方法
new Handler().postDelayed(new Runnable(){
public void run(){
syso("哈哈哈")
}
},2000)
第二个:time
//5秒后进行run方法
Timer timer =new Timer();
TimerTask task=new TimerTask(){
public void run(){
syso("呵呵呵")
}
};
timer.schedule(task,5000)[也可以是timer.schedule(task,5000,1000)]
//销毁方法
timer.cancel();
task.cancel();
七、将字符流转换成string的方法
public String inputStream2String(InputStream is) throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
八、服务器的逻辑
public void androidlogin() {
/*使用servletActionContext对象拿request与Response都很简单*/
HttpServletRequest request=ServletActionContext.getRequest();
String phone=request.getParameter("phone");
String password=request.getParameter("password");
System.out.println(phone);
HttpServletResponse response=ServletActionContext.getResponse();
response.setContentType("text/plain; charset=utf-8");
PrintWriter out;
try {
out = response.getWriter();
out.print("登录成功");
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
九、HttpURLConnection的get与post客户端逻辑
public void click1(View view) {
String name=name_edit.getText().toString().trim();
String password=password_edit.getText().toString().trim();
final String path="http://117.34.240.61:8080/AndroidServiceDome2/user_androidlogin.action?phone="+name+"&password="+password;
new Thread(){
@Override
public void run() {
try{
URL url=new URL(path);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
int code=conn.getResponseCode();
if(code == 200) {//200代表成功 404代表没有找到
InputStream inputStream = conn.getInputStream();
Util util=new Util();
String S =util.inputStream2String(inputStream);
//因為不可以在子線程展示ui所以
showToast(S);
}
}catch (Exception e){
}
}
}.start();
}
public void click2(View view) {
final String name=name_edit.getText().toString().trim();
final String password=password_edit.getText().toString().trim();
final String path="http://117.34.240.61:8080/AndroidServiceDome2/user_androidlogin.action";
new Thread(){
@Override
public void run() {
try{
String data="phone="+name+"&password="+password;
URL url=new URL(path);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(5000);
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length",data.length()+"");
conn.setDoOutput(true);
conn.getOutputStream().write(data.getBytes());
int code=conn.getResponseCode();
if(code == 200) {//200代表成功 404代表没有找到
InputStream inputStream = conn.getInputStream();
Util util=new Util();
String S =util.inputStream2String(inputStream);
//因為不可以在子線程展示ui所以
showToast(S);
}
}catch (Exception e){
}
}
}.start();
}
十、httpclient(使用直接子类Defaulthttpcliet)【该方法已经过时】
DefaultHttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(path);
HttpResponse response = client.execute(get);
int code = response.getStatusLine().getStatusCode();
if(code == 200){
InputStream inputStream=response.getEntity().getContent();
showToast(content);
}
DefaultHttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(path);
List<NameValuePair> lists= new ArrayList<NameValuePair>();
BasicNameValuePair nameValuePair = new BasicNameValuePair("phone",name);
BasicNameValuePair passwordValuePair = new BasicNameValuePair("password",password);
lists.add(nameValuePair);
lists.add(passwordValuePair);
URLEncodedFormEntity entity = new URLEncodedFormEntity(lists);
post.setEntity(entity);
HttpResponse response = client.execute(post);
int code = response.getStatusLine().getStatusCode();
if(code == 200){
InputStream inputStream=response.getEntity().getContent();
showToast(content);
}
十一、retrofit
下载包
//retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
十二、多线程加速下载
1.注意
- 不是线程越多下载越快
- 下载速度受服务器带宽的影响
2.多线程下载步骤
[1]获取文件大小
int length=conn.getContentLength()//得到字节长度
runningThread = threadCount;
[2]在客户端创建一个大小和服务器一模一样的文件提前申请好空间
RandomAccessFile rafAccessFile = new RandomAccessFile("dome.exe","rw");
rafAccessFile.setLength(length);
//[2.1]确定每个线程下载的开始位置与结束位置
int blockSize =length/threadCount;//提前声明好有几个线程
for(int i=0; i<theadCount;i++){
int startIndex = i*blockSize;
int endIndex=(i+1)*blockSize-1;
if(i==threadCount-1){
endIndex=length-1;
syso("线程"+i+"理论下载的位置"+startIndex+"-----"+endIndex)
//开启线程
DownLoadThread downLoadThread = new DownLoadThread(startIndex,endIndex,i);
downLoadThread.start();
}
}
[3]开多个线程去下载文件
//这里单独抽取一下
private static class DownLoadThread extends Thread{
private int startIndex;
private int endIndex;
private int threadId;
public DownLoadThread (int startIndex,int endIndex,int threadId){
this.startIndex=startIndex;
this.endIndex=endIndex;
this.threadId=threadId;
}
public void run(){
try{
URL url=new URL(path);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
//再次下载要先看看上一次是不是没下完、
File file =new File(threadId+".txt")
if(file.exists()&&file.length()>0){
FileInputStream fis =new FileInputStream(file);
BufferedReader bufr =new BufferedReader(new InputStreamReader(fis));
String lastPositionn=bufr.readLine();
int lastPosition=Integer.parseInt(lastPositionn);
startIndex=lastPosition;
syso("线程"+threadId+"真实下载的位置"+startIndex+"-----"+endIndex)
fis.close;
}
//[3.1]设置一个请求头Range
conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);
int code=conn.getResponseCode();
if(code == 200) {//200代表成功 404代表没有找到
//创建随机文件读写对象
RandomAccessFile raf = new RandomAccessFile("dome.exe","rw");
ref.seek(startIndex);
InputStream inputStream = conn.getInputStream();
//把内容写到文件中
int len=-1;
/*****断点续传(把当前进度用文件存起来)****/
int total=0;//代表当前下载的大小
byte[] buffer=new byte[2014*1024];//定义缓冲区的大小
while((len = in.read(buffer))!=-1){
raf.write(buffer,0,len);
total +=len;
int currentThreadPosition=startIndex+total;
/*该方式不安全*/
//File file = new File(threadId+".txt")
//FileOutputStream fos = new FileOutputStream(file);
//fos.write(String.valueOf(currentThreadPosition).getBytes());
//fos.close;
RandomAccessFile raff =new RandomAccessFile(threadId+".txt","rws");raff.write(String.valueOf(currentThreadPosition).getBytes());
raff.close;
}
raf.close();
syso("线程id:"+threadId+"加载完毕")
//把txt文件删除(之前声明一个runningThread 代表当前正在运行的线程)
synchronized(DownLoadThread.class){//线程锁
runningThread--;
if(runningThread==0){
//说明执行完毕
for(int i=0;i<threadCount;i++){
File deletFile=new File(i+".txt");
delteFile.delete();
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
public static String getFilenam(String path){
int start =path.lastIndexOf("/")+1;
return path.substring(start);
}
}
[4]知道每个线程什么时候下载完毕将文件拼接起来即可
第十五节 多媒体初步
一、计算机表示图形的几种方式
bmp:以高质量保存
jpg:以良好质量保存
png:以高质量保存(png与jpg差不多 只不过压缩算法不一样,png压缩算法更好)
图片大小的计算公式:(图片总像素 X 每一个像素的大小)
单色: 只有两种颜色(要么是黑要么是白) 每一个像素只要长度是1的二进制位表示(一个像素占1/8个byte)
16色: 每个像素最多可以表示16种颜色 0000-1111 只需要使用长度为4的二进制表示(一个像素占1/2个byte)
256色: 每个像素最多可以表示256种颜色 0000 0000-1111 1111 只需要使用长度为8的二进制表示(一个像素占1个byte)
24位: 每个像素最多可以表示1600万种颜色 RGB组合 一个像素占3byte(RGB各占一个byte)
在android中采用png,采用的是arpg一个像素四个byte
二、缩放加载大图片
Bitmap bitmap=BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg")
imageView.setImageBitmap()
(oom内存溢出异常,因为默认给每个应用就分配16m空间,而图片加载申请内存与图片大小有关 与真实大小无关)
正确方法:
- 1.获取图片分辨
- 2.获取手机分辨率
- 3.计算缩放比(宽除以宽,高除以高,然后按照大的缩放)
//获取手机宽与高
WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);
//第一种过时
int height = wm.getDefaultDisplay().getHeight();
int width = wm.getDefaultDisplay().getWidth();
//第二种
Point point = new Point();
wm.getDefaultDisplay().getSize(point);
int height1 = point.x;
int width1 = point.y;
//加载图片信息
//创建一个位图工厂创建一个参数
BitmapFactory.Options options=new BitmapFactory.Options();
//解码器不去真正的解析位图 但是还能够获取图片的宽与高信息
options.inJustDecodeBounds =true;
BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg",options);
int imgw=options.outWidth;
int imgh=options.outHeight;
//计算缩放比
int scale=(imgw/height)>=(imgh/height)?(imgw/height):(imgh/height);
//但是不能小于1
if (scale<1){
scale=1;
}
options.inSampleSize=scale;
//加载图片
options.inJustDecodeBounds =false;
Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg", options);
imageView.setImageBitmap(bitmap);
三、创建原图副本
//把普通照片转化成bitmap
Bitmap srcBitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background);
//创建了一个与原图一样的空白的白纸
Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
//用画笔画一个与原图一样的图
//1.拿画笔
Paint paint=new Paint();
//2.拿画布并把把画布铺在copy图上
Canvas canvas=new Canvas(copyBitmap);
//3.画图
canvas.drawBitmap(srcBitmap,new Matrix(),paint);
//已经画好的图修改(一次修改一个像素)
copyBitmap.setPixel(20,20, Color.BLUE);
四、图形处理的api
旋转
//1.拿画笔
Paint paint=new Paint();
//2.拿画布并把把画布铺在copy图上
Canvas canvas=new Canvas(copyBitmap);
Matrix matrix = new Matrix();
//旋转角度,基于什么中心点 matrix.setRotate(20,copyBitmap.getWidth()/2,copyBitmap.getHeight()/2);
canvas.drawBitmap(srcBitmap,matrix,paint);
缩放
//缩放比例(水平,竖直)
matrix.setScale(0.5f,0.4f);
平移
//水平,竖直
matrix.setTranslate(30,0);
镜面(旋转平移组合)
matrix.setScale(-1f,0);
//使用post是在修改的基础上修改
matrix.postTranslate(copyBitmap.getWidth(),0);
倒影
matrix.setScale(1f,-1f);
matrix.postTranslate(0,copyBitmap.getHeight());
五、使用mediaplayer播放音频文件(mediaPlayer只能播放mp4或者3gp格式)
MediaPlayer mediaPlayer=new MediaPlayer();
try {
mediaPlayer.setDataSource("/mnt/sdcard/dog.mp3");
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.stop();
六、surfaceview介绍(控件)
他是一个重量级控件
维护两个线程A、加载数据 B、显示数据 可以直接更新ui
View scroll = findViewById(R.id.scrollView1);
SurfaceHolder handler = (SurfaceHolder)scroll.getHandler();
MediaPlayer mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource("/mnt/sdcard/dog.mp4");
mediaPlayer.prepareAsync();
mediaPlayer.setDisplay(handler);
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
七、VideoView控件介绍
VideoView videoView = findViewById(R.id.video_view);
videoView.setVideoPath("/mnt/sdcard/dog.mp4");
videoView.start();
八、vitamio框架
九、照相与录像
第十六节 Fragment入门
一、fragment入门 [最低11版本(即3.0)]
它总是被嵌入到activity当中,生命周期被activity影响
- 1.ViewGroup可以有自己的孩子
- 2.view 没有自己的孩子
通过 onCreateView这个方法可以加载fragmen自己的布局
第一种加载fragment方式
<fragment
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:id="@+id/addfragment_first"
android:name="dome813.henan.com.fragmentdome.Fragment1"
/>
<fragment
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:id="@+id/addfragment_second"
android:name="dome813.henan.com.fragmentdome.Fragment2"
/>
public class Fragment2 extends Fragment {
//第一次调UI的时候执行该方法 加载fragment自己的控件
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
//通过打气筒将布局转化成view
View view = inflater.inflate(R.layout.fragmentlayout2,null);
return view;
}
}
第二种加载fragment方式(动态加载)
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
String state = null;
if (state=="横屏"){
fragmentTransaction.replace(android.R.id.content,new Fragment1());
}else{
fragmentTransaction.replace(android.R.id.content,new Fragment2());
}
fragmentTransaction.commit();
3.fragment的xml中 不能用onClick方法点击,只能通过id方式 setOnClickListener
4.兼容11版本以下的 就要使V4包
二、fragment的生命周期
onAttach()
onCreate()
onCreateView()//第一次画ui 必须重新
onActivityCreated()//准备view 已初始化
onStart()
onResum()
onPause()
onStop()
onDestroyView()
onDestroy()//回收内存
onDetach
三、fragment之间的通信 (通过activity)
public class Fragment1 extends Fragment {
//第一次调UI的时候执行该方法 加载fragment自己的控件
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
//通过打气筒将布局转化成view
View view = inflater.inflate(R.layout.fragmentlayout1,null);
view.findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Fragment2 fragment2 =(Fragment2)getActivity().getFragmentManager().findFragmentByTag("massage1");
}
});
return view;
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
.....
if (state=="横屏"){
fragmentTransaction.replace(android.R.id.content,new Fragment1(),"message1");
else{
fragmentTransaction.replace(android.R.id.content,new Fragment2(),"message2");
}
fragmentTransaction.commit();
}
}
网友评论