第一天

1、LinearLayout,线性布局
 vertically :垂直,纵向
 horizontally:水平,横向

a:方向,默认横向
android:orientation="horizontal"
horizontal,在水平方向上一个挨着一个
vertical,在垂直方向上一个挨着一个

b:控件的宽,高
android:layout_width="fill_parent":宽
android:layout_height="fill_parent":高

fill_parent/match_parent:匹配父控件
wrap_content:包裹内容物
固定值:100dp

c:单位
    dp:表示非文字大小的尺寸
    sp:设置文字大小

d:设置背景颜色 或 背景图片
android:background="#ff0000"
android:background="#f00"

使用RGB三原色设置背景颜色
ARGB,A:alpha,带透明度的三原色
Color.rbg();
Color.arbg();

完全透明:#00~~~
完全不透明:#ff~~~

设置背景图片:
android:background="@drawable/img001"
@drawable/img001:访问图片的资源文件

e:边距
内边距:控件内部,内容与边框的距离
android:padding:四个方向
paddingleft,rigth,top,bottom

外边距:控件之间的距离
android:layout_margin:四个方向
layout_marginLeft,right,top,bottom

f:
android:gravity:设置View中内容的位置
    1).设置在布局节点(LinearLayout),指定布局中控件的位置
    2).设置在控件中(Button,TextView……),指定控件中文字的位置
    android:gravity="right|bottom",右下角

android:layout_gravity:控件相对于容器的位置,使用在控件中
    在横向布局中,设置为right失效
    在纵向布局中,设置bottom失效

g:
android:weight:设置权重,在横向或纵向上,控件占用空间的比例
默认权重为0

在横向布局中,设置android:layout_width="0dp"
在纵向布局中,设置android:layout_height="0dp"

h:
android:id,设置控件的id,给控件起个名字,可以在应用中使用名字获得该控件
 android:id="@+id/mTextView",
可以理解为:在R文件中新增控件,名为mTextView

通过id获得控件
mTextView = (TextView) findViewById(R.id.mTextView);

2,FrameLayout:帧布局
在屏幕的某个区域可以添加多个控件,最近(最后)添加的显示在最上方

重点:
id
width
height

边距:padding,margin

weight:权重

gravity
layout_gravity

RelativeLayout相对布局

a:控件与布局之间的对齐方式,值为true
android:layout_centerInParent="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"

android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"

b:兄弟控件之间的相对位置,值为另外一个控件的id,不能设置第一个控件的左方和上方
android:layout_above="@id/tv1"
android:layout_below="@id/tv1"
android:layout_toLeftOf="@id/tv1"
android:layout_toRightOf="@id/tv1"

c:兄弟控件之间的对齐方式,值为另外一个控件的id
android:layout_alignLeft="@id/tv1"
android:layout_alignRight="@id/tv1"
android:layout_alignTop="@id/tv1"
android:layout_alignBottom="@id/tv1"

d:基准线对齐,本控件文字的底边与目标控件文字的底边对齐
android:layout_alignBaseLine

第二天
复习:
1、LinearLayout,线性布局
android:orientation
horizontal
vertical

2、
android:layout_width
android:layout_height

fill_parent,match_parent
wrap_content
固定值:100dp

3、background,背景颜色  和 背景图片
RGB
ARGB

@drawable/img

4、边距:
内边距:padding
外边距:margin

5、gravity:
layout_gravity:

6.weight:权重,默认0
水平布局,layout_width="0dp"
垂直布局,layout_height="0dp"

7.FrameLayout,帧布局

本课内容:
1、RelativeLayout,相对布局

参考文档

2、GridLayout,网格布局
与线性布局类似,需要指定控件的方向
android:orientation=  horizontal  或  vertical

指定行数和列数
android:rowCount="5":行数
android:columnCount="4":列数

合并单元格
android:layout_rowSpan="2",跨行合并
android:layout_columnSpan="2",跨列合并

合并之后应该进行填充
android:layout_gravity="fill_vertical",fill,fill_horiaontal

网格中的单元格可以通过索引访问,索引从0开始

指定控件在网格中显示的位置
android:layout_row="0"
android:layout_column="3"

3.TextView:用于显示文本

android:text="@string/textView":设置文本内容
        android:textSize="30sp":大小
        android:textColor="#f00":颜色
        android:textStyle="bold|italic":样式

android:autoLink="web",设置链接,phone,email,map,all

android:singleLine="true":单行显示,默认在末尾加...

android:marqueeRepeatLimit="marquee_forever",设置滚动的次数,
android:focusable="true",是否获得焦点
android:focusableInTouchMode="true",以触摸的形式获得焦点

4.EditText:用于输入文本,获得光标时,显示不同的软键盘
是TextView的子类

android:hint:设置提示
android:inputType:设置输入的类型,弹出的软键盘不同

5、Button
添加按钮的单击事件
a:创建子类(内部类),实现android.view.View.OnClickListener接口,重写onClick方法
b:匿名内部类
c:在布局文件中,Button标签添加onClick属性,属性值为方法名
该方法声明要求:
public void 方法名(view v){}

多个按钮处理单击事件
a:实现android.view.View.OnClickListener接口,重写方法
    1).创建子类
    2).MainActivity实现接口
b:在Button标签中添加onClick属性,设置方法名

switch判断具体的按钮

6、Toast,吐司
// 1.上下文;2.显示文本;3.显示的时间
Toast.makeText(this, "button1", Toast.LENGTH_LONG).show();
注:一定要有show()方法

7、查看logCat日志信息
verbose:啰嗦的,琐碎的,冗长的
    Log.v(),级别最低的一个,显示全部信息
debug:显示调试信息
    Log.d()
info:显示重要的调试信息,异常
     Log.i()
warn:显示警告信息
    Log.w()
error:显示错误信息
    Log.e()

8.RadioButton,单选按钮
保证单选按钮互斥访问,成为单选按钮,需要使用RadioGroup

默认选中:android:checked="true"

单击事件
a:在RadioButton标签中添加onClick属性
b:给RadioGroup注册监听,实现接口android.widget.RadioGroup.OnCheckedChangeListener

c: 获得选中的RadioButton的id
int id = rg.getCheckedRadioButtonId();

预习:
checkBox
ImageView

第三天

复习:
1、RelativeLayout,相对布局
16个属性

7个相对于布局
4个兄弟节点之间的相对位置
4个兄弟节点之间的对齐方式
1个基准线对齐

2.GridLayout,网格布局
方向,android:orientation

行数,rowcount
列数,columnCount

跨行合并,rowSpan
跨列合并,columnSpan
layout_gravity=“fill”

通过索引访问单元格,0起始
layout_row
layout_column

3.TextView

4.EditText,可编辑的
hint
inputType

5.Button,
单击事件,
a:OnClickListener
b:onClick="send"

public void send(View v){}

6.RadioButton
RadioGroup保证可单选

事件
a:RadioButton,onclick
b:RadioGroup,设置监听,onCheckedChangeListener

i18n: internationalization,国际化
本课内容:
1、CheckBox
事件
a:在CheckBox标签中添加onClick属性
b:使用onCheckedChangeListener

2.ImageView,显示图片

android:src="@drawable/bg04",指定显示的图片资源

三个属性搭配使用,调整图片的宽高,保证图片的宽高比例
android:adjustViewBounds="true",是否调整ImageView的边界来保持宽高比例
android:maxWidth="100dp",最大宽度
android:maxHeight="300dp",最大高度

android:cropToPadding="true",是否截取图片指定的区域使用空白来替代
android:scrollX="15dp",x轴方向截取
android:scrollY="17dp",y轴方向截取
//反向截取
android:scrollX="-15dp",x轴方向截取
android:scrollY="-17dp",y轴方向截取

android:tint="#f00",使用指定的颜色渲染图片
android:alpha="0.5f",设置透明度

android:scaleType="",设置裁剪的类型

matrix:保持图片原大小显示,从左上角开始显示

fitStart:保持横纵比例缩放图片,并将图片放到ImageView的左上角

fitEnd:保持横纵比例缩放图片,并将图片放到ImageView的右下角

fitCenter:保持横纵比例缩放图片,并将图片放到ImageView的中间

fitXY:横向,纵向独立缩放图片,适应Imageview的大小,不保持原来图片的比例,填充ImageView

center:不进行任何缩放,将图片放到ImageView的中央,如果图片大于ImageView,截取中间部分显示

centerCrop:保持横纵比例缩放图片,使图片覆盖ImageView,以图片和ImageView的中心为基准点,进行缩放,直到图片最小的某方向等于ImageView的某边(某一方向的长宽等于ImageView,另一方向大于ImageView),进行截取

centerInside:保持横纵比例缩放图片,使ImageView能完全显示图片,以图片和ImageView的中心为基准点,进行缩放,显示图片内容,如果图片小于ImageView,不进行缩放,居中显示;如果图片大于Imageview,进行缩放,居中显示

3、AutoCompleteTextView

控件
适配器(adapter:ArrayAdapter,SimpleAdapter,BaseAdapter……)
数据源

适配器作用:实现数据源和控件的关联

第四天
复习:
1.CheckBox
事件
a:onClick
b:onCheckedChangeListener

2.ImageView
src="@drawable/bg"
iv.setImageResource(resId);

scaleType
tint
alpha

3.ToggleButton  Switch

4.AutoCompleteTextView

适配器:ArrayAdapter,数据源是集合或数组

本课内容:
1、Spinner:下拉列表

在布局文件中,Spinner标签添加属性,与字符串数组资源关联
android:entries="@array/planets_array"(关联的资源是res文件夹下的values文件夹中string中的string_array)

给Spinner添加事件,实现接口是OnItemSelectedListener
注意:注册监听器

代码详见案例。

2、解析xml文件,首先在res下创建xml文件夹,放置xml文件

xml解析获得标签属性值:parser.getAttributeValue(null,"cityname")//null:固定值;cityname:属性的名字
3、ArrayAdapter,数据源是集合或数组

更新适配器:adapter.notifyDataSetChanged();
4、获取当前时间
安卓控件
http://www.cnblogs.com/menlsh/archive/2013/02/23/2923867.html
方法一
Calendar c = Calendar.getInstance();
取得系统日期:year = c.get(Calendar.YEAR)
               month = c.grt(Calendar.MONTH)
               day = c.get(Calendar.DAY_OF_MONTH)
取得系统时间:hour = c.get(Calendar.HOUR_OF_DAY);
                  minute = c.get(Calendar.MINUTE)

方法二
 TextView myTextView = (TextView)findViewById(R.id.myTextView);   
        Time time = new Time("GMT+8");    
        time.setToNow();   
        int year = time.year;   
        int month = time.month;   
        int day = time.monthDay;   
        int minute = time.minute;   
        int hour = time.hour;   
        int sec = time.second;   
        myTextView.setText("当前时间为:" + year +    
                            "年 " + month +    
                            "月 " + day +    
                            "日 " + hour +    
                            "时 " + minute +    
                            "分 " + sec +    
                            "秒");

第五天

1、Activity,四大组件之一,创建成功之后,都需要在清单文件中进行注册
可以认为是应用程序的页面

创建Activity
a:创建子类继承Activity,重写onCreate方法
b:创建布局文件
c:在清单文件中进行注册

2、Intent,意图
启动Activity
方法1:
    Intent intent = new Intent();
    // 1.上下文,代表当前Activity
    // 2.代表跳转的目的页面,传递的是Class类型的对象
    intent.setClass(MainActivity.this, NextActivity.class);
    // 启动意图
    startActivity(intent);

方法2:
    Intent intent = new Intent(MainActivity.this, NextActivity.class);
    // 启动意图
    startActivity(intent);

3、Intent传值
a:
A页面:Intent对象使用putExtra方法封装名值对
B页面:首先获得意图(getIntent())
    之后使用相应的get***Extra()方法获得值

b:
A页面:创建Bundle对象,封装数据,之后将Bundle对象再封装到Intent中
    Bundle bundle = new Bundle();
    bundle.putString("sex", "male");
    bundle.putInt("weight", 65);
    intent.putExtra("bundle", bundle);
B页面:首先获得Bundle对象,获得Bundle封装的数据
    Bundle bundle = intent.getBundleExtra("bundle");
    String sex = bundle.getString("sex");
    int weight = bundle.getInt("weight");

c:
A页面:使用Bundle对象封装数据
    intent.putExtras(bundle);
B页面:获得Bundle对象,可以获得前一个页面使用putExtra方法封装的名值对
    Bundle bundle = intent.getExtras();

d:传递对象,要求对象可序列化
A页面:封装对象
    Person person = new Person("jack", 21);
    intent.putExtra("person", person);
或者
    bundle.putSerializable("person", person);

B页面;获得对象
    Person person = (Person) intent.getSerializableExtra("person");
或者
    Person person = (Person) bundle.getSerializable("person");

4.StartActivityForResult
跳转到下一个页面并返回结果

A页面,使用StartActivityForResult启动下一个页面
    在onActivityResult方法中获得返回值
B页面,获得Intent(getIntent()),封装数据
    // 设置响应码和Intent对象
    setResult(2, intent);
    // 结束Activity的生命周期
    finish();

shutdown -s -t 60 -c
at 12:00 shutdown -s -t

5.Activity生命周期
方法有7个,是系统调用的
onCreate():创建并初始化Activity时使用
onStart():显示页面
onResume():获取用户焦点,与用户进行交互

onPause();失去焦点,暂停与用户的交互
onStop():关闭页面,处于后台运行
onDestroy():销毁页面

onRestart():页面重新启动并显示,级联调用onStart-onResume

6、分析生命周期
a:启动Activity,onCreate-onStart-onResume
b:当前Activity被其他Activity覆盖(第二个页面以对话框形式显示)  或 锁屏:onPause
    当前Acitvity回到前台显示 或 解锁:onResume
c:跳转到新的Activity  或 按HOme键:onPause-onStop

d:按回退 或 再运行应用:onRestart-onStart-onResume

e:当b,c两种情况,当前Activity已经不可用,如果系统资源不足,将杀死当前的Activity
f:当用户退出Activity,onPause-onStop-onDestroy

8、生命周期的阶段
完整生命周期:onCreate-onDestroy
可见声明周期:onStart-onStop
前台(焦点)生命周期:onResume-onPause

7。现场保护

保存数据
onPause-onSaveInstanceState-onStop-onDestroy

恢复数据
onCreate-onStart-onRestoreInstanceState-onResume

Activity生命周期
1.打开主界面,后退键退出
onCreate-onStart-onResume-onPause-onStop-onDestroy
2.打开主界面,进入第二个页面
onCreate-onStart-onResume-AonPause-BonCreate-BonStart-BonResume-AonStop
在B页面按返回键
BonPause-AonRestart-AonStart-AonResume-BonStop-BonDestroy

2.1、打开主页面,进入第二个页面,第二个页面以对话框显示
在清单文件中,Activity标签中设置页面主题:
android:theme="@android:style/Theme.Dialog"

onCreate-onStart-onResume-AonPause-BonCreate-BonStart-BonResume

按返回或点击屏幕其他位置
BonPause-AonResume-BonStop-BonDestroy
    
3.打开主界面,模拟电话进入及挂机,再次显示主界面
onCreate-onStart-onResume-onPause-onStop-onRestart-onStart-onResume

4.打开主界面,HOME键退出
onCreate-onStart-onResume-onPause-onStop
5.打开主界面,HOME键退出,再次启动app
onCreate-onStart-onResume-onPause-onStop-onRestart-onStart-onResume

6.打开主界面,切换横屏竖屏。
onCreate-onStart-onResume-onPause-onStop-onDestroy-onCreate-onStart-onResume

屏幕切换,销毁当前页面,再重新创建

http://flash.weather.com.cn/wmaps/xml/china.xml

第六天
一、Task
 * 任务栈 对 所有Activity涉及其操作的任务集合
 *
 * 特点:
 * 1 栈顶的元素 是用户直接看到的那个任务 并且 可以进行交互
 * 2 栈中的元素都处于停止的状态
 * 3 弹出的任务 销毁状态
二、启动模式
 * Activity 启动模式
 * 配置方法: 在清单文件中activity 标签下 设置 launchMode = “”;
 * 1.standard
 * 标准的启动模式(默认) , 每次启动的时候都会实例化一个新的Activity对象放入栈中
 * 2.singleTop
 * 如果当前任务处于栈顶,则不会创建新的实例入栈,会调用当前栈顶元素的 onNewIntent 方法 如果该任务没有处于栈顶,则创建新的实例放入栈中
 * 3.singleTask
 * 如果栈中已经存在该实例,则重用该实例,不会创建新的实例,重用时 ,会将该实例上面的所有Activity 弹出销毁
 * 当前实例处于栈顶
 * 4.singleInstance
 * 在一个新的栈当中创建Acitivty 的实例   对其他应用程序共享该实例
三、Intent 七大属性
 * Intent: 意图
 *  1.启动新的组件 Activity,Service ,BroadCast。。。
 *  2.传递数据
 *  3.根据action标记来启动系统页面
 * 七大属性:
 * 1. ComponentName 组成 ,要素,构成
 *     1) 显示意图 直接设置上下文对象及目标Activity 进行跳转等同与Intent直接设置参数
 *      2) 跨应用跳转Activity ( 包名,目标类的全路径)
 * 2. Action 标识符
 *     intent-filter 意图过滤器 过滤条件的集合     在清单文件中配置
 *     action 与category 必须同时使用
 *     1) 隐式意图作为跳转目标的标记 ,将目标Activity 的action设置一个字符串标识 根据Intent 的action 属性 进行跳转
 *  2) 根据Intent类的常量调用系统页面(模拟器有问题)
 * 3. Category种类
 *     和Action 同时使用 表示当前Activity的类型
 *      1) DEFAULT 默认
 *      2) LAUNCHER 该应用启动的Activity
 * 4.Data  Uri
 *     Uri  统一资源标识符    格式:scheme://host:port/path
 *     其他的 Android系统的  资源标识符
    
    系统内置的几个Data属性常量:
      tel://:号码数据格式,后跟电话号码。
      mailto://:邮件数据格式,后跟邮件收件人地址。
      smsto://:短息数据格式,后跟短信接收号码。
      content://:内容数据格式,后跟需要读取的内容。
      file://:文件数据格式,后跟文件路径。
      market://search?q=pname:pkgname:市场数据格式,在Google Market里搜索包名为pkgname的应用。
      geo://latitude, longitude:经纬数据格式,在地图上显示经纬度所指定的位置。

Uri 统一资源标识符
    的格式如下:scheme://host:port/path
    约束       主机名(域名:端口号)      路径
    url   http://www.baidu.com:8080/indext?page =1

//实现发短信
    Intent  intent  = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setType("vnd.android-dir/mms-sms");
    intent.putExtra("sms_body", "信息内容...");
    startActivity(intent);

* 5.type类型     
 *     字符串格式  abc/xyc
 * 6.Extra   携带数据
 * 7.Flag    启动模式

intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // -- singleTop
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // -- singleTask
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // -- singleInstance

四:
    1.清单文件中 versionCode代表 版本号
             versionName代表版本名称
        Permissions  权限 ,有需要的添加   
        设置category 的Activity 为应用启动的Activity
    2.注意命名规范  
        方法名使用驼峰式命名方法
        尽量使用英文 不要使用汉语拼音
    3.Debug 的两种方式

Intent意图

系统内置的几个Data属性常量:
  tel://:号码数据格式,后跟电话号码。
  mailto://:邮件数据格式,后跟邮件收件人地址。
  smsto://:短息数据格式,后跟短信接收号码。
  content://:内容数据格式,后跟需要读取的内容。
  file://:文件数据格式,后跟文件路径。
  market://search?q=pname:pkgname:市场数据格式,在Google Market里搜索包名为pkgname的应用。
  geo://latitude, longitude:经纬数据格式,在地图上显示经纬度所指定的位置。

Uri 统一资源标识符
的格式如下:scheme://host:port/path
    约束       主机名(域名:端口号)      路径
    url   http://www.baidu.com:8080/indext?page =1

Intent  intent  = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setType("vnd.android-dir/mms-sms");
intent.putExtra("sms_body", "信息内容...");
startActivity(intent);

第七天

Json
http://sns.maimaicha.com/api?apikey=b4f4ee31a8b9acc866ef2afb754c33e6&format=json&method=news.getListByType&page=0&row=15&type=52

Image    
https://www.baidu.com/img/bd_logo1.png

一、主线程(UI线程)不能访问网络 子线程不能更新UI
    主线程(UI线程)不能访问网络,使用子线程获得网络数据,子线程不能更新UI,获得数据交给主线程完成UI的修改
二、AsyncTask   异步任务   抽象类
    封装了 完成子线程的方法与 UI线程更新的方法

/**
     * 第一个参数 : 参数 一般传入 URL String 类型
     * 第二个参数 : 进度 Integer
     * 第三个参数 : 结果 数据类型 根据返回值决定
     *
     * @author Mr.Zhao
     *
     */
    class MyTask extends AsyncTask<String, Integer, Object> {

/**
         * 任务开始前的准备工作
         * 通常用作于 UI的初始化
         * 在UI 线程中
         */
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
        }

/**
         * ××××××××   重要 常用
         * 子线程执行 网络请求 等 工作
         * 参数 为 继承该抽象类的时候泛型中的第一个
         * 参数类型必须保持一致 返回值: 是 网络请求结果 的值
         * 数据类型 必须与 继承抽象类的泛型中的第三个
         * 子线程
         */
        @Override
        protected Object doInBackground(String... params) {
            // TODO Auto-generated method stub
            return null;
        }

/**
         * 更新进度 values 代表着当前进度 UI线程中
         *
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);
        }

/**
         * 子线程工作后结果处理的方法 这个方法 已经回到了UI(主)线程
         *  可以进行更新UI 的操作
         * 重要  常用
         */
        @Override
        protected void onPostExecute(Object result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
        }

/**
         *
         * 取消当前任务
         */
        @Override
        protected void onCancelled() {
            // TODO Auto-generated method stub
            super.onCancelled();
        }
    }

第八天

ListView:   列表视图
可使用ArrayAdapter  实现简单的布局效果

BaseAdapter    
自定义适配器   --- 抽象类

* 当Android 系统所提供的适配器无法满足用户需求的时候  那么就需要我们来自定义适配器达到想要的需求结果
 *
 * 自定义适配器需要继承现有的  BaseAdapter  
 * 抽象类  需重写各种方法
 *
 * 自定义适配器的步骤:
 * 1.定义一个子类继承BaseAdapter
 * 2.重写相应的方法
 *     a)getCount表示加载到当前适配器的条目总数
 *     b)getItem 表示根据位置获取Item 对象
 *     c)getItemId根据位置获取当前item 的ID
 *     d)getView 根据位置获取当前item 显示的View

引用XML文件布局作为 View
将xml布局转化为View对象

* 自定义的Adapter 的item不仅仅局限于 系统view  可以也 自定义 xml布局文件   那么  就需要将布局文件转换成为 View
     *
     * 将布局转换成为View 的方法   ---   
     * 一、LayoutInflater
     * LayoutInflater 的实现方式
     * 1.LayoutInflater.from(Context)
     * 2.Activity.getLayoutInflater();
     * 3.Context.getSystemService(Context_LAYOUT_INFLATER_SERVICE);
     *
     * 二、  直接将布局转换成为View
     * View view = View.inflater(Context,R.layout.XXX,null);
     *

ListView 优化
     * ListView 优化
     * 1. 重复利用convertView
     *     --   ViewHolder
        六步骤
    // 第一步 实例化 视图持有者 ViewHolder
    // 第二步 判断convertView 是否被创建
    // 第三步 实例化Holder 对象
    // 第四步holder 对象 内部引用的实例化
    // 第五步 -- 给convertView 设置 Holder标
    // 第六步 --当convertView 不为null 的时候 通过convertView获取到 holder
     * 2. ListView的高度设置   match_parent  或绝对数值
     * 3. 尽量不要再getView 方法中进行 业务逻辑繁杂或延时操作

// 当数据源发生改变 后 调用该方法 刷新数据
该方法为Adapter 的方法 注意使用
        notifyDataSetChanged();

第十一天

一,菜单 Menu

1,选项菜单(系统菜单)OptionsMenu

1.1 两种创建菜单项的方式

高版本:在xml中定义<item>菜单项

低版本:纯java代码创建Item项

1.2 高版本  在res/menu/main.xml中定义菜单项

<!--
            android:id              Menu资源的Id
            android:title             Menu菜单项的文本
            android:orderInCategory="100"   设置菜单项显示的顺序,值越小,越在上面显示
            android:showAsAction="never"    设置菜单项何时和怎样显示在Action Bar上
            
                    属性值: never  永远都不会显示在Action Bar上
                          ifRoom  如果有空间显示在Action Bar上
                          always  一直显示在Action Bar上

-->
        <item
            android:id="@+id/action_quit"
            android:orderInCategory="100"
            android:showAsAction="never"
            android:title="退出"/>

/**
     * 创建菜单项 ,当你创建Activity时调用
     *
     * 一个activity中只能有一个OptionsMenu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // 加载菜单布局
        getMenuInflater().inflate(R.menu.main, menu);

return true;
    }

/**
     * 点击Menu Item 的回调方法
     *
     * MenuItem item 当前点击的菜单项
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case R.id.action_quit:

finish();// 关闭当前页面

break;

case R.id.action_red:

tv.setTextColor(Color.RED);

break;

case R.id.action_size_40:
            
            tv.setTextSize(40);
            
            break;
        }

return super.onOptionsItemSelected(item);
    }

1.3 低版本的创建:纯JAVA代码

/**
         * groupId 分组ID
         * itemId 菜单项的Id
         * order 菜单显示的顺序
         * title 菜单文本
         */
        menu.add(Menu.NONE, 1, Menu.NONE, "代码菜单1");
        
        SubMenu subMenu = menu.addSubMenu(Menu.NONE, 2, Menu.NONE, "代码菜单2");
        subMenu.add(Menu.NONE, 21, Menu.NONE, "sub 2_1");
        subMenu.add(Menu.NONE, 22, Menu.NONE, "sub 2_2");

2,上下文菜单 ContextMenu  可以为任意View指定上下文菜单,长按View创建上下午菜单,通常使用在ListView或者GridView中

2.1 为View注册上下文菜单
    
        registerForContextMenu(tv);

2.2 onCreateContextMenu()

/**
     * 2, 重写父类的方法onCreateContextMeun()
     *
     * 当长按View时调用
     *
     * ContextMenu menu 需要显示的菜单 View v 用户选择的View ContextMenuInfo menuInfo
     * 所选择页面的额外信息,(ListView,GridView,Spinner) 携带了AdapterView中的position信息
     */
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenuInfo menuInfo) {

// 加载菜单布局
        getMenuInflater().inflate(R.menu.main, menu);

super.onCreateContextMenu(menu, v, menuInfo);
    }

2.3 onContextItemSelected()

@Override
    public boolean onContextItemSelected(MenuItem item) {
        // TODO Auto-generated method stub
        switch (item.getItemId()) {
        
        case R.id.action_delete:
            
             //得到当前选中ListView的Item的信息
            AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo)item.getMenuInfo();
            
            //获取当前选中ListView的Item的下标
            int posiotion = menuInfo.position;
            
            //移除数据
            data.remove(posiotion);
            //刷新适配器
            adapter.notifyDataSetChanged();
            break;

}
        return super.onContextItemSelected(item);
    }

3,PopUpMenu 弹出式菜单  API 11以上,可以为任一View创建弹出式菜单

public void click(View v)
    {
        //创建PopupMenu
        /**
         * context 上下文对象
         * anchor  当前弹出菜单的参考显示的位置
         */
        PopupMenu  pMenu = new PopupMenu(this, v);
        
        //加载菜单
        getMenuInflater().inflate(R.menu.main, pMenu.getMenu());
        
        pMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
            
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                // TODO Auto-generated method stub
                switch (item.getItemId()) {
                case R.id.action_size_20:
                    
                    tv.setTextSize(20);
                    
                    break;

case R.id.action_size_30:
                    
                    tv.setTextSize(30);
                    
                    break;
                }
                
                return false;
            }
        });
        
        //显示弹出式菜单
        pMenu.show();
    }

二,对话框  Dialog ,所有对话框都必须 show()

1,    普通对话框 AlertDialog
        
        进度条对话框 ProgressDialog
        
        日期选择对话框 DatePickerDialog

时间选择对话框 TimePickerDialog

自定义对话框

2,列表对话框(AlertDialog)

普通列表对话框

builder.setItems(R.array.arrColors,new OnClickListener() {});

单选对话框

builder2.setSingleChoiceItems(R.array.arrSizes, -1, new OnClickListener() {});

多选对话框

builder3.setMultiChoiceItems(R.array.arrHobbys, checkedItems, new OnMultiChoiceClickListener() {});

适配器对话框
    builder4.setAdapter(adapter, new OnClickListener() {});

三,Toast  土司

1, 普通土司

Toast.makeText(getApplicationContext(), "提示信息", Toast.LENGTH_LONG).show();
              
    2,指定位置的土司

//设置Toast显示的位置(显示的位置,X轴的偏移,Y轴的偏移)
            toast2.setGravity(Gravity.CENTER, 0, 0);

3,自定义

Toast toast3 = new Toast(this);
            
            TextView tv   = new TextView(this);
            
            tv.setText("我是自定义的Toast!");
            
            
            toast3.setView(tv);
            
            toast3.setDuration(Toast.LENGTH_LONG);
            
            toast3.show();

四,通知栏

第十二天

Fragment  碎片

一,Fragment是什么?

是Android3.0之后,API11以上,是Activity的一个组成部分

提高代码的重用性和改善用户的体验(UI的合理利用)

Fragment有自己的生命周期和接受处理用户的事件,这样我们就不用在Activity中写一堆代码,更为重要的是
我们可以添加,替换、移除Fragment

Fragment 可以显示内容

Fragmnet  可以和用户交互

note: Fragment 必须显示在Activity中

一个Activity中可以包含多个Fragment,同一个Fragment可以被多个Activity包含

二,如何创建Fragment?

1,定义一个类,继承Fragment

2,重写父类的生命周期方法:onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState);

LayoutInflater inflater 布局加载器
    
        ViewGroup container  当前Fragment所在的父容器对象
    
        Bundle savedInstanceState 存放的业务数据

三,Fragment的显示方式

1,静态方式显示

在Activity的布局文件中,<fragment>

必须要有id属性,来标示Fragment的唯一性
                       
         name:指定要显示的Fragment的名称

2,动态方式显示

2.1 需要在Activity布局中,通过布局容器占位

2.2 Activity中:显示

//1,得到Fragment的管理器对象
    FragmentManager manager = getFragmentManager();
        
    //2,开启Fragment的事务处理
    FragmentTransaction ft = manager.beginTransaction();
        
    //3,实例化Fragment(你要现实的Fragment)
    LeftFragment fragment = new LeftFragment();
        
    //4,动态显示Fragment
    //containerViewId  在Activity布局中为Fragment占位的Id, fragment  要显示的Fragment
    //ft.add(R.id.fragment_left_id, fragment);
    ft.replace(R.id.fragment_left_id, fragment);
        
    //5,提交事务(只能提交一次)
    ft.commit();

四,Activity向Fragment传值

1,向Fragment传入参数

Bundle args = new Bundle();
    args.putString("msg", "显示的内容:" + new Date());
        
    fragment.setArguments(args);

2,在Fragment中获取传入的参数

Bundle bundle = getArguments();
        
    String msg = bundle.getString("msg");
        
    tv.setText(msg);

五,Fragment向Activity传值

1,Fragment中定义回调接口

//定义回调接口
    public interface CallBack
    {
        public void getResult(String result);
    }

2,Fragment中:回调方法

//回调方法
    public void getEditText(CallBack callBack)
    {
        String msg = editText.getText().toString().trim();
        
        callBack.getResult(msg);
    }

3,Activity中:

fragment.getEditText(new CallBack() {

@Override
        public void getResult(String result) {
                
            tv.setText(result);
        }
            
    });

六,Fragment向Fragment传值

调用Fragmnet中定义的普通的方法:

Activity中:

Bundle bundle = new Bundle();
        bundle.putString("fileName", fileName);

ContentFragment fragment = ContentFragment.newInstance(bundle);
                    
    Fragment中:
        1,public static ContentFragment newInstance(Bundle bundle) {
        
            ContentFragment contentFragment = new ContentFragment();
        
            contentFragment.setArguments(bundle);
        
            return contentFragment;
                }

2,在onCreateView()中: fileName = getArguments().getString("fileName");

七,Fragment生命周期(11个)

1,初始化阶段--onCreate()
        
        onAttach(Activity activity) 与归属Activity进行连接

onCreate(Bundle savedInstanceState) 初始化Fragment

onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)   初始化Fragment中显示的UI

onActivityCreated(Bundle savedInstanceState) 归属Activity的onCreate()执行完成

2, 显示/隐藏阶段

onStart()  显示Fragment

onResume()  获取到焦点

onPause()   失去焦点

onStop()   关闭Fragment

3,销毁阶段---onDestroy()

onDestroyView() 销毁Fragment中显示的UI

onDestroy()     销毁Fragment

onDetach()       与归属Activity断开连接

显示:

F:onAttach() -- F:onCreate() -- F:onCreateView() -- A:onCreate() -- F:onActivityCreated()
    --A:onStart()--F:onStart()--A:onResume()  -- F:onResume()

退出:

F:onPause()--A:onPause() --F:onStop()--A:onStop() -- F:onDestroyView() --F:onDestroy()
    --F:onDetach()--A:onDestroy()

八,小应用:横竖屏切换(内容改变)

九,ListFragment

1, 定义一个类,继承ListFragment

2,碎片本身就UI控件 -- ListView

3,在onCreateView()生命周期方法之后,setListAdapter(adapter);

4,重写父类的onListItemClick(),监听

===============

Day12_Fragment_01   静态显示Fragment

Day12_Fragment_02    动态显示Fragmnet,Activity向Fragment传值

Day12_Fragment_03     Fragment向Activity传值

Day12_Fragment_04     Fragment显示笔记内容

Day12_Fragment_05     Fragment显示笔记内容(横竖屏)

Day12_Fragment_06     Fragment生命周期

第十三天   数据存储(读、写)

一,分类

共享参数
    
        内部存储

扩展存储

数据库

网络存储

二,共享参数  SharedPreferences (基本设置,登陆,定位)

1,特点: 应用程序卸载后,文件也会被删除

2,存储的数据类型: boolean , int ,long,String,float

3,数据存放的位置:data/data/应用程序包名/shared_prefs/***.xml

4,存储数据:

//1,获取共享参数的对象
        /**
         * name 文件名称(不需要写扩展名称)
         * mode 文件的操作模式
         *
         * Context.MODE_PRIVATE   文件只能被当前应用程序访问
         *             写入数据时,如果Key相同,Value会覆盖之前的内容
         *
         * Context.MODE_APPEND    写入的文件可以追加
         *
         *
         *
         * getSharedPreferences(name, mode)  需要指定文件名称
         * getPreferences(mode) 文件名称是当前Activity的类名
         */
        SharedPreferences sp = getSharedPreferences("mysets", Context.MODE_PRIVATE);
        
        
        //2,获取共享参数的编辑对象
        Editor editor = sp.edit();
        
        //3,向共享参数中写入数据
        editor.putInt("fontSize", 20);
        editor.putInt("color", Color.RED);
        editor.putString("msg", "你已经更改了设置!!!");
        
        //4,提交数据
        editor.commit();

5,读取数据:

//1,得到共享参数的对象
        
        SharedPreferences sp = getSharedPreferences("mysets", Context.MODE_PRIVATE);
        
        //2,获取数据
        /**
         * key 存入数据是的Key
         *
         * defValue 如果读取不到Key对应的数据,那么显示默认的数据
         */
        int fontSize = sp.getInt("fontSize", 15);
        int color = sp.getInt("color", Color.BLACK);
        String msg = sp.getString("msg", "读取数据失败");

三,内部存储

1,特点: 应用程序卸载后,文件也会被删除

2,路径:  data/data/应用程序包名/files/***.***

3,核心代码:FileOutputStream

3.1 存入数据:

//1,打开内部存储文件的输出流
            /**
             * name 文件名称
             * mode  文件操作模式
             *
             */
            FileOutputStream fos = openFileOutput(fileName, Context.MODE_PRIVATE);
            
            //2,写入数据
            fos.write(content.getBytes());
            
            //3,关闭流
            fos.close();

3.2 读取数据:

//1,打开内部存储的输入流
            FileInputStream fis = openFileInput(fileName);
            
            //2,读取长度
            byte[] buffer = new byte[fis.available()];
            //3,开始读取
            fis.read(buffer);
            //关闭流
            fis.close();

四,扩展存储 SD卡

1,特点:
    
        当卸载应用程序时,sd卡公共目录中的文件不会清除

2,路径:
        
        android 4.0之前:mnt/sdcard

android 4.0之后:storage/sdcard

3,读写扩展卡的权限

Environment.getExternalStorageState()

4,获取扩展卡的根目录

Environment.getExternalStorageDirectory()

5,获取当前扩展卡的状态

<!-- 写SD卡的状态 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
     <!-- 读SD卡的状态 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

第十四天

Oracle,SQL Server ,mySql,SQLite  都是关系型数据库

数据库  SQLite

一,特点

轻量级,零配置,跨平台,嵌入式

在程序的内部,可以在任何位置通过数据库的名称对其访问,其他的应用程序不可以访问。

二,数据类型

NULL(空值)
    INTEGER(整型)
    TEXT(文本)
    VARCHAR(可变长度的字符数据)
    BOOLEAN(布尔)
    FLOAT(单精度)
    DOUBLE(双精度)
    DATETIME(日期)

三,SQL语句

创建表:

create table 表名(字段名称 字段类型,...)
        
        create table user(_id integer primary key autoincrement,name varchar(50))

表中添加字段

alter table 表名 add 字段名 类型

alter table user add amount integer

插入数据:

insert into  表名(字段s) values(值s)

insert into user(name) values('刘能')

修改数据:

update 表名 set  字段=值 where 条件

update user set name='于军' where _id=1

删除数据:

delete from 表名  where 条件

delete from user where _id=2

查询数据:

select (字段s) from 表名 where 条件

select _id,name from user where _id=3

四,Android数据库的核心类   data/data/应用程序包名/database/数据库名称

1,SQLiteDatabase  管理和操作数据库

2,SQLiteOpenHelper 用于数据库的创建和版本的更新

作用: 初始化数据库

升级数据库

打开数据库的连接

2,.1 使用步奏:

2.1.1 定义一个类,继承SQLiteOpenHelper

2.1.2 重写父类的方法

/**
             * 第一次执行是调用(只执行一次)
             *
             * 创建初始表
             *
             *
             * SQLiteDatabase db 数据库操作类
             */
            @Override
            public void onCreate(SQLiteDatabase db) {
                
                }

/**
             * 更新数据库
              *
              * 如果数据库的版本发生变化,执行此方法
             *
             * SQLiteDatabase db     数据库的操作类
             * int oldVersion          旧版本
             * int newVersion        新版本
             */
            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        
                }

2.1.3 构造方法

/**
             * 构造方法
             * @param context  上下文对象
             * @param name     数据库名称
             * @param factory  游标工厂
             * @param version  版本号
             */
            public DbOpenHelper(Context context, String name, CursorFactory factory,
            int version) {
                super(context, name, factory, version);
        
                }
    
    
    3,Cursor 游标 (查询数据后,游标默认位置在数据之上,游标的下标从0开始)

cursor.move(offset) 将指针向上或向下移动指定的行数,
                    offset为正数代表向下,负数代表向上,判断数据是否存在
        cursor.moveToFirst() 游标移动到第一条数据,判断第一条数据是否存在
        cursor.moveToLast() 游标移动到最后一条数据,判断最后一条数据是否存在
        cursor.moveToNext() 游标移动到下一条数据,判断下一条数据是否存在
        cursor.moveToPrevious() 游标移动到上一条数据,判断上一条数据是否存在
        cursor.moveToPosition(position) 游标移动到某一条数据,判断数据是否存在

五,Android封装的方法(为不会写SQL语句的学者准备)

1,添加
    
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        
        /**
         * table            表名
         * nullColumnHack   可以为null   如果是ContentValues  insert into user () values ()
         * values            值 ContentValues  
         */
        ContentValues values = new ContentValues();
        //key 数据库的字段名, value  字段对应的内容
        values.put("name", "赵四");
        values.put("age", "18");
        values.put("amount", "100");
        
        
        db.insert("user", null, values);

2,修改:
    
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        
        /**
         * table         表名
         * values        值 ContentValues
         * whereClause    where语句之后的字句
         * whereArgs    占位符的取值
         */
        ContentValues values = new ContentValues();
        values.put("amount", "20000");
        
        db.update("user", values, "_id=?", new String[]{"1"});

3,查询:

SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
        /**
         * distinct       是否去除重复
         * table       表名
         * columns      查询的字段              new String[]{"name","age","amount"}
         * selection      where字句之后的内容         _id=?
         * selectionArgs  占位符的值              new String[]{"1"}
         * groupBy       分组
         * having        放到where后面,再次筛选
         * orderBy        排序
         * limit      区间
         */
           Cursor cursor = db.query("user", new String[]{"name","age","amount"}, null, null, null, null, null);
        
       while (cursor.moveToNext()) {
            
        Log.i("info", "姓名:" + cursor.getString(cursor.getColumnIndex("name")) +
            ",年龄:" + cursor.getInt(cursor.getColumnIndex("age"))+
            ",钱包:" + cursor.getInt(cursor.getColumnIndex("amount")));
        }

4,删除:

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        
        /**
         * table        表名
         * whereClause  条件,where之后的字句
         * whereArgs    占位符对应的值
         */
        db.delete("user", "_id=?", new String[]{"1"});

六,数据库的事务:

事务的好处?
    
        1,确保数据的一致性

2,提高效率

什么时候使用事务?

进行批量操作的时候使用事务

比如批量进行添加、修改、删除时,必须手动开启事务

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        
        //1,开启事务
        db.beginTransaction();
        
        try{
            
            for(int i=1;i<=100;i++)
            {
                db.execSQL("insert into user(name,age,amount) values('张三'"+i+",21,100)");
            }
            //2,成功后提交事务
            //添加 事务成功的标记,当结束事务时提交事务, 如果不写,事务回滚,sql语句不执行
            db.setTransactionSuccessful();
            
        }catch(Exception e)
        {
            e.printStackTrace();
            
        }finally
        {
            //3,关闭事务
            db.endTransaction();
        }

七,应用:通讯录

八,作业: 记事本(增,删,改,查)

第十五天

Content Provider  内容提供者(Android四大组件) 图书馆(短信记录,电话记录,联系人)

一,作用

将应用的私有数据库中的私有数据向外提供一个访问的接口,是基于URI的方式,向外提供统一的数据访问接口

二,URI统一资源标示

类似与URL网络资源地址,URI是用来标示Android应用中某一资源的唯一标识,在ContentProvider组件中,通过Uri向外部应用提供一个私有数据库中的“一张表”的资源唯一标示

内容提供者的URI格式:    content://应用程序包名.provider/student

三,访问ContentProvider提供的URI接口

ContentProvider  内容提供者   负责暴露数据(数据库)            “服务器”

ContentResolver  内容解析器    负责解析ContentProvider暴露出来的数据    “客户端”

四,案例一:访问短信记录

数据库路径:com.android.providers.telephony中sms表

/**
     * 短信的uri
     *
     * content://sms  所有短信
     * content://sms/outbox 发送的短信
     * content//sms/inbox  接收的短信
     */
    private Uri smsUri = Uri.parse("content://sms");

------------------------------------------------------------------

//1,得到ContentResolver对象
        ContentResolver contentResolver = getContentResolver();
        
    //2,通过URI访问数据
    /**
    * uri 访问ContentProvider的路径
    * projection  查询数据库表中列的集合 new String[]{}
    *     _id,
    *     address: 地址(发送者)
    *     body: 短信内容
    *     type: 类型    1 接收,2  发送,3 草稿 ,4 未读,5 发送失败
    *                     
    * selection    查询条件
    * selectionArgs 查询条件中占位符对应的集合
    * sortOrder     排序
    */
        
    cursor = contentResolver.query(smsUri, new String[]{"_id","address","body"}, null, null, null);
        
    //3,读取游标中数据
        
    adapter =  new SimpleCursorAdapter(getApplicationContext(),
                           R.layout.item_lv,
                        cursor,
                        new String[]{"address","body"},
                        new int[]{R.id.address,R.id.body},
                        SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        
    lv.setAdapter(adapter);
        
    //4,在清单文件中增加访问短信记录的权限

五,案例二:访问电话记录

数据库路径:com.android.providers.contacts 中calls表

//private Uri uri = Uri.parse("content://call_log/calls");
    
      private Uri uri = CallLog.Calls.CONTENT_URI;
      
      private String[]  columns = {CallLog.Calls._ID,//主键
                      CallLog.Calls.NUMBER,//电话号码
                      CallLog.Calls.DATE//时间
                    };

-----------------------------------------------------------------------

//1,得到CotentResolver对象
        
    ContentResolver contentResolver = getContentResolver();
        
    //2,通过URI接口查询数据
        
    cursor = contentResolver.query(uri, columns, null, null, null);
    
    //3,读取游标中的数据
    while (cursor.moveToNext()) {
            
        long id = cursor.getLong(cursor.getColumnIndex(CallLog.Calls._ID));
        String phone = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
        long date = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE));
            
        String dataText = sf.format(new Date(date));
            
        data.add(new CallInfo(id, phone, dataText));
    }
        
    //刷新适配器
    adapter.notifyDataSetChanged();
        
        
    //4,添加读取电话记录的权限

六,案例三:访问联系人

联系人数据库所在的位置

data/data/com.android.provides.contacts/database/contats2

联系人表URI:

content://com.android.contacts/raw_contacts
    
   数据表URI:

content://com.android.contacts/data

表一:raw_contacts (有多少联系人就有多少条数据)

_id: 主键

display_name: 联系人姓名

表二: data

_id: 主键

mimetype_id : 外键 (mimetypes的主键)

raw_contact_id :  外键(raw_contacts的主键)

data1: 数据

表三:mimetypes

_id 主键(1 邮箱,5 电话,7 姓名)

查找 zhansan  的 电话 :select data1 from data where raw_contact_id=1

查找 姓名,电话 : select raw_contact_id,display_name,data1 from raw_contacts as t1,data as t2 where t1._id = t2.raw_contact_id and mimetype_id=5

第十六天

自定义ContentProvider 组件

一,如何创建

1,创建一个类,继承ContentProvider ,同时实现父类的6个抽象方法

onCreate()   --- 初始化数据库的操作对象(工具类)

query(Uri uri....)

Uri uri  = insert(Uri uri...)

update(Uri uri...)

delete(Uri uri...)

getType(Uri uri...) 一般情况,只实现,不做处理

2,声明ContentProvider组件的唯一标识 :  authority

note :authority 内容必须是小写字母, 建议以当前的应用的包名+标识资源的名称(数据库的名称)

3,声明访问数据库的那些表的code(t_user = 0 ,t_older = 6)

4,定义Uri的匹配器,并实例化,同时增加完整的Uri访问资源的接口

5,定义数据库的操作类,并oncreate()方法中实例化

6,在相应查询,修改,删除,添加方法中,通过Uri匹配器,判断当前的Uri是访问那张表的code

7,在Manifist清单文件中注册ContentProvider组件;同时声明权限

<!-- 注册ContentProvider
            android:name=""    名称
            android:authorities="" ContentProvider 组件的唯一标识(和authority是一致的)
            android:exported="" 设置当前的组件可以被外部应用访问
            android:permission="" 声明访问当前组件的权限
         -->
        <provider
            android:name="com.qf.day16_contentprovider.MyContentProvider"
            android:authorities="com.qf.day16_contentprovider_01.users"
            android:exported="true"
            android:permission="com.qf.day16_contentprovider_01.users.WRITE_READ"
            />

<!-- 声明访问MyContentProvider组件的权限-->
        <permission android:name="com.qf.day16_contentprovider_01.users.WRITE_READ"/>

<!-- 使用访问MyContentProvider 组件的权限
        <uses-permission android:name="com.qf.day16_contentprovider_01.users.WRITE_READ"/>-->

第十七天

Loader

一,什么是Loader?

装载器,Android3.0后引进,它使得我们在activity或Fragment中异步加载数据变的简单。

二,作用

通过异步的方式加载数据(数据库中的数据,网络数据,大数据)

实时刷新数据,当Cursor中的数据发生变化时,ListView自动刷新

系统内部使用LoaderManager和LoaderCallBacks来管理Loader

三,LoaderManager

作用:
    1,初始化启动Loader, 无条件加载数据

getLoaderManager().initLoader(编号,查询的条件,回调接口)

2,重启Loader, 有条件或者条件发生变化时加载数据

getLoaderManager().restartLoader(编号,查询的条件,回调接口)

注意:  1,一个Activity或Fragment中只能有一个LoaderManager,但是一个LoaderManager可以管理多个Loader

2,在启动Loader时,必须要提供一个回调接口,即LoaderCallBacks接口

四,CursorLoader

CursorLoader内部使用ContentResolve来加载ContentProvider暴露出来的数据,通过URI

内置了ContentOberver ,监听数据源的改变

五,LoaderCallBacks<Cursor> 回调方法

包含3个回调方法:

创建新的Loader,一般返回的是CursorLoader

onCreateLoader(int id,Bundle args)

Loader数据加载完成后,显示数据
    onLoaderFinished(Loader<Cursor> loader,Cursor data)

在创建新的Loader之前,重置之前Loader加载的数据
    onLoaderReset(Loader<Cursor> loader )

六,使用Loader加载联系人的姓名和电话

1, 在Activity或Fragment中实现LoaderCallBacks接口,重写父类3方法

2,在Activity或Fragment的初始化阶段的生命周期方法中,通过getLoaderManager()的到管理器的对象,初始化
        启动Loader

3,在LoaderCallBacks的创建Loader的方法中( onCreateLoader),实例化CursorLoader,并返回
 
      4,在LoaderCallBacks的加载完成的回调方法中(onLoaderFinished()),将加载完成的数据显示出来
 
              SimpleCursorAdapter.swapCursor(cursor);
 
      5,在LoaderCallBacks的重置方法中(onLoaderReset()),清除之前的数据
 
              SimpleCursorAdapter.swapCursor(null);

6,在有条件或者条件发生变化时,重启启动Loader

七,短信记录

八,自定义Loader

1,定义一个类,继承AsyncTaskLoader<D>,并声明返回数据结果的泛型

2,重写方法onStartLoading()方法,执行forceLoad()方法(强制执行后台方法,来加载数据)

3,重写父类的方法:loadInBackground(),在此方法中执行获取数据的操作,并把结果返回

-------拓展---------

九,SearchView

<!-- android:iconifiedByDefault="false" 设置当前的控件是否以图标的方式显示 -->
        <SearchView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/search_view_id"
            android:iconifiedByDefault="false"
        />

十,ContentObserver   内容观察者

1,自定义内容观察者

//自定义内容观察者MyContentObServer
    public class MyContentObServer extends ContentObserver
    {

public MyContentObServer(Handler handler) {
            super(handler);
            // TODO Auto-generated constructor stub
        }
        
        //当被观测的内容发生变化时,调用此方法
        @Override
        public void onChange(boolean selfChange) {
            
            super.onChange(selfChange);
            
            //重启Loader
            getLoaderManager().restartLoader(2, null, MainActivity.this);
            
            
        }
        
    }

2,注册内容观察者

//自定义内容观察者MyContentObServer
    public class MyContentObServer extends ContentObserver
    {

public MyContentObServer(Handler handler) {
            super(handler);
            // TODO Auto-generated constructor stub
        }
        
        //当被观测的内容发生变化时,调用此方法
        @Override
        public void onChange(boolean selfChange) {
            
            super.onChange(selfChange);
            
            //重启Loader
            getLoaderManager().restartLoader(2, null, MainActivity.this);
            
            
        }
        
    }

第十八天

Handler  的使用

一,Android使用线程的规则:

1,UI线程不能被阻塞

2,子线程不能操作UI线程(除ProgressBar)

3,使用handler线程间的通信机制,实现子线程向UI线程发送数据

二,了解Handler的原理

Handler实现了线程间的通信的核心类:

Looper类: 循环读取MessageQueue中的Message(for(;;))

MessageQueue类:存放的是Handler发送过来的消息(Message)

Handler类:消息的发送者,又是消息的处理者

方法:
                handleMessage() : 处理消息

obtainMessage() : 从消息队列中取出消息

sendEmptyMessage() : 发送的是Message中只给what属性赋值

sendEmptyMessageDelayed()

sendMessage(Message msg) : 发送Message

sendMessageDelayed() : 延迟发送消息

sendMessageAtTime() : 在当前设置的时间发送

post(Runnable) : 运行在主线程中,更新UI

postDelayed()

postAtTime()

Message类: 包装了线程间发送的数据

参数:8 个

int what : 发送空消息(状态)

int  arg1

int  arg2

Object obj :存放的Object类型的对象

Handler target :标签,由那个handler处理,就设为那个handler

Message sPool : 消息对象

int sPoolSize :当前内存中消息的数量

int MAX_POOL_SIZE = 50 :内存中最大的消息数量

方法:
                obtain()  从消息池中取出消息

recycle() 将用完的消息对象及时回收到消息池中

setTarget(Handler target) 给每个消息对象贴标签,只有贴了标签,Looper才能知道                                要传给那个Handler做处理
                
                Handler getTarget()

sendToTarget() 把消息发送给那个handler

三,子线程向主线程发送消息

1,在主线程中实例化Handler对象,并重写handleMessage()方法,

在此方法中用于处理handler通过sendMessage()方法发送的消息

2,在子线程中,某一个合适的位置(数据加载完成) ,实例化Message对象,将相关的数据放入Message对象中

3,在子线程中发送消息:
    
    3.1  send方法发送(3种)

handler.sendMessage(Message msg);

3.2 以post方式发送

handler.post(new Runnable(){})

这种方式,是子线程向主线程发送一段代码(代码块是在主线程中执行)

------扩展--------

计时器:

一:

Timer timer = new Timer();
        //schedule(定时的任务, 第一次执行延迟的时间, 每隔多长时间执行一次)
        timer.schedule(new TimerTask() {
            
            @Override
            public void run() {
                // TODO 定时执行的内容(发送消息)
                
                handler.sendEmptyMessage(1);
                
            }
        }, 0, 1000);

timer.cancel();//停止计时器

二:

ScheduledExecutorService scheduled = Executors.newSingleThreadScheduledExecutor();
        
        scheduled.scheduleWithFixedDelay(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                handler.sendEmptyMessage(1);
            }
        }, 1, 1, TimeUnit.SECONDS);

scheduled.shutdowm();//停止

第十八天

Handler  的使用

一,Android使用线程的规则:

1,UI线程不能被阻塞

2,子线程不能操作UI线程(除ProgressBar)

3,使用handler线程间的通信机制,实现子线程向UI线程发送数据

二,了解Handler的原理

注意 : 一个线程只能有一个Looper对象,可以有多个Handler对象

Handler实现了线程间的通信的核心类:

Looper类: 循环读取MessageQueue中的Message(for(;;))
                
           方法:
             prepare()   : 创建Looper对象
    
             loop()    :  循环读取MessageQueue中的message

myLooper()  返回子线程中的Looper对象

属性:

ThreadLocal<Looper> sThreadLocal : 本地线程的变量,能创建Looper对象

Looper sMainLooper :主线程中Looper对象

MessageQueue mQueue : 消息队列

MessageQueue类:存放的是Handler发送过来的消息(Message)

Handler类:消息的发送者,又是消息的处理者

方法:
                handleMessage() : 处理消息

obtainMessage() : 从消息队列中取出消息

sendEmptyMessage() : 发送的是Message中只给what属性赋值

sendEmptyMessageDelayed()

sendMessage(Message msg) : 发送Message

sendMessageDelayed() : 延迟发送消息

sendMessageAtTime() : 在当前设置的时间发送

post(Runnable) : 运行在主线程中,更新UI

postDelayed()

postAtTime()

Message类: 包装了线程间发送的数据

参数:8 个

int what : 发送空消息(状态)

int  arg1

int  arg2

Object obj :存放的Object类型的对象

Handler target :标签,由那个handler处理,就设为那个handler

Message sPool : 消息对象

int sPoolSize :当前内存中消息的数量

int MAX_POOL_SIZE = 50 :内存中最大的消息数量

方法:
                obtain()  从消息池中取出消息

recycle() 将用完的消息对象及时回收到消息池中

setTarget(Handler target) 给每个消息对象贴标签,只有贴了标签,Looper才能知道                                要传给那个Handler做处理
                
                Handler getTarget()

sendToTarget() 把消息发送给那个handler

三,子线程向主线程发送消息

1,在主线程中实例化Handler对象,并重写handleMessage()方法,

在此方法中用于处理handler通过sendMessage()方法发送的消息

2,在子线程中,某一个合适的位置(数据加载完成) ,实例化Message对象,将相关的数据放入Message对象中

3,在子线程中发送消息:
    
    3.1  send方法发送(3种)

handler.sendMessage(Message msg);

方式一:

Message msg = Message.obtain();
        msg.obj = bitmap;
        handler.sendMessage(msg);
        
        方式二:

Message msg = Message.obtain();
        msg.obj = bitmap;
        msg.setTarget(handler);
        msg.sendToTarget();

方式三:

Message msg = handler.obtainMessage();
        msg.obj = bitmap;
        msg.sendToTarget();

3.2 以post方式发送

handler.post(new Runnable(){})

这种方式,是子线程向主线程发送一段代码(代码块是在主线程中执行)

方式四:
        
        使用这种方法,不需要再重写handleMessage()方法

handler.post(new Runnable(){

//执行主线程赋值的操作

});

四,主线程向子线程发送消息

1,将子线程升级为Looper线程,即在子线程中创建Looper对象

Looper.perpare()方法,这个方法内部会创建一个MessageQueue对象,同时将创建的Looper保存在线程的本地变量中

2,在子线程中实例化Handler对象

new Handler()时,会判断当前的本地线程中是否存在Looper对象

3,在子线程中执行Looper.loop()  循环读取MessageQueue中的Message

五,实例 :

Day18_Handler_01  子线程向主线程发送消息的4中方式

Day18_Handler_02   图片自动播放+计时器

Day18_Handler_03   主线程向子线程发送消息

Day18_Handler_04   新闻网页

------扩展--------

计时器:

一:

Timer timer = new Timer();
        //schedule(定时的任务, 第一次执行延迟的时间, 每隔多长时间执行一次)
        timer.schedule(new TimerTask() {
            
            @Override
            public void run() {
                // TODO 定时执行的内容(发送消息)
                
                handler.sendEmptyMessage(1);
                
            }
        }, 0, 1000);

timer.cancel();//停止计时器

二:

ScheduledExecutorService scheduled = Executors.newSingleThreadScheduledExecutor();
        
        scheduled.scheduleWithFixedDelay(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                handler.sendEmptyMessage(1);
            }
        }, 1, 1, TimeUnit.SECONDS);

scheduled.shutdowm();//停止

四大类
Handler
属性:MessageQueue -- handler讲消息发送出去实际就是放在这里
           Looper--每个handler有一个looper为其不断的接受消息队列消息
   CallBack--回调接收 方法handleMessage
方法:
handleMessage : 回调接口中的方法
obtainMessage:获取消息实例  本质还是调用Message.Obtain
sendMessage:
sendMessageDelayed:
sendMessageAtTime:
sendEmptyMessage:
sendEmptyMessageDelayed:
sendEmptyMessageAtTime:
post:
postDelayed:
postAiTime:
enqueueMessage:将消息压入到MessageQueue中
dispatchMessage:当looper不断的获取到消息后调用该方法分发消息触发CallBack里的handleMessage方法进行消息处理

Looper
    属性:
ThreadLocal<Looper>:  本地线程变量 由于该变量保证每个线程只有一个Looper也就是说一个线程只能有一个Looper
方法:
prepare:所有的Handler 所在线程都必须有一个looper 准备好
prepareMainLooper:获取主线程中的looper对象
myLooper:从线程本地变量中获取当前线程的Looper对象
loop:循环接收消息队列中的消息

Message
属性:
what: 标记消息
arg1: 存放消息的 int
arg2: 存放消息的 int
obj: 用来存放消息的Object
data:用来存放消息属性Bundle
target:标示  Handler
sPool:消息池中的每个消息对象
sPoolSize:纪录消息池中剩余消息的数量
MAX_POOL_SIZE:消息池中的最大消息对象数量
replyTo: Messenger:自定义信使对象  跨进程

方法:
obtain:      获取Message实例
setTarget: 设置收到消息的Handler
getTarget: 获取应该收到消息的Handler
setData:   设置Bunldle对象
getData:   设置Bunldle对象
sendToTarget: 将消息发送到目标Handler对象,本质还是调用sendMessage
recycle:     往消息池中归还消息对象

MessageQueue
属性:
enqueueMessage:通过改方法将消息添加到自己的队列中
next:下一个Message

Handler 消息机制:

由于主线程(UI线程)不能访问网络,子线程不能更新UI,所以有了Handler消息传送机制用来将子线程的数据发送到主线程中来进行更新UI,在主线程中实例化Handler对象,
在子线程中将将要发送的数据 添加到Message对象中去,Message是可携带int,object,Bundle对象的,通过handler调用handler.sendMessage方法将消息发送出去,
这个时候消息会进入到消息队列MessageQueue 中去,MessageQueue 是个消息队列,enqueueMessage方法讲消息添加到队列中,looper提供了消息运转的动力,
每个线程只允许有一个looper,消息在MessageQueue中不断的一个一个的做进栈出栈动作,handler调用dispatchMessage方法对MessageQueue中出来Message进行分发
,分发根据的是target(Handler),而dispatchMessage方法执行的就是一个CallBack回调方法,也就是主线程中用来接收消息的的handleMessage方法,
这样主线程就可以对接收到的数据进行处理了。

第十九天

一,ActionBar

1,说明

ActionBar上Android3.0 后引入的,显示在标题栏上,创建的方式和菜单是相同的。

与菜单的主要区别是:showAsAction属性

2,ActionBar动作项(Item)显示的属性

showAsAction:

ifRoom : 如果标题栏有空间,则会显示动作项的图标

always :不管有没有空间,都会显示在标题栏

withText:带有文本信息显示
    
            collapseActionView :可以被折叠的,单用户点击按钮时,这个操作视图才会被展开

3,创建ActionBar显示的动作项

3.1 在res/menu/目录下创建菜单资源,再声明<item>时必须声明显示属性(showAsAction)和图标属性(android:icon)
        
      3.2 在Activity类中,重写onCreateOptionsMenu()方法,加载菜单资源

3.3 重写onOptionsItemSelected()方法,来处理ActionBar中的每个Action项的点击事件

4,分割栏操作:

API 14以上

在<application>或<activity>元素中添加android:uiOptions="splitActionBarWhenNarrow" 属性设置

5,启动程序导航图标

5.1     作用: 让APP的Logo也变成可点击的图标

5.2     getActionBar() 获取当前Activity的ActionBar对象
        
        5.3
            //设置是否显示Logo的图标
            getActionBar().setDisplayShowHomeEnabled(true);

//设置是否将Logo图标作为可点击的按钮,并且在图标的前面添加一个向左的箭头
            getActionBar().setDisplayHomeAsUpEnabled(true);

5.4    android.R.id.home://应用导航图标的ID

6,ActionView的使用

作用:可以编辑的动作项,如:searchView可以直接显示在ActionBar中

方式:

1,actionViewClass属性,指定实现CollapsibleActionView的子类

2,actionLayout属性,可以指定一个布局文件

二,ActionBar+Fragment

ActionBarTab 导航 : 通常通过选项标签切换Fragment

1,获取当前Activity的ActionBar对象(getActionBar()),并设置导航模式为TABS

2,在当前Activity中,实现ActionBar.TabListener接口(重写3个方法)

3,增加ActionBar.Tab 到ActionBar中

ActionBar.Tab tab = actionBar.newTab();

setText()    设置Tab显示的文本

setIcon()    设置Tab显示的图标

setTabListener() 设置Tab的监听

actionBar.addTab(tab,true);//第二个参数:默认选中的Tab项

第二十天

ViewPager

一,特点: 可以左右滑动的控件,需要PagerAdapter配合使用,由V4包

二,类名:android.support.v4.view.ViewPager;

三,使用步骤

1,在布局文件中使用<android.support.v4.view.ViewPager>标签

2,在代码中增加显示的页面

3,在Activity中实例化ViewPager组件,并设置适配器(PagerAdapter)

四:V4 包下Fragment

1,创建Fragment的子类,继承Fragment(V4包下)

2,Activity必须继承FragmentActivity(V4包下)

3,FragmentManager manager = getSupportFragmentManager();

第二十一天

案例
<不推荐使用方法>
一、TabHost+Activity 方法实现
二、ActionBar+Fragment 方法实现
三、ActionBar+ViewPager 方法实现
四、FragmentTabHost + Fragment 方法实现   ----√

<推荐使用方法>

五、ViewPager+PagerTabStrip+Fragment 方法实现√
六、RadioGroup+Fragment  方法实现 √
七、RadioGroup+ViewPager+Fragment 方法实现 √
八、HorizontalScrollView+Linearlayout+ViewPager 方法实现√

作业!
使用java代码方式写布局计算器

第二十二天

* 广播:  全局性的 ,系统级别的 消息传递机制
 *
 * 广播的三要素:
 * 1.发送方 Context.sendBroadCast
 * 2.发送内容  Intent
 * 3.广播接收者  BroadCastReceiver
 *
 * 广播根据注册方式不同分为
 * 静态注册
 *     在清单文件中 <application> 标签下 添加<receiver> 标签   并 添加IntentFilter   及其 action 属性
 * 动态注册
 *     在 Activity 中 实例化  MyReceiver 对象  ,再定义一个 IntentFilter 对象  添加Action 属性   
 *     调用 registeReceiver方法  注册  同时 需要在  onDestory方法中调用 unRegisterReceiver方法  解除注册
 *
 * 静态注册与动态注册的区别:
 * 1.动态注册的接收效率要比静态注册的稍高一些
 * 2.静态注册的时候,app 退出 activity 的销毁  都不会影响广播接收者的接收
 * 3.动态注册的时候,如果当前Activity 销毁 则不会再接收到广播
 *
 * 关键:
 * 定义action
 * 发送方的Intent 定义action(可以理解为一个频道)
 * 所有存在于该频道的Receiver都能接收到这个频道的消息
 *
 * 发送方定义了action  所有的接收方(receiver) 只要都设置了这个action 就都能接收到 这个广播
 *
 * 广播的生命周期:
 * 从接收方 onReceive方法 执行开始    当前的广播接收者有10s的活跃期   这个阶段 不会受到任何的影响,10s过后处于 失活状态
 * 这个时候如果有系统进程需要内存空间,则,该广播接收者有可能会被回收,所以 如果需要在该方法中执行延时操作,需要开启一个衍生线程(子线程)来
 * 完成这样的工作
 *
 * 广播实现步骤:
 * 1.定义发送
 * 2.定义发送的action   --定义的时候一般定义为 包名+广播接收者的类名  (按照需求其名称  标准即可)
 * 3.发送广播
 * 4.定义广播接收者
 * 5.注册广播接收者
 *         动态
 *         静态

有序广播
 * 根据优先级 来决定广播接收者 接收广播的顺序
 * 发送广播的方法 :  sendOrderBroadCast
 * 第二个参数  是权限名称  不需要的时候为 null
 *
 * 需要在注册的时候 添加优先级 (优先级 属性在   IntentFalter  标签内部  proprity 该属性)
 *  取值范围   -1000  到  +1000    数值越大 优先级越高  会先接收到广播
 *  
 * 所有的广播接收者不可更改 发送方发送出来的Intent
 *
 * 如果需要广播接收者之间传递数据
 * 可使用  setResultExtra(Bundle)  方法   
 * 接收方  getResultExtras( boolean)   
 * 如果 boolean 为false  则  直接返回 传递对象  可为Bundle  可为null  
 * 如果boolean 为true   则  一定会返回  bundle 对象   但是  可能有值 可能 没有键值
 *
 * 只有优先级高的 可以向优先级低的传递数据
 *
 *拦截有序广播的方法
 *     abortBroadCast()
 * 拦截后 ,所有优先级比当前优先级低的广播  都将不再接收到 广播

第二十三天

* Service:  服务
 * 服务像Windows后台服务一样,服务是在后台运行,悄悄的承担着不为人知的一些工作
 * Service 在后台运行,他是不可见的、无界面的程序
 *
 * 没有页面的程序 应用场景:播放音乐,下载 ,定位等
 *
 * 概念:
 * 1.Service运行在后台不可与用户进行交互
 * 2.服务不是一个单独的进程 ,服务对象本身并不是在自己的进程中运行。
 * 3.服务不是一个线程,服务Service 与其他的组件一样,默认的代码是执行在主线程中的
 * 4.需要一个上下文对象来启动服务
 *
 * Service 很大程度上承担了后台线程管理的角色:在Activity中开启一个线程,当Activity销毁后,Thread会继续工作,
 * 但是与开启它的Activity 失去了联系,也就是说当前线程处于无人管理的状态,但是Service 可以有效的对后台线程进行管理
 *
 * Service比线程的优点:
 * 1.Service 可以放在独立的进程中,所以更安全
 * 2.系统可以重启被异常杀死的Service
 * 3.Service可以依赖现有的Binder 机制(明天讲)
 *
 * Service与Activity的关系
 * 1.相同点:都是四大组件,都需要在清单文件中注册,都具有一定的生命周期
 * 2.不同点:Acitivty 存在着与用户交互的页面,Service 在后台运行
 *
 * 分类:本地服务LocalService  与远程服务 RemoteService
 * A:本地服务 LocalService
 * 根据启动模式分为两种:
 * 1.startService(Intent)
 *     被启动的服务是通过其他的组件(Activity或BroadCast)调用startService 方法启动的。该方法会导致Service 的生命周期中
 *     onCreate与onStartCommand方法执行,当Service 启动后,则与他的调用者的生命周期无关,即便调用者已经被销毁。Service 也会
 *     一直存在,在后台执行,除非调用了stopService 或者stopSelf
 * 2.bindService(。。。)(明天讲)
 *
 * Service 的生命周期:
 *
 * onCreat 服务被创建
 * onStartCommand  服务开始执行
 * onDestory  服务被销毁
 *
 * 说明:
 * 1.当调用startService 方法后,服务会执行 onCreate  onStartCommand 方法 ,此时服务处于is Running状态
 * 2.如果服务已经被创建 则再次调用startService 方法,服务只会执行onStartCommand 方法,如果没有被创建,则执行onCreate onStartCommand方法
 * 3.如果调用了stopService 或者 stopSelf方法   服务会执行onDestory 方法 销毁
 * 4.所以 Service 的生命周期为  onCreat --->onStartCommand (可以被多次调用)--->onDestory
 *
 * onStartCommand方法:
 * 根据返回值划分:
 * 1. START_STIKEY:粘性的 常量为 1
 *     如果服务被异常杀死 服务会被重新启动,但是Intent 值为null
 * 2. START_NO_STIKEY :非粘性的  常量为 2
 *  如果服务被异常杀死 服务不会被重新启动
 * 3. STRAT_REDELIVER_INTENT  常量为 3
 *     如果服务被异常杀死,服务会被重启,且Intent 有值
 
 * B 远程服务 RemoteService(接下来讲)
 * C IntentService(接下来讲)

第二十四天
* 绑定服务
 *
 * 绑定服务是允许其它应用程序绑定并且与之交互的Service的实现类。为了提供绑定,必须实现onBind()回调方法。
 * 该方法返回IBinder对象,它定义了服务类与Activity交互的程序接口
 *
 *
 * Activity通过bindService()方法绑定到服务类,同时Activity必须提供ServiceConnection接口的实现类,
 * 它监视Activity与服务类之间的连接。在重写ServiceConnection接口的onServiceConnected()方法时,
 * 实现了将服务类顺利赋值到了Activity中,实现了在Activity中使用该服务类并执行其中的方法
 *
 *
 * 同一个Service可以绑定多个服务链接,这样可以同时为多个不同的组件提供服务 全部组件关闭后 他会关闭
 *
 * 作用
 *
 * 作用是在Service和调用者之 间建立一个桥梁,并不负责更多的工作(例如一个Service需要连接服务器 的操作),
 * 一般使用bindService来绑定到一个现有的Service(即通过 StartService启动的服务)
 *
 * 生命周期 onCreate():创建服务 onBind():绑定服务,服务开始运行 onUnbind():取消绑定 onDestroy() :服务被停止
 *
 * 在程序中调用:context.bindService()会触发执行Service生命周期中的onCreate()、onBind()回调方法,
 * 此时服务开始运行
 *
 * onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。
 * 此后调用者(Context,例如Activity)会和Service绑定在一起
 *
 * 如果调用Service的调用者Context退出了,那么会依次调用Service生命周期中的onUnbind()、onDestroy()回调方法,
 * 会让服务停止
 *
 * 所以BindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind -->
 * onDestory

文件存储

临时存储
内存
持久化存储

内部存储
data-data-包名-/

1.cache   缓存
2.file      文件
3.database   数据库
4.shared_pref   .xml  sharedpreference  存储

Context.getCacheDir();
Context.getFileDir();
...

data-app
存放的都是apk 文件
真机可能需要root 权限才能看到

SD卡(外部存储)
1.判断是否存在SD卡  (挂载)
Environment.getExternalStorageState();
2.获取SD卡的根目录
Environment.getExternalStorageDirectory();
3.获取9大公有目录的路径
Environment.getExternalStoragePublicDirectory(TYPE);Environment.XXX

File.separator    分隔符

SD中的私有目录:
Android-data-包名-    files
            cache
//获取SD卡中私有目录的路径方法
Context.getExternalCacheDir();
Context.getExternalFilesDir();

//现成的文件引用工具类

package com.steven.helper;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.StatFs;

public class SDCardHelper {

// 判断SD卡是否被挂载
    public static boolean isSDCardMounted() {
        // return Environment.getExternalStorageState().equals("mounted");
        return Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED);
    }

// 获取SD卡的根目录
    public static String getSDCardBaseDir() {
        if (isSDCardMounted()) {
            return Environment.getExternalStorageDirectory().getAbsolutePath();
        }
        return null;
    }

// 获取SD卡的完整空间大小,返回MB
    public static long getSDCardSize() {
        if (isSDCardMounted()) {
            StatFs fs = new StatFs(getSDCardBaseDir());
            long count = fs.getBlockCountLong();
            long size = fs.getBlockSizeLong();
            return count * size / 1024 / 1024;
        }
        return 0;
    }

// 获取SD卡的剩余空间大小
    public static long getSDCardFreeSize() {
        if (isSDCardMounted()) {
            StatFs fs = new StatFs(getSDCardBaseDir());
            long count = fs.getFreeBlocksLong();
            long size = fs.getBlockSizeLong();
            return count * size / 1024 / 1024;
        }
        return 0;
    }

// 获取SD卡的可用空间大小
    public static long getSDCardAvailableSize() {
        if (isSDCardMounted()) {
            StatFs fs = new StatFs(getSDCardBaseDir());
            long count = fs.getAvailableBlocksLong();
            long size = fs.getBlockSizeLong();
            return count * size / 1024 / 1024;
        }
        return 0;
    }

// 往SD卡的公有目录下保存文件
    public static boolean saveFileToSDCardPublicDir(byte[] data, String type,
            String fileName) {
        BufferedOutputStream bos = null;
        if (isSDCardMounted()) {
            File file = Environment.getExternalStoragePublicDirectory(type);
            try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                bos.write(data);
                bos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

// 往SD卡的自定义目录下保存文件
    public static boolean saveFileToSDCardCustomDir(byte[] data, String dir,
            String fileName) {
        BufferedOutputStream bos = null;
        if (isSDCardMounted()) {
            File file = new File(getSDCardBaseDir() + File.separator + dir);
            if (!file.exists()) {
                file.mkdirs();// 递归创建自定义目录
            }
            try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                bos.write(data);
                bos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

// 往SD卡的私有Files目录下保存文件
    public static boolean saveFileToSDCardPrivateFilesDir(byte[] data,
            String type, String fileName, Context context) {
        BufferedOutputStream bos = null;
        if (isSDCardMounted()) {
            File file = context.getExternalFilesDir(type);
            try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                bos.write(data);
                bos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

// 往SD卡的私有Cache目录下保存文件
    public static boolean saveFileToSDCardPrivateCacheDir(byte[] data,
            String fileName, Context context) {
        BufferedOutputStream bos = null;
        if (isSDCardMounted()) {
            File file = context.getExternalCacheDir();
            try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                bos.write(data);
                bos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

// 保存bitmap图片到SDCard的私有Cache目录
    public static boolean saveBitmapToSDCardPrivateCacheDir(Bitmap bitmap,
            String fileName, Context context) {
        if (isSDCardMounted()) {
            BufferedOutputStream bos = null;
            // 获取私有的Cache缓存目录
            File file = context.getExternalCacheDir();

try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                if (fileName != null
                        && (fileName.contains(".png") || fileName
                                .contains(".PNG"))) {
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
                } else {
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
                }
                bos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

// 从SD卡获取文件
    public static byte[] loadFileFromSDCard(String fileDir) {
        BufferedInputStream bis = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

try {
            bis = new BufferedInputStream(
                    new FileInputStream(new File(fileDir)));
            byte[] buffer = new byte[8 * 1024];
            int c = 0;
            while ((c = bis.read(buffer)) != -1) {
                baos.write(buffer, 0, c);
                baos.flush();
            }
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                baos.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

// 从SDCard中寻找指定目录下的文件,返回Bitmap
    public Bitmap loadBitmapFromSDCard(String filePath) {
        byte[] data = loadFileFromSDCard(filePath);
        if (data != null) {
            Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);
            if (bm != null) {
                return bm;
            }
        }
        return null;
    }

// 获取SD卡公有目录的路径
    public static String getSDCardPublicDir(String type) {
        return Environment.getExternalStoragePublicDirectory(type).toString();
    }

// 获取SD卡私有Cache目录的路径
    public static String getSDCardPrivateCacheDir(Context context) {
        return context.getExternalCacheDir().getAbsolutePath();
    }

// 获取SD卡私有Files目录的路径
    public static String getSDCardPrivateFilesDir(Context context, String type) {
        return context.getExternalFilesDir(type).getAbsolutePath();
    }

public static boolean isFileExist(String filePath) {
        File file = new File(filePath);
        return file.isFile();
    }

// 从sdcard中删除文件
    public static boolean removeFileFromSDCard(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            try {
                file.delete();
                return true;
            } catch (Exception e) {
                return false;
            }
        } else {
            return false;
        }
    }
}

第二十五天
远程服务
* 跨进程通信
 * AIDL
*安卓接口定义语言
 * android  interface  definition  language
 *
 * 1.在服务端创建接口文件  .aidl
 * 2. 在服务端将该接口实例返回
 * 3.客户端需要将 服务端的.aidl文件所在 的包连同文件copy 到 客户端
 * 4.客户端拿到接口实例,调用接口的方法

第二十六天
Android:
补间动画 :补充开始和结束中间的动画效果,支持 位移,缩放,透明度,旋转
属性动画 :通过改变对象的属性起始及结束的值来执行动画
帧动画   :多个图片连续播放 ,一帧一帧的  类似GIF 图

补间动画
实现方式:
1.通过xml代码方式
    在res文件夹下创建 anim 文件夹  创建xml文件
2.通过java代码方式
    Animation 子类:   AlphaAnimation   TranslateAnimation RotateAnimation  ScaleAnimation AnimationSet

属性动画:
实现方式
1.通过xml代码方式:
    在res文件夹下创建animator文件夹   创建xml文件
2.通过Java代码方式
    ObjectAnimator     根据属性名称决定动画执行种类
    属性名称:
    alpha
     
    rotation
    
    rotationX     
 
    rotationY     
    
    translationX                  
     
    translationY
     
    scaleY
    
    scaleX

帧动画:
实现方式
1.通过xml  设置
     在res文件夹下创建drawable文件夹下 创建 根标签为 animation-list 的xml文件
2.通过Java代码方式
    AnimationDrawable 类

第二十七天

屏幕适配
重要的概念:
一、屏幕尺寸:
    屏幕对角线的长度 单位   英寸    1英寸 = 2.54cm
二、屏幕的分辨率:
    屏幕宽高的像素点数 单位是 px像素   
    一些主流机型的分辨率:   1280×720   1920×1080    2560×1440
三、屏幕像素密度:
    屏幕每英寸所含像素点数
四、dpi
    单位 --   屏幕像素密度的单位
五、px
    单位 --   像素的单位
六、sp
    单位  --   描述字体大小的单位
七、dp(dip)
    独立的像素密度,与密度无关的像素
    dp =  px ×  比例     
    比例 根据屏幕像素密度的不同而改变   如果屏幕的像素密度为160 dpi   则 dp与px 1:1    如果像素密度为 240dpi 则dp:px = 1:1.5   等等

适配的方案:
一、多套图片
    制作多套分辨率不同的图片 来放在res  文件夹下不同的drawable资源文件夹下 ,系统会根据当前屏幕的像素密度的不同来
    分别加载不同文件夹下的资源
    注意:相同的图片在不同的文件夹下 名字必须一致
    如果只有一套图片,则默认放在hdpi文件夹下 ,因为 系统加载该文件夹下的文件消耗最小
    多套图片:不是所有的图片都需要做多套图片  一般情况下  最需要做多套图片的是 logo
二、多使用线性布局与相对布局 禁用绝对布局
    使用 wrap_content  match_parent   weight    确保灵活使用并适应各种布局
三、布局
    通过添加修饰符来加载不同的布局文件
    layout-land       横向屏幕 加载该文件夹下的文件
    了解
    layout-small      屏幕尺寸小于3英寸左右的屏幕
    layout-normal      屏幕尺寸小于4.5英寸左右的屏幕
    layout-large      4英寸到7英寸之间
    layout-xlarge     大于7英寸小于 11英寸
四、dimention
    使用资源文件 values
    新建文件夹  values-1377x899      命名规范:  values 加上中横线  -    在加上屏幕分辨率中数大的   1377 加上小x  再加上屏幕分辨率 数小的  899
    在该文件夹下 添加  文件   dimens.xml
    使用方法与 values  -  dimens.xml 一样
    在新建文件夹下的dimens文件中创建的值必须在 values 文件夹下  添加默认值
    了解
    values-small
    values-normal
    values-large
    values-xlarge
    
    sw<600>    最小宽度大于600 的屏幕
五、代码适配方案
    通过 DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    获取屏幕宽度  
    定义基础宽度,计算获取到的屏幕宽度与基础宽度的比例
    拿到比例后 计算每个View 的位置及大小  进行等比例缩放
    所有单位为像素px    必须使用java代码的方式写布局
六、点9图片
    工具路径:sdk/tools/draw9path.bat
    可拉抻的图片
七、清单文件 中
    supports-screens
      anyDensity   = true  支持任何像素密度的屏幕     默认就是支持的
八、国际化语言
    创建  values文件夹    values -   加上国家缩写
    在文件夹中创建string.xml 文件 将语言进行国际化转换  
    注意  在新建文件夹中 添加的 字符串必须在 values文件夹下 的string 里建立默认值
九、SDK 版本适配
    
    
    
UI美化
一、Style
 Style 风格样式
        适用于 View
        定义后可以通过  parent 属性或  直接style 的名称加上点.再加上子类名称的方式进行继承
        子类拥有父类的所有属性
        并且可以更改 更改后以子类的为基准
        如果 xml使用过程中再次修改属性
        以xml的为准
二、Theme
     Theme  主题样式  
        适用于   Applicaton   activity
        如果activity 设置了 theme  则设置了该主题的activity会覆盖application的主题
        一般这里只是用两种
        1 .Theme.NoTitleBar.Fullscreen
        2. Theme.NoTitleBar
三、selector
    选择器
    state_focused   焦点     是否聚焦    EditText  Search
    state_enabled    能够接受点击或触摸事件    button  textView
    state_checkable   是否可以被选中  CheckBox   RadioButton
    state_selected     选中     spinner   listView
    state_pressed    按下   Button
四、shape
    形状
    属性参数 :
    corners: 角度   五个属性同时设置  radius无效
    gradient :   渐变  与solid同时设置  无效
    stroke: 边框  虚线边框   dashWidth  dashGap
    solid : 填充  颜色   与gradient设置 时  gradient 无效
五、level-list
    等级     
      <item
        android:drawable="@drawable/wifi1"
        android:maxLevel="10"
        android:minLevel="5"/>

包含maxLevel   不包含minLevel
    使用时 必须设置为IamgeView 的src 属性
     一般用于   wifi信号 手机sim卡信号   电池电量信号  移动数据信号等
六、layer-list
    叠加图片在 一个ImageView里

第二十八天
format  可以用|来同时使用
1、reference   参考某一资源Id
2、color          颜色值
3、boolean     布尔值
4、dimension 尺寸值(带有单位的 sp/dp)
5、float           浮点型
6、integer 整形
7、string 字符串
8、fraction       百分比
9、enum 枚举
10、flag 位或运算

第二十八天——————之后见项目

/
第八周,高级第一周(第三方Jar包的应用)

1,volley

jsonString:
    StringRequest
    JsonArrayRequest
    jsonObjectRequest
    自定义

步骤:1,得到queue 队列   2,请求  3,请求添加到队列   4,设置取消标记  5,进行取消

图片:
    ImageRequest
    ImageLoader  :1,获取ImageLoader 对象   2,获取ImageListener  3,get(Url,iamgeListener)
    NetworkImage :1,在布局中添加  2,获取ImageLoader  3,setImageUrl(Url,imagloder)

2,Xutils

ViewUtils  :1,注入注解工具  2,可以进行注入  3类: 资源   控件   事件
    HttpUtils  :get /post / 下载
    BtmapUtils :  本地图片  网络图片   display(控件  地址 )
    DbUtils    : 创建数据库  数据库升级   创建数据库表  增删改查

3,PullToRefresh:刷新控件
    1,导入jar
    2,在布局中添加刷新控件
    3,进行设置  
    4,进行监听  
    5,下载完成 进行关闭
  swipeRefreshLayout
    1,在布局中添加
    2,进行设置  背景  进度颜色
    3,进行监听'
    4,进行关闭
4,SlidingMenu:  主视图 被菜单 推出屏幕
    1,导包
    2,在布局中添加
    3,进行设置
    对布局里的控件  查找出来  进行监听

drawerLayout: 主视图  被菜单 覆盖
       1,布局中  添加菜单
       第一个子元素  :主视图    匹配父控件   
       第二个子元素  :菜单      不能设置大于320dp   layout_grivat
 5,OkHttp
    高效的下载库   
    get  /post

同步 和异步

1,获取OKHttpClient对象
    2,获取Request对象
    3,通过OKHttpClient对象调用  newCall()  将 Request对象  变成Call任务
    4,启动任务 (同步 和异步)
6,Picasso

1,导包
    2,一行代码就ok
    Picasso.with(Context).load().into(控件)
    图片的设置  需要在  into之前调用

7、RecyclerView

9、感应器;例:微信摇一摇的实现

第一天

1、LinearLayout,线性布局
 vertically :垂直,纵向
 horizontally:水平,横向

a:方向,默认横向
android:orientation="horizontal"
horizontal,在水平方向上一个挨着一个
vertical,在垂直方向上一个挨着一个

b:控件的宽,高
android:layout_width="fill_parent":宽
android:layout_height="fill_parent":高

fill_parent/match_parent:匹配父控件
wrap_content:包裹内容物
固定值:100dp

c:单位
    dp:表示非文字大小的尺寸
    sp:设置文字大小

d:设置背景颜色 或 背景图片
android:background="#ff0000"
android:background="#f00"

使用RGB三原色设置背景颜色
ARGB,A:alpha,带透明度的三原色
Color.rbg();
Color.arbg();

完全透明:#00~~~
完全不透明:#ff~~~

设置背景图片:
android:background="@drawable/img001"
@drawable/img001:访问图片的资源文件

e:边距
内边距:控件内部,内容与边框的距离
android:padding:四个方向
paddingleft,rigth,top,bottom

外边距:控件之间的距离
android:layout_margin:四个方向
layout_marginLeft,right,top,bottom

f:
android:gravity:设置View中内容的位置
    1).设置在布局节点(LinearLayout),指定布局中控件的位置
    2).设置在控件中(Button,TextView……),指定控件中文字的位置
    android:gravity="right|bottom",右下角

android:layout_gravity:控件相对于容器的位置,使用在控件中
    在横向布局中,设置为right失效
    在纵向布局中,设置bottom失效

g:
android:weight:设置权重,在横向或纵向上,控件占用空间的比例
默认权重为0

在横向布局中,设置android:layout_width="0dp"
在纵向布局中,设置android:layout_height="0dp"

h:
android:id,设置控件的id,给控件起个名字,可以在应用中使用名字获得该控件
 android:id="@+id/mTextView",
可以理解为:在R文件中新增控件,名为mTextView

通过id获得控件
mTextView = (TextView) findViewById(R.id.mTextView);

2,FrameLayout:帧布局
在屏幕的某个区域可以添加多个控件,最近(最后)添加的显示在最上方

重点:
id
width
height

边距:padding,margin

weight:权重

gravity
layout_gravity

RelativeLayout相对布局

a:控件与布局之间的对齐方式,值为true
android:layout_centerInParent="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"

android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"

b:兄弟控件之间的相对位置,值为另外一个控件的id,不能设置第一个控件的左方和上方
android:layout_above="@id/tv1"
android:layout_below="@id/tv1"
android:layout_toLeftOf="@id/tv1"
android:layout_toRightOf="@id/tv1"

c:兄弟控件之间的对齐方式,值为另外一个控件的id
android:layout_alignLeft="@id/tv1"
android:layout_alignRight="@id/tv1"
android:layout_alignTop="@id/tv1"
android:layout_alignBottom="@id/tv1"

d:基准线对齐,本控件文字的底边与目标控件文字的底边对齐
android:layout_alignBaseLine

第二天
复习:
1、LinearLayout,线性布局
android:orientation
horizontal
vertical

2、
android:layout_width
android:layout_height

fill_parent,match_parent
wrap_content
固定值:100dp

3、background,背景颜色  和 背景图片
RGB
ARGB

@drawable/img

4、边距:
内边距:padding
外边距:margin

5、gravity:
layout_gravity:

6.weight:权重,默认0
水平布局,layout_width="0dp"
垂直布局,layout_height="0dp"

7.FrameLayout,帧布局

本课内容:
1、RelativeLayout,相对布局

参考文档

2、GridLayout,网格布局
与线性布局类似,需要指定控件的方向
android:orientation=  horizontal  或  vertical

指定行数和列数
android:rowCount="5":行数
android:columnCount="4":列数

合并单元格
android:layout_rowSpan="2",跨行合并
android:layout_columnSpan="2",跨列合并

合并之后应该进行填充
android:layout_gravity="fill_vertical",fill,fill_horiaontal

网格中的单元格可以通过索引访问,索引从0开始

指定控件在网格中显示的位置
android:layout_row="0"
android:layout_column="3"

3.TextView:用于显示文本

android:text="@string/textView":设置文本内容
        android:textSize="30sp":大小
        android:textColor="#f00":颜色
        android:textStyle="bold|italic":样式

android:autoLink="web",设置链接,phone,email,map,all

android:singleLine="true":单行显示,默认在末尾加...

android:marqueeRepeatLimit="marquee_forever",设置滚动的次数,
android:focusable="true",是否获得焦点
android:focusableInTouchMode="true",以触摸的形式获得焦点

4.EditText:用于输入文本,获得光标时,显示不同的软键盘
是TextView的子类

android:hint:设置提示
android:inputType:设置输入的类型,弹出的软键盘不同

5、Button
添加按钮的单击事件
a:创建子类(内部类),实现android.view.View.OnClickListener接口,重写onClick方法
b:匿名内部类
c:在布局文件中,Button标签添加onClick属性,属性值为方法名
该方法声明要求:
public void 方法名(view v){}

多个按钮处理单击事件
a:实现android.view.View.OnClickListener接口,重写方法
    1).创建子类
    2).MainActivity实现接口
b:在Button标签中添加onClick属性,设置方法名

switch判断具体的按钮

6、Toast,吐司
// 1.上下文;2.显示文本;3.显示的时间
Toast.makeText(this, "button1", Toast.LENGTH_LONG).show();
注:一定要有show()方法

7、查看logCat日志信息
verbose:啰嗦的,琐碎的,冗长的
    Log.v(),级别最低的一个,显示全部信息
debug:显示调试信息
    Log.d()
info:显示重要的调试信息,异常
     Log.i()
warn:显示警告信息
    Log.w()
error:显示错误信息
    Log.e()

8.RadioButton,单选按钮
保证单选按钮互斥访问,成为单选按钮,需要使用RadioGroup

默认选中:android:checked="true"

单击事件
a:在RadioButton标签中添加onClick属性
b:给RadioGroup注册监听,实现接口android.widget.RadioGroup.OnCheckedChangeListener

c: 获得选中的RadioButton的id
int id = rg.getCheckedRadioButtonId();

预习:
checkBox
ImageView

第三天

复习:
1、RelativeLayout,相对布局
16个属性

7个相对于布局
4个兄弟节点之间的相对位置
4个兄弟节点之间的对齐方式
1个基准线对齐

2.GridLayout,网格布局
方向,android:orientation

行数,rowcount
列数,columnCount

跨行合并,rowSpan
跨列合并,columnSpan
layout_gravity=“fill”

通过索引访问单元格,0起始
layout_row
layout_column

3.TextView

4.EditText,可编辑的
hint
inputType

5.Button,
单击事件,
a:OnClickListener
b:onClick="send"

public void send(View v){}

6.RadioButton
RadioGroup保证可单选

事件
a:RadioButton,onclick
b:RadioGroup,设置监听,onCheckedChangeListener

i18n: internationalization,国际化
本课内容:
1、CheckBox
事件
a:在CheckBox标签中添加onClick属性
b:使用onCheckedChangeListener

2.ImageView,显示图片

android:src="@drawable/bg04",指定显示的图片资源

三个属性搭配使用,调整图片的宽高,保证图片的宽高比例
android:adjustViewBounds="true",是否调整ImageView的边界来保持宽高比例
android:maxWidth="100dp",最大宽度
android:maxHeight="300dp",最大高度

android:cropToPadding="true",是否截取图片指定的区域使用空白来替代
android:scrollX="15dp",x轴方向截取
android:scrollY="17dp",y轴方向截取
//反向截取
android:scrollX="-15dp",x轴方向截取
android:scrollY="-17dp",y轴方向截取

android:tint="#f00",使用指定的颜色渲染图片
android:alpha="0.5f",设置透明度

android:scaleType="",设置裁剪的类型

matrix:保持图片原大小显示,从左上角开始显示

fitStart:保持横纵比例缩放图片,并将图片放到ImageView的左上角

fitEnd:保持横纵比例缩放图片,并将图片放到ImageView的右下角

fitCenter:保持横纵比例缩放图片,并将图片放到ImageView的中间

fitXY:横向,纵向独立缩放图片,适应Imageview的大小,不保持原来图片的比例,填充ImageView

center:不进行任何缩放,将图片放到ImageView的中央,如果图片大于ImageView,截取中间部分显示

centerCrop:保持横纵比例缩放图片,使图片覆盖ImageView,以图片和ImageView的中心为基准点,进行缩放,直到图片最小的某方向等于ImageView的某边(某一方向的长宽等于ImageView,另一方向大于ImageView),进行截取

centerInside:保持横纵比例缩放图片,使ImageView能完全显示图片,以图片和ImageView的中心为基准点,进行缩放,显示图片内容,如果图片小于ImageView,不进行缩放,居中显示;如果图片大于Imageview,进行缩放,居中显示

3、AutoCompleteTextView

控件
适配器(adapter:ArrayAdapter,SimpleAdapter,BaseAdapter……)
数据源

适配器作用:实现数据源和控件的关联

第四天
复习:
1.CheckBox
事件
a:onClick
b:onCheckedChangeListener

2.ImageView
src="@drawable/bg"
iv.setImageResource(resId);

scaleType
tint
alpha

3.ToggleButton  Switch

4.AutoCompleteTextView

适配器:ArrayAdapter,数据源是集合或数组

本课内容:
1、Spinner:下拉列表

在布局文件中,Spinner标签添加属性,与字符串数组资源关联
android:entries="@array/planets_array"(关联的资源是res文件夹下的values文件夹中string中的string_array)

给Spinner添加事件,实现接口是OnItemSelectedListener
注意:注册监听器

代码详见案例。

2、解析xml文件,首先在res下创建xml文件夹,放置xml文件

xml解析获得标签属性值:parser.getAttributeValue(null,"cityname")//null:固定值;cityname:属性的名字
3、ArrayAdapter,数据源是集合或数组

更新适配器:adapter.notifyDataSetChanged();
4、获取当前时间
安卓控件
http://www.cnblogs.com/menlsh/archive/2013/02/23/2923867.html
方法一
Calendar c = Calendar.getInstance();
取得系统日期:year = c.get(Calendar.YEAR)
               month = c.grt(Calendar.MONTH)
               day = c.get(Calendar.DAY_OF_MONTH)
取得系统时间:hour = c.get(Calendar.HOUR_OF_DAY);
                  minute = c.get(Calendar.MINUTE)

方法二
 TextView myTextView = (TextView)findViewById(R.id.myTextView);   
        Time time = new Time("GMT+8");    
        time.setToNow();   
        int year = time.year;   
        int month = time.month;   
        int day = time.monthDay;   
        int minute = time.minute;   
        int hour = time.hour;   
        int sec = time.second;   
        myTextView.setText("当前时间为:" + year +    
                            "年 " + month +    
                            "月 " + day +    
                            "日 " + hour +    
                            "时 " + minute +    
                            "分 " + sec +    
                            "秒");

第五天

1、Activity,四大组件之一,创建成功之后,都需要在清单文件中进行注册
可以认为是应用程序的页面

创建Activity
a:创建子类继承Activity,重写onCreate方法
b:创建布局文件
c:在清单文件中进行注册

2、Intent,意图
启动Activity
方法1:
    Intent intent = new Intent();
    // 1.上下文,代表当前Activity
    // 2.代表跳转的目的页面,传递的是Class类型的对象
    intent.setClass(MainActivity.this, NextActivity.class);
    // 启动意图
    startActivity(intent);

方法2:
    Intent intent = new Intent(MainActivity.this, NextActivity.class);
    // 启动意图
    startActivity(intent);

3、Intent传值
a:
A页面:Intent对象使用putExtra方法封装名值对
B页面:首先获得意图(getIntent())
    之后使用相应的get***Extra()方法获得值

b:
A页面:创建Bundle对象,封装数据,之后将Bundle对象再封装到Intent中
    Bundle bundle = new Bundle();
    bundle.putString("sex", "male");
    bundle.putInt("weight", 65);
    intent.putExtra("bundle", bundle);
B页面:首先获得Bundle对象,获得Bundle封装的数据
    Bundle bundle = intent.getBundleExtra("bundle");
    String sex = bundle.getString("sex");
    int weight = bundle.getInt("weight");

c:
A页面:使用Bundle对象封装数据
    intent.putExtras(bundle);
B页面:获得Bundle对象,可以获得前一个页面使用putExtra方法封装的名值对
    Bundle bundle = intent.getExtras();

d:传递对象,要求对象可序列化
A页面:封装对象
    Person person = new Person("jack", 21);
    intent.putExtra("person", person);
或者
    bundle.putSerializable("person", person);

B页面;获得对象
    Person person = (Person) intent.getSerializableExtra("person");
或者
    Person person = (Person) bundle.getSerializable("person");

4.StartActivityForResult
跳转到下一个页面并返回结果

A页面,使用StartActivityForResult启动下一个页面
    在onActivityResult方法中获得返回值
B页面,获得Intent(getIntent()),封装数据
    // 设置响应码和Intent对象
    setResult(2, intent);
    // 结束Activity的生命周期
    finish();

shutdown -s -t 60 -c
at 12:00 shutdown -s -t

5.Activity生命周期
方法有7个,是系统调用的
onCreate():创建并初始化Activity时使用
onStart():显示页面
onResume():获取用户焦点,与用户进行交互

onPause();失去焦点,暂停与用户的交互
onStop():关闭页面,处于后台运行
onDestroy():销毁页面

onRestart():页面重新启动并显示,级联调用onStart-onResume

6、分析生命周期
a:启动Activity,onCreate-onStart-onResume
b:当前Activity被其他Activity覆盖(第二个页面以对话框形式显示)  或 锁屏:onPause
    当前Acitvity回到前台显示 或 解锁:onResume
c:跳转到新的Activity  或 按HOme键:onPause-onStop

d:按回退 或 再运行应用:onRestart-onStart-onResume

e:当b,c两种情况,当前Activity已经不可用,如果系统资源不足,将杀死当前的Activity
f:当用户退出Activity,onPause-onStop-onDestroy

8、生命周期的阶段
完整生命周期:onCreate-onDestroy
可见声明周期:onStart-onStop
前台(焦点)生命周期:onResume-onPause

7。现场保护

保存数据
onPause-onSaveInstanceState-onStop-onDestroy

恢复数据
onCreate-onStart-onRestoreInstanceState-onResume

Activity生命周期
1.打开主界面,后退键退出
onCreate-onStart-onResume-onPause-onStop-onDestroy
2.打开主界面,进入第二个页面
onCreate-onStart-onResume-AonPause-BonCreate-BonStart-BonResume-AonStop
在B页面按返回键
BonPause-AonRestart-AonStart-AonResume-BonStop-BonDestroy

2.1、打开主页面,进入第二个页面,第二个页面以对话框显示
在清单文件中,Activity标签中设置页面主题:
android:theme="@android:style/Theme.Dialog"

onCreate-onStart-onResume-AonPause-BonCreate-BonStart-BonResume

按返回或点击屏幕其他位置
BonPause-AonResume-BonStop-BonDestroy
    
3.打开主界面,模拟电话进入及挂机,再次显示主界面
onCreate-onStart-onResume-onPause-onStop-onRestart-onStart-onResume

4.打开主界面,HOME键退出
onCreate-onStart-onResume-onPause-onStop
5.打开主界面,HOME键退出,再次启动app
onCreate-onStart-onResume-onPause-onStop-onRestart-onStart-onResume

6.打开主界面,切换横屏竖屏。
onCreate-onStart-onResume-onPause-onStop-onDestroy-onCreate-onStart-onResume

屏幕切换,销毁当前页面,再重新创建

http://flash.weather.com.cn/wmaps/xml/china.xml

第六天
一、Task
 * 任务栈 对 所有Activity涉及其操作的任务集合
 *
 * 特点:
 * 1 栈顶的元素 是用户直接看到的那个任务 并且 可以进行交互
 * 2 栈中的元素都处于停止的状态
 * 3 弹出的任务 销毁状态
二、启动模式
 * Activity 启动模式
 * 配置方法: 在清单文件中activity 标签下 设置 launchMode = “”;
 * 1.standard
 * 标准的启动模式(默认) , 每次启动的时候都会实例化一个新的Activity对象放入栈中
 * 2.singleTop
 * 如果当前任务处于栈顶,则不会创建新的实例入栈,会调用当前栈顶元素的 onNewIntent 方法 如果该任务没有处于栈顶,则创建新的实例放入栈中
 * 3.singleTask
 * 如果栈中已经存在该实例,则重用该实例,不会创建新的实例,重用时 ,会将该实例上面的所有Activity 弹出销毁
 * 当前实例处于栈顶
 * 4.singleInstance
 * 在一个新的栈当中创建Acitivty 的实例   对其他应用程序共享该实例
三、Intent 七大属性
 * Intent: 意图
 *  1.启动新的组件 Activity,Service ,BroadCast。。。
 *  2.传递数据
 *  3.根据action标记来启动系统页面
 * 七大属性:
 * 1. ComponentName 组成 ,要素,构成
 *     1) 显示意图 直接设置上下文对象及目标Activity 进行跳转等同与Intent直接设置参数
 *      2) 跨应用跳转Activity ( 包名,目标类的全路径)
 * 2. Action 标识符
 *     intent-filter 意图过滤器 过滤条件的集合     在清单文件中配置
 *     action 与category 必须同时使用
 *     1) 隐式意图作为跳转目标的标记 ,将目标Activity 的action设置一个字符串标识 根据Intent 的action 属性 进行跳转
 *  2) 根据Intent类的常量调用系统页面(模拟器有问题)
 * 3. Category种类
 *     和Action 同时使用 表示当前Activity的类型
 *      1) DEFAULT 默认
 *      2) LAUNCHER 该应用启动的Activity
 * 4.Data  Uri
 *     Uri  统一资源标识符    格式:scheme://host:port/path
 *     其他的 Android系统的  资源标识符
    
    系统内置的几个Data属性常量:
      tel://:号码数据格式,后跟电话号码。
      mailto://:邮件数据格式,后跟邮件收件人地址。
      smsto://:短息数据格式,后跟短信接收号码。
      content://:内容数据格式,后跟需要读取的内容。
      file://:文件数据格式,后跟文件路径。
      market://search?q=pname:pkgname:市场数据格式,在Google Market里搜索包名为pkgname的应用。
      geo://latitude, longitude:经纬数据格式,在地图上显示经纬度所指定的位置。

Uri 统一资源标识符
    的格式如下:scheme://host:port/path
    约束       主机名(域名:端口号)      路径
    url   http://www.baidu.com:8080/indext?page =1

//实现发短信
    Intent  intent  = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setType("vnd.android-dir/mms-sms");
    intent.putExtra("sms_body", "信息内容...");
    startActivity(intent);

* 5.type类型     
 *     字符串格式  abc/xyc
 * 6.Extra   携带数据
 * 7.Flag    启动模式

intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // -- singleTop
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // -- singleTask
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // -- singleInstance

四:
    1.清单文件中 versionCode代表 版本号
             versionName代表版本名称
        Permissions  权限 ,有需要的添加   
        设置category 的Activity 为应用启动的Activity
    2.注意命名规范  
        方法名使用驼峰式命名方法
        尽量使用英文 不要使用汉语拼音
    3.Debug 的两种方式

Intent意图

系统内置的几个Data属性常量:
  tel://:号码数据格式,后跟电话号码。
  mailto://:邮件数据格式,后跟邮件收件人地址。
  smsto://:短息数据格式,后跟短信接收号码。
  content://:内容数据格式,后跟需要读取的内容。
  file://:文件数据格式,后跟文件路径。
  market://search?q=pname:pkgname:市场数据格式,在Google Market里搜索包名为pkgname的应用。
  geo://latitude, longitude:经纬数据格式,在地图上显示经纬度所指定的位置。

Uri 统一资源标识符
的格式如下:scheme://host:port/path
    约束       主机名(域名:端口号)      路径
    url   http://www.baidu.com:8080/indext?page =1

Intent  intent  = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setType("vnd.android-dir/mms-sms");
intent.putExtra("sms_body", "信息内容...");
startActivity(intent);

第七天

Json
http://sns.maimaicha.com/api?apikey=b4f4ee31a8b9acc866ef2afb754c33e6&format=json&method=news.getListByType&page=0&row=15&type=52

Image    
https://www.baidu.com/img/bd_logo1.png

一、主线程(UI线程)不能访问网络 子线程不能更新UI
    主线程(UI线程)不能访问网络,使用子线程获得网络数据,子线程不能更新UI,获得数据交给主线程完成UI的修改
二、AsyncTask   异步任务   抽象类
    封装了 完成子线程的方法与 UI线程更新的方法

/**
     * 第一个参数 : 参数 一般传入 URL String 类型
     * 第二个参数 : 进度 Integer
     * 第三个参数 : 结果 数据类型 根据返回值决定
     *
     * @author Mr.Zhao
     *
     */
    class MyTask extends AsyncTask<String, Integer, Object> {

/**
         * 任务开始前的准备工作
         * 通常用作于 UI的初始化
         * 在UI 线程中
         */
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
        }

/**
         * ××××××××   重要 常用
         * 子线程执行 网络请求 等 工作
         * 参数 为 继承该抽象类的时候泛型中的第一个
         * 参数类型必须保持一致 返回值: 是 网络请求结果 的值
         * 数据类型 必须与 继承抽象类的泛型中的第三个
         * 子线程
         */
        @Override
        protected Object doInBackground(String... params) {
            // TODO Auto-generated method stub
            return null;
        }

/**
         * 更新进度 values 代表着当前进度 UI线程中
         *
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);
        }

/**
         * 子线程工作后结果处理的方法 这个方法 已经回到了UI(主)线程
         *  可以进行更新UI 的操作
         * 重要  常用
         */
        @Override
        protected void onPostExecute(Object result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
        }

/**
         *
         * 取消当前任务
         */
        @Override
        protected void onCancelled() {
            // TODO Auto-generated method stub
            super.onCancelled();
        }
    }

第八天

ListView:   列表视图
可使用ArrayAdapter  实现简单的布局效果

BaseAdapter    
自定义适配器   --- 抽象类

* 当Android 系统所提供的适配器无法满足用户需求的时候  那么就需要我们来自定义适配器达到想要的需求结果
 *
 * 自定义适配器需要继承现有的  BaseAdapter  
 * 抽象类  需重写各种方法
 *
 * 自定义适配器的步骤:
 * 1.定义一个子类继承BaseAdapter
 * 2.重写相应的方法
 *     a)getCount表示加载到当前适配器的条目总数
 *     b)getItem 表示根据位置获取Item 对象
 *     c)getItemId根据位置获取当前item 的ID
 *     d)getView 根据位置获取当前item 显示的View

引用XML文件布局作为 View
将xml布局转化为View对象

* 自定义的Adapter 的item不仅仅局限于 系统view  可以也 自定义 xml布局文件   那么  就需要将布局文件转换成为 View
     *
     * 将布局转换成为View 的方法   ---   
     * 一、LayoutInflater
     * LayoutInflater 的实现方式
     * 1.LayoutInflater.from(Context)
     * 2.Activity.getLayoutInflater();
     * 3.Context.getSystemService(Context_LAYOUT_INFLATER_SERVICE);
     *
     * 二、  直接将布局转换成为View
     * View view = View.inflater(Context,R.layout.XXX,null);
     *

ListView 优化
     * ListView 优化
     * 1. 重复利用convertView
     *     --   ViewHolder
        六步骤
    // 第一步 实例化 视图持有者 ViewHolder
    // 第二步 判断convertView 是否被创建
    // 第三步 实例化Holder 对象
    // 第四步holder 对象 内部引用的实例化
    // 第五步 -- 给convertView 设置 Holder标
    // 第六步 --当convertView 不为null 的时候 通过convertView获取到 holder
     * 2. ListView的高度设置   match_parent  或绝对数值
     * 3. 尽量不要再getView 方法中进行 业务逻辑繁杂或延时操作

// 当数据源发生改变 后 调用该方法 刷新数据
该方法为Adapter 的方法 注意使用
        notifyDataSetChanged();

第十一天

一,菜单 Menu

1,选项菜单(系统菜单)OptionsMenu

1.1 两种创建菜单项的方式

高版本:在xml中定义<item>菜单项

低版本:纯java代码创建Item项

1.2 高版本  在res/menu/main.xml中定义菜单项

<!--
            android:id              Menu资源的Id
            android:title             Menu菜单项的文本
            android:orderInCategory="100"   设置菜单项显示的顺序,值越小,越在上面显示
            android:showAsAction="never"    设置菜单项何时和怎样显示在Action Bar上
            
                    属性值: never  永远都不会显示在Action Bar上
                          ifRoom  如果有空间显示在Action Bar上
                          always  一直显示在Action Bar上

-->
        <item
            android:id="@+id/action_quit"
            android:orderInCategory="100"
            android:showAsAction="never"
            android:title="退出"/>

/**
     * 创建菜单项 ,当你创建Activity时调用
     *
     * 一个activity中只能有一个OptionsMenu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // 加载菜单布局
        getMenuInflater().inflate(R.menu.main, menu);

return true;
    }

/**
     * 点击Menu Item 的回调方法
     *
     * MenuItem item 当前点击的菜单项
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case R.id.action_quit:

finish();// 关闭当前页面

break;

case R.id.action_red:

tv.setTextColor(Color.RED);

break;

case R.id.action_size_40:
            
            tv.setTextSize(40);
            
            break;
        }

return super.onOptionsItemSelected(item);
    }

1.3 低版本的创建:纯JAVA代码

/**
         * groupId 分组ID
         * itemId 菜单项的Id
         * order 菜单显示的顺序
         * title 菜单文本
         */
        menu.add(Menu.NONE, 1, Menu.NONE, "代码菜单1");
        
        SubMenu subMenu = menu.addSubMenu(Menu.NONE, 2, Menu.NONE, "代码菜单2");
        subMenu.add(Menu.NONE, 21, Menu.NONE, "sub 2_1");
        subMenu.add(Menu.NONE, 22, Menu.NONE, "sub 2_2");

2,上下文菜单 ContextMenu  可以为任意View指定上下文菜单,长按View创建上下午菜单,通常使用在ListView或者GridView中

2.1 为View注册上下文菜单
    
        registerForContextMenu(tv);

2.2 onCreateContextMenu()

/**
     * 2, 重写父类的方法onCreateContextMeun()
     *
     * 当长按View时调用
     *
     * ContextMenu menu 需要显示的菜单 View v 用户选择的View ContextMenuInfo menuInfo
     * 所选择页面的额外信息,(ListView,GridView,Spinner) 携带了AdapterView中的position信息
     */
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenuInfo menuInfo) {

// 加载菜单布局
        getMenuInflater().inflate(R.menu.main, menu);

super.onCreateContextMenu(menu, v, menuInfo);
    }

2.3 onContextItemSelected()

@Override
    public boolean onContextItemSelected(MenuItem item) {
        // TODO Auto-generated method stub
        switch (item.getItemId()) {
        
        case R.id.action_delete:
            
             //得到当前选中ListView的Item的信息
            AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo)item.getMenuInfo();
            
            //获取当前选中ListView的Item的下标
            int posiotion = menuInfo.position;
            
            //移除数据
            data.remove(posiotion);
            //刷新适配器
            adapter.notifyDataSetChanged();
            break;

}
        return super.onContextItemSelected(item);
    }

3,PopUpMenu 弹出式菜单  API 11以上,可以为任一View创建弹出式菜单

public void click(View v)
    {
        //创建PopupMenu
        /**
         * context 上下文对象
         * anchor  当前弹出菜单的参考显示的位置
         */
        PopupMenu  pMenu = new PopupMenu(this, v);
        
        //加载菜单
        getMenuInflater().inflate(R.menu.main, pMenu.getMenu());
        
        pMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
            
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                // TODO Auto-generated method stub
                switch (item.getItemId()) {
                case R.id.action_size_20:
                    
                    tv.setTextSize(20);
                    
                    break;

case R.id.action_size_30:
                    
                    tv.setTextSize(30);
                    
                    break;
                }
                
                return false;
            }
        });
        
        //显示弹出式菜单
        pMenu.show();
    }

二,对话框  Dialog ,所有对话框都必须 show()

1,    普通对话框 AlertDialog
        
        进度条对话框 ProgressDialog
        
        日期选择对话框 DatePickerDialog

时间选择对话框 TimePickerDialog

自定义对话框

2,列表对话框(AlertDialog)

普通列表对话框

builder.setItems(R.array.arrColors,new OnClickListener() {});

单选对话框

builder2.setSingleChoiceItems(R.array.arrSizes, -1, new OnClickListener() {});

多选对话框

builder3.setMultiChoiceItems(R.array.arrHobbys, checkedItems, new OnMultiChoiceClickListener() {});

适配器对话框
    builder4.setAdapter(adapter, new OnClickListener() {});

三,Toast  土司

1, 普通土司

Toast.makeText(getApplicationContext(), "提示信息", Toast.LENGTH_LONG).show();
              
    2,指定位置的土司

//设置Toast显示的位置(显示的位置,X轴的偏移,Y轴的偏移)
            toast2.setGravity(Gravity.CENTER, 0, 0);

3,自定义

Toast toast3 = new Toast(this);
            
            TextView tv   = new TextView(this);
            
            tv.setText("我是自定义的Toast!");
            
            
            toast3.setView(tv);
            
            toast3.setDuration(Toast.LENGTH_LONG);
            
            toast3.show();

四,通知栏

第十二天

Fragment  碎片

一,Fragment是什么?

是Android3.0之后,API11以上,是Activity的一个组成部分

提高代码的重用性和改善用户的体验(UI的合理利用)

Fragment有自己的生命周期和接受处理用户的事件,这样我们就不用在Activity中写一堆代码,更为重要的是
我们可以添加,替换、移除Fragment

Fragment 可以显示内容

Fragmnet  可以和用户交互

note: Fragment 必须显示在Activity中

一个Activity中可以包含多个Fragment,同一个Fragment可以被多个Activity包含

二,如何创建Fragment?

1,定义一个类,继承Fragment

2,重写父类的生命周期方法:onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState);

LayoutInflater inflater 布局加载器
    
        ViewGroup container  当前Fragment所在的父容器对象
    
        Bundle savedInstanceState 存放的业务数据

三,Fragment的显示方式

1,静态方式显示

在Activity的布局文件中,<fragment>

必须要有id属性,来标示Fragment的唯一性
                       
         name:指定要显示的Fragment的名称

2,动态方式显示

2.1 需要在Activity布局中,通过布局容器占位

2.2 Activity中:显示

//1,得到Fragment的管理器对象
    FragmentManager manager = getFragmentManager();
        
    //2,开启Fragment的事务处理
    FragmentTransaction ft = manager.beginTransaction();
        
    //3,实例化Fragment(你要现实的Fragment)
    LeftFragment fragment = new LeftFragment();
        
    //4,动态显示Fragment
    //containerViewId  在Activity布局中为Fragment占位的Id, fragment  要显示的Fragment
    //ft.add(R.id.fragment_left_id, fragment);
    ft.replace(R.id.fragment_left_id, fragment);
        
    //5,提交事务(只能提交一次)
    ft.commit();

四,Activity向Fragment传值

1,向Fragment传入参数

Bundle args = new Bundle();
    args.putString("msg", "显示的内容:" + new Date());
        
    fragment.setArguments(args);

2,在Fragment中获取传入的参数

Bundle bundle = getArguments();
        
    String msg = bundle.getString("msg");
        
    tv.setText(msg);

五,Fragment向Activity传值

1,Fragment中定义回调接口

//定义回调接口
    public interface CallBack
    {
        public void getResult(String result);
    }

2,Fragment中:回调方法

//回调方法
    public void getEditText(CallBack callBack)
    {
        String msg = editText.getText().toString().trim();
        
        callBack.getResult(msg);
    }

3,Activity中:

fragment.getEditText(new CallBack() {

@Override
        public void getResult(String result) {
                
            tv.setText(result);
        }
            
    });

六,Fragment向Fragment传值

调用Fragmnet中定义的普通的方法:

Activity中:

Bundle bundle = new Bundle();
        bundle.putString("fileName", fileName);

ContentFragment fragment = ContentFragment.newInstance(bundle);
                    
    Fragment中:
        1,public static ContentFragment newInstance(Bundle bundle) {
        
            ContentFragment contentFragment = new ContentFragment();
        
            contentFragment.setArguments(bundle);
        
            return contentFragment;
                }

2,在onCreateView()中: fileName = getArguments().getString("fileName");

七,Fragment生命周期(11个)

1,初始化阶段--onCreate()
        
        onAttach(Activity activity) 与归属Activity进行连接

onCreate(Bundle savedInstanceState) 初始化Fragment

onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)   初始化Fragment中显示的UI

onActivityCreated(Bundle savedInstanceState) 归属Activity的onCreate()执行完成

2, 显示/隐藏阶段

onStart()  显示Fragment

onResume()  获取到焦点

onPause()   失去焦点

onStop()   关闭Fragment

3,销毁阶段---onDestroy()

onDestroyView() 销毁Fragment中显示的UI

onDestroy()     销毁Fragment

onDetach()       与归属Activity断开连接

显示:

F:onAttach() -- F:onCreate() -- F:onCreateView() -- A:onCreate() -- F:onActivityCreated()
    --A:onStart()--F:onStart()--A:onResume()  -- F:onResume()

退出:

F:onPause()--A:onPause() --F:onStop()--A:onStop() -- F:onDestroyView() --F:onDestroy()
    --F:onDetach()--A:onDestroy()

八,小应用:横竖屏切换(内容改变)

九,ListFragment

1, 定义一个类,继承ListFragment

2,碎片本身就UI控件 -- ListView

3,在onCreateView()生命周期方法之后,setListAdapter(adapter);

4,重写父类的onListItemClick(),监听

===============

Day12_Fragment_01   静态显示Fragment

Day12_Fragment_02    动态显示Fragmnet,Activity向Fragment传值

Day12_Fragment_03     Fragment向Activity传值

Day12_Fragment_04     Fragment显示笔记内容

Day12_Fragment_05     Fragment显示笔记内容(横竖屏)

Day12_Fragment_06     Fragment生命周期

第十三天   数据存储(读、写)

一,分类

共享参数
    
        内部存储

扩展存储

数据库

网络存储

二,共享参数  SharedPreferences (基本设置,登陆,定位)

1,特点: 应用程序卸载后,文件也会被删除

2,存储的数据类型: boolean , int ,long,String,float

3,数据存放的位置:data/data/应用程序包名/shared_prefs/***.xml

4,存储数据:

//1,获取共享参数的对象
        /**
         * name 文件名称(不需要写扩展名称)
         * mode 文件的操作模式
         *
         * Context.MODE_PRIVATE   文件只能被当前应用程序访问
         *             写入数据时,如果Key相同,Value会覆盖之前的内容
         *
         * Context.MODE_APPEND    写入的文件可以追加
         *
         *
         *
         * getSharedPreferences(name, mode)  需要指定文件名称
         * getPreferences(mode) 文件名称是当前Activity的类名
         */
        SharedPreferences sp = getSharedPreferences("mysets", Context.MODE_PRIVATE);
        
        
        //2,获取共享参数的编辑对象
        Editor editor = sp.edit();
        
        //3,向共享参数中写入数据
        editor.putInt("fontSize", 20);
        editor.putInt("color", Color.RED);
        editor.putString("msg", "你已经更改了设置!!!");
        
        //4,提交数据
        editor.commit();

5,读取数据:

//1,得到共享参数的对象
        
        SharedPreferences sp = getSharedPreferences("mysets", Context.MODE_PRIVATE);
        
        //2,获取数据
        /**
         * key 存入数据是的Key
         *
         * defValue 如果读取不到Key对应的数据,那么显示默认的数据
         */
        int fontSize = sp.getInt("fontSize", 15);
        int color = sp.getInt("color", Color.BLACK);
        String msg = sp.getString("msg", "读取数据失败");

三,内部存储

1,特点: 应用程序卸载后,文件也会被删除

2,路径:  data/data/应用程序包名/files/***.***

3,核心代码:FileOutputStream

3.1 存入数据:

//1,打开内部存储文件的输出流
            /**
             * name 文件名称
             * mode  文件操作模式
             *
             */
            FileOutputStream fos = openFileOutput(fileName, Context.MODE_PRIVATE);
            
            //2,写入数据
            fos.write(content.getBytes());
            
            //3,关闭流
            fos.close();

3.2 读取数据:

//1,打开内部存储的输入流
            FileInputStream fis = openFileInput(fileName);
            
            //2,读取长度
            byte[] buffer = new byte[fis.available()];
            //3,开始读取
            fis.read(buffer);
            //关闭流
            fis.close();

四,扩展存储 SD卡

1,特点:
    
        当卸载应用程序时,sd卡公共目录中的文件不会清除

2,路径:
        
        android 4.0之前:mnt/sdcard

android 4.0之后:storage/sdcard

3,读写扩展卡的权限

Environment.getExternalStorageState()

4,获取扩展卡的根目录

Environment.getExternalStorageDirectory()

5,获取当前扩展卡的状态

<!-- 写SD卡的状态 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
     <!-- 读SD卡的状态 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

第十四天

Oracle,SQL Server ,mySql,SQLite  都是关系型数据库

数据库  SQLite

一,特点

轻量级,零配置,跨平台,嵌入式

在程序的内部,可以在任何位置通过数据库的名称对其访问,其他的应用程序不可以访问。

二,数据类型

NULL(空值)
    INTEGER(整型)
    TEXT(文本)
    VARCHAR(可变长度的字符数据)
    BOOLEAN(布尔)
    FLOAT(单精度)
    DOUBLE(双精度)
    DATETIME(日期)

三,SQL语句

创建表:

create table 表名(字段名称 字段类型,...)
        
        create table user(_id integer primary key autoincrement,name varchar(50))

表中添加字段

alter table 表名 add 字段名 类型

alter table user add amount integer

插入数据:

insert into  表名(字段s) values(值s)

insert into user(name) values('刘能')

修改数据:

update 表名 set  字段=值 where 条件

update user set name='于军' where _id=1

删除数据:

delete from 表名  where 条件

delete from user where _id=2

查询数据:

select (字段s) from 表名 where 条件

select _id,name from user where _id=3

四,Android数据库的核心类   data/data/应用程序包名/database/数据库名称

1,SQLiteDatabase  管理和操作数据库

2,SQLiteOpenHelper 用于数据库的创建和版本的更新

作用: 初始化数据库

升级数据库

打开数据库的连接

2,.1 使用步奏:

2.1.1 定义一个类,继承SQLiteOpenHelper

2.1.2 重写父类的方法

/**
             * 第一次执行是调用(只执行一次)
             *
             * 创建初始表
             *
             *
             * SQLiteDatabase db 数据库操作类
             */
            @Override
            public void onCreate(SQLiteDatabase db) {
                
                }

/**
             * 更新数据库
              *
              * 如果数据库的版本发生变化,执行此方法
             *
             * SQLiteDatabase db     数据库的操作类
             * int oldVersion          旧版本
             * int newVersion        新版本
             */
            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        
                }

2.1.3 构造方法

/**
             * 构造方法
             * @param context  上下文对象
             * @param name     数据库名称
             * @param factory  游标工厂
             * @param version  版本号
             */
            public DbOpenHelper(Context context, String name, CursorFactory factory,
            int version) {
                super(context, name, factory, version);
        
                }
    
    
    3,Cursor 游标 (查询数据后,游标默认位置在数据之上,游标的下标从0开始)

cursor.move(offset) 将指针向上或向下移动指定的行数,
                    offset为正数代表向下,负数代表向上,判断数据是否存在
        cursor.moveToFirst() 游标移动到第一条数据,判断第一条数据是否存在
        cursor.moveToLast() 游标移动到最后一条数据,判断最后一条数据是否存在
        cursor.moveToNext() 游标移动到下一条数据,判断下一条数据是否存在
        cursor.moveToPrevious() 游标移动到上一条数据,判断上一条数据是否存在
        cursor.moveToPosition(position) 游标移动到某一条数据,判断数据是否存在

五,Android封装的方法(为不会写SQL语句的学者准备)

1,添加
    
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        
        /**
         * table            表名
         * nullColumnHack   可以为null   如果是ContentValues  insert into user () values ()
         * values            值 ContentValues  
         */
        ContentValues values = new ContentValues();
        //key 数据库的字段名, value  字段对应的内容
        values.put("name", "赵四");
        values.put("age", "18");
        values.put("amount", "100");
        
        
        db.insert("user", null, values);

2,修改:
    
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        
        /**
         * table         表名
         * values        值 ContentValues
         * whereClause    where语句之后的字句
         * whereArgs    占位符的取值
         */
        ContentValues values = new ContentValues();
        values.put("amount", "20000");
        
        db.update("user", values, "_id=?", new String[]{"1"});

3,查询:

SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
        /**
         * distinct       是否去除重复
         * table       表名
         * columns      查询的字段              new String[]{"name","age","amount"}
         * selection      where字句之后的内容         _id=?
         * selectionArgs  占位符的值              new String[]{"1"}
         * groupBy       分组
         * having        放到where后面,再次筛选
         * orderBy        排序
         * limit      区间
         */
           Cursor cursor = db.query("user", new String[]{"name","age","amount"}, null, null, null, null, null);
        
       while (cursor.moveToNext()) {
            
        Log.i("info", "姓名:" + cursor.getString(cursor.getColumnIndex("name")) +
            ",年龄:" + cursor.getInt(cursor.getColumnIndex("age"))+
            ",钱包:" + cursor.getInt(cursor.getColumnIndex("amount")));
        }

4,删除:

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        
        /**
         * table        表名
         * whereClause  条件,where之后的字句
         * whereArgs    占位符对应的值
         */
        db.delete("user", "_id=?", new String[]{"1"});

六,数据库的事务:

事务的好处?
    
        1,确保数据的一致性

2,提高效率

什么时候使用事务?

进行批量操作的时候使用事务

比如批量进行添加、修改、删除时,必须手动开启事务

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        
        //1,开启事务
        db.beginTransaction();
        
        try{
            
            for(int i=1;i<=100;i++)
            {
                db.execSQL("insert into user(name,age,amount) values('张三'"+i+",21,100)");
            }
            //2,成功后提交事务
            //添加 事务成功的标记,当结束事务时提交事务, 如果不写,事务回滚,sql语句不执行
            db.setTransactionSuccessful();
            
        }catch(Exception e)
        {
            e.printStackTrace();
            
        }finally
        {
            //3,关闭事务
            db.endTransaction();
        }

七,应用:通讯录

八,作业: 记事本(增,删,改,查)

第十五天

Content Provider  内容提供者(Android四大组件) 图书馆(短信记录,电话记录,联系人)

一,作用

将应用的私有数据库中的私有数据向外提供一个访问的接口,是基于URI的方式,向外提供统一的数据访问接口

二,URI统一资源标示

类似与URL网络资源地址,URI是用来标示Android应用中某一资源的唯一标识,在ContentProvider组件中,通过Uri向外部应用提供一个私有数据库中的“一张表”的资源唯一标示

内容提供者的URI格式:    content://应用程序包名.provider/student

三,访问ContentProvider提供的URI接口

ContentProvider  内容提供者   负责暴露数据(数据库)            “服务器”

ContentResolver  内容解析器    负责解析ContentProvider暴露出来的数据    “客户端”

四,案例一:访问短信记录

数据库路径:com.android.providers.telephony中sms表

/**
     * 短信的uri
     *
     * content://sms  所有短信
     * content://sms/outbox 发送的短信
     * content//sms/inbox  接收的短信
     */
    private Uri smsUri = Uri.parse("content://sms");

------------------------------------------------------------------

//1,得到ContentResolver对象
        ContentResolver contentResolver = getContentResolver();
        
    //2,通过URI访问数据
    /**
    * uri 访问ContentProvider的路径
    * projection  查询数据库表中列的集合 new String[]{}
    *     _id,
    *     address: 地址(发送者)
    *     body: 短信内容
    *     type: 类型    1 接收,2  发送,3 草稿 ,4 未读,5 发送失败
    *                     
    * selection    查询条件
    * selectionArgs 查询条件中占位符对应的集合
    * sortOrder     排序
    */
        
    cursor = contentResolver.query(smsUri, new String[]{"_id","address","body"}, null, null, null);
        
    //3,读取游标中数据
        
    adapter =  new SimpleCursorAdapter(getApplicationContext(),
                           R.layout.item_lv,
                        cursor,
                        new String[]{"address","body"},
                        new int[]{R.id.address,R.id.body},
                        SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        
    lv.setAdapter(adapter);
        
    //4,在清单文件中增加访问短信记录的权限

五,案例二:访问电话记录

数据库路径:com.android.providers.contacts 中calls表

//private Uri uri = Uri.parse("content://call_log/calls");
    
      private Uri uri = CallLog.Calls.CONTENT_URI;
      
      private String[]  columns = {CallLog.Calls._ID,//主键
                      CallLog.Calls.NUMBER,//电话号码
                      CallLog.Calls.DATE//时间
                    };

-----------------------------------------------------------------------

//1,得到CotentResolver对象
        
    ContentResolver contentResolver = getContentResolver();
        
    //2,通过URI接口查询数据
        
    cursor = contentResolver.query(uri, columns, null, null, null);
    
    //3,读取游标中的数据
    while (cursor.moveToNext()) {
            
        long id = cursor.getLong(cursor.getColumnIndex(CallLog.Calls._ID));
        String phone = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
        long date = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE));
            
        String dataText = sf.format(new Date(date));
            
        data.add(new CallInfo(id, phone, dataText));
    }
        
    //刷新适配器
    adapter.notifyDataSetChanged();
        
        
    //4,添加读取电话记录的权限

六,案例三:访问联系人

联系人数据库所在的位置

data/data/com.android.provides.contacts/database/contats2

联系人表URI:

content://com.android.contacts/raw_contacts
    
   数据表URI:

content://com.android.contacts/data

表一:raw_contacts (有多少联系人就有多少条数据)

_id: 主键

display_name: 联系人姓名

表二: data

_id: 主键

mimetype_id : 外键 (mimetypes的主键)

raw_contact_id :  外键(raw_contacts的主键)

data1: 数据

表三:mimetypes

_id 主键(1 邮箱,5 电话,7 姓名)

查找 zhansan  的 电话 :select data1 from data where raw_contact_id=1

查找 姓名,电话 : select raw_contact_id,display_name,data1 from raw_contacts as t1,data as t2 where t1._id = t2.raw_contact_id and mimetype_id=5

第十六天

自定义ContentProvider 组件

一,如何创建

1,创建一个类,继承ContentProvider ,同时实现父类的6个抽象方法

onCreate()   --- 初始化数据库的操作对象(工具类)

query(Uri uri....)

Uri uri  = insert(Uri uri...)

update(Uri uri...)

delete(Uri uri...)

getType(Uri uri...) 一般情况,只实现,不做处理

2,声明ContentProvider组件的唯一标识 :  authority

note :authority 内容必须是小写字母, 建议以当前的应用的包名+标识资源的名称(数据库的名称)

3,声明访问数据库的那些表的code(t_user = 0 ,t_older = 6)

4,定义Uri的匹配器,并实例化,同时增加完整的Uri访问资源的接口

5,定义数据库的操作类,并oncreate()方法中实例化

6,在相应查询,修改,删除,添加方法中,通过Uri匹配器,判断当前的Uri是访问那张表的code

7,在Manifist清单文件中注册ContentProvider组件;同时声明权限

<!-- 注册ContentProvider
            android:name=""    名称
            android:authorities="" ContentProvider 组件的唯一标识(和authority是一致的)
            android:exported="" 设置当前的组件可以被外部应用访问
            android:permission="" 声明访问当前组件的权限
         -->
        <provider
            android:name="com.qf.day16_contentprovider.MyContentProvider"
            android:authorities="com.qf.day16_contentprovider_01.users"
            android:exported="true"
            android:permission="com.qf.day16_contentprovider_01.users.WRITE_READ"
            />

<!-- 声明访问MyContentProvider组件的权限-->
        <permission android:name="com.qf.day16_contentprovider_01.users.WRITE_READ"/>

<!-- 使用访问MyContentProvider 组件的权限
        <uses-permission android:name="com.qf.day16_contentprovider_01.users.WRITE_READ"/>-->

第十七天

Loader

一,什么是Loader?

装载器,Android3.0后引进,它使得我们在activity或Fragment中异步加载数据变的简单。

二,作用

通过异步的方式加载数据(数据库中的数据,网络数据,大数据)

实时刷新数据,当Cursor中的数据发生变化时,ListView自动刷新

系统内部使用LoaderManager和LoaderCallBacks来管理Loader

三,LoaderManager

作用:
    1,初始化启动Loader, 无条件加载数据

getLoaderManager().initLoader(编号,查询的条件,回调接口)

2,重启Loader, 有条件或者条件发生变化时加载数据

getLoaderManager().restartLoader(编号,查询的条件,回调接口)

注意:  1,一个Activity或Fragment中只能有一个LoaderManager,但是一个LoaderManager可以管理多个Loader

2,在启动Loader时,必须要提供一个回调接口,即LoaderCallBacks接口

四,CursorLoader

CursorLoader内部使用ContentResolve来加载ContentProvider暴露出来的数据,通过URI

内置了ContentOberver ,监听数据源的改变

五,LoaderCallBacks<Cursor> 回调方法

包含3个回调方法:

创建新的Loader,一般返回的是CursorLoader

onCreateLoader(int id,Bundle args)

Loader数据加载完成后,显示数据
    onLoaderFinished(Loader<Cursor> loader,Cursor data)

在创建新的Loader之前,重置之前Loader加载的数据
    onLoaderReset(Loader<Cursor> loader )

六,使用Loader加载联系人的姓名和电话

1, 在Activity或Fragment中实现LoaderCallBacks接口,重写父类3方法

2,在Activity或Fragment的初始化阶段的生命周期方法中,通过getLoaderManager()的到管理器的对象,初始化
        启动Loader

3,在LoaderCallBacks的创建Loader的方法中( onCreateLoader),实例化CursorLoader,并返回
 
      4,在LoaderCallBacks的加载完成的回调方法中(onLoaderFinished()),将加载完成的数据显示出来
 
              SimpleCursorAdapter.swapCursor(cursor);
 
      5,在LoaderCallBacks的重置方法中(onLoaderReset()),清除之前的数据
 
              SimpleCursorAdapter.swapCursor(null);

6,在有条件或者条件发生变化时,重启启动Loader

七,短信记录

八,自定义Loader

1,定义一个类,继承AsyncTaskLoader<D>,并声明返回数据结果的泛型

2,重写方法onStartLoading()方法,执行forceLoad()方法(强制执行后台方法,来加载数据)

3,重写父类的方法:loadInBackground(),在此方法中执行获取数据的操作,并把结果返回

-------拓展---------

九,SearchView

<!-- android:iconifiedByDefault="false" 设置当前的控件是否以图标的方式显示 -->
        <SearchView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/search_view_id"
            android:iconifiedByDefault="false"
        />

十,ContentObserver   内容观察者

1,自定义内容观察者

//自定义内容观察者MyContentObServer
    public class MyContentObServer extends ContentObserver
    {

public MyContentObServer(Handler handler) {
            super(handler);
            // TODO Auto-generated constructor stub
        }
        
        //当被观测的内容发生变化时,调用此方法
        @Override
        public void onChange(boolean selfChange) {
            
            super.onChange(selfChange);
            
            //重启Loader
            getLoaderManager().restartLoader(2, null, MainActivity.this);
            
            
        }
        
    }

2,注册内容观察者

//自定义内容观察者MyContentObServer
    public class MyContentObServer extends ContentObserver
    {

public MyContentObServer(Handler handler) {
            super(handler);
            // TODO Auto-generated constructor stub
        }
        
        //当被观测的内容发生变化时,调用此方法
        @Override
        public void onChange(boolean selfChange) {
            
            super.onChange(selfChange);
            
            //重启Loader
            getLoaderManager().restartLoader(2, null, MainActivity.this);
            
            
        }
        
    }

第十八天

Handler  的使用

一,Android使用线程的规则:

1,UI线程不能被阻塞

2,子线程不能操作UI线程(除ProgressBar)

3,使用handler线程间的通信机制,实现子线程向UI线程发送数据

二,了解Handler的原理

Handler实现了线程间的通信的核心类:

Looper类: 循环读取MessageQueue中的Message(for(;;))

MessageQueue类:存放的是Handler发送过来的消息(Message)

Handler类:消息的发送者,又是消息的处理者

方法:
                handleMessage() : 处理消息

obtainMessage() : 从消息队列中取出消息

sendEmptyMessage() : 发送的是Message中只给what属性赋值

sendEmptyMessageDelayed()

sendMessage(Message msg) : 发送Message

sendMessageDelayed() : 延迟发送消息

sendMessageAtTime() : 在当前设置的时间发送

post(Runnable) : 运行在主线程中,更新UI

postDelayed()

postAtTime()

Message类: 包装了线程间发送的数据

参数:8 个

int what : 发送空消息(状态)

int  arg1

int  arg2

Object obj :存放的Object类型的对象

Handler target :标签,由那个handler处理,就设为那个handler

Message sPool : 消息对象

int sPoolSize :当前内存中消息的数量

int MAX_POOL_SIZE = 50 :内存中最大的消息数量

方法:
                obtain()  从消息池中取出消息

recycle() 将用完的消息对象及时回收到消息池中

setTarget(Handler target) 给每个消息对象贴标签,只有贴了标签,Looper才能知道                                要传给那个Handler做处理
                
                Handler getTarget()

sendToTarget() 把消息发送给那个handler

三,子线程向主线程发送消息

1,在主线程中实例化Handler对象,并重写handleMessage()方法,

在此方法中用于处理handler通过sendMessage()方法发送的消息

2,在子线程中,某一个合适的位置(数据加载完成) ,实例化Message对象,将相关的数据放入Message对象中

3,在子线程中发送消息:
    
    3.1  send方法发送(3种)

handler.sendMessage(Message msg);

3.2 以post方式发送

handler.post(new Runnable(){})

这种方式,是子线程向主线程发送一段代码(代码块是在主线程中执行)

------扩展--------

计时器:

一:

Timer timer = new Timer();
        //schedule(定时的任务, 第一次执行延迟的时间, 每隔多长时间执行一次)
        timer.schedule(new TimerTask() {
            
            @Override
            public void run() {
                // TODO 定时执行的内容(发送消息)
                
                handler.sendEmptyMessage(1);
                
            }
        }, 0, 1000);

timer.cancel();//停止计时器

二:

ScheduledExecutorService scheduled = Executors.newSingleThreadScheduledExecutor();
        
        scheduled.scheduleWithFixedDelay(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                handler.sendEmptyMessage(1);
            }
        }, 1, 1, TimeUnit.SECONDS);

scheduled.shutdowm();//停止

第十八天

Handler  的使用

一,Android使用线程的规则:

1,UI线程不能被阻塞

2,子线程不能操作UI线程(除ProgressBar)

3,使用handler线程间的通信机制,实现子线程向UI线程发送数据

二,了解Handler的原理

注意 : 一个线程只能有一个Looper对象,可以有多个Handler对象

Handler实现了线程间的通信的核心类:

Looper类: 循环读取MessageQueue中的Message(for(;;))
                
           方法:
             prepare()   : 创建Looper对象
    
             loop()    :  循环读取MessageQueue中的message

myLooper()  返回子线程中的Looper对象

属性:

ThreadLocal<Looper> sThreadLocal : 本地线程的变量,能创建Looper对象

Looper sMainLooper :主线程中Looper对象

MessageQueue mQueue : 消息队列

MessageQueue类:存放的是Handler发送过来的消息(Message)

Handler类:消息的发送者,又是消息的处理者

方法:
                handleMessage() : 处理消息

obtainMessage() : 从消息队列中取出消息

sendEmptyMessage() : 发送的是Message中只给what属性赋值

sendEmptyMessageDelayed()

sendMessage(Message msg) : 发送Message

sendMessageDelayed() : 延迟发送消息

sendMessageAtTime() : 在当前设置的时间发送

post(Runnable) : 运行在主线程中,更新UI

postDelayed()

postAtTime()

Message类: 包装了线程间发送的数据

参数:8 个

int what : 发送空消息(状态)

int  arg1

int  arg2

Object obj :存放的Object类型的对象

Handler target :标签,由那个handler处理,就设为那个handler

Message sPool : 消息对象

int sPoolSize :当前内存中消息的数量

int MAX_POOL_SIZE = 50 :内存中最大的消息数量

方法:
                obtain()  从消息池中取出消息

recycle() 将用完的消息对象及时回收到消息池中

setTarget(Handler target) 给每个消息对象贴标签,只有贴了标签,Looper才能知道                                要传给那个Handler做处理
                
                Handler getTarget()

sendToTarget() 把消息发送给那个handler

三,子线程向主线程发送消息

1,在主线程中实例化Handler对象,并重写handleMessage()方法,

在此方法中用于处理handler通过sendMessage()方法发送的消息

2,在子线程中,某一个合适的位置(数据加载完成) ,实例化Message对象,将相关的数据放入Message对象中

3,在子线程中发送消息:
    
    3.1  send方法发送(3种)

handler.sendMessage(Message msg);

方式一:

Message msg = Message.obtain();
        msg.obj = bitmap;
        handler.sendMessage(msg);
        
        方式二:

Message msg = Message.obtain();
        msg.obj = bitmap;
        msg.setTarget(handler);
        msg.sendToTarget();

方式三:

Message msg = handler.obtainMessage();
        msg.obj = bitmap;
        msg.sendToTarget();

3.2 以post方式发送

handler.post(new Runnable(){})

这种方式,是子线程向主线程发送一段代码(代码块是在主线程中执行)

方式四:
        
        使用这种方法,不需要再重写handleMessage()方法

handler.post(new Runnable(){

//执行主线程赋值的操作

});

四,主线程向子线程发送消息

1,将子线程升级为Looper线程,即在子线程中创建Looper对象

Looper.perpare()方法,这个方法内部会创建一个MessageQueue对象,同时将创建的Looper保存在线程的本地变量中

2,在子线程中实例化Handler对象

new Handler()时,会判断当前的本地线程中是否存在Looper对象

3,在子线程中执行Looper.loop()  循环读取MessageQueue中的Message

五,实例 :

Day18_Handler_01  子线程向主线程发送消息的4中方式

Day18_Handler_02   图片自动播放+计时器

Day18_Handler_03   主线程向子线程发送消息

Day18_Handler_04   新闻网页

------扩展--------

计时器:

一:

Timer timer = new Timer();
        //schedule(定时的任务, 第一次执行延迟的时间, 每隔多长时间执行一次)
        timer.schedule(new TimerTask() {
            
            @Override
            public void run() {
                // TODO 定时执行的内容(发送消息)
                
                handler.sendEmptyMessage(1);
                
            }
        }, 0, 1000);

timer.cancel();//停止计时器

二:

ScheduledExecutorService scheduled = Executors.newSingleThreadScheduledExecutor();
        
        scheduled.scheduleWithFixedDelay(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                handler.sendEmptyMessage(1);
            }
        }, 1, 1, TimeUnit.SECONDS);

scheduled.shutdowm();//停止

四大类
Handler
属性:MessageQueue -- handler讲消息发送出去实际就是放在这里
           Looper--每个handler有一个looper为其不断的接受消息队列消息
   CallBack--回调接收 方法handleMessage
方法:
handleMessage : 回调接口中的方法
obtainMessage:获取消息实例  本质还是调用Message.Obtain
sendMessage:
sendMessageDelayed:
sendMessageAtTime:
sendEmptyMessage:
sendEmptyMessageDelayed:
sendEmptyMessageAtTime:
post:
postDelayed:
postAiTime:
enqueueMessage:将消息压入到MessageQueue中
dispatchMessage:当looper不断的获取到消息后调用该方法分发消息触发CallBack里的handleMessage方法进行消息处理

Looper
    属性:
ThreadLocal<Looper>:  本地线程变量 由于该变量保证每个线程只有一个Looper也就是说一个线程只能有一个Looper
方法:
prepare:所有的Handler 所在线程都必须有一个looper 准备好
prepareMainLooper:获取主线程中的looper对象
myLooper:从线程本地变量中获取当前线程的Looper对象
loop:循环接收消息队列中的消息

Message
属性:
what: 标记消息
arg1: 存放消息的 int
arg2: 存放消息的 int
obj: 用来存放消息的Object
data:用来存放消息属性Bundle
target:标示  Handler
sPool:消息池中的每个消息对象
sPoolSize:纪录消息池中剩余消息的数量
MAX_POOL_SIZE:消息池中的最大消息对象数量
replyTo: Messenger:自定义信使对象  跨进程

方法:
obtain:      获取Message实例
setTarget: 设置收到消息的Handler
getTarget: 获取应该收到消息的Handler
setData:   设置Bunldle对象
getData:   设置Bunldle对象
sendToTarget: 将消息发送到目标Handler对象,本质还是调用sendMessage
recycle:     往消息池中归还消息对象

MessageQueue
属性:
enqueueMessage:通过改方法将消息添加到自己的队列中
next:下一个Message

Handler 消息机制:

由于主线程(UI线程)不能访问网络,子线程不能更新UI,所以有了Handler消息传送机制用来将子线程的数据发送到主线程中来进行更新UI,在主线程中实例化Handler对象,
在子线程中将将要发送的数据 添加到Message对象中去,Message是可携带int,object,Bundle对象的,通过handler调用handler.sendMessage方法将消息发送出去,
这个时候消息会进入到消息队列MessageQueue 中去,MessageQueue 是个消息队列,enqueueMessage方法讲消息添加到队列中,looper提供了消息运转的动力,
每个线程只允许有一个looper,消息在MessageQueue中不断的一个一个的做进栈出栈动作,handler调用dispatchMessage方法对MessageQueue中出来Message进行分发
,分发根据的是target(Handler),而dispatchMessage方法执行的就是一个CallBack回调方法,也就是主线程中用来接收消息的的handleMessage方法,
这样主线程就可以对接收到的数据进行处理了。

第十九天

一,ActionBar

1,说明

ActionBar上Android3.0 后引入的,显示在标题栏上,创建的方式和菜单是相同的。

与菜单的主要区别是:showAsAction属性

2,ActionBar动作项(Item)显示的属性

showAsAction:

ifRoom : 如果标题栏有空间,则会显示动作项的图标

always :不管有没有空间,都会显示在标题栏

withText:带有文本信息显示
    
            collapseActionView :可以被折叠的,单用户点击按钮时,这个操作视图才会被展开

3,创建ActionBar显示的动作项

3.1 在res/menu/目录下创建菜单资源,再声明<item>时必须声明显示属性(showAsAction)和图标属性(android:icon)
        
      3.2 在Activity类中,重写onCreateOptionsMenu()方法,加载菜单资源

3.3 重写onOptionsItemSelected()方法,来处理ActionBar中的每个Action项的点击事件

4,分割栏操作:

API 14以上

在<application>或<activity>元素中添加android:uiOptions="splitActionBarWhenNarrow" 属性设置

5,启动程序导航图标

5.1     作用: 让APP的Logo也变成可点击的图标

5.2     getActionBar() 获取当前Activity的ActionBar对象
        
        5.3
            //设置是否显示Logo的图标
            getActionBar().setDisplayShowHomeEnabled(true);

//设置是否将Logo图标作为可点击的按钮,并且在图标的前面添加一个向左的箭头
            getActionBar().setDisplayHomeAsUpEnabled(true);

5.4    android.R.id.home://应用导航图标的ID

6,ActionView的使用

作用:可以编辑的动作项,如:searchView可以直接显示在ActionBar中

方式:

1,actionViewClass属性,指定实现CollapsibleActionView的子类

2,actionLayout属性,可以指定一个布局文件

二,ActionBar+Fragment

ActionBarTab 导航 : 通常通过选项标签切换Fragment

1,获取当前Activity的ActionBar对象(getActionBar()),并设置导航模式为TABS

2,在当前Activity中,实现ActionBar.TabListener接口(重写3个方法)

3,增加ActionBar.Tab 到ActionBar中

ActionBar.Tab tab = actionBar.newTab();

setText()    设置Tab显示的文本

setIcon()    设置Tab显示的图标

setTabListener() 设置Tab的监听

actionBar.addTab(tab,true);//第二个参数:默认选中的Tab项

第二十天

ViewPager

一,特点: 可以左右滑动的控件,需要PagerAdapter配合使用,由V4包

二,类名:android.support.v4.view.ViewPager;

三,使用步骤

1,在布局文件中使用<android.support.v4.view.ViewPager>标签

2,在代码中增加显示的页面

3,在Activity中实例化ViewPager组件,并设置适配器(PagerAdapter)

四:V4 包下Fragment

1,创建Fragment的子类,继承Fragment(V4包下)

2,Activity必须继承FragmentActivity(V4包下)

3,FragmentManager manager = getSupportFragmentManager();

第二十一天

案例
<不推荐使用方法>
一、TabHost+Activity 方法实现
二、ActionBar+Fragment 方法实现
三、ActionBar+ViewPager 方法实现
四、FragmentTabHost + Fragment 方法实现   ----√

<推荐使用方法>

五、ViewPager+PagerTabStrip+Fragment 方法实现√
六、RadioGroup+Fragment  方法实现 √
七、RadioGroup+ViewPager+Fragment 方法实现 √
八、HorizontalScrollView+Linearlayout+ViewPager 方法实现√

作业!
使用java代码方式写布局计算器

第二十二天

* 广播:  全局性的 ,系统级别的 消息传递机制
 *
 * 广播的三要素:
 * 1.发送方 Context.sendBroadCast
 * 2.发送内容  Intent
 * 3.广播接收者  BroadCastReceiver
 *
 * 广播根据注册方式不同分为
 * 静态注册
 *     在清单文件中 <application> 标签下 添加<receiver> 标签   并 添加IntentFilter   及其 action 属性
 * 动态注册
 *     在 Activity 中 实例化  MyReceiver 对象  ,再定义一个 IntentFilter 对象  添加Action 属性   
 *     调用 registeReceiver方法  注册  同时 需要在  onDestory方法中调用 unRegisterReceiver方法  解除注册
 *
 * 静态注册与动态注册的区别:
 * 1.动态注册的接收效率要比静态注册的稍高一些
 * 2.静态注册的时候,app 退出 activity 的销毁  都不会影响广播接收者的接收
 * 3.动态注册的时候,如果当前Activity 销毁 则不会再接收到广播
 *
 * 关键:
 * 定义action
 * 发送方的Intent 定义action(可以理解为一个频道)
 * 所有存在于该频道的Receiver都能接收到这个频道的消息
 *
 * 发送方定义了action  所有的接收方(receiver) 只要都设置了这个action 就都能接收到 这个广播
 *
 * 广播的生命周期:
 * 从接收方 onReceive方法 执行开始    当前的广播接收者有10s的活跃期   这个阶段 不会受到任何的影响,10s过后处于 失活状态
 * 这个时候如果有系统进程需要内存空间,则,该广播接收者有可能会被回收,所以 如果需要在该方法中执行延时操作,需要开启一个衍生线程(子线程)来
 * 完成这样的工作
 *
 * 广播实现步骤:
 * 1.定义发送
 * 2.定义发送的action   --定义的时候一般定义为 包名+广播接收者的类名  (按照需求其名称  标准即可)
 * 3.发送广播
 * 4.定义广播接收者
 * 5.注册广播接收者
 *         动态
 *         静态

有序广播
 * 根据优先级 来决定广播接收者 接收广播的顺序
 * 发送广播的方法 :  sendOrderBroadCast
 * 第二个参数  是权限名称  不需要的时候为 null
 *
 * 需要在注册的时候 添加优先级 (优先级 属性在   IntentFalter  标签内部  proprity 该属性)
 *  取值范围   -1000  到  +1000    数值越大 优先级越高  会先接收到广播
 *  
 * 所有的广播接收者不可更改 发送方发送出来的Intent
 *
 * 如果需要广播接收者之间传递数据
 * 可使用  setResultExtra(Bundle)  方法   
 * 接收方  getResultExtras( boolean)   
 * 如果 boolean 为false  则  直接返回 传递对象  可为Bundle  可为null  
 * 如果boolean 为true   则  一定会返回  bundle 对象   但是  可能有值 可能 没有键值
 *
 * 只有优先级高的 可以向优先级低的传递数据
 *
 *拦截有序广播的方法
 *     abortBroadCast()
 * 拦截后 ,所有优先级比当前优先级低的广播  都将不再接收到 广播

第二十三天

* Service:  服务
 * 服务像Windows后台服务一样,服务是在后台运行,悄悄的承担着不为人知的一些工作
 * Service 在后台运行,他是不可见的、无界面的程序
 *
 * 没有页面的程序 应用场景:播放音乐,下载 ,定位等
 *
 * 概念:
 * 1.Service运行在后台不可与用户进行交互
 * 2.服务不是一个单独的进程 ,服务对象本身并不是在自己的进程中运行。
 * 3.服务不是一个线程,服务Service 与其他的组件一样,默认的代码是执行在主线程中的
 * 4.需要一个上下文对象来启动服务
 *
 * Service 很大程度上承担了后台线程管理的角色:在Activity中开启一个线程,当Activity销毁后,Thread会继续工作,
 * 但是与开启它的Activity 失去了联系,也就是说当前线程处于无人管理的状态,但是Service 可以有效的对后台线程进行管理
 *
 * Service比线程的优点:
 * 1.Service 可以放在独立的进程中,所以更安全
 * 2.系统可以重启被异常杀死的Service
 * 3.Service可以依赖现有的Binder 机制(明天讲)
 *
 * Service与Activity的关系
 * 1.相同点:都是四大组件,都需要在清单文件中注册,都具有一定的生命周期
 * 2.不同点:Acitivty 存在着与用户交互的页面,Service 在后台运行
 *
 * 分类:本地服务LocalService  与远程服务 RemoteService
 * A:本地服务 LocalService
 * 根据启动模式分为两种:
 * 1.startService(Intent)
 *     被启动的服务是通过其他的组件(Activity或BroadCast)调用startService 方法启动的。该方法会导致Service 的生命周期中
 *     onCreate与onStartCommand方法执行,当Service 启动后,则与他的调用者的生命周期无关,即便调用者已经被销毁。Service 也会
 *     一直存在,在后台执行,除非调用了stopService 或者stopSelf
 * 2.bindService(。。。)(明天讲)
 *
 * Service 的生命周期:
 *
 * onCreat 服务被创建
 * onStartCommand  服务开始执行
 * onDestory  服务被销毁
 *
 * 说明:
 * 1.当调用startService 方法后,服务会执行 onCreate  onStartCommand 方法 ,此时服务处于is Running状态
 * 2.如果服务已经被创建 则再次调用startService 方法,服务只会执行onStartCommand 方法,如果没有被创建,则执行onCreate onStartCommand方法
 * 3.如果调用了stopService 或者 stopSelf方法   服务会执行onDestory 方法 销毁
 * 4.所以 Service 的生命周期为  onCreat --->onStartCommand (可以被多次调用)--->onDestory
 *
 * onStartCommand方法:
 * 根据返回值划分:
 * 1. START_STIKEY:粘性的 常量为 1
 *     如果服务被异常杀死 服务会被重新启动,但是Intent 值为null
 * 2. START_NO_STIKEY :非粘性的  常量为 2
 *  如果服务被异常杀死 服务不会被重新启动
 * 3. STRAT_REDELIVER_INTENT  常量为 3
 *     如果服务被异常杀死,服务会被重启,且Intent 有值
 
 * B 远程服务 RemoteService(接下来讲)
 * C IntentService(接下来讲)

第二十四天
* 绑定服务
 *
 * 绑定服务是允许其它应用程序绑定并且与之交互的Service的实现类。为了提供绑定,必须实现onBind()回调方法。
 * 该方法返回IBinder对象,它定义了服务类与Activity交互的程序接口
 *
 *
 * Activity通过bindService()方法绑定到服务类,同时Activity必须提供ServiceConnection接口的实现类,
 * 它监视Activity与服务类之间的连接。在重写ServiceConnection接口的onServiceConnected()方法时,
 * 实现了将服务类顺利赋值到了Activity中,实现了在Activity中使用该服务类并执行其中的方法
 *
 *
 * 同一个Service可以绑定多个服务链接,这样可以同时为多个不同的组件提供服务 全部组件关闭后 他会关闭
 *
 * 作用
 *
 * 作用是在Service和调用者之 间建立一个桥梁,并不负责更多的工作(例如一个Service需要连接服务器 的操作),
 * 一般使用bindService来绑定到一个现有的Service(即通过 StartService启动的服务)
 *
 * 生命周期 onCreate():创建服务 onBind():绑定服务,服务开始运行 onUnbind():取消绑定 onDestroy() :服务被停止
 *
 * 在程序中调用:context.bindService()会触发执行Service生命周期中的onCreate()、onBind()回调方法,
 * 此时服务开始运行
 *
 * onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。
 * 此后调用者(Context,例如Activity)会和Service绑定在一起
 *
 * 如果调用Service的调用者Context退出了,那么会依次调用Service生命周期中的onUnbind()、onDestroy()回调方法,
 * 会让服务停止
 *
 * 所以BindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind -->
 * onDestory

文件存储

临时存储
内存
持久化存储

内部存储
data-data-包名-/

1.cache   缓存
2.file      文件
3.database   数据库
4.shared_pref   .xml  sharedpreference  存储

Context.getCacheDir();
Context.getFileDir();
...

data-app
存放的都是apk 文件
真机可能需要root 权限才能看到

SD卡(外部存储)
1.判断是否存在SD卡  (挂载)
Environment.getExternalStorageState();
2.获取SD卡的根目录
Environment.getExternalStorageDirectory();
3.获取9大公有目录的路径
Environment.getExternalStoragePublicDirectory(TYPE);Environment.XXX

File.separator    分隔符

SD中的私有目录:
Android-data-包名-    files
            cache
//获取SD卡中私有目录的路径方法
Context.getExternalCacheDir();
Context.getExternalFilesDir();

//现成的文件引用工具类

package com.steven.helper;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.StatFs;

public class SDCardHelper {

// 判断SD卡是否被挂载
    public static boolean isSDCardMounted() {
        // return Environment.getExternalStorageState().equals("mounted");
        return Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED);
    }

// 获取SD卡的根目录
    public static String getSDCardBaseDir() {
        if (isSDCardMounted()) {
            return Environment.getExternalStorageDirectory().getAbsolutePath();
        }
        return null;
    }

// 获取SD卡的完整空间大小,返回MB
    public static long getSDCardSize() {
        if (isSDCardMounted()) {
            StatFs fs = new StatFs(getSDCardBaseDir());
            long count = fs.getBlockCountLong();
            long size = fs.getBlockSizeLong();
            return count * size / 1024 / 1024;
        }
        return 0;
    }

// 获取SD卡的剩余空间大小
    public static long getSDCardFreeSize() {
        if (isSDCardMounted()) {
            StatFs fs = new StatFs(getSDCardBaseDir());
            long count = fs.getFreeBlocksLong();
            long size = fs.getBlockSizeLong();
            return count * size / 1024 / 1024;
        }
        return 0;
    }

// 获取SD卡的可用空间大小
    public static long getSDCardAvailableSize() {
        if (isSDCardMounted()) {
            StatFs fs = new StatFs(getSDCardBaseDir());
            long count = fs.getAvailableBlocksLong();
            long size = fs.getBlockSizeLong();
            return count * size / 1024 / 1024;
        }
        return 0;
    }

// 往SD卡的公有目录下保存文件
    public static boolean saveFileToSDCardPublicDir(byte[] data, String type,
            String fileName) {
        BufferedOutputStream bos = null;
        if (isSDCardMounted()) {
            File file = Environment.getExternalStoragePublicDirectory(type);
            try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                bos.write(data);
                bos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

// 往SD卡的自定义目录下保存文件
    public static boolean saveFileToSDCardCustomDir(byte[] data, String dir,
            String fileName) {
        BufferedOutputStream bos = null;
        if (isSDCardMounted()) {
            File file = new File(getSDCardBaseDir() + File.separator + dir);
            if (!file.exists()) {
                file.mkdirs();// 递归创建自定义目录
            }
            try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                bos.write(data);
                bos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

// 往SD卡的私有Files目录下保存文件
    public static boolean saveFileToSDCardPrivateFilesDir(byte[] data,
            String type, String fileName, Context context) {
        BufferedOutputStream bos = null;
        if (isSDCardMounted()) {
            File file = context.getExternalFilesDir(type);
            try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                bos.write(data);
                bos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

// 往SD卡的私有Cache目录下保存文件
    public static boolean saveFileToSDCardPrivateCacheDir(byte[] data,
            String fileName, Context context) {
        BufferedOutputStream bos = null;
        if (isSDCardMounted()) {
            File file = context.getExternalCacheDir();
            try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                bos.write(data);
                bos.flush();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

// 保存bitmap图片到SDCard的私有Cache目录
    public static boolean saveBitmapToSDCardPrivateCacheDir(Bitmap bitmap,
            String fileName, Context context) {
        if (isSDCardMounted()) {
            BufferedOutputStream bos = null;
            // 获取私有的Cache缓存目录
            File file = context.getExternalCacheDir();

try {
                bos = new BufferedOutputStream(new FileOutputStream(new File(
                        file, fileName)));
                if (fileName != null
                        && (fileName.contains(".png") || fileName
                                .contains(".PNG"))) {
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
                } else {
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
                }
                bos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

// 从SD卡获取文件
    public static byte[] loadFileFromSDCard(String fileDir) {
        BufferedInputStream bis = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

try {
            bis = new BufferedInputStream(
                    new FileInputStream(new File(fileDir)));
            byte[] buffer = new byte[8 * 1024];
            int c = 0;
            while ((c = bis.read(buffer)) != -1) {
                baos.write(buffer, 0, c);
                baos.flush();
            }
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                baos.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

// 从SDCard中寻找指定目录下的文件,返回Bitmap
    public Bitmap loadBitmapFromSDCard(String filePath) {
        byte[] data = loadFileFromSDCard(filePath);
        if (data != null) {
            Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);
            if (bm != null) {
                return bm;
            }
        }
        return null;
    }

// 获取SD卡公有目录的路径
    public static String getSDCardPublicDir(String type) {
        return Environment.getExternalStoragePublicDirectory(type).toString();
    }

// 获取SD卡私有Cache目录的路径
    public static String getSDCardPrivateCacheDir(Context context) {
        return context.getExternalCacheDir().getAbsolutePath();
    }

// 获取SD卡私有Files目录的路径
    public static String getSDCardPrivateFilesDir(Context context, String type) {
        return context.getExternalFilesDir(type).getAbsolutePath();
    }

public static boolean isFileExist(String filePath) {
        File file = new File(filePath);
        return file.isFile();
    }

// 从sdcard中删除文件
    public static boolean removeFileFromSDCard(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            try {
                file.delete();
                return true;
            } catch (Exception e) {
                return false;
            }
        } else {
            return false;
        }
    }
}

第二十五天
远程服务
* 跨进程通信
 * AIDL
*安卓接口定义语言
 * android  interface  definition  language
 *
 * 1.在服务端创建接口文件  .aidl
 * 2. 在服务端将该接口实例返回
 * 3.客户端需要将 服务端的.aidl文件所在 的包连同文件copy 到 客户端
 * 4.客户端拿到接口实例,调用接口的方法

第二十六天
Android:
补间动画 :补充开始和结束中间的动画效果,支持 位移,缩放,透明度,旋转
属性动画 :通过改变对象的属性起始及结束的值来执行动画
帧动画   :多个图片连续播放 ,一帧一帧的  类似GIF 图

补间动画
实现方式:
1.通过xml代码方式
    在res文件夹下创建 anim 文件夹  创建xml文件
2.通过java代码方式
    Animation 子类:   AlphaAnimation   TranslateAnimation RotateAnimation  ScaleAnimation AnimationSet

属性动画:
实现方式
1.通过xml代码方式:
    在res文件夹下创建animator文件夹   创建xml文件
2.通过Java代码方式
    ObjectAnimator     根据属性名称决定动画执行种类
    属性名称:
    alpha
     
    rotation
    
    rotationX     
 
    rotationY     
    
    translationX                  
     
    translationY
     
    scaleY
    
    scaleX

帧动画:
实现方式
1.通过xml  设置
     在res文件夹下创建drawable文件夹下 创建 根标签为 animation-list 的xml文件
2.通过Java代码方式
    AnimationDrawable 类

第二十七天

屏幕适配
重要的概念:
一、屏幕尺寸:
    屏幕对角线的长度 单位   英寸    1英寸 = 2.54cm
二、屏幕的分辨率:
    屏幕宽高的像素点数 单位是 px像素   
    一些主流机型的分辨率:   1280×720   1920×1080    2560×1440
三、屏幕像素密度:
    屏幕每英寸所含像素点数
四、dpi
    单位 --   屏幕像素密度的单位
五、px
    单位 --   像素的单位
六、sp
    单位  --   描述字体大小的单位
七、dp(dip)
    独立的像素密度,与密度无关的像素
    dp =  px ×  比例     
    比例 根据屏幕像素密度的不同而改变   如果屏幕的像素密度为160 dpi   则 dp与px 1:1    如果像素密度为 240dpi 则dp:px = 1:1.5   等等

适配的方案:
一、多套图片
    制作多套分辨率不同的图片 来放在res  文件夹下不同的drawable资源文件夹下 ,系统会根据当前屏幕的像素密度的不同来
    分别加载不同文件夹下的资源
    注意:相同的图片在不同的文件夹下 名字必须一致
    如果只有一套图片,则默认放在hdpi文件夹下 ,因为 系统加载该文件夹下的文件消耗最小
    多套图片:不是所有的图片都需要做多套图片  一般情况下  最需要做多套图片的是 logo
二、多使用线性布局与相对布局 禁用绝对布局
    使用 wrap_content  match_parent   weight    确保灵活使用并适应各种布局
三、布局
    通过添加修饰符来加载不同的布局文件
    layout-land       横向屏幕 加载该文件夹下的文件
    了解
    layout-small      屏幕尺寸小于3英寸左右的屏幕
    layout-normal      屏幕尺寸小于4.5英寸左右的屏幕
    layout-large      4英寸到7英寸之间
    layout-xlarge     大于7英寸小于 11英寸
四、dimention
    使用资源文件 values
    新建文件夹  values-1377x899      命名规范:  values 加上中横线  -    在加上屏幕分辨率中数大的   1377 加上小x  再加上屏幕分辨率 数小的  899
    在该文件夹下 添加  文件   dimens.xml
    使用方法与 values  -  dimens.xml 一样
    在新建文件夹下的dimens文件中创建的值必须在 values 文件夹下  添加默认值
    了解
    values-small
    values-normal
    values-large
    values-xlarge
    
    sw<600>    最小宽度大于600 的屏幕
五、代码适配方案
    通过 DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    获取屏幕宽度  
    定义基础宽度,计算获取到的屏幕宽度与基础宽度的比例
    拿到比例后 计算每个View 的位置及大小  进行等比例缩放
    所有单位为像素px    必须使用java代码的方式写布局
六、点9图片
    工具路径:sdk/tools/draw9path.bat
    可拉抻的图片
七、清单文件 中
    supports-screens
      anyDensity   = true  支持任何像素密度的屏幕     默认就是支持的
八、国际化语言
    创建  values文件夹    values -   加上国家缩写
    在文件夹中创建string.xml 文件 将语言进行国际化转换  
    注意  在新建文件夹中 添加的 字符串必须在 values文件夹下 的string 里建立默认值
九、SDK 版本适配
    
    
    
UI美化
一、Style
 Style 风格样式
        适用于 View
        定义后可以通过  parent 属性或  直接style 的名称加上点.再加上子类名称的方式进行继承
        子类拥有父类的所有属性
        并且可以更改 更改后以子类的为基准
        如果 xml使用过程中再次修改属性
        以xml的为准
二、Theme
     Theme  主题样式  
        适用于   Applicaton   activity
        如果activity 设置了 theme  则设置了该主题的activity会覆盖application的主题
        一般这里只是用两种
        1 .Theme.NoTitleBar.Fullscreen
        2. Theme.NoTitleBar
三、selector
    选择器
    state_focused   焦点     是否聚焦    EditText  Search
    state_enabled    能够接受点击或触摸事件    button  textView
    state_checkable   是否可以被选中  CheckBox   RadioButton
    state_selected     选中     spinner   listView
    state_pressed    按下   Button
四、shape
    形状
    属性参数 :
    corners: 角度   五个属性同时设置  radius无效
    gradient :   渐变  与solid同时设置  无效
    stroke: 边框  虚线边框   dashWidth  dashGap
    solid : 填充  颜色   与gradient设置 时  gradient 无效
五、level-list
    等级     
      <item
        android:drawable="@drawable/wifi1"
        android:maxLevel="10"
        android:minLevel="5"/>

包含maxLevel   不包含minLevel
    使用时 必须设置为IamgeView 的src 属性
     一般用于   wifi信号 手机sim卡信号   电池电量信号  移动数据信号等
六、layer-list
    叠加图片在 一个ImageView里

第二十八天
format  可以用|来同时使用
1、reference   参考某一资源Id
2、color          颜色值
3、boolean     布尔值
4、dimension 尺寸值(带有单位的 sp/dp)
5、float           浮点型
6、integer 整形
7、string 字符串
8、fraction       百分比
9、enum 枚举
10、flag 位或运算

第二十八天——————之后见项目

/
第八周,高级第一周(第三方Jar包的应用)

1,volley

jsonString:
    StringRequest
    JsonArrayRequest
    jsonObjectRequest
    自定义

步骤:1,得到queue 队列   2,请求  3,请求添加到队列   4,设置取消标记  5,进行取消

图片:
    ImageRequest
    ImageLoader  :1,获取ImageLoader 对象   2,获取ImageListener  3,get(Url,iamgeListener)
    NetworkImage :1,在布局中添加  2,获取ImageLoader  3,setImageUrl(Url,imagloder)

2,Xutils

ViewUtils  :1,注入注解工具  2,可以进行注入  3类: 资源   控件   事件
    HttpUtils  :get /post / 下载
    BtmapUtils :  本地图片  网络图片   display(控件  地址 )
    DbUtils    : 创建数据库  数据库升级   创建数据库表  增删改查

3,PullToRefresh:刷新控件
    1,导入jar
    2,在布局中添加刷新控件
    3,进行设置  
    4,进行监听  
    5,下载完成 进行关闭
  swipeRefreshLayout
    1,在布局中添加
    2,进行设置  背景  进度颜色
    3,进行监听'
    4,进行关闭
4,SlidingMenu:  主视图 被菜单 推出屏幕
    1,导包
    2,在布局中添加
    3,进行设置
    对布局里的控件  查找出来  进行监听

drawerLayout: 主视图  被菜单 覆盖
       1,布局中  添加菜单
       第一个子元素  :主视图    匹配父控件   
       第二个子元素  :菜单      不能设置大于320dp   layout_grivat
 5,OkHttp
    高效的下载库   
    get  /post

同步 和异步

1,获取OKHttpClient对象
    2,获取Request对象
    3,通过OKHttpClient对象调用  newCall()  将 Request对象  变成Call任务
    4,启动任务 (同步 和异步)
6,Picasso

1,导包
    2,一行代码就ok
    Picasso.with(Context).load().into(控件)
    图片的设置  需要在  into之前调用

7、RecyclerView

9、感应器;例:微信摇一摇的实现

安卓开发入门学习笔记相关推荐

  1. Web前端html5+css3前端开发入门学习笔记

    文章目录 前言 HTML 认识HTML 1. 网页组成和本质 2.了解浏览器 3.web标准 4.HTML感知 5.HTML骨架 6.编译软件vscode的简介和使用 7.注释 8.标签组成和关系 9 ...

  2. java web开发入门学习笔记(手打超细版)

    我刚实习的时候写的,先分享分享吧,当时写的也不容易.有错的话请指出,博主会修正,适合入门小白当资料使用.包括包括java基础.数据库.js.jQuery.css.html等等知识点. 详细笔记内容如下 ...

  3. Unity3D游戏开发入门学习笔记

    学习内容概要: 软件面板功能.材质球.预制体.摄像机.灯光.鼠标键盘输入.组件.刚体.碰撞体.PC端游戏打包发布.... 第1课:课程介绍与Unity3D环境搭建 1.Unity3D,一个游戏开发引擎 ...

  4. Arduino 开发入门 学习笔记 Arduino编程基础

    11月2日至5日 事务缠身 11月6日 继续坚持 Arduino语言 其实是基于C/C++的嵌入式设备开发框架,核心库文件提供的API使传统开发方式中各个寄存器的意义以及寄存器配置过程变得透明. 增强 ...

  5. Android NDK开发入门学习笔记(图文教程,极其详尽)

    以前也简单用过JNI,但是只是简单用一下,好多都不明白.最近在看源码部分,有涉及到JNI调用的,所以这次打算彻底把它搞定. 先普及一下JNI的调用关系:JAVA-------------------- ...

  6. Python快速编程入门#学习笔记01# |第一章 :Python基础知识 (Python发展历程、常见的开发工具、import模块导入)

    全文目录 ==先导知识== 1 认识Python 1.1.1 Python的发展历程 1.1.2 Python语言的特点 2. Python解释器的安装与Python程序运行 1.2.1 安装Pyth ...

  7. 嵌入式入门学习笔记1:资料收集

    嵌入式入门学习笔记1:资料收集 一:网上购买的500G资料 资料地址:https://pan.baidu.com/s/1siwOPjtcRCPZNikN4-Z2tw 密码:lhnr 二.嵌入式涉及的知 ...

  8. 阿里云HaaS100物联网开发板学习笔记(六)做个智能灯---一个完整的开发例子

    摘要:本篇文章将前期几个专题综合起来,基于阿里云HaaS100的新固件设计制作一个智能灯.这个智能灯由云平台.手机APP端和设备端组成,基本上涵盖了一个物联网小项目所需的主要步骤. 目录 1.在阿里云 ...

  9. android安卓开发入门视频教程资料百度网盘下载

    android安卓开发入门视频教程资料讲解安卓核心基础,包含视频+笔记,适合新手入门学习. 百度网盘:https://pan.baidu.com/s/1uciMAAa97nm5RSLILtdPdg&a ...

最新文章

  1. ES6 模块化操作
  2. [react] 在react中无状态组件有什么运用场景
  3. 如何跨越线程调用窗体控件?(1)
  4. windows 软链接_Linux 基础-软链接和硬链接区别
  5. Linux操作系统:文件和目录命令
  6. 全球最厉害的14位程序员
  7. jsp代码编写简单的BBS论坛项目的总结
  8. 基于PyQT5的图书管理系统(含文档,源码,安装部署简单)
  9. 【精品课设】经典PID与模糊PID控制的对比与分析(一)
  10. String对象的match方法
  11. 带通滤波器是什么,它的原理是什么
  12. 来势汹汹,奥米克戎;是否补种,特定疫苗? | 经济学人全球早报精选
  13. 【Swift 60秒】51 - Closures as parameters
  14. 使用python将罗马字转换为对应的阿拉伯数字
  15. 图解三代测序(SMRT Sequencing)
  16. Google瓦片地图URL
  17. [RK3568 Android11] 开发之开发者选项中添加USB OTG模式切换(一)
  18. 又一重量级国字头赛事!中国大学生计算机设计大赛全面启动
  19. iis6.0的网站建设及权限设置
  20. pcap_next_ex() 捕获数据包

热门文章

  1. vue-codemirror实现一个前端代码在线编辑器,可处理 HTML,VUE,JS,CSS代码在线编辑
  2. Windows System32目录下所有文件详解
  3. 怎么用python制作随机点名软件_如何用python编写一个简易的随机点名软件?
  4. matlab显示全球海岸线
  5. 一个和CSDN类似的博客网站
  6. Linux内核5.10编译 与调试
  7. 杜克大学计算机世界排名,杜克大学(Duke University)计算机科学Computer Science专业排名第51位(2021年THE世界大学商科排名)...
  8. [COCI2008-2009#2] PERKET
  9. 合肥工业大学宣城校区Java技术实验一 Java开发环境的安装与配置
  10. 斩获iOS美国游戏榜单TOP2 触宝游戏战略进入高速发展轨道