• Android学习笔记(二)

    2015-11-29 14:40:09
    六大布局 FrameLayout GridLayout LinearLayout RealativeLayout TableLayout AbsoluteLayout FrameLayout 帧布局中,容器为每个加入其中的控件创建一个空白的区域(称为一帧)。每个控件占据一帧,这些帧会按 ...
    六大布局
    FrameLayout GridLayout LinearLayout RealativeLayout TableLayout AbsoluteLayout
    FrameLayout
    帧布局中,容器为每个加入其中的控件创建一个空白的区域(称为一帧)。每个控件占据一帧,这些帧会按


    gravity属性执行自动对齐。
    帧布局的效果是将其中所有的控件都叠在一起
    常用属性:
    top       顶端对齐
    bottom    底端对齐
    fill_vertical 垂直方向填充
    fill_horizontal 水平方向填充


    GridLayout
    GridLayout布局使用虚细线将布局分为行、列和单元格,支持一个控件在行、列上交错排列
    XML属性          说明
    columnCount     设置一行的最大列数
    orientation     设置布局方向 


    Spinner控件
    Spinner是现实列表框的控件,Spinner控件通过下拉选项的方式可以达到节省屏幕空间,又能从多个选项中进行选


    择的目的。
    常用属性:
    Prompt 列表框选项的提示
    entries 使用数组资源设置下拉框的内容


    Switch控件
    Switch是Android4.0提供的控件,功能相当于CheckBox,weight用户提供了更加友好的操作体验
    textOn  被选择时的文本
    textOff  不被选择时的文本
    checked: true:被选择
    示例代码:
    <Switch
    android:text="电玩"
    android:textOn="on"
    android:textOff="off"
    />






    Activity类
    Activity类用户管理一个屏幕,该类通过Java代码制定res/layout中的xml文件作为屏幕的显示内容。
    Activity类位于android.app包下,该类继承树:
    Object->Context->ContextWrapper->ContextThemeWrapper->Activity


    Activity类的常用方法
    1.setContentView(int resId)
    作用:设置Activity显示的布局
    2.onCreate(Bundle savedInstanceState)
    作用:该方法在Activity的实例被Android系统创建后第一个被调用。通常在该方法中设置屏幕显示的布局、初始


    化数据、设置控件被点击的事件响应代码。
    3.View.findViewById(int resId)
    作用:在serContentView()设置的布局文件中查找控件标签,以此来创建该控件的Java对象。
    参数:resId:xml布局文件中控件的值
    返回View类型的对象
    提示:必须先执行setContentView(),设置Activity的布局文件,然后执行findViewById,从布局文件中查找控件
    4.void finish()
    关闭当前的Activity


    TextView类的常用方法
    1.public Editable getText()
    作用:返回Editable对象
    提示:通过Editable对象的toString(),可以获得TextView的text属性值
    2.public void setText(String text)
    作用:设置text的值
    3.public void setVisibility(int visiblity)
    作用:设置可见性:View.GONE 不可见并且不占屏幕位置
    4.public void setTextSize(float size)
    作用:设置字号,单位:px
    5.public void setTextColor(int color)
    作用:设置字体颜色
    6.public void setBackground(int color)
    作用:设置背景颜色
    7.public void setGravity()
    作用:设置内部字体对齐方式
    8.public void setError(Charsequence text)
    作用:显示错误信息
    示例:
    tv.setError("出错啦");


    ImageView类的常用方法
    1.public void setImageResource(int resId);
    作用:设置ImageView显示的图片


    RadioButton类的常用方法
    1.Boolean isChecked();
    作用:返回该控件是否被选中,若选中则返回值为true,否则为false
    2.public void setChecked(Boolean checked)
    作用:设置复选框控件是否打钩
    3.public void setOnClickListener(OnClickListener I)
    作用:注册控件单击事件


    CheckBox类的常用方法
    1.Boolean isChecked();
    作用:返回该控件是否被选中
    2.public void setChecked(Boolean checked)
    作用:设置复选框控件是否打钩
    3.public void setOnCheckedChangeListener(OnCheckedChangeListener listener)
    作用:注册复选框控件选择/未选择发生改变的事件


    OnCheckedChangeListener接口
    概述:该接口负责监听CheckBox等控件的选中状态
    常用方法:
    void onCheckedChanged(Compound buttonView,boolean isChexked);
    作用:监听空间选中状态的变化
    参数说明:
    (1)buttonView:被点击的控件对象
    (2)isChecked:若控件处于选中状态,值为true,否则为false


    RadioGroup类的相关方法
    常用方法:
    public void onCheckedChanged(RadioGroup group,int CheckId)
    作用:响应该控件内的CheckBox控件的选择发生改变的事件
    参数1group:当前的RadioGroup控件对象
    参数2:打钩控件的索引
    说明:该方法与CheckBox控件的事件同名,但位于不同的包下。


    Toast
    Toast类用于显示字符串,在开发过程中测试时可以用来代替日志输出调试信息,也常用于给用户简短的提示
    常用方法:
    1.makeText(Context context,Charsequence msg,int duration);
    说明:第三个参数是显示消息的持续时间,单位是毫秒,有两个预定义的常量值
    Toast.LENGTH_LONG:持续3-4秒
    Toast.LENGTH_SHORT:持续2-3秒


    ProgressBar是UI中非常实用的控件,类名:ProgressBar,用于动态显示某个比较耗时的操作的进度,可避免因操


    作时间长,降低用户体验。
    XMl属性
    max                            设置进度条的最大值
    progress                       设置已完成的进度值
    secondaryProgress              设置第二进度条
    progressDrawable               设置进度条的轨道的绘制方式
    progressBarStyle               设置进度条样式
    progressBarStyleHorizontal     水平进度条样式
    progressBarStyleLarge          大进度条样式
    progressBarStyleSmall          小进度条样式


    progressBar的Style有如下值:
    horizontal                     水平进度条
    inverse                        不断跳跃,旋转的进度条
    large                          大进度条
    large.inverse                  不断跳跃、旋转的进度条
    small                          小进度条
    small.Inverse                  不断跳跃、旋转的小进度条
    以上Style属性值前面都要加上如下的前缀
    @android:style/Widget.ProgressBar.


    SeekBar控件
    拖动条控件,允许用户通过拖动滑块来改变值。
    SeekBar是ProgressBar的间接子类,因此SeekBar的属性参见ProgressBar属性列表
    OnSeekBarChangeListener接口
    该接口负责监听SeekBar的滑动
    常用方法:
    1.void onProgressChanged(SeekBar seekBar,int progress,bool fromUser)
    参数:当前的SeekBar对象
    参数:滑块的进度值
    参数:用户是否拖动了滑块
    2.void onStartTrackingTouch(SeekBar seekBar)
    作用:开始移动滑块时,触发本方法执行,滑块移动中本方法不再被触发
    3.void onStopTrackingTouch(SeekBar seekBar)
    作用:结束移动滑块时,触发本方法
     
    Android事件处理机制
    一、匿名内部类实现OnCLickListener接口
    实现机制
    通过匿名内部类实现OnCLick接口
    适用于代码没有复用性的地方


    二、内部类实现OnCLickListener接口
    实现机制
    在Activity类中定义内部OnCLickListener接口
    适用于监听器具有复用性,并且项目较大时,方便代码的管理


    三、Activity类实现OnCLickListener接口
    适用于按钮较多,但每个按钮的响应代码较短


    四、外部类实现OnCLickListener接口
    适用于监听器具有复用价值。有利于代码的混淆。


    Intent类
    Intent类是Android组件间的桥梁。该类负责启动组件,在组件之间传递数据
    创建Intent对象
    方式一:Intent intent=new Intent(起始组件的对象,目标组件类.class)
    方式二:Intent intent=new Intent()
    intent.setClass(起始组件的对象,目标组件类.class)
    方式三:获取上一个Activity传递过来的intent对象
            Intent intent=getIntent();
    常用方法:
    1.putExtra(key,value);
    作用:一键值对形式在intent对象那中保存数据
    2.putExtra(key ,(Seriazable()obhect))
    作用:若存放的是对象,则要间对象序列化在存放数据
    3.getIntExtra(key ,int DefaultValue)
    作用:获取存放在intent对象中的键为key的int类型的数据。若获取不到,则附一个默认值
    3.getDoubleExtra(key,doubleValue)
    作用:获取存放在intent对象中的键为key的double类型的数据。若获取不到,则附一个默认值
    4.getString(key)
    5.getSerilizable(key)
    .....


    获取目标Activity的返回值
    概述:当从本Activity跳转至目标Activity,有时需要目标Activity返回一些结果,这是需要调用Activity中的相


    关方法进行处理
    相关方法:
    1.public void startActivityForResult(Intent intent,int requestCode);
    作用:启动目标Activity,并要求目标Activity返回处理结果
    参数一:requestCode:该数据称为请求码,又能与目标Activity返回结果识别启动的位置
    说明:有可能丛原ACtivity中的多个地方启动目标Activity,因此请求码用来识别启动的位置
    2.onActivityResult(int requestCode, int resultCode, Intent data);
    3.setResult(int ResultCode,Intent intent);在目标Activity中


    Bundle类
    概述:Bundle类用于Activity之间的数据传递,以及在Activity状态切换时用于保存Activity的数据
    Bundle通过键值对的方式存取数据。查看Intent类的源码发现:Intent类中有一个名为mExtras的成员变量,该变


    量的类型是Bundle
    Intent类中的putExtra方法实质上是将数据放在了mExtras对象中
    Intent类将Bundle对象的存取数据进行了包装,简化了数据存取的操作
    常用方法:
    1.public void clear()
    作用:清除此Bundle中的所有数据
    2.getString getString(String key)
    作用:返回指定key的字符串
    3.public void putString(String key,String value)
    作用:存放键为key的字符串值 
    4.public void remove(String key)
    作用:移除指定key的值
    5.public void putXXX(String key,xxx value)
    作用:存放基本类型数据的值
    6.public void putSerializable(key, Object value)
    作用:存放(序列化之后)对象
    7.public void putxxxArray(key, array Value)
    作用:存放元素是基本类型的数组




    Intent类中相关方法
    1.public void putExtras(Bundle bundle)
    作用:将bundle对象保存在Intent对象中
    2.public Bundle getExtras()
    作用:从Intent对象中获取Bundle对象




    保存Activity数据的回调方法
    onSaveInstanceState(Bundle outState)
    outState.putString(key,value);
    恢复的时候从savedInstanceState中拿出来
    恢复方法
    onRestoreInstanceState(Bundle savedInstanceState)


    Intent进阶
    Android的组件设计启动,体现了弱耦合的关系,各组件之间的通过Intent传递数据和每个组件之间都最大程度的


    保持独立性。某个组件的修改,不会影响其他组件。避免了“牵一发,动全身”的现象出现
    Intent类的成员变量
    1.mComponent:ConponentName类型,指定组件间跳转的原组件和目标组件的包名,用于显式意图
    2.mAction:String :表示启动的动作
    3.mCategory:String:表示启动组件的类别
    4.mData:该参数是Uri类型,存放操作的数据,如访问网址,打电话等
    Uri类 Uri.parse(String strUri);
    Uri的结构
    scheme://host:port/path------协议名://主机名:端口号/数据的路径和值
    5.mType
    格式:大类型/小类型
       mType= "audio/mp3"


    跨应用启动另外的Activity只能使用隐式意图


    隐式意图总结:
    1.同一个Intent Filter可以有多个category
    2.category除DEFAULT外都是或的关系
    3.若data和type属性要同时设置,则应调用setDataAndType()


    ListView控件
    一、概述:
    Android中,列表行控件是数据源为数组或集合的控件
    列表类型的控件有ListView,GridView,Gallery,Spinner
    列表型控件支持适配器模式显示数据
    二、AdapterView类
    (一)概述
    AdapterView是ViewGroup的子类,由一个适配器和绑定到适配器的数据集组合而成。当需要在Android的View中显


    示数组或集合数据时,AdapterView是非常有用的。
    ListView、GridView、Gallery和Spinner是AdapterView的子类,这些View的数据源是数组或集合,并且用一种特


    定的方式来显示这些数据
    AdapterView对象有两个主要的功能
    1.用数据填充视图
    2.处理用户的操作事件
    (二)AdapterView的继承图
    Object-java.lang
     View-android.view
      ViewGroup-android.view
       AdapterView<T>-android.widget
        AbsListView-android.widget
         GridView-android.widget
         ListView-android.widget
          ExpandableListView-android.widget
       AbsSpinner-android.widget
        Gallery-android.widget
        Spinner-android.widget


    提示
    1.AdapterView的addView不被支持
    2.AdapterView需要一个Adapter对象为其提供item
    (三)、BaseAdapter类
    (一)概述
    AdapterView通过Adapter(适配器类)象AdapterView的子类视图填充数据,BaseAdapter是所有适配器的基类。该类


    是抽象类。
    (二)适配器的继承树图
    Object-java.lang
     BaseAdapter-android.widget
      ArrayAdapter<T>-android.widget
      CursorAdapter-android.widget
      SimpleAdapter-android.widget
    (三)常用方法
    1.public View getView(int position,View convertView,ViewGroup)
    作用:创建并返回列表项,包括列表项的显示样式和数据,Android根据getView返回的对象绘制列表项
    说明:
    参数1:position当前列表项在列表的索引值
    参数2:convertView当前列表项的View对象
    参数3:parent列表项的父容器对象-列表对象
    提示:该方法是抽象方法
    2.public long getItemId(int position)
    作用:返回当前列表项的索引值
    参数position:当前列表项的索引值
    提示:该方法是抽象方法
    3.public Object getItem(int position)
    作用:返回当前列表项对象
    参数:position当前列表项的索引值
    提示:该方法是抽象方法
    4.public int getCount()
    作用:返回列表对象总数,Android系统根据getCount()返回的值绘制指定数量的列表项
    提示:该方法是抽象方法
    说明:返回数组的总长度,该数组的长度也就是列表项的个数
    四、ListView控件
    (一)概述
    ListView是Android应用程序中使用频率最高的控件。该控件使用垂直列表的方式显示多个选项,特别适合于手机


    这种屏幕较小的设备
    ListView控件的特点:每个列表独占一行,每行的布局都相同,数据和布局分离,支持数据来自适配器,ListView
    只负责布局。
    说明:
    ListView是AdapterView的间接子类,支持适配器填充数据
    ListView就是一个容器,可以包含Item(Child)
    (二)常用属性:
    XML属性                          说明
    choiceMode                     设置ListView的选择方式,有三个值
                                    1.none:默认方式
                                    2.SingleChoice:单选
                                    3.multipleChoice:复选
    divider                        设置列表项的分隔条,分隔条可以是颜色值亦可以是图片
    dividerHeight                  分隔线的高度值
    entries                        设置显示的内容,该内容是字符串数组
    (三)常用方法
    1.Object getItemAtPosition(int position)
    作用:获得当前列表项
    参数position:当前列表项的索引值 
    2.void setChioceMode(int choiceMode)
    作用:设置列表的选择方式
    参数choiceMode有以下三个可选值:
    ListView.CHOICE_MODE_NONE
    ListView.CHOICE_MODE_SINGLE
    ListView.CHOICE_MODE_MULTIPLE
    (四)ListView的使用步骤
    1.创建布局文件,在该布局文件中声明ListView控件
    2.创建第二个布局文件,在该文件中为ListView的Item设置显示样式
    3.在Activity中实例化ListView
    4.获取数据集,即创建需要在ListView中显示的数据
    5.创建适配器对象
    6.设置ListView对象与步骤5创建的适配器相关联
    说明:
    Android的AdapterView采用MVC模式显示、处理数据,其中以上的1,2是View层,负责设置显示的样式,4是


    Module层,创建需要显示的数据,5是控制层,用于将数据显示在View中,并负责对数据的增删改的操作。
    五、View.inflate()
    public static View inflate(Context context,int resource,ViewGroup root)
    作用:将一个布局文件实例化为一个View类型的Java对象
    参数context:当前的Activity对象
    参数resource:布局文件的id值
    参数root:该布局需要方式在哪个容器对象中。若无则设置为null。


    OnItemClick接口
    (一)概述
    OnItemClick接口负责监听列表项点击事件
    (二)常用方法
    void OnItemClick(AdapterView<?>parent,View view,int position,long id)
    作用:监听用户点击列表项的事件
    说明:
    参数parent:当前列表对象,如ListView对象
    参数view:被点击的列表项对象
    参数position:被点击的view在适配器中的索引值
    参数id:被点击的列表项在列表中的索引值
    position与id之相同


    OnItemLongClick接口
    (一)概述
    OnItemLongClick接口负责监听用户长安列表项的事件
    (二)常用方法
    public boolean onItemLongClick(AdapterView<?>parent,View view,int position,long id)
    作用:监听用户长按列表项事件
    返回:返回true表示本方法处理了长按事件,该事件不会分发给其他控件处理
          返回false,则本次长按事件换回分发给其他控件处理。
    说明:长按是指触摸控件的时间超过0.5秒


    SimpleAdapter
    一(概述)
    SimpleAdapter是BaseAdapter的子类。用该适配器可定义表现丰富的信息,例如在ListView列表项中显示带文字和


    图片的信息
    二构造方法
    SimpleAdapter(Context context,List<? extenes Map<String,?>>data,int resource,String[]from,int []to)
    参数说明:
    1.第一个参数context:上下文参数,该参数与ArrayAdapter类的构造方法的第一个参数相同。
    2.第二个参数data:一个List类型的集合,该集合的元素是Map类型,改map集合的键是String类型,值是任意类型


    ,每个map对象都存放一个列表项的信息
    3.第三个参数resource:是布局文件的资源索引值,该布局控制列表项的显示样式
    4.第四个参数from:一个字符串类型的数组,该数组存放着所有键名,就是存放列表项的Map对象出现的键。
    5.第五个参数to:存放列表项的布局文件中出现的所有控件的资源索引值


    ArrayAdapter类:数组适配器类,该类负责将数组或集合的数据按指定布局方式显示,通过与ListView等控件相关


    联,在列表类型的控件按指定布局样式显示数组或集合的数据
    说明:
    1.ArrayAdapter的内部的数据一维数组或list集合
    2.其返回的item中必须包含一个TextView控件,数组中的每个元素将被绑定到TextView控件上显示
    (二)创建ArrayAdapter对象
    ArrayAdapter<T>(Context context,int layoutRes,int textViewId,T[]data)
    参数context:上下文对象
    参数layoutRes:item界面对应的layout布局文件
    参数TextViewId:item中包含的TextView的id
    参数data:需要显示的数据集,可以是数组,也可以是集合
    ArrayAdapter<T>(Context context,int layoutRes,T[] data)
    参数context:上下文对象
    参数layoutRes:item界面对应的layout布局,且该布局的根标签应当是TextView或TextView的子类
    参数data:需要显示的数据集
    ArrayAdapter.createFromResource(Context context,int arrayRes,int layoutRes)
    参数context:上下文对象
    参数arrayRes:数组资源
    参数layoutRes:item界面对应的layout布局,且该布局的根标签应当是TextView或TextView的子类对象


    Spinner控件
    (一)概述
    Spinner是显示列表框的控件。Spinner是AdapterView的间接子类,所以它也是个容器型的View,该控件可以通过


    适配器对象来显示数据,基改View的每个列表项可以用布局来设置,布局中可以包含若干控件
    (二)常用属性
    entries                   使用数组资源设置下拉列表框的内容
    spinnerMode               下拉列表的风格,有以下两种:
                               1.对话框风格 dialog2.下拉列表风格dropdown
    prompt                    Spinner下拉列表的提示标题,该属性使用时注意以下两点:
                               1.该标题的文本必须在strings.xml中定义为字符资源,然后再prompt中使用
                               2.只有spinnerMode属性值是dialog时,prompt才会起作用


    (三)常用方法
    setOnItemSelectedListener(OnItemSelectedListener listener)
    作用:Spinner列表项选择发生改变的响应事件,参数是OnItemSelectedListener接口的一个实例
    说明:
    OnItemSelectedListener接口,负责监听列表项发生改变的事件,该接口中定义了两个方法:
    void onItemSelected(AdapterView<?> parent,View view,int position,long id)
    参数parent:列表对象
    参数view:列表项对象
    参数position:列表项在适配器中的索引值,位置值从零开始计算
    参数id:列表项的索引值。一般情况下,第三第四个参数的值是一致的。
    void nothingSelected(AdapterView<?>parent)
    作用:未发生选择时,触发本方法
    参数parent:当前列表对象
    (四)相关方法
    void setDropDownViewResource(int Resource)
    作用:ArrayAdapter类中的方法,用于设置列表下拉后的布局
    参数resource:布局文件的资源索引值
    说明:
    1.Android为下拉选框提供默认的布局,也可根据业务需求自定义布局。
    2.与Spinner绑定的ArrayAdapter要用以下的方式创建:
    ArrayAdapter<CharSequence>adapterMonths=ArrayAdapter.createFromResource


    (this,R.array.months,android.R.layout.simple_item)
    参数context:上下文对象
    参数:下拉列表中显示的数据
    参数:下拉列表的布局


    GridView组件
    (一)概述
    GridView控件称为网格列表,在Android中的使用频率较高,该控件可按多行多列显示信息,表现能力很强。


    (二)常用属性
    属性名                   Java的方法                   作用
    columnWidth              setColumnWidth (int)         设置列宽度
    gravity                  setGravity(int)            设置对齐方式
    horizontalSpacing        setHorizontalSpacing(int)    设置各元素之间的水平间距
    numColumns               setNumColumns(int)           设置列数
    stretchMode              setStretchMode(int)          设置拉升模式
    verticalSpacing          setVerticalSpacing(int)      设置各元素间的处置间距
    其中,stretchMode有以下可选的值
    NO_STRETCH                        不拉升
    STRETCH_SPACING                 仅拉伸元素之间的间距
    STRETCH_SPACING_UNIFORM          表格元素本身、元素之间一起拉升伸
    STRETCH_COLUMN_WIDTH             仅拉伸表格元素本身
    (三)常用方法
    1.public void setAdapter(Adapter adapter)
    作用:设置与适配器对象相关联
    说明:
    参数adapter适配器对象,如ArrayAdapter,BaseAdapter等
    2.public void setOnItemClickListener(Context context)
    作用:设置列表项单击事件
    说明:context是一个上下文对象
    3.public boolean setItemLongClickListener(Context context)
    作用:设置列表项的长按事件
    参数context:上下文对象


    Gallery控件
    (一)概述
    Gallery控件与spinner类相似,都是AbsSpinner类的子类。它们之间的区别在于:Spinner显示的是一个垂直列表选


    择框,而Gallery显示的是一个水平列表选择框,并且Gallery允许用户水平拖动列表框
    ListView是垂直列表,Gallery可以理解为是水平列表
    (二)常用属性
    XML属性         相关方法                        说明
    Gravity          setGravity(int)             设置对齐方式
    Spacing          setSpacing(int)           设置列表项之间的间距
    unselectedAlpha  setunselectedAlpha(float)   设置为选中的列表的透明度
    (三)重要接口、方法
    1.OnItemClickListener接口:监听列表项单击事件。该接口定义了如下方法
    2.void onItemClick(AdapterView<?>parent,View view,int position,long id)
    作用:响应用户单击列表项的事件
    参数说明:
    parent:当前的Gallery对象
    view:被单击的列表项
    position:被单击的列表项在适配器中的位置(索引值)
    id:被单击的列表项在列表中所在列的索引值
    3.void setOnItemClickListener(OnItemClickListener listener)
    作用:设置响应用户单击事件的方法
    参数说明:listener实现了OnItemClickListener


    对话框控件
    一、概述
    对话框是UI设计中常用的控件,在Windows操作系统中,对话框可分为模式对话框和非模式对话框。模式对话框在


    使用时,项目中其他UI是不允许操作的,如保存文件对话框。非模式对话框允许操作其他的UI
    Android的对话框模式是非模式对话框,但可以设置为模式对话框。
    Android提供了丰富的对话框功能,并且允许自定义对话框。以下介绍其中应用最为广泛的,功能最为丰富的


    AlertDialog控件。
    1.带消息和多个按钮的对话框
    2.带列表和按钮的对话框
    3.带单选列表项和按钮的对话框
    4.带复选列表项和按钮的对话框
    二、常用方法
    1.public void show()
    作用:启动并显示对话框
    2.public void dismiss();
    作用:取消对话框
    3.public void setCancelable(boolean cancelable)
    作用:设置是否允许返回键关闭对话框
    参数值为true,允许返回键关闭对话框
    三、内部类
    Builder类是AlertDialog类的内部类,用于创建对话框
    (一)常用属性
    AlertDialog.Builder类中常用的属性
    XML属性                        说明
    icon                           对话框图标
    title                          对话框标题
    message                        对话框中显示的消息文本
    (二)常用方法
    1.Builder setPositiveButton(Charsequence text,final OnClickListener listener)
    作用:为对话框创建带有确定意义的按钮并返回AlertDialog.Builder类的对象
    参数text:按钮的标题
    参数listener:单击事件的接口的实现类对象
    提示:
    OnCLickListener接口所在包是:android.content.DaiLogInterface。
    SetPositiveButton()创建的按钮代表具有确定意义的按钮,如标题为OK、确定等
    2.public Builder setNegativeButton(CharSequence text,final OnClickListener listener)
    作用:创建具有否定、cancel标题的按钮,并返回一个AlertDialog.Builder对象。
    提示:Android的一个对话框最多可以创建三个按钮
    3.Builder setNeutralButton(CharSequence text,final OnClickListener listener)
    作用:创建具有忽略意义的按钮,并返回一个AlertDialog.Builder对象
    4.public Builder setIcon(int resId)
    作用:为对话框设置图标,参数是图标的资源索引值
    5.public Builder setTitle(int resId)
      public Builder setTitle(Charsequence text)
    作用:设置对话框标题,参数可以是字符串,也可以是资源中定义的字符串
    6.public Builder setMessage(int resId)
      public Builder setMessage(Charsequence text)
    作用:设置对话框中显示的消息,参数可以是字符串,也可以是资源中定义的字符串
    7.public Builder setCacncelable(boolean cancleable)
    作用:设置对话框模式为模式对话框,并且不允许Back键关闭对话框
    8.public Builder setView(View view)
    作用:设置view代表的布局为对话框的内容
    9.AlertDialog create()
    作用:创建并返回AlertDialog类型的对话框对象
    四、标准对话框
    (一)直接new
    //以下创建一个带有按钮的对话框并显示该对话框
    new AlertDialog.Builder(this).setIcon(R.drawable.Icon)
    .setMessage("hello world")
    .setTitle("Dialog")
    .setPositiveButton("Confirm",null)
    .show();
    说明:以上new了一个对话框对象,参数this是当前Activity对象。创建之后,可以立即使用setIcon、setTitle、


    setMessage等方法设置对话框的图标、标题、按钮等,最后要调用show()放法创建对话框并显示。
    (二)创建并保存在对象中
    Builder builder=AlertDialog.Builder(this);
    builder.setIcon(R.drawable.Icon);
    builder.setMessage("hello world");
    builder.setTitle("Dialog");
    builder.setPositiveButton("Confirm",null);
    AlertDialog dialog=builder.create();
    dialog.show();
    说明:
    第一行的Builder实质上就是AlertDialog.Builder
    第五行设置按钮的第二个参数是null,表明该对话框的这个按钮事件没有监听事件的功能,即这个按钮只是摆个样


    子。该处可以写成匿名内部类。
    (五)自定义对话框
    (一)概述
    自定义对话框允许用XML文件设置的布局作为对话框的显示内容和显示样式
    (二)实现步骤
    步骤1.定义用于对话框的内容、显示样式的布局文件
    步骤2.用布局解析器解析步骤1的XML文件为一个View对象
    步骤3.创建对话框对象,并调用addView()设置步骤2的View对象为对话框的显示内容、样式。
    (三)示例
    步骤1、在res/layout下创建input_dialog.xml文件.
    步骤2、解析步骤1中定义的布局文件为一个View对象。示例代码:
    LayoutInflater  inflater=LayoutInflater.from(this);
    view=inflater.inflater(R.layout.input_dialog,null);
    步骤3、通过view.findViewById()实例化该布局文件中用于输入数据的控件对象。示例代码:
    EditText etInput=(EditText)view.findViewById(R.id.etInput);
    步骤4、创建对话框对象,并将步骤2中的View对象设置为对话框的显示内容和样式。示例代码:
    Builder builder=new AlertDialog.Builder(this);
    builder.setView(view);
    AlertDialog dialog=builder.create();
    builder.show();
    六、DialogInterface接口
    (一)概述
    该接口是对话框接口,定义了与对话框按钮相关的常量
    (二)常用的常量
    以下是该接口中常用的常用:
    public static final int Button_POSITIVE=-1;
    public static final int BUTTON_NEGATIVE=-2;
    public static final int BUTTON_NEUTRAL=-3;
    这三个常量分别代表setPositiveButton、setNegativeButton、setNeutralButton这三个按钮在对话框中的索引值
    (三)内部接口
    OnClickListener();
    作用:监听用户单击对话框中的按钮事件
    该接口定义了一个方法
    void onClick(DialogInterface dialog,int which)
    作用:监听用户单击对话框按钮的事件
    说明:第一个参数Dialog:当前的对话框对象
    第二个参数which:被单击按钮的索引值
    七、列表对话框
    (一)概述
    AlertDialog提供了多种风格的对话框,列表对话框其中一种
    重要方法:
    setItems(Charsequence[]items,final onClickListener listerner)
    作用:创建并显示列表对话框,并返回AlertDailog.Builder类的对象。
    第一个参数Items:存放菜单中个菜单项标题的数组。
    第二个参数listener:菜单项单击事件


    ListView进阶
    (一)ListView的运行机制
    1.使用match_parent定义ListView的高度和宽度属性
       使用ListView控件的高度和宽度要避免使用wrap_content,否则会导致BaseAdapter.getView重复调用N次。尽量


    使用match_parent或固定值设置height或width。
    2.缓存列表项
       ListView控件在设计上采用了只创建并显示当前屏幕中的列表项的布局对象(该布局对象由


    BaseAdapter.getView方法的convertView参数保存)。例如:一屏显示是个列表项(包括部分显示在屏幕上的列表


    项),则只创建十个convertView,ListView会反复使用这十个convertView显示进入当前列表项的内容。
    在向上滚屏时,当第一个convertView移出屏幕,该convertView将从屏幕下边出现。同理,在向下滚屏时,若屏幕


    最下边的convertView移除屏幕,该convertView从最上边进入屏幕。
    提示:一屏不一定显示是个列表项,具体多少根据列表项布局的高度和设备的高度而定。
    因此,在BaseAdapter.getView中,不用每次创建布局对象,而是采取每次判断convertView是否为空,若为空,则


    创建布局对象。若convertView不为空,说明当前屏幕所需的所有布局对象均以创建好,此时将保存的布局对象取


    出,然后显示新的数据。
    那么,布局对象中包含的各个控件如何与当前的convertView建立对应关系呢?
    当然可以每次通过findViewById()方法获取布局中的控件,但由于findViewById()需要在xml文件中遍历查找控件


    ,所以这样做做效率不高。
    高效率的做法是将每次convertView的所有控件也保存起来,以后取出来显示新数据即可。下面是两个View类中的


    方法。
    (二)View.setTag()与View.getTag()
    1.setTag(Object obj)
    作用:将obj对象的地址保存在当前View对象的mTag成员变量中。
    2.Object getTag()
    作用:从当前View对象中mTag成员变量中取出保存的对象,返回Object类型
     用以上两个方法将当前的convertView中的所有控件保存在一个对象中,然后将该对象与当前的convertView相关


    联。


    线程通信与网络客户端编程
    一、Handler类
    (一)概述
    在Android中Handler类可用于在线程间收发消息。
    例如:handler在工作线程中发送消息,该消息发送至消息队列中,等待处理。在主线程中Handler从消息队列中接


    收消息,并根据消息中的信息决定如何更新UI。
    每个Handler实例都会绑定到创建它的线程中。
    Handler可以发送消息或Runnable值某个线程的消息队列中。
    (二)构造方法
    1.public Handler Handler();
    无参构造方法
    2.Handler(Looper looper)
    作用:带参构造方法,用于创建Handler对象。将自定义的用于管理消息队列的looper对象放在Handler.mLooper成


    员变量中。
    参数looper:自定义的管理消息队列的对象。
    3.Handler(Callback callback)
    作用:带参构造方法,创建Handler对象
    参数:callback:自定义的用于处理消息的对象,该对象必须实现Handler.Callback接口。
    说明:
    1)Callback接口中声明了handleMessage()方法,程序员在该方法中编写处理消息的代码。
    2)callback保存在Handler.mCallback成员变量中。在handler处理消息时将调用mCallBack中封装的代码。
    4.Handler(Looper looper,CallBack callback)
    作用:带参构造方法,创建Handler对象
    参数:参见2和3中关于looper和callback的说明
    (三)常用方法
    1.sendEmptyMessage(int what)
    作用:从work thread(工作线程)向主线程发送空消息
    参数:当多个线程向主线程发送消息,what用于区别不同的线程
    2.sendMessage(Message msg)
    作用:从workThread向主线程发送消息
    参数msg:存放消息的数据对象
    3.handleMessage(Message msg)
    作用:接收并处理从work thread发送的消息
    参数msg:sendMessage发送过来的消息对象
    4.public final Boolean android.os.Handler.post(Runnable r)
    作用:将一个实现了Runnable的任务对象发送中目标线程的消息队列中。
    二、线程通信进阶:
    1.Android系统提供了一个Looper类的对象(循环队列)用来管理消息队列,各线程通过Handler类的Send***Message


    命令将消息发送至消息队列,Loop而再将消息队列中的消息依次交给主线程处理。
    2.Handler在创建时必须关联到一个Looper对象,如果未明确关联到某个looper,则默认关联到其创建时所在线程


    的looper,因此Handler发送的Message对象都发送至Looper管理的消息队列中。
    3.Looper与UIThread相关联,因此Looper将消息队列中取出的Message交给UI线程执行。
    4.Handler及时消息的发送者,也是消息的处理者。
    提示:默认情况下,工作线程中没有消息队列和Looper对象。


    三、Message对象
    (一)概述
    Message类用于存放消息中的数据,该类通常与Handler类配合使用
    (二)常用属性
    1.int arg1:存放一个int类型的数据
    2.int arg2:存放一个int类型的数据
    3.int what:存放一个int类型的数据,该数据表示信息的类型,用来区别其他消息
    4.Object obj:存放任意类型的对象
    (三)常用方法
    1.public Message()
    作用:无参构造方法,创建Message对象
    2.public static Message ontain()
    从全局中返回一个新的Message实例。在大多数情况下这样可以避免分配新的对象。
    说明:
    在Java中,创建和销毁对象是比较消耗资源的。如果在应用中需要频繁创建销毁某个类型的对象,这样会产生很多


    临时对象,当失去引用的临时对象较多时,虚拟机会进行垃圾回收(GC),CPU在进行GC是会导致应用程序的运行得不


    到响应,从而导致应用的响应性降低。
    资源池就是用来解决这个问题的,当需要使用对象的时候,从资源池中获取,资源池负责维护对象的生命周期。
    Android的资源池中存放一定数量的同样类型的对象,当程序需要使用时,可以从资等待源池中获取,使用完成,


    收回资源池等待下一次被使用。
    示例:从资源池中获取Message对象
    Message msg=Message.obtainMessage();
    提示:若之前没有创建过Message,则创建给对象。Android系统会在适当时候回收该对象,方便下次调用。


    四、Looper类
    (一)概述
    Android应用程序是消息驱动的,Android系统提供了消息循环机制。Android通过Looper、Handler来实现消息循环


    机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。
    Android系统中Looper负责管理线程的消息队列(Message queue)个消息循环。
    (二)重要方法
    1.public static Looper.prepare();
    作用:创建Loop而对象和MessageQueue(消息队列)对象。并将Looper对象与当前线程关联起来。
    2.public static Looper.getMainLooper();
    作用:获得主线程的Looper对象。
    3.public static Looper.myLooper();
    作用:获得当前工作线程的Looper对象。
    4.public static Looper.loop();
    作用:进入消息循环,Looper对象不断地从消息队列中获取信息交给与其关联的线程执行这些消息。
    (三)Looper运行机制
    Looper负责管理线程的消息队列和消息循环,Looper不能用new来实例化,而是要用Looper.prepare()实例化。
    Android中每个线程都可以(但不是必须)跟着一个Looper,Looper可以帮助线程维护一个消息队列,每个线程可以存


    在一个消息对列和一个消息循环(Looper),特定线程的消息只能分发给本线程,不能进行进行跨线程通讯。但是创


    建的工作线程是没有消息循环和消息队列的,如果想让该线程具有消息对列和消息循环,需要在线程中首先调用


    Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。如下例所示:
    Looper.prepare();//创建消息队列
    myLooper=Looper.myLooper();//获得当前线程中的Looper对象
    Looper.loop();//进入当前线程的消息循环
    一个Activity中可以创建多个工作线程,如果这些线程把它们的消息放入主线程消息队列,那么该消息就会在主线


    程中处理了。以为主线程一般负责界面的更新操作,所以这种方式可以很好的实现Android界面更新。
    其他线程通过Handler对象将消息放入主线程的消息队列。只要Handler对象由主线程的Looper创建,那么调用


    Handler的sendMessage等方法,会把消息放入主线程的消息队列。并且在主线程中调用Handler的handleMessage方


    法来处理消息。
    不管是主线程还是工作线程,只要有Looper的线程,别的线程就可以向这个线程的消息队列中发送消息和计划任务


    ,然后做相应的处理。
    Handler对象在创建时,可以再构造方法中带参数,也可以不带参数。若用无参构造方法创建,则Handler在所在的


    线程中工作。
    若用带参的构造方法创建,则按参数在指定的线程中工作。
    四、Android中的网络客户端编程
    (一)概述
    Android中的HTTP编程与Javase中的HTTP编程的基本相同,区别在于:
    1.Android4要求所有的网络读写操作必须放在工作线程中执行。
    2.更新UI的代码在UI线程中完成。


    一、共享首选项-SharedPreferences
    (一)概述
    有些应用需要保存配置信息,如是否打开音效开关、登录时是否保存上次输入的用户名、密码等。Android对这类


    应用提供了SharedPreferences(共享首选项),共享首选项适合数据量少、数据已键值对的方式保存的应用。
    (二)常用类/接口
    1.SharedPreferences接口
    作用:该接口定义了共享首选项操作的方法和一个重要的内部接口:Editor
    2.Editor接口
    作用:该接口定义了存储、删除数据的方法。
    (三)常用方法
    1.context.getSharedPreferences(String fileName,int mode)
    作用:创建SharedPreference接口的实现类的对象
    第一个参数fileName:指定共享项数据保存、读取的文件名。该文件存放在data/data/packageName/Shared_prefs


    文件夹下。通过eclipse中的DDMS视图中的file-explorer视图可以找到当前项目中的以上文件夹。默认文件名:类


    名.xml。
    第二个参数mode:共享首选项文件的存取权限。通过以下常量设置:
    MODE_WORLD_PRIVATE :私有属性,只有本项目才能存取第一个参数指定的.xml文件。
    MODE_WORLD_READABLE:允许其他项目读取本项目中的xml文件内容
    MODE_WORLD_WRITEABLE:允许其他项目向本项目中的xml文件写数据
    2.共享首选项的文件存储的相关方法
    (1)Editor.putInt(String key,int value);
    作用:存放键名为key的int类型的数据
    (2)Editor.putFloat(String key,String value)
    作用:存放键名为key的float行数据
    。。。。。
    (3)Editor.remove(String key)
    移除键名为key的键值对
    (8)Editor.commit();
    作用:提交修改,保存结果
    提示:只有执行了本方法,以上的put方法存储才能真正有效。
    3.共享首选项的文件读取的相关方法(以下方法均从属于SharedPreference接口的实现类)
    (1)int getInt(String key,int defValue)
    作用:获取键名是key的int类型的数据,若没有key,则可以设置一个默认值:defValue.
    (2)Boolean getBoolean(String key,Boolean defValue)
    作用:获取键名是key的Boolean类型的数据,若没有key,则可以设置一个默认值:defValue
    (3)long getLong(String key,float defValue)
    作用:获取键名是key的long类型的数据,若没有key,则可以设置一个默认值:defValue
    (四)共享首选项的使用步骤
    1、保存数据步骤
    步骤1-实例化共享首选项对象
    SharedPreferences sp=getSharedPreferences("profile",MPDE_PRIVATE);
    步骤2-创建编辑对象
    Editor editor=sp.edit();
    步骤3-添加数据
    editor.putXXX();
    步骤4-保存数据
    Editor.commit();
    2、读取数据步骤
    步骤1-实例化首选项对象
    SharedPreferences sp=getSharedPreferences("profile",MODE_PRIVATE);
    步骤2-读取数据
    Object value=sp.getXXX();


    AndroidIO编程
    (一)概述
    当需要存取复杂的、大量的数据是需要IO流操作。Android在Context类中提供了存取文件的方法。
    (二)常用方法
    1.InputStream openFileInput(String fileName)
    作用:读取指定文件名的文件
    参数fileName:当前项目下data/data/projectPackage/files文件夹下的fileName文件
    2.OutputStream openFileOutput(String fileName,int mode)
    作用:存储数据至指定文件名,其中第二个参数有以下四个常量值:
    (1)MODE_PRIVATE :设置文件属性为私有,即只能被当前项目读写
    (2)MODE_APPEND:以添加方式打开文件,并向文件中添加数据
    (3)MODE_WORLD_READABLE:该文件可被其他项目读取
    (4)MODE_WORLD_WRITEABLE:该文件可被其他项目写入
    3.File getFileDir()
    作用:创建并返回文件的默认存储目录(data/data/包名/files)
    示例:获取当前项目的默认文件路径并在日志窗口输出
    File file=getFilesDir();
    sysout(file.getAbsolutelyPath());
    4.File getCacheDir()
    作用:创建并返回默认的缓存目录(data/data/包名/cache)
    示例:获取默认的缓存目录,并在日志窗口中输出
    File cache=getCacheDir();
    sysout(cache.getAbsolutelyPath());
    5.File getDatabasePath(String dbName)
    作用:返回指定数据库文件的保存路径(data/data/包名/databases/dbName)
    File db=getDatabasePath("user.db")
    sysout(db.getAbsolutelyPath());
    6.File getDir(String dir ,int mode)
    作用:创建并返回指定名称的目录(data/data/包名/app_dir)
    示例:获取指定名称的路径并输出
    File dir=getDir("abc",MODE_PRIVATE);
    sysout(dir.getAbsolutelyPath());
    7.InputStream getAsserts.open();
    作用:获取执行assert目录中文件的输入流
    示例:从项目中的assert文件夹下读取1.jpg
    InputStream is=getAsserts().open("1.jpg");
    8.File getExternalCacheDir()
    作用:创建并获取程序在sdcard中的缓存目录/mnt/sdcard/Android/data/包名/cache
    示例:获取项目在SD卡的缓存目录并输出
    File cache=getExternalCacheDir();
    sysout(cache.getAbsolutePath());
    9.File getExternalFilesDir(type)
    作用:创建并获取本程序中指定类型的文件,在sdcard中的目录(mnt/sdcard/Android/data/包名/files/目录名



    示例:
    File musics=getExternalFilesDir(Environment.DIRECTORY_MUSIC);
    sysout(musics.getAbsolutePath());
    (三)Environment类
    1.概述
    提供访问环境变量的类
    2.静态方法
    1).getDataDirectory()
    作用:返回data目录
    2).getRootDirectory()
    作用:返回system目录
    3).getDownloadCacheDirectory()
    作用:返回cache目录
    4).getExternalStorageDirectory()
    作用:返回sdcard根目录
    5)getExternalStoragePublicDirectory()
    作用:返回sdcard中指定类型文件的公共存储目录
    6).getExternalStorageState()
    作用:返回当前的sdcard状态
    3.常量
    DIRECTORY_MUSIC  音乐存放的标准目录
    DIRECTORY_MOVIES 电影存放的标准目录
    DIRECTORY_DOWNLOADS 存放下载文件的目录
    MEDIA_MOUNTED        存储媒体是否已挂载,并且是否具有读写权限
    MEDIA_UNMOUNTED      存储媒体无法挂载


    三、从资源中获取图片
    (一)概述
    许多情况下,图片资源从网络、SD卡登出获取,并在程序中显示,因此涉及到图片的加载。Android提供了相关的


    API可方便地处理图片的读取以及读取中的优化操作。
    (二)BitmapFactory类
    该类是一个工具类,提供了大量操作Bitmap对象的方法
    (三)加载图片称为内存中Bitmap的对象
    1.public static Bitmap decodeByteArray(byte[]data,int offset,int length)
    作用:从字节数加载一个位图对象
    参数data:存放图片的字节数组
    参数offset:偏移量,从数组的offset起始位置加载图片
    参数length:从数组中加载数据的长度
    2.public static Bitmap decodeFile(String pathName)
    作用:从文件加载位图对象
    参数pathName:文件的路径
    3.public static Bitmap decodeResource(Resource res,int id)
    作用:从资源加载位图对象
    参数res:Resources类的对象
    参数id:图片的资源索引值
    4.public static Bitmap decodeStream(InputStream is)
    作用:从输入流加载位图对象


    SQLite数据库
    (一)概述
    SQLite是一款轻型的数据库,其设计目标是嵌入式,目前已经在很多嵌入式产品中使用。
    SQLite占用资源非常低,在嵌入式设备中,可能只需要几百k的内存就够了
    SQLite能够支持Windows/Linux/Unix等主流的操作系统,同时能够跟很多程序语言相结合,比如C#,PHP,Java等


    ,还有ODBC接口,与MySQL等开源数据库相比,SQLite的执行效率比他们都高。
    Android和IOS使用的数据库都是SQLite.
    (二)SQLiteDatabase类
    1.概述
    该类提供了对SQLite数据库的增删改查的操作
    2.常用方法
    1.execSQL(String sql)
    作用:用该方法并结合SQL语句对数据执行增删改操作
    参数sql:SQL命令的字符串
    2.execSQL(String sql,String[]args)
    作用:执行SQL语句中的数据库的增删改操作
    参数args:字符串数组,当第一个参数有占位符时,第二个参数用实际数据填充占位符。若没有占位符,则args的


    值设置为null。
    3.long insert(TABLE_NAME,String nullColumnHack,ContentValues contentValues);
    作用:添加记录
    参数TABLE_NAME:表名
    参数nullColumnHack:若插入的数据是空行,则本参数必须设置为null
    参数contentValues:Map类型的数据,每组键-值对代表一列及其该列的数据。可以存放多个键值对数据,代表需


    要插入的一行数据。
    示例:向当前数据库的 person 表插入一行记录,该行包括字段 name 和 phone 的数据,
    代码如下: 
    ContentValures values=new ContentValues();//创建可存操作键-值对的对象 
    values.put(“name”,”张三”);//存放姓名数据 
    values.put(“phone”,”11315577889”);//存放电话数据 
    //实例化SQLiteDatabase对象 
    SQLiteDatabase db= this.getWritableDatabase(); 
    db.insert(“person”,null,values);//插入values 中保存的数据 
    4.int delete(TABLE_NAME,String where,String []whereValue)
    作用:删除记录。
    参数TABLE_NAME:表名
    参数where:删除的条件,相当于SQL语句中的where部分的SQL命令
    参数whereValue:符合where的数据。
    示例:
    delete("person","personId=?",new String[]{"1"});
    5.int update(TABLE_NAME,contentValues,String where,String[]where)
    作用:更新记录
    参数TABLE_NAME:表名
    参数contentValue:Map类型的数据,每组键值对代表一列及其该列的的数据
    参数where:更新的条件,相当于SQL中where部分的SQL命令
    参数whereValue:符合参数where的数据。该值是String{}类型的数组
    示例:更新当前数据库的person表中,personId=1的记录
    ContentValues values=new ContentValues();
    values.put("name","李四");
    values.put("phone","13133313135");
    SQLiteDatabase db=this.getWritableDatabase();
    db.update("person",values,"personId=?",new String[]{"1"});
    6.Cursor rawQuery(String sql,String []selectionArgs)
    作用:执行待占位符的SQL查询,返回的结果存放在Cursor对象中
    参数sql:查询的SQL命令
    参数selectionArgs:查询的条件数据
    提示:(1)若SQL中没有占位符,则第二个参数为null
          (2)对数据表进行变更操作时,使用execSQL,对数据表进行查询时,使用rawQuery方法
    7.Cursor query(table,projection,selection,selectionArgs,groupby,having,orderby)
    cursor:返回值类型,返回查询结果游标对象
    table:String,要查询的列名
    selection:String,查询条件
    selectionArgs:String[] 为selection 中的?补值的数组。 
    groupby : String,分组列的列名。 
    having:String,分组在查询的条件。 
    orderby:String 排序列的列名。 
    8.Context.openOrCreateDatabase(String dbname,int mode)
    作用:打开数据库,若数据库未创建则创建数据库
    参数mode:访问权限,有以下常量选择:
    1.MODE_PRIVATE   不允许其他应用程序访问本数据库
    2.MODE_WORLD_READABLE 允许其他应用程序读本数据库
    3.MODE_WORLD_WRITABLE  允许其他应用程序写本数据库
    4.MODE_APPEND  若数据库存在,则允许向数据库中添加数据


    ContentValue类
    1.概述 
    contentValues类包装了HashMap类,该类用于存取键值对,每一个键值对表示一列和该列的数据。
    2.常用方法:
    (1)ContentValues()
    作用:无参构造方法,创建一个内部成员变量为HashMap<String,Object>的对象。给成员变量名为mvalues.
    2.void put(String key,Object value)
    作用:向成员变量中存放键值对数据
    提示:value可以是Java的所有基本数据类型、数组、对象
    3.Object get(String key)
    作用:获取键名为key对应的值
    4.XXX getAsXXX(String key)
    作用:返回XXX类型的值
    提示:XXX可以是所有基本类型的包装类。如Integer,还有ByteArray等


    SQLLiteOpenHelper类
    1.概述:
    SQLiteOpenHelper类是Android提供的用于操作SQLite数据库的工具类,该工具类能方便地创建数据库,表以及管


    理数据库的版本
    2.常用方法:
    1.synchronized SQLiteDatabase getReadableDatabase();
    作用:以读的方式打开数据库对应的SQLiteDatabase类的对象
    2.synchronized SQLiteDatabase getWriteableDatabase();
    作用:以写方式创建或打开数据库对用的SQLiteDatabase类的对象
    3.abstract onCreate(SQLiteDatabase db)
    作用:首次创建数据库时,回调该方法。
    4.abstract onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
    作用:数据库版本更新时,回调本方法
    5.synchronized void close()
    作用:关闭所有打开的SQLiteDatabase对象


    Cursor类
    1.概述
    Cursor类存放查询的结果集,并提供了对结果集中记录进行访问的方法
    2.常用方法
    (1)move(int offset)
    作用:将指针向上(或向下)移动offset个记录。若offset值为正数则向下移动,若为负数则向上移动。
    (2)Boolean moveToFirst()
    作用:指针移至结果集的第一个记录。若移动成功则返回true,否则返回false。
    (3)Boolean moveToLast()
    作用:指针移至结果集的最后一个记录。若移动成功则返回 true,否则返回 false。 
    (4)Boolean moveToNext();
    作用:指针向后移动一条记录,若移动成功则返回true,否则返回false
    (5)Boolean moveToPrevious
    作用:指针向后移动一条记录,若移动成功则返回true,否则返回false
    (6)isLast();
    作用:若指针在Cursor结果集的最后一条记录,则返回true,否则返回false
    (7)isAfterLast();
    作用:若指针在Cursor结果集的最后一条记录之后,则返回true,否则返回false
    (8)isFirst();
    作用:若指针在 Cursor结果集的第一条记录,则返回 true,否则返回 false。 
    (9)isBeforeFirst(); 作用:若指针在Cursor结果集的第一条记录之前则返回true,否则返回false
    (10)isClose();
    作用:Cursor对象是否关闭,若关闭则返回true,否则返回false
    (11)int getColumnIndex(String columnName)
    作用:获得指定列的索引值
    参数columnName:指定列的列名
    示例:
    int  index=cursor.getColumnIndex("name");
    (12)String getString(int columnIndex)
    参数columnIndex:指定列的索引值


    一、概述
    Notification是Android中一个重要的系统服务,Notification显示通知信息在手机状态栏,手机状态栏位于屏幕


    最上方,那是通常显示手机当前的网络状态、电池状态、时间、短信等。Notification代表的是一种具有全局效果


    的通知,Notification对象要通过NotificationManager(通知管理器类)的对象来发送。
    二、Notification类
    1.概述
    Notification类用于存放通知标题、内容、图标以及发送的目标对象
    2.常用属性
    1).icon:通知的图标,示例代码
    Notification notif=new Notification();
    notif.icon=R.drawable.ic_launcher;
    说明:第一行创建通知对象,第二行设置通知对象的icon图标,该图标用了Android默认的图标,也可以是其他图



    2).tickerText:通知标题,该标题显示在窗口的状态栏中
    示例代码:notif.tickerText="通知来啦";
    3).flags:设置通知状态的标志值,有如下可选常量值:
    FLAG_AUTO_CANCEL :在通知栏点击此通知后被清除
    FLAG_INSISTENT:重复发出声音,直到用户响应此通知
    FLAG_ONGOING_EVENT :将此通知放大通知栏的“OnGoing”即“正在运行”组中
    FLAG_NO_CLEAR:禁止手动清除此通知
    4).defaults:设置通知的默认属性,默认属性包括声音提示,震动提示,闪光提示等
    DEFAULT_SOUND :默认声音
    DRFAULT_VIRBATE:默认震动
    DEFAULT_LIGHTS:默认闪光灯
    ALL:使用默认声音,默认闪光灯和默认震动
    5).public int contentIntent:存放一个PendingIntent类型的对象
    6).public int contentView:存放一个RemoteViews类型的对象。使用该对象可以在状态栏显示如下载进度这类


    的效果
    3.创建通知对象
    通知对象可以入以上所示创建,再设置对象的图标、标题等属性,也可以按如下方法创建
    Notification notification=new Notifiication(R.drawable.icon,"通知来啦",System.currentMills());
    4.常用方法:
    public void setLatestEventInfo(Context context,String title,String content,PendingIntent pi)
    作用:蛇蛇点击通知的响应事件
    参数context:当前的Activity对象
    参数title:通知的标题,该标题不是上述的窗口状态栏的标题
    参数content:通知的详细内容
    NotificationManager类
    1.概述
    NotificationManager类是通知管理器对象,用于发送、取消通知
    2.常用方法:
    1.notify(int id,Notification notification)
    作用:发送一个通知
    参数id:通知的id值,用于区别其他通知
    参数notification:一个已经创建的Notification对象
    2.cancel(int );
    作用:关闭指定索引值的通知。
    示例:notifyManager.cancel(100);


    PendingIntent类
    一.概述
    1.PendingIntent用于描述Intent及其最终的行为
    2,PendingIntent对象可以递交给其他应用程序,然后继续处理。这样可稍后才处理PendingIntent中描述的Intent


    及其最终行为。
    二.常用方法:
    1.public static PendingIntent getActivity(Context context,int requestCode,Intent intent,int flags)
    作用:从系统取得一个用于启动目标(intent中设置)Activity的PendingIntent对象
    参数context:当前组件的对象
    参数requestCode:请求码,用0即可
    参数intent:Intent对象,用于指定通知的目标组件
    参数flags:设置通知的类型,有两个常用的可选值:
    1)FLAG_CANCEL_CURRENT:设定在提取PendingIntent时,先关闭之前的PendingIntent实例,这样得到的


    PendingIntent就是新的了
    2)FLAG_UPDATE_CURRENT:设置新的Intent更新之前的PendingIntent中的Intent对象数据,例如更新Intent中的


    Extras.
    提示:本应用中设置flags值为0即可
    2.public static PendingIntent getService(Context context,int requestCode,Intent intent,int flags



    作用:从系统中取得一个用于启动目标Service中的PendingIntent对象
    3.public static PendingIntent getBroadCast(Context context,int requestCode,Intent intent,int flags)
    作用:从系统中取得一个用于启动BroadcastReceiver的Intent广播的PendingIntent对象


    Service概述
    1.Service是Android的四大组件之一
    2.Service本质上是后台运行的可执行程序,他没有Activity那样的用户界面
    3.Service通常不予用户交互,不能自己启动、运行在后台
    4.Service主要应用于长时间的后台服务以及周期性的后台监控,如:
    (1)播放多媒体的时候启动了其他Activity,此时程序要在后台继续播放
    (2)检测SD卡上大文件的变化
    (3)在后台记录手机当前的地理信息位置的改变
    (4)在后台完成下载大文件等等
    5.Service具有更高的进程优先级,在Android中进程有五个优先级别,Service位于第三个级别
    6.Service具有全局唯一性。同一时间系统内只能存在一个Service实例
    7.Service必须要在项目清单中注册才能使用,这一点与Activity相同
    8.Android服务分为系统服务与应用服务
    1)系统服务:Android提供了大量的系统Service,如:MediaService用于媒体录制、播放的系统服务
    2)应用服务由开发者自己定义
    二、启动、停止、绑定Service的相关方法
    1.public ComponentName startService(Intent service)
    作用:启动指定的Service
    参数service:Intent对象,在该意图对象中指定启动的目标Service。启动方法有显式意图和隐式意图两种
    说明:
    1)startService()在ContextWrapper类中定义,该类的父类是Context
    2)ContextWrapper类是Activity的间接父类和Service的直接父类
    示例代码
    Intent intent=new Intent(启动方组件的对象,目标Service.class)
    startService(intent);
    2.public boolean stopServices(Intent service)
    作用:停止指定的service
    说明:stopService方法是在ContextWrapper类中定义
    3.public final stopSelf()
    作用:在Service内部停止Service。
    说明:stopSelf方法的执行不会会带哦Service中的onDestroy方法
    4.bindService(Intent intent,ServiceConnection conn,int flags)
    作用:与Intent指定的目标Service绑定
    (1)参数intent:启动intent对象中指定的目标Service
    (2)参数Conn:是一个实现了ServiceConnection接口的对象,该对象用于监听访问者与Service之间的绑定情况


    。当绑定成功时,将调用ServiceConnection对象的onServiceConnected方法
    (3参数flags:指定绑定时是否自动创建Service。值为0只不自动创建Service,当只是BIND_AUTO_CREATE常量(


    值为1)时,自动创建Service。
    5.ServiceConnection接口
    作用:用于监听访问者与Service之间的连接情况,该接口定义了两个方法:
    (1)onServiceConnected(ComponentName name,IBinder service)
    作用:绑定成功时触发本方法
    参数name:被绑定的Service的一个实例
    参数Service:通过被绑定的Service类中的onBind方法返回一个IBind类型的对象。
    (2)onServiceDisconnected(ComponentName name)
    作用:绑定出现异常、应用程序出现崩溃时触发本方法
    6.UnBindService(ServiceCoonnection conn)
    作用:解除与Conn相关联的Service的绑定
    参数Conn:是一个实现了ServiceConnection接口的对象
    说明:unbindService执行后,系统会回调Service的onDestroy()方法。


    Service的生命周期
    startService()启动服务的执行流程
    1.其他组件(例如Activity)调用startService();
    2.服务被创建后,回调onCreate(),执行初始化操作
    3.onStartCommand()方法被触发,服务处于运行状态
    4.当执行stopService()后,将结束SErvice对象,同时onDestroy()方法被触发
    提示:
    如果Service还没有运行,则Android先调用onCreate()然后调用onStartCommand();
    如果Service已经运行,则只调用onStartCommand(),所以一个Service的onStartCommand方法可能被重复滴啊用多


    次,但onCreate方法只调用一次。
    bindService()绑定的执行流程
    onCreate()->onBind()->onUnbind->onDestroy()
    1.若Service未启动,则创建Service,回调onCreate(),若Service已启动,则跳过本步骤。
    2.onBind()被触发,并返回一个IBinder的实现类对象,该对象将在绑定的组件中被获得
    3.onUnbind()在在unbindService()方法执行后触发
    4.onDestroy()在Service被关闭是触发,在onDestroy方法中释放战友的系统资源,如结束Service中的线程,注销


    各种监听器,注销广播接收者等。


    再绑定的执行流程
    Service在绑定后,可以解除绑定后,再次绑定。但前提是首次绑定时,该Service已存在,基:只有在单纯绑定的


    情况下,解除绑定,才能再次绑定。若是绑定的同时启动Service,则解除绑定时,该Service将被关闭
    执行流程:onBind()->onUnbind()->onRebind()


    进程的优先级
    (一)概述
    Android规定,进程的优先级分为以下五个级别
    1.Active Process
    2.Visible Process
    3.Started Service Process
    4.BackGround Process
    5.Empty Process


    Active Process
    前台进程是包含(与用户交互的)控件的那种应用程序。这些是Android通过回收资源来极力保护的进程,Active 


    Process包括:
    (1)处于active状态的Activity,他们运行在前台来处理用户的响应事件。
    (2)Activity Service或者正在执行onReceive事件处理函数的Broadcast Receiver
    (3)进程中包含正在与用户交互的activity绑定的service
    (4)进程中包含执行了startForeground()的Service
    (5)进程中包含了正在执行的onCreate,onStartCommand()或onDestroy方法的Service.


    Visible Process
    可见但不活动的进程是那些拥有"可见Activity"的进程。可见Activity是那些在屏幕上可见,比不是在前台或不响


    应用户事件的Activity。这种情况发生在当一个Activity被部分遮盖的时候。可见进程只在极端的情况下,才会被


    杀死保证Active Process的运行。包括以下情况:
    (1)可见的Activity,但处于onPause()状态
    (2)被可见Activity绑定的Service


    Service Process
    进程中包含已经启动的Service。Service以动态的方式持续运行但没有可见的界面。因为Service不直接和用户交


    互,它们拥有比Visible Process较低的优先级。它们还是可以被认为是前台进程,不会被杀死


    Background Process
    进程中的Activity不可见和进程中没有启动任何的Service,这些进程都可以看作是后台进程。在系统中,拥有大


    量的后台进程,在内存资源紧张时,Android按照后看见先杀掉的原则杀掉后台进程以获取资源给前台进程。


    Empty Process
    当一个进程被杀掉,进程保留,变成空进程
    空进程中所有的组件都被关闭掉,但为了改善整个系统的性能,Android会在内存中保留那些已经走完生命周期的


    应用程序。Android维护这些缓存来改善应用程序重新启动的时间。但这些进程在资源需要的时候常常被杀掉。


    Service的应用场景
    在开发Android应用项目中,若将耗时操作放在Activity的线程中(如播放音乐),将可能出现以下情况:
    因接电话操作,使得应用程序成为后台进程(Activity被完全遮盖)或成为空进程(应用程序的所有Activity被关


    闭)。虽然这是进程中的线程仍然在工作,如播放音乐,但空进程或后台进程在系统内存匮乏时,容易被系统彻底


    杀掉,这样线程也将被停止。
    为避免这种问题出现,将应用程序中的耗时操作放在Service中进行,由于Service的进程优先级为第三季,要高于


    第四级、第五级的后台进程和空进程,将大大降低耗时操作被系统杀掉的几率。


    Service的绑定原理
    (一)概述
    1.一个Activity与一个Service只能绑定一次
    2.一个Activity可以同时绑定到多个Service
    3.一个Service亦可以同时被多个Activity绑定
    4.当绑定到一个Service,且将flag设置为BIND_AUTO_CREATE时,如果该Service为创建,则先创


    建Service(onCreate),然后再绑定,如果Service已经被创建,则直接绑定
    5.如果Service被多个Activity绑定,只有当所有的客户端都解除绑定时才能被destroy
    6.Binder类用于客户端与服务端的通信,Binder的意思是别针,可以理解为用Binder将服务端与客户端"别"在一起



    (二)绑定组件时数据传值分析
    以下代码展示绑定的Service的类结构
    public class TargetService extends Service{
    private int mCount;
    private int inc(){
    mCount++;
    return mCount;
    }
    public class InfoBind extends BInder{
    public int count(){
    return inc();
    }
    }
    public IBinder onBind(Intent arg0){
    InfoBind bind=new InfoBind();
    return bind;
    }
    }
    说明:
    以上代码中的InfoBind是TargetService的一个内部类,该类只是示范,类名不一定非是InfoBind,该类是Binder


    类的子类。而Binder类实现了IBinder接口。因此在onBind方法中可以返回InfoBind对象。
    当然,可以在onBind方法的return命令后面直接写new IBinder(),机返回接口的一个实现类的对象,但要实现


    接口所有声明的方法。
    Android框架提供了Binder类,该类实现了IBInder接口中的方法,由于这些方法是被系统调用,应用开发时无需调


    用。因此,以上的InfoBinder类通过继承Binder,间接实现了IBinder接口,则onBInd方法允许返回InfoBind对象
    实际开发中通常采用以上方式返回一个对象。该对象中可以自定义与业务需求相关的方法,这些方法可以返回当前


    Service的一些数据。
    onBind()方法返回的对新爱国将在onServiceConnected()方法的第二个参数service获得,基service中存放了


    onBind()放回的对象的地址。
    (三)bindService与startService对比
    1.通过多次调用startService,每次都触发Service类中的onStartCommand(),并用Intent对象向Service传递不同


    的数据,从而达到控制Service。但这种方式不太方便。
    2.通过绑定的方式,使用onBind传给ServiceConnection的IBinder控制Service。这种方式比较便捷,实际开发中


    推荐使用这种方式。
    (四)关于onServiceDisconnected方法的调用
    1.当ACtivity与Service绑定时,若出现进程崩溃,则onServiceDisconnected方法将被触发
    2.当进程被终止时,将触发onServiceDisconnected方法
    3.onServiceDisconnected方法有Android系统调用,不要程序触发,不要在此方法中编写业务逻辑代码。


    隐式意图启动Service
    1.使用隐式意图可以启动另外一个应用程序的服务,从而实现跨进程启动服务
    2.在项目的清单文件中注册Service时,通过添加Intent-filter,允许设备中其他应用程序通过隐式意图来启动本


    程序中的Service
    提示:
    若不想让其他应用程序启动本应用的Service,则不要在Service标签中声明Intent-filter。


    BroadcastReceiver
    BroadReceiver顾名思义就是广播接收者,用于接收来自系统和应用中的广播。
    Android系统中,广播的应用非常广泛,例如:开机完成后会产生一条广播,接收到这条广播就能实现开机启动服


    务的功能;当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电


    池量改变时,系统会产生一条广播,接收到这条广播告知用户及时保存进度,等等。
    Android中的广播机制设计的非常出色,很多事情原本需要开发者亲自操作的,只需等待广播告知自己就可以了,


    大大减少了开发的工作量和开发周期。
    Android系统的广播模拟广播电台的广播机制,首先由一方发送广播,其次定义一个BroadcastReceiver对象,该对


    象用来接收发送的广播
    Android用字符串模拟电台的频率,当出现一个广播时,同时在Intent对象中设置Action字符串,该字符串可看作


    是该广播的频率。
    广播接收者需要Activity和Service那样在项目清单中注册,注册的方式与intent.action的字符串值相同。这样发


    送和接收者通过Action属性就处在同一频段上了。
    BroadcastReceiver是抽象类。


    注册BroadcastReceiver
    注册BroadcastReceiver有两种方式
    1.在项目清单文件中注册
    示例代码:
    <receiver android:name=".ReceiverAndroid">
    <intent-filter>
    <action android:name="liuke.RECEIVE_ANDROID"/>
    <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
    提示:
    在项目清单中注册的广播时常驻型的。当应用关闭后,如有广播消息传来,Receiver也会被系统调用而自动执行。
    2.Java代码中注册
    MyReceiver receiver=new MyReceiver();//创建一个自定义的MyReceiver类的一个对象
    IntentFilter filter=new IntentFilter();
    filter.addAction("liuke.mybroadcast");
    regeisterReceiver(receiver,filter);
    说明:
    1.以上代码的第2,3行相当于<intent-filter>标签的功能,创建过滤器并设置过滤条件
    2.第4行是注册广播接收者
    提示:
    1.registerReceiver是android.content.ContextWrapper类中的方法,Activity和Service都继承了


    ContextWrapper,所以可以直接调用。在实际应用中,若在Activity或Service中注册了一个BroadcastReceiver,


    当这个Activity或Service被销毁时如果没有解除注册,系统会报一个异常,提示是否忘记解除注册了。所以,建


    议在onStop、onDestroy等生命周期方法中将代码注册的广播解除注册,如下代码所示
    protected void onStop(){
    super.onStop();
    unregiter();
    }
    private void unregister(){
    if(MyReceiver!=null){
    unregisterReceiver(mReceiverJava);
    mReceiverJava=null;
    }
    }
    2.代码注册方式与项目清单中注册不同,它不是常驻型的,即广播岁组件的生命周期。


    常用方法:
    1.Boolean isOrderedBroadcast()
    作用:若是有序广播则返回true,若是无序广播则返回false
    2.absortBroadcast();
    作用:终止广播,优先级低的广播接收者将无法得到当前的广播
    3.setResultExtras(Bundle bundle)
    作用:修改数据并发送给优先级低的接收者
    示例代码:
    Bundle bundle=new Bundle();
    bundle.putString("msg","第二个广播接收者接到了广播")
    setResultExtras(bundle);
    以上代码将字符串一键值对的形式保存在Bundle对象中,并通过setResultExtras方法发送给优先级低的广播接收


    者。
    void setResultData(String data)
    作用:重载的方法
    参数data:发送给优先级低的接收者的字符串
    4.int getResultCode();
    作用:返回前一个接收者返回结果码
    5.String getResultCode();
    Bundle getResultExtras(boolean markMap)
    作用:获取优先级高的接收者发送的数据
    参数markMap:值为true时,当上一个将广播接收者的setResultExtras参数为null,则getResultExtras获取的


    Bundle对象是一个有地址但内容为空的集合
    markMap值为false时,则getResultExtras获取的对象是null。
    6.public abstract void onReceive(Context context,Intent intent)
    作用:该方法必须有子类实现,当接收到广播时将回调本方法。
    参数context:发送广播的组件对象
    参数intent:发送广播的组件对象发送过来的Intent对象。该题图对象可封装传递过来的数据
    提示:onReceive方法中不能执行超过10秒的耗时操作,否则将出现异常。


    四、相关方法:
    1.public void sendBroadcast(Intent intent)
    作用:发送广播。
    说明:
    该方法是android.content.ContextWrapper类中的方法,带一个Intent类型的参数
    2.public Intent registerReceiver(BroadcastReceiver receiver,Intentfilter filter)
    作用:注册广播接收者
    参数receiver:被注册的广播接收者对象
    参数filter:封装action的过滤器对象
    3.public void unregisterReceiver(BroadcastReceiver receiver)
    作用:取消广播接收者的注册
    提示:registerReceiver()和UNregisterReceiver()均是用于Java代码注册广播接收者。


    IntentFilter类
    1.概述:
    IntentFilter类用于代码注册广播接收者,该类的作用相当于项目清单文件中的<Intent-filter>
    2.常用方法:
    public final void addAction(String action)
    作用:向过滤器对象添加action属性
    参数action:该属性相当于清单文件中<intent-filter>标签中的<action>子标签


    有序广播
    1.概述
    Android提供了两种发送广播的方式,一种是发送无序广播,第二种是有序广播。两种广播的特点是:
    无序广播的效率较高,所有符合条件的广播接收者都能接收到广播。
    有序广播:接收广播有先有后,先后次序通过设置优先级来区别。在项目清单文件中通过以下代码完成优先级的设


    定:
    <intent-filter anrdoid:priprity="10">
    以上设置了优先级为10.那么比10小的广播接收者将在当前广播接收者之后接收发到的有序广播。
    有序广播的另一个特点是优先级高的广播接收者可以修改发送广播中的数据,并可以传递给优先级低的接收者。优


    先级高的接收者可以终止优先级低的接收者发送,即终止广播。
    2.常用属性:
    1).Priority属性:
    作用:该属性设置广播的优先级别,在项目清单中注册receiver时定义。取值范围-1000——1000,实际的值可以


    超过该范围。
    2).示例代码:
    <receiver android:name=".FirstReceiver">
    <intent-filter android:priority="20">
    <action android:name="liuke.Receiver">
    </intent-filter>
    3.常用权限
    1)android.permission.RECEIVE_SMS
    作用:申请接收短信的权限
    示例代码:
    <uses-permission>
    android:name="android.permission.RECEIVE_SMS"
    </uses-permission>
    2)android.permission.PROCESS_OUTGOING_CALLS
    作用:申请播出电话的权限
    示例代码:
    <uses-permission>
    android:name="android.permission.PROCESS_OUTGOING_CALLS"
    </uses-permission>
    3)android.permission.RECEIVE_BOOT_COMPLETED
    作用:设置开机自动启动权限
    示例代码:
    <uses-permission>
    android:name="android.permission.RECEIVE_BOOT_COMPLETED"
    </uses-permission>
    4.发送有序广播
    context对象的sendOrderedBroadcast(intent,permission)
    5.接收有序广播
    1)优先级高的接收器先接受
    2)相同优先级,动态注册的接收器先接收
    3)相同优先级的静态注册的接收器,先注册的先接收


    常用方法:
    1.Boolean isOrderedBroadcast()
    作用:若是有序广播则返回true,若是无序广播则返回false
    2.absortBroadcast();
    作用:终止广播,优先级低的广播接收者将无法得到当前的广播
    3.setResultExtras(Bundle bundle)
    作用:修改数据并发送给优先级低的接收者
    示例代码:
    Bundle bundle=new Bundle();
    bundle.putString("msg","第二个广播接收者接到了广播")
    setResultExtras(bundle);
    以上代码将字符串一键值对的形式保存在Bundle对象中,并通过setResultExtras方法发送给优先级低的广播接收


    者。
    void setResultData(String data)
    作用:重载的方法
    参数data:发送给优先级低的接收者的字符串
    4.int getResultCode();
    作用:返回前一个接收者返回结果码
    5.String getResultCode();
    Bundle getResultExtras(boolean markMap)
    作用:获取优先级高的接收者发送的数据
    参数markMap:值为true时,当上一个将广播接收者的setResultExtras参数为null,则getResultExtras获取的


    Bundle对象是一个有地址但内容为空的集合
    markMap值为false时,则getResultExtras获取的对象是null。
    6.public abstract void onReceive(Context context,Intent intent)
    作用:该方法必须有子类实现,当接收到广播时将回调本方法。
    参数context:发送广播的组件对象
    参数intent:发送广播的组件对象发送过来的Intent对象。该题图对象可封装传递过来的数据
    提示:onReceive方法中不能执行超过10秒的耗时操作,否则将出现异常。


    粘滞广播
    1.概述:
    粘滞广播:发出的广播会一直滞留(等待),以便有人注册这则广播消息后能够尽快的收到这条广播。其他功能与


    sendBroadcast相同。
    提示:
    使用sendStickyBroadcast发送广播,需要获得BROADCAT_STICKY permission,如果没有这个permission则会抛出


    异常。
    2.广播的发送与移除
    1)发送:context对象的sendStickyBroadcast(inttent)
    2)移除:context对象的removeStickyBroadcast(intent)


    ContentProvider
    一、概述
    ContentProvider-内容提供器,是应用程序之间共享数据的接口,Android系统将这种机制应用到方方面面。比如


    :联系人提供器专为不同应用程序提供联系人数据
    ContentProvider是Android四大组件之一,Android提供了四种数据存取技术。这四种技术都能由ContentProvider


    提供的方法按统一的代码格式存取,即以一致性的方法操作多样性的数据源。
    ContentProvider机制可支持在多个应用中存储和阅读数据。这是跨应用共享数据的重要方式。
    Android提供了预定义的ContentProvider用于方便操作内存卡和SD可上存储的音频、视频图片和私人通讯录等。
    ContentProvider是一个抽象类,必须继承,由子类实现该类中的抽象方法。
    ContentProvider类中定义了insert(),delete(),update(),query()等抽象方法用于外部应用程序操作数据提供增


    删改查的操作。
    ContentProvider类中定义了供外部应用操作数据的地址:Uri
    外部应用程序通过ContentResolver类,按Uri的地址操作ContentProvider暴露的数据。
    二、系统预定义ContentProvider的使用步骤
    步骤1:获取系统预定义的Uri
    步骤2:在应用程序中获取ContentProvider类的对象,调用该类的增删改查的方法
    通过步骤一获取的系统预定义的uri地址操作数据
    提示:contentResovler类相当于操作数据的中间件,该类中提供了增删改查等方法用来操作ContentProvider所暴


    露的数据。
    三、ContentProvider类的抽象方法
    1.public Cursor query(Uri uri,String []projection,String selection,String []selectionArgs,String 


    sortOrder)
    作用:查询指定数据地址的数据源,返回的Cursor类型的结果集。
    参数Uri:采标被操作数据的地址
    参数projection:查询表中指定列,若是所有列的都查询,则写为null
    参数selection:查询条件(SQL字符串),若没有条件,则写为null;
    参数selectionArgs:若上一个参数(查询条件)中有占位符,则本参数提供与占位符对应的数据。
    参数sortOrder:排序方式,null表示默认的排序方式(升序)
    2.public String getType(Uri uri)
    作用:返回当前Uri代表的数据的MIME类型
    3.public Boolean onCreate()
    作用:在ContentProvider对象创建后第一个调用的方法
    4.public Uri insert(Uri uri,ContentValues values)
    作用:根据Uri的地址,向指定文件或数据库中插入values中的数据。并返回该记录的_id值,该列是主键并且是自


    动增长的标识列
    5.public int delete(Uri uri,String selection,String[] selectionArgs)
    作用:删除Uri中指定条件的数据
    参数selection:指定条件的SQL字符串
    参数selectionArgs:若selection的条件中有占位符,则本参数提供填充占位符的数据
    返回值:返回删除的行数
    6.public int update(Uri uri,ContentValues values,String selection,String []selectionArgs)
    作用:根据Uri,按selection指定的条件将数据修改为values
    参数selectionArgs:若selection的条件中有占位符,则本参数提供填充占位符的数据
    返回值:返回修改的行数
    四、Uri
    (一)概述
    Uri是Uniform Resource Identifier的简称,Web的资源,如HTML文档等都用一个通用资源标识符来定位
    (二)Uri的结构
    Uri的结构类似于网址,如下:
    scheme://authority/path
    1.scheme协议:Android规定:在数据共享时,协议名必须是content。
    2.authority授权:唯一标识ContentProvider的字符串,该字符串在Android应用程序中必须唯一
    与网站类似,每个ContentProvider都会使用字符串注册自己,该字符串类似于网站的域名没在Android中称为授权


    ,该授权必须唯一,ContentProvider以该字符串作为向外部提供数据访问的基础Uri。
    3.路径(或资源索引值):通过索引值查找指定的资源。
    content://com.ityw.myContentProvider/books
    表示该ContentProvider提供了操作若干书籍的地址,这里
    content://是android操作数据的scheme
    com.ityw.myProvider是authority
    books表示一个存放书籍的集合
    content://com.ityw.myProvider/books/#20
    表示数据集合中索引值是20的一本书
    五、MIME
    (一)概述
    MIME的英文全称是"Multipurpose Internet Mail Extensions"多用途互联网邮件扩张,他是一个互联网标准,在


    1992年最早应用于电子邮件系统,后来应用到浏览器。服务器将MIME标志符放入传送的数据中来告诉浏览器使用哪


    些插件读取相关文件。
    MIME类型:设定某种扩展名的文件用一种应用程序来打开,当该扩展名被访问的时候,Android会自动使用指定应


    用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
    MIME包含两部分:类型和子类型,中间有/分隔,以下是主要的MIME类型
    text/html
    text/css
    images/jpeg
    application/pdf
    application/vnd.ms-excel
    如果供应商具有专有的数据格式,那么子类型名称将以vnd开头,例如:
    application/vnd.ms-excel
    如果是非专用的数据格式,则无需vnd开头
    application/pdf
    (二)Android的MIME
    Android遵循类似的约定来定义MIME类型。Android类型中的vnd表示这些类型和子类型具有非标准的、供应商专用


    的格式
    为确保唯一性,Android的MIME使用了类型/子类型的格式
    Android使用两种格式分别表示多条记录和单条记录
    1.单挑记录的MIME格式类似于以下格式
    vnd.android.cursor.item/vnd.companyname.contenttype
    提示:
    单条记录的主类型必须是vnd.android.cursor.item,子类型若是自定义的专用类型则必须是vnd开头,后面的字符


    串自行定义。
    2.多条记录的MIME格式类似于以下格式:
    vnd.android.cursor.dir/vnd.com[anyname.contenttype
    提示:
    多条记录的主类型必须是vnd.android .cursor.dir,子类型若是自定义自定义的专用类型则必须是vnd来头,后面


    的字符串自行定义。
    说明:
    1)MIME类型在你Android中使用非常广泛,尤其在Intent中,系统通过MIME类型决定调用响应的Activity处理数据
    2)MIME类型通过ContentProvider的Uri得出
    六、ContentResolver类
    (一)概述
    ContentResovler类相当于操作数据的中间件,该类中提供了与ContentProvider类中相同的增删改查的方法。在外


    部操作ContentProvider所暴露的数据。
    只要获得了ContentProvider类中定义的Uri,就能调用ContentProvider类中的同名方法操作ContentProvider所暴


    露的数据。
    (二)常用方法
    1.public Uri insert(Uri uri,ContentValues values)
    作用:根据Uri提供的地址,向指定数据源(数据库、文件等)插入values中的数据。
    说明:
    Uri是ContentProvider类在项目清单文件中注册时的authority属性的值。
    2.public int insert(Uri uri,String selection selection,String[] selectionArgs)
    作用:根据Uri提供的地址,调用ContentProvider类中的同名方法,在指定数据库(或文件)中删除selection中


    指定条件的记录。
    3.public int update(Uri uri,ContentValues values,String selection,String [] selectionArgs)
    作用:根据Uri提供的地址,调用ContentProvider类中的同名方法,在指定数据库(或文件)中按selection指定


    的条件将数据修改为values中的值
    4.public Cursor query(Uri uri,String[]projection,String selection,String[]selectionArgs,String 


    sortOrder)
    作用:根据Uri提供的地址,调用ContentProvider类中的同名方法,在指定数据库中查询出所有匹配selection的


    数据,其中projection是要查询列名表,指定查询的数据列。
    (三)获取ContentResolver对象
    ContentResolver ContentResolver=this.getContentResolver();
    说明:this是当前组件对象,例如:Activity类的对象。
    七、通过预定义的Uri操作多媒体数据
    (一)概述
    Android为应用程序操作常见的数据提供了预定义的Uri,如视频、音频、图片、文件、数据库等。
    Android中的以上数据可能存放在内存卡中,也可能存放在SD卡。并且很可能存放在不同文件夹中。通过Uri首先可


    以指定要访问的是哪种类型的数据,其次指定访问的数据是在内存卡还是在SD卡,最后通过条件查找符合条件的数


    据。至于那个文件夹则无需考虑。
    以上访问数据的方式提供了对不同类型的数据一致性的操作,有效地简化了编程
    (二)操作图片数据
    1.访问SD卡图片的Uri
    android.provider.MeadiaStore.Images.Media.EXTERNAL_CONTENT_URI
    2.访问内存卡中的图片Uri
    android.provider.MediaStrore.Images.Media.INTERNAL_CONTENT_URI
    3.MediaStore类
    MediaStore类是Android多媒体开发常用类,该类位于android.provider包下,该类中定义了Images、Audio、


    Video等内部类,用于表示图片、音频和视频。
    4.Images类
    Images类是MediaStore类的内部类,用于表示图片数据。该类中定义了一组常量用于表示图片的属性。
    5.Images.Media的常量
    1)Images.Media._ID:图片的id值,该值由系统创建
    2)Images.Media.DISPLAY_NAME:图片的显示名
    3)Images.Media.DESCRIPTION:图片的详细描述
    。。。。
    6.Thumbnails
    Thumbnails类用于获取系统中的视频或图片的缩略图。该类提供了以下方法获取缩略图:
    public static Bitmap getThumbnail(ContentResolver cr,long origId,int kind,BitmapFactory.Options 


    options)
    作用:获取指定id的缩略图
    参数-cr:ContentResolver对象
    参数origId:图片的Id值
    参数-kind:缩略图的类型,有两个常量:
    Thumbnails.MICRO_KIND:最小类型
    Thumbnails.MINI_KIND:迷你类型
    参数:options:BitmapFactory.Options类型用于预防图片过大造成内存溢出,若不考虑溢出问题,则此处可设置


    为null。
    (三)操作音频数据
    1.访问SD卡音频的Uri
    android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
    2.访问内存卡音频的Uri
    android.provider.MediaStore.Audio.Media.IMTERNAL_CONTENT_URI
    3.Audio.Media的常量
    1)Audio.Media._ID:音频的id值,该值由系统创建
    2)Audio.Media.DISPLAY_NAME:音频的显示名
    。。。。
    (四)操作视频数据
    1.访问SD卡视频的Uri
    android.priovider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI
    2.访问内存卡视频的Uri
    android.provider.MediaStore.Video.Media.INTERNAL_CONTENT_URI
    3.Video.Media类的常量
    1)Video.Media._ID:视频的id
    2)Video.Media.ALBUM:视频所在的专辑
    。。。
    八、解决模拟器中文乱码问题
    在Android模拟器中,默认情况下使用ISO-8859-1对中文进行编码/译码,而Windows系统默认的编码是GBK。
    从模拟器的SD卡读到的数据是按Windows的GBK解码的,所以会出现乱码问题
    因此在程序中要将获取的中文字符串先编码为ISO8859-1的字节数组,然后该字节数组解码为GBK。示例代码:
    String title=c.getString(c.getColumnIndex(Audio.Media.TITLE));
    title=new String(title.getBytes("iso8859-1"),"GBK");

















    展开全文
  • Android学习之路笔记

    2018-03-08 12:49:10
    最近是看了下大二上学期的时候学习Android的一些笔记资料,觉得以前是写的还有需要改进的地方,所以准备再重新整理整理发出来。学习Android的时候,是采用的视频+文档结合的方式。书呢看的是:郭大神的《第一行代码...

    这里写图片描述

    最近是看了下大二上学期的时候学习Android的一些笔记资料,觉得以前是写的还有需要改进的地方,所以准备再重新整理整理发出来。学习Android的时候,是采用的视频+文档结合的方式。书呢看的是:郭大神的《第一行代码》。所以本篇笔记还是按照郭大神的目录来进行记录的。

    第一章:了解全貌——Android王国简介

    1、Android的四层架构:Linux内核层、系统运行库层、应用框架层和应用层
    2、Android的四大组件:活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)、内容提供器(Content Provider)
    3 、使用res目录下的资源的两种方式

    例如:
        使用Res/values/string.xml文件,内容如下   
    <resources>
        <string name="app_name>HrlloWorld</string>
    </resources>
    在代码中采用R.string.app_name获得HelloWorld的引用
    在XML中通过@string/app_name获得引用
    

    4、app下的build.grade文件分析

    apply plugin: 'com.android.application'   应用的插件,有两种值可以选,com.android.application 表示这是一个应用程序模块,com.android.library表示这是一个库模块。
                                             应用程序模块和库模块的最大区别是应用程序模块是可以直接运行的,而库模块只能作为代码库依赖于其他应用程序模块来运行。
    android
       compileSdkVersion 25   项目编译的版本
       buildToolsVersion "25.0.2"   项目构建工具的版本
       defaultConfig {  对项目的更多细节进行配置
           applicationId "com.example.dimple.alertdialog"   指定项目的包名
           minSdkVersion 25    指定项目最低兼容的安卓版本
           targetSdkVersion 25    表示Android25上已经做了充分的测试,
           versionCode 1      指定项目的版本号
           versionName "1.0"    指定项目的版本名
           testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
       }
       buildTypes { 用于指定安装文件的相关配置
           release {
               minifyEnabled false   表示是否对项目进行代码混淆
               proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  指定混淆的时候使用的规则文件
           }
       }
    }
    ​
    dependencies {  指定当前项目的依赖关系,Android中一共有三种依赖方式:本地依赖、库依赖、远程依赖。
                    本地依赖可以对本地的jar包或者目录添加依赖关系,库依赖可以对项目中的库模块进行添加依赖关系。
                    远程依赖可以对jcenter库上的开源项目添加依赖关系。
       compile fileTree(dir: 'libs', include: ['*.jar'])   本地依赖声明,表示将lib目录下的所有的jar文件添加到项目的构建路径中
       androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
           exclude group: 'com.android.support', module: 'support-annotations'
       })
       compile 'com.android.support:appcompat-v7:25.3.0'   标准的远程依赖库格式,其中“com.android.support”是域名部分,用于和其他公司的库作区分。
                                                            “appcompat-v7”是组名称,用于和同一个公司的不同的库作区分。“25.3.0”是版本号,用于和同一个库的不同版本作区分。
       compile 'com.android.support.constraint:constraint-layout:1.0.1'
       testCompile 'junit:junit:4.12'
    }
    

    第二章 先从看得到的入手——探究活动

    1、Genenate Layout File 是表示自动为Activity创建一个布局,Launcher Activity表示将当前活动设置为主活动。

    2、配置主活动的方法:在Manifest中的Activity标签中添加标签,并在这个标签中添加

    <action android:name="android.intent.action.MAIN"/>
    <category android:name-"android.intent.category.LAUNCHER"/>  
    

    3、在活动中使用Menu

    在res中新建Menu文件夹,创建XML文件,然后在相应的Java代码中重写onCreateOptionMenu()和onOptionItemSelect()方法。

    在onCreateOptionMenu():  
    public boolean onCreateOptionMenu()
    {
       getMenuInflater().inflater(R.menu.main,menu);
       return true;
    }  
    

    onOptionItemSelect()用于响应用户的事件。在此函数中,可以通过:item.getItemId判断点击的是哪一个菜单项。

    4、销毁一个活动使用finish()函数,效果和按下back键是一样的

    5、使用Intent

    Intent是Android程序中各组件之间进行交互的主要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同的组件之间进行数据的传递。Intent一般可以用于启动活动,启动服务,以及发送广播等场景。

    Intent有多个构造函数:Intent(Context packageContext,Class

    6、Intent向上一个活动传递数据。

    使用startActivityForResult()方法在A中来启动Activity B;第一个参数是Intent,第二个参数是请求码,用于在回调之后来判断数据的来源。

    在B中Intent中putExtra()来返回要传递的数据。

    同时调用setResult()方法,第一个参数是要上一个活动A传递的处理结果,一般只使用RESULT_OK或RESULT_CANCELED两个值,第二个参数价将带有数据的Intent返回。

    在A中重写onActivityResult()方法,此方法有三个参数,第一个参数requestCode是我们在A中使用startActivityForResult()方法传入的,第二个参数是resultCode是在B中返回数据的时候传入的setResult方法中的结果码。第三个参数是传入的数据。

    一般是重写back键按下之后回调的方法:onBackPressed();

    7、活动的生命周期

    • 当活动被首次创建的时候:
      会依次调用:    onCreate()方法、onStart()方法、onResume方法。
    • 当从当前的活动跳转到另一个界面的时候(当前活动不可见)
      会依次调用:      onPause()方法、onStop()方法
    • 当从其他活动再跳转回来的时候
      会依次调用:     onRestart()方法、onStart()方法、onResume()方法
    • 当从当前活动跳转自另一个活动的时候(当前活动可见)
      会依次调用:    onPause()方法
    • 当从这个活动跳转会主活动的时候
      会依次调用:    onResume()方法
    • 结束当前活动的时候
      会依次调用:     onDestory()方法
      # 第三章 常用控件的使用方法

    ListView的使用

    listView 的简单用法

            首先,需要在布局中加入ListView控件,然后需要定义适配器(一般是ArrayAdapter)
    ArrayAdapteradapter=new ArrayAdapter(
       context,resourse,data);
    listView.setAdapter(adapter);

    ListView之自定义适配器

        首先先创建一个类,这个类中包含了需要进行自定义适配器需要用到的相关资源文件,此类为JAVABean。
    此外,还需要一个适配器,适配器继承自ArrayAdapter<>并泛型为定义的类。在此适配器中,重写getView()方法。

       `     
        /***  
        * 自定义适配器,继承自ArrayAdapter,并将泛型指定为Fruit类  
        */  
        class MyAdapter extends ArrayAdapter<Fruit>{  
           /**  
            *  
            * @param context  
            * @param resource  
            * @param objects  
            */  
           private  int Resource;  
           public MyAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<Fruit> objects) {  
               super(context, resource, objects);  
               Resource=resource;  
           }  
        ​  
           @NonNull  
           @Override  
           public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {  
               View view;  
               ViewHolder viewHolder;  
               Fruit fruit=getItem(position);    
               if (convertView==null){  
                   view= LayoutInflater.from(getContext()).inflate(Resource,parent,false);  
                   viewHolder-new ViewHolder();  
                   viewHolder.ImageView imageView=view.findViewById(R.id.imageView);  
                   viewHolder.TextView textView=view.findViewById(R.id.textView);  
                   view.setTag(viewHolder);//将ViewHolder存储在View中
               }else{  
                   view=convertView;  
                   viewHolder=(ViewHolder)getTag();//重新获取ViewHolder  
               }  
               imageView.setImageResource(fruit.getFruitImage());  
               textView.setText(fruit.getFruitName());  
               return view;  
           }  
           class ViewHolder(){  
               ImageView fruitImage;  
               TextView fruitName;  
               
           }  `
    

    其中getItem()方法是每个子项滚动到屏幕类的时候被调用。
        convertView将之前的项目进行缓存。
    新建一个内部类ViewHolder,用于对实例对象进行缓存,把控件的实例都存放在viewHolder中,然后调用View的setTag()方法,将ViewHolder对象保存在View中,当convertView不为null的时候,调用View中的getTag()方法,将ViewHolder取出。这样系统每次就不用通过findViewById()来获取控件实例。
        点击事件需要设置监听

       @Override
       public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
          Fruit fruit=fruitList.get(i);
           Toast.makeText(getApplicationContext(),"position"+i+fruit.getFruitName(),Toast.LENGTH_LONG).show();
       }
    

    RecyclerView使用

    由于是新增的部件,所以需要添加依赖包

    compile 'com.android.support:recyclerview-v7:26.+'  //此处需要注意的是V7:后的,是appcompat后面的版本号

          initData();//初始化集合List中的数据
           RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
           FruitAdapter adapter = new FruitAdapter(fruitList);
           /**
            * 这里用于指定RecyclerView的布局方式
            */
           LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
           recyclerView.setLayoutManager(linearLayoutManager);
           recyclerView.setAdapter(adapter);
    public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{
       private List<Fruit>mFruitList;
    
           /**
            * 构造函数用于将要展示的数据传入,并赋值给一个全局变量mFruitList。
            * @param mFruitList
            */
           public FruitAdapter(List<Fruit> mFruitList) {
               this.mFruitList = mFruitList;
           }
        ​
           /**
            * 首先定义了一个内部类ViewHolder,ViewHolder要继承自RecyclerView.ViewHolder,然后ViewHolder的构造函数要传入一个view参数
            * 这个参数通常就是RecyclerView子项最外层的布局。
            */
           static class ViewHolder extends RecyclerView.ViewHolder{
               ImageView fruitImage;
               private View fruitView;//设置view点击事件  添加此变量来保存子项最外层的布局的实例
               TextView fruitName;
               public ViewHolder(View view) {
                   super(view);
                   fruitView=view;//获取View
                   fruitImage=view.findViewById(R.id.imageView);
                   fruitName=view.findViewById(R.id.textView);
        ​
               }
           }
        ​
           /**
            * onCreateViewHolder()是用于创建ViewHolder实例的,在这个方法中,将布局加载到ViewHolder的实例中,然后将ViewHolder实例返回
            * @param parent
            * @param viewType
            * @return
            */
           @Override
           public FruitAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
               View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
        ​
               final ViewHolder holder=new ViewHolder(view);
               /**
                * ViewHolder设置点击事件
                */
               holder.fruitView.setOnClickListener(new View.OnClickListener() {
                   @Override
                   public void onClick(View view) {
                       /*
                       通过ViewHolder的getAdapterPosition获得position
                       然后可以通过该位置在List中获取实例
                        */
                       int position =holder.getAdapterPosition();
                       Fruit fruit=mFruitList.get(position);
                       Toast.makeText(view.getContext(),"this is "+ fruit.getFruitName(),Toast.LENGTH_LONG).show();
                   }
               });
               return holder;
           }
        ​
           /**
            * 用于对RecyclerView子项的数据进行赋值,当每个子项被滚动到屏幕内的时候被调用
            * @param holder
            * @param position
            */
           @Override
           public void onBindViewHolder(FruitAdapter.ViewHolder holder, int position) {
               Fruit fruit=mFruitList.get(position);
               holder.fruitImage.setImageResource(fruit.getFruitImage());
               holder.fruitName.setText(fruit.getFruitName());
           }
        ​
           /**
            * 返回当前要加载的数据源的长度
            * @return
            */
           @Override
           public int getItemCount() {
               return mFruitList.size();
           }
        }`
    

    瀑布流使用: StaggeredGridLayoutManager  设置瀑布流的布局方式

    如果需要设置为横向滑动的话就需要在MainActivity中的LinearLayoutManager设置方向

     linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

    使用步骤:

    获取RecyclerView实例——设置layoutManager布局对象——绑定适配器
    创建JavaBean类
    创建适配器——继承自《RecyclerView.Adapter

    第四章 碎片

    碎片的静态加载

    在MainActivity的布局文件中添加的使用

    <fragment  
           android:id="@+id/id_fragment_title"  
           android:name="com.zhy.zhy_fragments.TitleFragment"  
           android:layout_width="match_parent"  
           android:layout_height="45dp" />  
    

    在TitleFragment的布局文件和JAVA文件中进行操作
    在java文件中重写onCreateView方法,注意要继承只V4包的Fragment

    碎片的动态加载

    在MainActivity的布局文件中声明一个区域作为碎片出现的位置 。
    找MainActivity的JAVA文件中首先设置默认的显示的Fragment

    prvate void setDefaultFragment(){
       FragmentManager fm =getFragmentManager();
       FragmentTransaction transaction = fm.beginTransaction();
       FragmentOne one=new FragmentOne();
       transaction.replace(R.id.mainactivity_fragment,one);
       transaction.commit();
    }
    

    Fragment常用的API:

    Fragment常用的三个类

    要用于定义Fragment。

    • android.app.FragmentManager主要用于在Activity中操作Fragment。
    • android.app.FragmentTransaction保证一系列Fragment操作的原子性。

    获取FragmentManager的方式
    - getFragmentManager()  //在V4包中,getSupportFragmentManager()

    主要的操作中都是FragmentTransaction的方法
        FragmentManager fm =getSupportFragmentManager();
        FragmentTransaction transaction = fm.beginTransaction();//开启一个事务
        transaction.add(); //向Activity中添加一个Fragment
        transaction.remove();//从Activity中移除一个Fragment
        transaction.replace();//使用另一个Fragment替换掉当前的Fragment,实际上是remove()和add()的合体
        transaction.show();//显示之前隐藏的Fragment
        transaction.hide();//隐藏当前的Fragment,仅仅设置为隐藏,并不会销毁
        transaction.commit();//提交一个事务

    注:如果希望保留用户在当前fragment的数据,就使用hide()和show()组合,这样不会销毁当前的Fragment
            如果不希望保留用户当前在Fragment的数据,就使用remove()和add()组合或者直接使用replace(),这样就会销毁当前的Fragment,再进去的时候就会重新加载
            如果使用replace的话,当程序已经不在当前的界面的时候,在其他界面按Back键的时候,将不会返回到这个界面,直接退出
            想要保留这个fragment的话,就需要将它添加到回退栈中: transaction.addToBackStack();
    参考博客

    第五章 广播

    1、广播的两种类型

    • 标准广播  广播发出后,所有的广播接收器会在同一时间接收到这条广播。效率较高,无法截断。
    • 有序广播  广播发出后,同一时刻只有一个广播接收器能够接收到这条广播,并且优先级高的可以先接收到这条广播,并且前面的广播接收器可以截断广播。

    2、注册广播的两种方式

        在代码中注册——动态注册——在程序关闭之后就不能再接收到广播
        在AndroidManifest中注册——静态注册——与程序是否关闭无关

    3、创建广播接收器的方法

    新建一个类,继承自BroadcastReceiver,并重写父类的onReceive()方法。
    这样,当有广播到来的时候,onReceive()方法就会得到调用。

    4、动态注册广播的方法

    创建IntentInfilter的实例,添加一个action

    IntentInfilter intentInfliter =new IntentInfliter();//创建IntentFilter实例  
    intentInfilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");//为这个IntentFilter添加action  
    registerReceiver(networkChageReceiver,interFilter);//动态注册广播,这样networkChangeReceiver就会接收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广播   
    

    动态注册的广播需要取消注册,在onDestory中调用unRegisterReceiver()方法来实现的。

    5、静态注册广播的方法

    使用android Studio的快捷键创建一个广播接收器,点击包名——New——Other——Broadcast Receiver,Exported表示是否允许广播接收器接收本程序以外的广播,Enable表示是否启用这个广播。

    (静态注册的广播接收器必须要在AndroidManifest中注册才能使用,使用Android Studio创建的广播接收器已经在AndroidManifest中注册了)
    注册方式信息如下:

           <receiver
               android:name=".MyReceiver"
               android:enabled="true"//是否启用该广播接收器
               android:exported="true">//是否允许接受本程序之外的广播
               <intent-filter  android:priority="100">//priority是设置广播的优先级,优先级越高系统响应越快
                   <action android:name="android.intent.action.BATTERY_LOW"/>//当系统低电量的时候就会发出这个广播
               </intent-filter>
           </receiver>
       </application>
    
    在<intent-filter>标签中添加相应的Action标签
    

    注意某些操作需要用到权限,需要在Manifest中添加权限。

    此外,在onReceive中不能添加太多的逻辑以及耗时操作,在广播接收器中是不允许多开线程的,当此方法运行了太长时间而没有结束,程序会报错。

    发送动态广播的方法:

    Intent intent=new Intent("android.net.conn.CONNECTIVITY_CHANGE");
    sendBroadcast(intent);
    

    6、自定义广播

    创建一个自定义的广播接收器继承自BroadcastReceicer,并重写onReceive()方法。
    然后使用Intent发送广播。

    • 有序广播
      在Manifest中的Broadcast标签的Intent-filter中设置优先级属性priority
      发送广播的时候与标准广播不同
      sendOrderBroadcast(intent,null)//第一个是Intent实例,第二个是与权限相关的字符串,这里传入null
    • 截断广播
      abortBroadcast();//在广播接收器中调用这个方法就可以截断广播

    7、本地广播

        本地广播的出现是为了解决广播在整个系统中运行中的安全性的问题,全局广播会被其他程序所截获,造成信息泄露。
        本地广播的用法
    主要是使用一个LocalBroadcastManager来对广播进行管理,并提供了发送广播和和注册广播的方法。

     localBroadcastManager=LocalBroadcastManager.getInstance(getApplicationContext());
           Button button= (Button) findViewById(R.id.button);
           button.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   Intent intent=new Intent("com.example.dimple.mytest.MYMY");
                   localBroadcastManager.sendBroadcast(intent);
               }
           });
           IntentFilter intentFilter=new IntentFilter();
           intentFilter.addAction("com.example.dimple.mytest.MYMY");
           localBroadcastManager.registerReceiver(new MyReceiver(),intentFilter);
    

    本地广播只能通过动态注册。

    通过LocalBroadcastMaager的getInstance()方法得到实例。
    然后注册广播的时候就调用LocalBroadcastManager()的registerRecriver()方法.
    在发送广播的时候就调用LocalBroadcastManager的sendBroadcastManager()方法。

    第六章 存储

    1、使用SharedPreferences

    SharedPreferences是采用键值对的方式来存储数据的。
    要使用SharedPreferences需要首先获得SharedPreferences对象。获取该对象的方式有:
    - Context类中的getSharedPreferences()方法,接收两个参数,第一个参数是文件的名称,第二个参数指定操作模式MODE,目前只能选择MODE_PRIVATE。
    - Activity类中的getPreferences()方法,和getSharePreferences差不多,区别是使用getPreferences会使用当前的类名作为文件名。
    - PrederencesManager类中的getDefaultSharedPreferences()方法 ,这是一个静态方法,接收一个context参数,会自动将当前的应用程序的包名作为前缀来命名SharePreferences文件

    使用步骤
    调用sharedPreferences对象的editor()方法来获得一个 sharedPerferences.Editor()对象
    向sharedPerferences.Editor()对象添加数据。调用editor的putString()等些列方法
    调用editor的apply()方法提交数据。

    读取SharedPreferences的方法

    获取SharedPreferences对象,通过getString()系列方法得到数据。注意这里有两个参数,第一个是要读取的数据的键,第二个参数是没有找到这个数据返回的值。

    2、使用SQLite数据库进行存储

    android中为了更好的管理数据库,提供了一个SQLiteOpenHelper的帮助类(抽象类),需要实现SQLiteOpenHelper抽象方法中的抽象方法onCreate()(在数据库被创建的时候会调用),onUpgrade()(在数据库版本被升级的时候被调用)

    获取实例的方法:(都可以打开或者创建数据库)
    getReadableDatebase()只能是通过只读的方式来打开数据库。
    getWritableDatebase()使用读写的方式打开数据库。

    使用方法:
    新建一个JAVA类继承自SQLiteOpenHelper,实现两个抽象方法onCreate()和onUpgrade(),创建构造器(使用参数较少的那一个构造器),其参数有

    Context context, String name, SQLiteDatabase.CursorFactory factory, int version
    

    /*
    第一个参数是context上下文
    第二个参数是数据库名
    第三个参数允许在查询的时候返回一个自定义的Cursor,一般为null
    第四个参数是当前数据库的版本号,可用于对数据库的更新操作
    */

    构建出SQLiteOpenHelper实例(new)以后,调用getReadableDatebase()或者getWritableDatebase()就可以创建数据库了。
    创建的数据库的名字一般放在 /data/data//datebase目录下。

    配置adb 在安卓SDK的目录下有一个platform-tools文件夹,将这个文件夹配置到系统的环境变量中,使用adb shell进入控制台终端

    使用sqlite3 +数据库名称 打开数据库
    使用.table 查看数据库中的表、
    使用 .schema 查看建表语句

    3、向SQLite数据库中添加数据
    调用SQLiteOpenHelper的getReadableDatebase()或者getWritableDatebase()创建数据库后会返回一个SQLiteDatabase对象,借助这个对象就可以对数据库进行CRUD操作
    添加数据
        SQLiteDatabase提供一个insert()方法用于添加数据。这个方法有三个参数。第一个参数是数据库表名称,第二个是用于指定在未添加数据的情况下给某些数据项自动赋值为null,第三个参数是ContentValues对象,它提供一系列的put()方法重载,用于向contentValues添加数据。使用此方法的时候需要创建ContentValues对象。

    sqLiteDatabase.insert("Book",null,contentValues);//插入信息到Book
    

    更新数据
        SQLiteDatabase提供一个update()的方法,第一个参数是表名,第二个参数是ContentValues对象,第三四个参数用于约定更新某一行或者某几行的数据。
        

        sqLiteDatabase.update("Book",contentValues,"id=?",new String[]{"1"});//更新id为1的书的信息
        第三个参数对应的是SQL的where语句,第四个提供约束的具体信息。
    

    删除数据

        SQLiteDatabase提供一个delete()的方法,第一个参数是表名,第二三个参数用于约定删除某一行或者某几行的数据。

     sqLiteDatabase.delete("Book","pages>?",new String[]{"500"});//删除页数超过500页的书的信息  
    

    查询数据

    query(table, columns, selection, selectionArgs, groupBy, having, orderBy, limit)方法各参数的含义:
    

     
    - table:表名。相当于select语句from关键字后面的部分。如果是多表联合查询,可以用逗号将两个表名分开。
     
    - columns:要查询出来的列名。相当于select语句select关键字后面的部分。
     
    - selection:查询条件子句,相当于select语句where关键字后面的部分,在条件子句允许使用占位符“?”
     
    - selectionArgs:对应于selection语句中占位符的值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常。
     
    - groupBy:相当于select语句group by关键字后面的部分
     
    - having:相当于select语句having关键字后面的部分
     
    - orderBy:相当于select语句order by关键字后面的部分,如:personid desc, age asc;
     
    - limit:指定偏移量和获取的记录数,相当于select语句limit关键字后面的部分。

                       sqLiteDatabase=mySqlite.getWritableDatabase();
                       Cursor cursor=sqLiteDatabase.query("Book",null,null,null,null,null,null);//查询完毕后得到一个游标Cursor对象,通过这个对象去访问数据
                       if (cursor.moveToFirst()){//将游标移动到第一行的位置
                           do{//接下来是循环
                               //在这个循环中可以通过Cursor的getColumnIndex()方法获取到某一行的索引。将这个索引传入相应的取值方法中就可以得到相应的数据了。
        ​
                               String name=cursor.getString(cursor.getColumnIndex("name"));
                               String author =cursor.getString(cursor.getColumnIndex("author"));
                               int pages=cursor.getInt(cursor.getColumnIndex("pages"));
                               double price=cursor.getDouble(cursor.getColumnIndex("price"));
                               builder.append("name\n").append(name).append("author\n").append(author).append("pages\n").append(pages).append("price\n").append(price);
                           }while (cursor.moveToNext());
                           textView.setText(builder);
                       }
    

    4、使用LitePal操作数据库

    配置过程
        编辑app/build.gradle文件,在dependencies闭包中加入

       compile 'org.litepal.android:core:1.3.2'
    

    然后在app/src/main目录新建一个assets目录,在此目录下创建一个litepal.xml文件

    <litepal>  
        <!--dbname标签用于指定数据库名,version标签用于指定数据库版本号,list标签用于指定所有的映射模型-->  
        <dbname value="BookStore"></dbname>
        <version value ="2"></version>
        <list>
            <mapping class="com.example.dimple.sharedpreferencestest.Book"></mapping>
            <mapping class="com.example.dimple.sharedpreferencestest.Category"></mapping>
        </list>
    </litepal>
    

    最后需要在AndroidManifest中将项目的application配置为

       <application
           android:name="org.litepal.LitePalApplication"
           android:allowBackup="true"
           android:icon="@mipmap/ic_launcher"
    

    创建数据库:
        使用面向对象的思想,定义一个Book类JavaBean类。
        将关系模型添加到映射模型列表中

    <mapping class="com.example.dimple.sharedpreferencestest.Book"></mapping>
    

    注:不管需要多少关系模型,注意要使用完整的类名,不管需要多少模型类映射,都需要用同样的方式在标签下配置。

    在代码中使用Connector.getDatabase();创建数据库。

    升级版本号,就可以直接修改litepal.xml中的version即可。此升级不会删除数据库。

    添加数据
        需要将JavaBean类继承自DataSupport类。
        在java代码中,直接new一个JavaBean类的对象出来,然后调用set系列方法,最后调用DataSupport父类传下来的save()方法即可。

    更新数据
    直接new一个JavaBean对象出来,调用要修该的数据项的set函数,然后updataAll();  此方法可以设置约束条件

     book.updateAll("name=?","the da vinvi code ");//修改所有name=the da vinvi code的数据项。
    

    删除数据

     DataSupport.deleteAll(Book.class,"price>?","17");
    第一个参数是指定表,后面的参数是指定参数的数据项的属性。
    

    查询数据
    注:     
    for (Book book:bookList)的意思是循环bookList集合,每次取得的实例就给Book book;

    List bookList=DataSupport.findAll(Book.class);  
    findAll()返回的是一个List集合。

    常用的查询API
    - 查询第一条数据

      Book book =DataSupport.findFirst(Book.class);  
    
    • 查询最后一条数据

      Book book =DataSupport.findLast(Book.class);  
      
    • select()方法可以指定查询那几列的数据。

      List<Book> bookList =DataSupport.select("name","pages").find(Book.class);
      
    • where()方法可以指定查询的约束条件

       List<Book> bookList =DataSupport.where("name=?","bianxiaofeng").find(Book.class);  
      
    • order()方法可以指定结果的排序方式

      List<Book> bookList =DataSupport.order("pages desc").find(Book.class);
      
    • limit()方法可以指定结果的数量

      List<Book> bookList =DataSupport.limit(3).find(Book.class);
      

    还可以对所有的方法进行连缀组合操作。

    第七章 内容提供器

    1、内容提供器主要在不同的应用程序之间实现数据共享的功能。使用内容提供器是Android跨程序共享数据的标准方式。

    2、运行时权限

        权限分为普通权限和危险权限。危险权限分为9组24个。每一个危险权限都属于一个权限组,一旦用户同意了某个权限组的一个权限,则对应的权限组都可以被获得权限。
        
    步骤:
    第一步需要判断用户是不是已经授权了。借助的是ContextCompat.setSelfPermission()方法。此方法接收两个参数:第一个是Context上下文,第二个是具体的权限名。
    Manifest.permission.“`。使用方法的返回值和PackageManager.PERMISSION_GEANTED作比较,相等就表示已经授权。不相等就表示没有授权。

    如果授权的话就执行具体的逻辑。如果不等的话就说明还没有得到用户的权限的同意,这个时候需要申请用户的权限。

    调用ActivityCompat.requestPermission()方法来向用户申请权限。requrestPermission接受三个参数,第一个参数是要求的Activity实例,第二个参数是String数组,这里需要把要申请的权限名写在里面,第三个是请求码,需要唯一。在这个方法中判断是否得到授权。

    //申请权限
    if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission
                           .CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
                       ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest
                               .permission.CALL_PHONE},1);
                   }else {
                      //do something else
                   }
    @Override
       public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                              @NonNull int[] grantResults) {
               switch (requestCode){
                   case 1:
                       if (grantResults[0]==PackageManager.PERMISSION_GRANTED){
                           //do something else.
                       }else{
                           Toast.makeText(MainActivity.this,"failed!",Toast.LENGTH_LONG).show();
                       }
               }
       }
    

    3、contentResolver的基本用法

    对于一个应用程序来说,如果访问内容提供器中的共享的数据,就一定要借助ContentResolver类。可以通过getContentReslover来获取该类的实例。

    使用inset()用于添加数据。update()用于更新数据。delete()用户删除数据。queny()用于查询数据。ContentReslover的增删查改需要接受Uri对象作为参数。

    和数据库的操作一样是使用ContentValues作为中转传数据。调用CRUD函数进行操作。

    第八章 运用手机多媒体

    1、使用通知Notification

    使用通知的步骤
    首先需要一个NotificationManager来对通过通知进行管理,可以调用Context的getSystemService()方法来获取到。
    getSystemService()方法接受一个字符串参数用于确定获取系统的哪一个服务。这里传入Context.NOTIFICATION_SERVICE即可。

    NotificationManger manager=(NitificationManager)getSystemSerice(Context.NOTIFICATION_SERVICE);
    

    接下来需要一个Builder构造器来创建Notification对象(为了实现兼容,使用v4包的NotificationCompat类)

    Notification notification =new NotificationCompat.Builer(context);  
    

    然后调用一系列的set方法对这个实体类进行设置。最后调用manager的notify()方法,该方法接收两个参数,第一个参数是Notification的id,第二个参数是Notification的类。
    以上功能实现后就可以发送通知了,不过通知不可点击。如果需要点击需要PendingIntent

    注:PendingIntent和Intent的异同点:

    • 都可以指明一个意图,都可以用于启动活动,服务,以及发送广播。

    • Intent倾向于立即去执行某个动作,PendingIntent更加倾向在合适的时机去执行某个动作。

    pendingIntent的使用方法:
    使用静态方法获得PendingIntent的实例,getActivity()、getBroadcast()、getService()。

    四个参数:content,0,Intent对象、0

    Intent intent=new Intent(MainActivity.this,nextActivity.class);
    PendingIntent pi=PendingIntent.getActivity(MainActivity.this,0,intent,0);
    

    不能这个通知写在OnClickListener的匿名内部类

    2、调用摄像头

    private void camera() {
           /**
            * 首先创建了一个File对象,用于存放摄像头拍下的图片,这里将图像命名为outputImage.jpg,并将它存放在手机的关联目录下、
            * 接着进行一个判断,如果设备系统的版本低于7.0 的话就,就调用Uri的fromFile()方法将File转化为Uri对象,这个Uri对象标识着outputImage这张图片的真实路径,否则
            * 就调用FileProvider的geUriForFile()将File转化成一个封装过的Uri对象。
            */
           File outputImage=new File(Environment.getExternalStorageDirectory(),"output_image.jpg");
           try{
               if (outputImage.exists()) {
                   outputImage.delete();
               }
               outputImage.createNewFile();
           } catch (IOException e) {
               e.printStackTrace();
           }
           if (Build.VERSION.SDK_INT>=24){
               imageUri= FileProvider.getUriForFile(MainActivity.this,"com.example.dimple.test619.fileProvider",outputImage);
           }else{
               imageUri=Uri.fromFile(outputImage);
           }
           Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");
           intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);//填入指定的文件输出地址
           startActivityForResult(intent,TAKE_PHOTO);
       }
    @Override
       protected void onActivityResult(int requestCode, int resultCode, Intent data) {
           switch (requestCode) {
               case TAKE_PHOTO: {
                   if (resultCode == RESULT_OK) {
                       try{
                           Bitmap bitmap= BitmapFactory.decodeStream(getContentResolver()//将图片解析为bitmap对象
                                   .openInputStream(imageUri));
                           picture.setImageBitmap(bitmap);
                       } catch (FileNotFoundException e) {
                           e.printStackTrace();
                       }
    ​
                   }
               }
               break;
               default:
                   break;
           }
    

    因为用到了ContentProvider,所以需要在Manifest中注册

    <provider
                android:authorities="com.example.dimple.test619.fileProvider"
                android:name="android.support.v4.content.FileProvider"
               android:exported="false"
               android:grantUriPermissions="true">
               <meta-data   //指定Uri的共享路径
                   android:name="android.support.FILE_PROVIDER_PATHS"
                   android:resource="@xml/file_paths"/>
            </provider>
        </application>
    ​
    </manifest>
    

    新建XML文件

        <?xml version ="1.0" encoding="utf-8" ?>
            <path xmlns:android="http://schemas.android.com/apk/res/android">
            <external-path name="my_images" path=""/>
        </path>  
    

    3、播放音频

    在Android中播放音频文件一般都是使用MediaPayer来实现的,它对多种格式的音频文件提供了非常全面的控制方法,使得音频播放比较简单。

    方法名         功能描述          备注    
    setDataSource()     设置要播放的文件的地址     
    prepare()       在开始播放之前调用这个方法完成准备工作     
    start()     开始播放视频      
    pause()     暂停播放音频      
    reset()     将Mediaplayer对象重置为刚开始创建的状态       
    seekTo()        从指定的位置开始播放音频        
    stop()      停止播放音频      
    release()       释放掉与MediaPlyer相关的资源     
    isPlaying()     判断当前MediaPlayer是否在播放        
    getDuration()    获取之路的音频文件的时长   
    

    4、播放视频

    方法名            功能描述       备注    
    setVideoPat()   设置要播放的视频文件的地址           
    start()     开始或继续播放视频       
    pause()     暂停播放视频      
    resume()        将视频从头播放     
    isPlaying()     是否正在播放      
    getDuration()       获取时长    
    seekTo()        从指定的位置开始播放  
    XML中包含VideoView。
    通过VideoView来进行操作。
    

    第九章 使用网络技术

    1、WebView 的用法

    获取WebView的实例,通过getSetting()方法对浏览的相关属性进行设置。调用setJavaScriptEnabled()方法来让WebView支持JavaScript脚本。调用wenViewClient()方法。是希望网页依然在本程序中,不然就会跳转出去。

    2、使用HTTP协议访问网络

    首先开启一个子线程,在子线程中进行网络数据请求操作,获取HttpRULConnection实例。

    URL url=new URL("https://www.baidu.com");
    HttpURLConnection connection==(HttpURLConnection)url.openConnection();
    connection.setConnectionTimeOut(8000);//设置链接超时
    connection.setReadTimeOut(8000);//设置读取超时
    

    之后再调用getInputStream()方法获取到服务器返回的流,然后进行流读取。

    ImputStream in=connection.getInputStream();
    //下面进行流读取
    BufferedReader reader=new BufferedReader(new InputStreamReader(in));
    String line;
    StringBuilder builder=new StringBuilder();
    while((line=reader.readerLine())!=null){
      builder.append(line);
    }
    
    最后关闭链接
    connection.disconnect();
    

    3、使用OkHttp

    在app/build.grade中dependencies中添加如下内容:

    compile 'com.squareup.okhttp3:okhttp:3.4.1'
    

    具体使用步骤:
    创建一个OkHttpClient实例

    OkHttpClient client=new OkHttpClient();  
    

    接下来如果需要发起一条Http请求,需要创建一个Request对象

    Request request=new Request.Builder().build();  
    

    以上代码创建了一个空的Request对象,没有什么实际作用,在build()方法之前加入很多连缀来丰富这个request对象,可以使用uri()方法来设置目标网络地址。

    之后调用OkHttpClient的newCall()方法来创建一个Call对象,并调用它的execute()方法来发送请求并获得服务器的数据。

    Response response=client.newCall(request).execute();
    

    其中Resopnse就是服务器返回的数据了,以下方法可以得到具体的内容

    String responseData=response.body().string();
    

    发送数据:

    首先需要构建出一个RequestBody对象来存放待提交的数据。

    ResquestBody requestBody=new FormBody.Builder().add("username","admin").add("password","123456").build();
    

    接下来就和Get请求一样了,调用execute()方法来获得服务器返回的数据。

    4、解析JOSN数据

    http://mobileapi.72g.com/index.php?tp=andv4/quan&op=qinfo

    5、解析Json数据

    首先配置服务器:电脑端访问网址为127.0.0.1 模拟器访问端网址:10.0.2.2

    使用JsonObject:
    首先将从服务器中的数据放在JSONArray中,然后循环遍历这个Json数组,从中取出的每一个元素都是JSONObject对象,调用getString()方法就可以取出数据。

                   JSONArray jsonArray=new JSONArray(response);
                       for (int i=0;i<jsonArray.length();i++){
                           JSONObject jsonObject=jsonArray.getJSONObject(i);
                           Log.d("TAG", "Data :"+jsonObject.getString("name")+jsonObject.getString("id")+jsonObject.getString("version"));
                       }
    

    使用Gson
    首先需要添加依赖

       compile 'com.google.code.gson:gson:2.8.1'
    

    Gson可以将一段Json格式的字符串自动映射成为一个对象,从而不需要我们编写代码进行解析。

    比如:一段json格式的数据如下
    {“name”:”Tom”,”age”:”20”}
    我们可以定义 一个Person类,并加入name和age两个字段。只需要简单的调用如下代码就可以将JSON数据自动解析成一个Person对象了。

    Gson gson=new Gson();
    Person person=gson.fromJson(jsonData,Person.class)  
    

    如果解析的是一段json数组就需要借助TypeToken将期望解析成的数据类型传入到fromJson中。

    Gson gson=new Gson();
     List<App>appList=gson.fromJson(response,new TypeToken<List<App>>(){}.getType());
    for (App app:appList){
      Log.d("TAG", "run: "+app.getId()+"\n"+app.getName()+"\n"+app.getVersion());
     }
    

    定义Okhttp工具类

    public class HttpUtility {
       public static void senOkHttpRequest(String address,okhttp3.Callback callback){
           OkHttpClient client=new OkHttpClient();
           Request request=new Request.Builder().url(address).build();
           client.newCall(request).enqueue(callback);
       }
    }
    ​
    

    在调用工具类的时候就这样写:

    HttpUtility.senOkHttpRequest("http://10.0.2.2/me.json", new okhttp3.Callback() {
               @Override
               public void onFailure(Call call, IOException e) {
    //                T网络请求失败"相关逻辑
               }
    ​
               @Override
               public void onResponse(Call call, Response response) throws IOException {
    //                网络请求成功相关逻辑
                   String responseData=response.body().string();
                   showResponse(responseData);
               }
           });
    

    第12章 Material Design

    1、ToolBar代替ActionBar

     需要在Manifest中设置主题为NoActionBar

    <android.support.v7.widget.Toolbar
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           android:id="@+id/toolBar"
           android:background="@color/colorPrimary" />
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolBar);
           setSupportActionBar(toolbar);
    

    扩展:在ToolBar中添加其他控件
    新建menu文件夹,在其中添加Menu resourse file,创建一个xml文件。

    <item  
           android:id="@+id/delete"
           android:icon="@drawable/wujiaoxing"//指定按钮的图标
           android:title="@string/delete"//指定按钮的文字
           app:showAsAction="always" />//指定按钮的显示位置。always表示永远显示再ToolBar中,如果屏幕不够就不显示
                                       nerver表示永远显示再菜单项中,ifRoom表示屏幕空间足够的情况下就显示在ToolBar中,如果不够的话就显示在菜单项中。
    ​
    

    在MainActivity中重写onCreateOptionMenu方法显示出menu,使用onOptionsItemSelected()方法来响应点击事件。

    2、滑动菜单——DrawerLayout

    首先它是一个布局,在布局中只允许放入 两个直接控件,第一个子控件是主屏幕显示的内容,第二个子控件是滑动菜单栏显示的内容。

    使用

    <android.support.v4.widget.DrawerLayout></android.support.v4.widget.DrawerLayout>
    

    注意:

    第二个子控件需要设置android:layout_gravity=”“,需要告诉DrawerLayout菜单是再屏幕的右边还是左边

    android:layout_gravity="start"  
    

    使用ToolBar的最左边加入导航按钮。

    ActionBar actionBar=getSupportActionBar();//获取ActionBar的实例,这个实例的具体提现是由ToolBar来实现的。
    
    
           if (actionBar!=null){
               actionBar.setDisplayHomeAsUpEnabled(true);//显示导航键
               actionBar.setHomeAsUpIndicator(R.drawable.meiyuan);//设置导航键的图标。这个图标叫做HOMEAsUp
           }
    

    在onOptionsItemSelected()方法来对HomeAsUp按钮的点击事件,HomeAsUp的id是
    android.R.id.home

    然后调用DrawerLayout的openDrawert()方法将滑动菜单显示出来。

    3、NavigationView

    NavigationView是DesignSupport库中提供的一个控件。

    使用的时候需要添加闭包关系

       compile 'de.hdodenhof:circleimageview:2.1.0'
       compile 'com.android.support:design:26.0.0-alpha1'
    

    在使用Navigation之前,需要准备两个东西,menu和headLayout

    menu是在navigationView中显示菜单项的,headLayout是在NavigationView中显示头部布局的。

    头部布局中使用circleImage来设置圆形头像。

    设置完毕后在MainActivity中添加

    <android.support.design.widget.NavigationView
           android:id="@+id/nav_view"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:layout_gravity="start"
           app:headerLayout="@layout/nav_header"
           app:menu="@menu/nav_menu" />
    

    同时可以设置监听

    public boolean onNavigationItemSelected(@NonNull MenuItem item)
    

    4、悬浮按钮和可交互提示

    悬浮按钮

    添加FloatActionButton布局

           <android.support.design.widget.FloatingActionButton
               android:id="@+id/floatButton"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_gravity="bottom|end"
               app:elevation="8dp"设置悬浮高度
               android:layout_margin="10dp"
               android:src="@drawable/meiyuan" />
    

    setOnClickListener可以设置点击事件

    可交互提示:SnakeBar

       Snackbar.make(view,"Date Deleted",Snackbar.LENGTH_LONG)   //make方法创建了一个Snake对象,第一个 参数需要传入一个人View,只要是当前界面的任意一个View都可以,第二个参数是SnakeBar中显示的内容,第三个是显示的时长
                           .setAction("Un Do", new View.OnClickListener() {//调用setAction来设置一个动作,从而让SnakeBar不仅仅是一个提示,而是可以和用户进行交互的//第一个是按钮的名字,第二个是点击事件。
                               @Override
                               public void onClick(View view) {
                                   Toast.makeText(MainActivity.this, "Un Do Successed!", Toast.LENGTH_SHORT).show();
                               }
                           }).show();
    

    调用setAction来设置一个动作,从而让SnakeBar不仅仅是一个提示,而是可以和用户进行交互的//第一个是按钮的名字,第二个是点击事件。

    出现界面重叠,需要使用CoordinatorLayout来替换掉FrameLayout,它可以监听所有子控件的所有事件,从而自动做出合理的响应。

    5、卡片式布局

    添加依赖:

       compile 'com.android.support:recyclerview-v7:26.0.0-alpha1'
       compile 'com.android.support:cardview-v7:26.0.0-alpha1'
       compile 'com.github.bumptech.glide:glide:4.0.0-RC1'
    

    Glide是一个强大的图片加载库,它不仅可以加载本地图片,还可以加载网络图片,GIF图片,甚至是本地视频。

    具体操作见recyclerView,只是将recyclerView 的子项布局的外层布局改为CardView

    这种会将ToolBar遮盖,使用AppBarLayout
    使用AppBarLayout包裹ToolBar,同时指定RecyclerView的布局行为

               app:layout_behavior="@string/appbar_scrolling_view_behavior"
    

    同时还可以设置滑动隐藏
    在toolBar中加入

                   app:layout_scrollFlags="scroll|enterAlways|snap"
    

    scroll表示RecyclerView向上滑动的时候,ToolBar会一起向上隐藏,enterAlways表示向下滚动的时候,ToolBar会同步显示。snap表示还没有完全隐藏的时候,会自动向上向下偏移。

    6、下拉刷新

    SwipeRefreshLayout就是用于下拉刷新的核心类。使用《swipeRefreshLayout》包裹需要下拉刷新的部件。

    wipeRefresh.setColorSchemeResources(R.color.colorPrimary);
           swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
               @Override
               public void onRefresh() {
                   refreshFuite();
               }
           });  
    

    7、可折叠标题栏ColapsingToolbarLayout

    colapsing不能独立存在,在设计的时候就只能用作AppBarLayout的直接子布局。而AppBarLayout又必须是CoordinatorLayout的子布局

    DrawerLayout ——滑动菜单栏——只能拥有两个字节子布局(第一个是主屏幕的内容、第二个是滑动屏的内容)

    NavigationView——滑动菜单栏滑动屏布局(需要headLayout、和menu布局 (showAsAction))——需要Design Support库

    FloatActionButton——悬浮按钮

    SnakeBar——提示工具

    CoordinatorLayout——加强版的FrameLayout——监听子控件各种事件、自动做出合理的响应。防止控件重叠。

    CardView_FrameLayout——提供圆角和阴影效果(cardCornerRadius指定卡片的弧度,elevation指定卡片的高度)

    Glide——图片加载库

    AppBarLayout——解决FrameLayout中的默认位置为左上角造成的遮挡ToolBar——将ToolBar嵌套到AppBarLayout(    app:layout_scrollFlags=”scroll|snap|enterAlways”),然后给重叠的部件指定布局行为behaver

    SwipeRefreshLayout——下拉刷新——包裹需刷新的部件

    CollapsingToolbarLayout——可折叠标题栏——不能独立存在只能作为AppBarLayout的直接子布局、AppBarLayout必须是coordinatorLayout的子布局

    NestedScrollView——ScrollView——内部只能存在一个直接子布局

    总结

    以上就是我的Android学习过程中整理的一些东西,重新再整理了一遍,也算是重新学习了一次,在重新学习的过程中,还是发现有很多不足不周到的地方。温故而知新,自己还记录了很多笔记,趁现在还时间充裕,还会再好好温习一下。

    展开全文
  • 欢迎使用Markdown编辑器写博客本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦: Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接和图片上传 LaTex数学公式 UML序列图和流程图 ...

    Android项目的目录结构(熟悉)

    • Activity:应用被打开时显示的界面
    • src:项目代码
    • R.java:项目中所有资源文件的资源id
    • Android.jar:Android的jar包,导入此包方可使用Android的api
    • libs:导入第三方jar包
    • assets:存放资源文件,比方说mp3、视频文件
    • bin:存放编译打包后的文件
    • res:存放资源文件,存放在此文件夹下的所有资源文件都会生成资源id
    • drawable:存放图片资源
    • layout:存放布局文件,把布局文件通过资源id指定给activity,界面就会显示出该布局文件定义的布局
    • menu:定义菜单的样式
    • Strings.xml:存放字符串资源,每个资源都会有一个资源id

    Android的配置文件(清单文件)(熟悉)

    • 指定应用的包名

      package="com.itheima.helloworld"
      
      • data/data/com.itheima.helloworld(上面代码指定的包名)
      • 应用生成的文件都会存放在此路径下
    • Android的四大组件在使用前全部需要在清单文件中配置

    • 的配置对整个应用生效
    • 的配置对该activity生效

    DDMS(掌握)

    • Dalvik debug monitor service
    • Dalvik调试监控服务

    常用的adb指令(掌握)

    Android debug bridge:安卓调试桥

    • adb start-server:启动adb进程
    • adb kill-server:杀死adb进程
    • adb devices:查看当前与开发环境连接的设备,此命令也可以启动adb进程
    • adb install XXX.apk:往模拟器安装apk
    • adb uninstall 包名:删除模拟器中的应用
    • adb shell:进入linux命令行
      • ps:查看运行进程
      • ls:查看当前目录下的文件结构
    • netstat -ano:查看占用端口的进程

    电话拨号器(掌握)

    功能:用户输入一个号码,点击拨打按钮,启动系统打电话的应用把号码拨打出去

    1. 定义布局

    1. 组件必须设置宽高,否则不能通过编译

      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      
    2. 如果要在java代码中操作某个组件,则组件需要设置id,这样才能在代码中通过id拿到这个组件

      android:id="@+id/et_phone"
      

    2. 给按钮设置点击侦听

    1. 给按钮设置侦听

       //通过id拿到按钮对象
      Button bt_call = (Button) findViewById(R.id.bt_call);
      //给按钮设置点击
      bt_call.setOnClickListener(new MyListener());
      

    3. 得到用户输入的号码

        //得到用户输入的号码,先拿到输入框组件
            EditText et_phone = (EditText) findViewById(R.id.et_phone);
            String phone = et_phone.getText().toString();
    

    4. 把号码打出去

    1. Android系统中基于动作机制,来调用系统的应用,你告诉系统你想做什么动作,系统就会把能做这个动作的应用给你,如果没有这个应用,会抛异常
    2. 设置动作,通过意图告知系统

      //把号码打出去
          //先创建一个意图对象
          Intent intent = new Intent();
          //设置动作,打电话
          intent.setAction(Intent.ACTION_CALL);
          intent.setData(Uri.parse("tel:" + phone));
          //把意图告诉系统
          startActivity(intent);
      
    3. 添加权限

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

    点击事件的四种写法(掌握)

    第一种

    • 定义一个MyListener实现onClickListener接口

      Button bt1 = (Button) findViewById(R.id.bt1);
      bt1.setOnClickListener(new MyListener());
      

    第二种

    • 定义一个匿名内部类实现onClickListener接口

      Button bt2 = (Button) findViewById(R.id.bt2);
      bt2.setOnClickListener(new OnClickListener() {
      
          @Override
          public void onClick(View v) {
              System.out.println("第二种");
      
          }
      });
      

    第三种

    • 让当前activity实现onClickListener接口

      Button bt3 = (Button) findViewById(R.id.bt3);
      bt3.setOnClickListener(this);
      

    第四种

    • 给Button节点设置onClick属性,

       android:onClick="click"
      
    • 然后在activity中定义跟该属性值同名的方法

      public void click(View v){
          System.out.println("第四种");
      }
      

    短信发送器(掌握)

    功能:用户输入号码和短信内容,点击发送按钮,调用短信api把短信发送给指定号码

    1. 定义布局

    • 输入框的提示

      android:hint="请输入号码"  
      

    2. 完成点击事件

    • 先给Button组件设置onClick属性

    • onClick=”send”
    • 在Activity中定义此方法

    • public void send(View v){}

    3. 获取到用户输入的号码和内容

        EditText et_phone = (EditText) findViewById(R.id.et_phone);
        EditText et_content = (EditText) findViewById(R.id.et_content);
        String phone = et_phone.getText().toString();
        String content = et_content.getText().toString();
    

    4. 调用发送短信的api

        //调用发送短信的api
        SmsManager sm = SmsManager.getDefault();
    
        //发送短信
        sm.sendTextMessage(phone, null, content, null, null);
    

    * 添加权限

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

    * 如果短信过长,需要拆分

        List<String> smss = sm.divideMessage(content);
    

    常用布局

    线性布局

    • LinearLayout
    • 指定各个节点的排列方向

      android:orientation="horizontal"
      
    • 设置右对齐

      android:layout_gravity="right"
      
    • 当竖直布局时,只能左右对齐和水平居中,顶部底部对齐竖直居中无效
    • 当水平布局时,只能顶部底部对齐和竖直居中
    • 使用match_parent时注意不要把其他组件顶出去
    • 线性布局非常重要的一个属性:权重

      android:layout_weight="1"
      
    • 权重:按比例分配屏幕的剩余宽度或者高度

    常见布局

    相对布局(掌握)

    RelativeLayout
    • 组件默认左对齐、顶部对齐
    • 设置组件在指定组件的右边

       android:layout_toRightOf="@id/tv1"
      
    • 设置在指定组件的下边

      android:layout_below="@id/tv1"
      
    • 设置右对齐父元素

      android:layout_alignParentRight="true"
      
    • 设置与指定组件右对齐

       android:layout_alignRight="@id/tv1"
      

    线性布局(掌握)

    LinearLayout
    • 指定各个节点的排列方向

      android:orientation="horizontal"
      
    • 设置右对齐

      android:layout_gravity="right"
      
    • 当竖直布局时,只能左右对齐和水平居中,顶部底部对齐竖直居中无效
    • 当水平布局时,只能顶部底部对齐和竖直居中
    • 使用match_parent时注意不要把其他组件顶出去
    • 线性布局非常重要的一个属性:权重

      android:layout_weight="1"
      
    • 权重:按比例分配屏幕的剩余宽度或者高度

    帧布局(掌握)

    FrameLayout
    • 默认组件都是左对齐和顶部对齐,每个组件相当于一个div
    • 可以设置上下左右对齐,水平竖直居中,设置方式与线性布局一样

      android:layout_gravity="bottom"
      
    • 不能相对于其他组件布局

    表格布局(熟悉)

    TableLayout
    • 每个节点是一行,它的每个子节点是一列
    • 表格布局中的节点可以不设置宽高,因为设置了也无效

      • 根节点的子节点宽为匹配父元素,高为包裹内容
      • 节点的子节点宽为包裹内容,高为包裹内容
      • 以上默认属性无法修改
    • 根节点中可以设置以下属性,表示让第1列拉伸填满屏幕宽度的剩余空间

      android:stretchColumns="1"
      

    绝对布局(熟悉)

    AbsoluteLayout
    • 直接指定组件的x、y坐标

      android:layout_x="144dp"
      android:layout_y="154dp"
      

    logcat(掌握)

    • 日志信息总共分为5个等级
      • verbose
      • debug
      • info
      • warn
      • error
    • 定义过滤器方便查看
    • System.out.print输出的日志级别是info,tag是System.out
    • Android提供的日志输出api

      Log.v(TAG, "加油吧,童鞋们");
      Log.d(TAG, "加油吧,童鞋们");
      Log.i(TAG, "加油吧,童鞋们");
      Log.w(TAG, "加油吧,童鞋们");
      Log.e(TAG, "加油吧,童鞋们");
      

    文件读写操作

    • Ram内存:运行内存,相当于电脑的内存
    • Rom内存:内部存储空间,相当于电脑的硬盘
    • sd卡:外部存储空间,相当于电脑的移动硬盘

    在内部存储空间中读写文件(掌握)

    小案例:用户输入账号密码,勾选“记住账号密码”,点击登录按钮,登录的同时持久化保存账号和密码

    1. 定义布局
    2. 完成按钮的点击事件
    • 弹土司提示用户登录成功

      Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
      
    3. 拿到用户输入的数据
    • 判断用户是否勾选保存账号密码

      CheckBox cb = (CheckBox) findViewById(R.id.cb);
      if(cb.isChecked()){
      
      }
      
    4. 开启io流把文件写入内部存储
    • 直接开启文件输出流写数据

      //持久化保存数据
          File file = new File("data/data/com.itheima.rwinrom/info.txt");
          FileOutputStream fos = new FileOutputStream(file);
          fos.write((name + "##" + pass).getBytes());
          fos.close();
      
    • 读取数据前先检测文件是否存在

      if(file.exists())
      
    • 读取保存的数据,也是直接开文件输入流读取

      File file = new File("data/data/com.itheima.rwinrom/info.txt");
      FileInputStream fis = new FileInputStream(file);
      //把字节流转换成字符流
      BufferedReader br = new BufferedReader(new InputStreamReader(fis));
      String text = br.readLine();
      String[] s = text.split("##");
      
    • 读取到数据之后,回显至输入框

      et_name.setText(s[0]);
      et_pass.setText(s[1]);
      
    • 应用只能在自己的包名目录下创建文件,不能到别人家去创建

    直接复制项目

    • 需要改动的地方:
      • 项目名字
      • 应用包名
      • R文件重新导包

    使用路径api读写文件(掌握)

    • getFilesDir()得到的file对象的路径是data/data/com.itheima.rwinrom2/files
      • 存放在这个路径下的文件,只要你不删,它就一直在
    • getCacheDir()得到的file对象的路径是data/data/com.itheima.rwinrom2/cache

      • 存放在这个路径下的文件,当内存不足时,有可能被删除
    • 系统管理应用界面的清除缓存,会清除cache文件夹下的东西,清除数据,会清除整个包名目录下的东西


    在外部存储读写数据

    sd卡的路径(掌握)

    • 2.2之前,sd卡路径:sdcard
    • 4.3之前,sd卡路径:mnt/sdcard
    • 4.3开始,sd卡路径:storage/sdcard

    • 最简单的打开sd卡的方式

      File file = new File("sdcard/info.txt");
      
    • 写sd卡需要权限

      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
      
    • 读sd卡,在4.0之前不需要权限,4.0之后可以设置为需要

      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
      
    • 使用api获得sd卡的真实路径,部分手机品牌会更改sd卡的路径

      Environment.getExternalStorageDirectory()
      
    • 判断sd卡是否准备就绪

      if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
      

    查看源代码查找获取sd卡剩余容量的代码(掌握)

    • 导入Settings项目
    • 查找“可用空间”得到

       <string name="memory_available" msgid="418542433817289474">"可用空间"</string>
      
    • 查找”memory_available”,得到

      <Preference android:key="memory_sd_avail" 
          style="?android:attr/preferenceInformationStyle" 
          android:title="@string/memory_available"
          android:summary="00"/>
      
    • 查找”memory_sd_avail”,得到

      //这个字符串就是sd卡剩余容量
      formatSize(availableBlocks * blockSize) + readOnly
      //这两个参数相乘,得到sd卡以字节为单位的剩余容量
      availableBlocks * blockSize
      
    • 存储设备会被分为若干个区块,每个区块有固定的大小

    • 区块大小 * 区块数量 等于 存储设备的总大小

    Linux文件的访问权限(掌握)

    • 在Android中,每一个应用是一个独立的用户
    • drwxrwxrwx
    • 第1位:d表示文件夹,-表示文件
    • 第2-4位:rwx,表示这个文件的拥有者用户(owner)对该文件的权限
      • r:读
      • w:写
      • x:执行
    • 第5-7位:rwx,表示跟文件拥有者用户同组的用户(grouper)对该文件的权限
    • 第8-10位:rwx,表示其他用户组的用户(other)对该文件的权限

    openFileOutput的四种模式(熟悉)

    • MODE_PRIVATE:-rw-rw—-
    • MODE_APPEND:-rw-rw—-
    • MODE_WORLD_WRITEABLE:-rw-rw–w-
    • MODE_WORLD_READABLE:-rw-rw-r–

    SharedPreference(掌握)

    用SharedPreference存储账号密码

    • 往SharedPreference里写数据

      //拿到一个SharedPreference对象
      SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE);
      //拿到编辑器
      Editor ed = sp.edit();
      //写数据
      ed.putBoolean("name", name);
      ed.commit();
      
    • 从SharedPreference里取数据

      SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE);
      //从SharedPreference里取数据
      String name = sp.getBoolean("name", "");
      

    生成XML文件备份短信

    • 创建几个虚拟的短信对象,存在list中
    • 备份数据通常都是备份至sd卡

    使用StringBuffer拼接字符串(了解)

    • 把整个xml文件所有节点append到sb对象里

      sb.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>");
      //添加smss的开始节点
      sb.append("<smss>");
      .......
      
    • 把sb写到输出流中

      fos.write(sb.toString().getBytes());
      

    使用XMl序列化器生成xml文件(掌握)

    • 得到xml序列化器对象

      XmlSerializer xs = Xml.newSerializer();
      
    • 给序列化器设置输出流

      File file = new File(Environment.getExternalStorageDirectory(), "backupsms.xml");
      FileOutputStream fos = new FileOutputStream(file);
      //给序列化器指定好输出流
      xs.setOutput(fos, "utf-8");
      
    • 开始生成xml文件

      xs.startDocument("utf-8", true);
      xs.startTag(null, "smss");
      ......
      

    Pull解析xml文件(掌握)

    • 先自己写一个xml文件,存一些天气信息

    拿到xml文件

        InputStream is = getClassLoader().getResourceAsStream("weather.xml");
    

    拿到pull解析器

        XmlPullParser xp = Xml.newPullParser();
    

    开始解析

    • 拿到指针所在当前节点的事件类型

      int type = xp.getEventType();
      
    • 事件类型主要有五种

      • START_DOCUMENT:xml头的事件类型
      • END_DOCUMENT:xml尾的事件类型
      • START_TAG:开始节点的事件类型
      • END_TAG:结束节点的事件类型
      • TEXT:文本节点的事件类型
    • 如果获取到的事件类型不是END_DOCUMENT,就说明解析还没有完成,如果是,解析完成,while循环结束

      while(type != XmlPullParser.END_DOCUMENT)
      
    • 当我们解析到不同节点时,需要进行不同的操作,所以判断一下当前节点的name

      • 当解析到weather的开始节点时,new出list
      • 当解析到city的开始节点时,创建city对象,创建对象是为了更方便的保存即将解析到的文本
      • 当解析到name开始节点时,获取下一个节点的文本内容,temp、pm也是一样

        case XmlPullParser.START_TAG:
        //获取当前节点的名字
            if("weather".equals(xp.getName())){
                citys = new ArrayList<City>();
            }
            else if("city".equals(xp.getName())){
                city = new City();
            }
            else if("name".equals(xp.getName())){
                //获取当前节点的下一个节点的文本
                String name = xp.nextText();
                city.setName(name);
            }
            else if("temp".equals(xp.getName())){
                String temp = xp.nextText();
                city.setTemp(temp);
            }
            else if("pm".equals(xp.getName())){
                String pm = xp.nextText();
                city.setPm(pm);
            }
            break;
        
    • 当解析到city的结束节点时,说明city的三个子节点已经全部解析完了,把city对象添加至list

      case XmlPullParser.END_TAG:
          if("city".equals(xp.getName())){
                  citys.add(city);
          }
      

    测试(了解)

    • 黑盒测试
      • 测试逻辑业务
    • 白盒测试

      • 测试逻辑方法
    • 根据测试粒度

      • 方法测试:function test
      • 单元测试:unit test
      • 集成测试:integration test
      • 系统测试:system test
    • 根据测试暴力程度

      • 冒烟测试:smoke test
      • 压力测试:pressure test

    单元测试junit(熟悉)

    • 定义一个类继承AndroidTestCase,在类中定义方法,即可测试该方法

    • 在指定指令集时,targetPackage指定你要测试的应用的包名

      <instrumentation 
      android:name="android.test.InstrumentationTestRunner"
      android:targetPackage="com.itheima.junit"
      ></instrumentation>
      
    • 定义使用的类库

      <uses-library android:name="android.test.runner"></uses-library>
      
    • 断言的作用,检测运行结果和预期是否一致

    • 如果应用出现异常,会抛给测试框架

    SQLite数据库(掌握)

    • 轻量级关系型数据库
    • 创建数据库需要使用的api:SQLiteOpenHelper

      • 必须定义一个构造方法:

        //arg1:数据库文件的名字
        //arg2:游标工厂
        //arg3:数据库版本
        public MyOpenHelper(Context context, String name, CursorFactory factory, int version){
        
        }
        
      • 数据库被创建时会调用:onCreate方法
      • 数据库升级时会调用:onUpgrade方法

    创建数据库

    //创建OpenHelper对象
    MyOpenHelper oh = new MyOpenHelper(getContext(), "person.db", null, 1);
    //获得数据库对象,如果数据库不存在,先创建数据库,后获得,如果存在,则直接获得
    SQLiteDatabase db = oh.getWritableDatabase();
    
    • getWritableDatabase():打开可读写的数据库
    • getReadableDatabase():在磁盘空间不足时打开只读数据库,否则打开可读写数据库
    • 在创建数据库时创建表

      public void onCreate(SQLiteDatabase db) {
          // TODO Auto-generated method stub
          db.execSQL("create table person (_id integer primary key autoincrement, name char(10), phone char(20), money integer(20))");
      }
      

    数据库的增删改查(掌握)

    SQL语句

    • insert into person (name, phone, money) values (‘张三’, ‘159874611’, 2000);
    • delete from person where name = ‘李四’ and _id = 4;
    • update person set money = 6000 where name = ‘李四’;
    • select name, phone from person where name = ‘张三’;

    执行SQL语句实现增删改查

        //插入
        db.execSQL("insert into person (name, phone, money) values (?, ?, ?);", new Object[]{"张三", 15987461, 75000});
        //查找
        Cursor cs = db.rawQuery("select _id, name, money from person where name = ?;", new String[]{"张三"});
    

    * 测试方法执行前会调用此方法

        protected void setUp() throws Exception {
            super.setUp();
            //                  获取虚拟上下文对象
            oh = new MyOpenHelper(getContext(), "people.db", null, 1);
        }
    

    * 测试方法执行后会调用此方法:tearDown()

    使用api实现增删改查

    • 插入

      //以键值对的形式保存要存入数据库的数据
      ContentValues cv = new ContentValues();
      cv.put("name", "刘能");
      cv.put("phone", 1651646);
      cv.put("money", 3500);
      //返回值是改行的主键,如果出错返回-1
      long i = db.insert("person", null, cv);
      
    • 删除

      //返回值是删除的行数
      int i = db.delete("person", "_id = ? and name = ?", new String[]{"1", "张三"});
      
    • 修改

      ContentValues cv = new ContentValues();
      cv.put("money", 25000);
      int i = db.update("person", cv, "name = ?", new String[]{"赵四"});
      
    • 查询

      //arg1:要查询的字段
      //arg2:查询条件
      //arg3:填充查询条件的占位符
      Cursor cs = db.query("person", new String[]{"name", "money"}, "name = ?", new String[]{"张三"}, null, null, null);
      while(cs.moveToNext()){
          //                          获取指定列的索引值
          String name = cs.getString(cs.getColumnIndex("name"));
          String money = cs.getString(cs.getColumnIndex("money"));
          System.out.println(name + ";" + money);
      }
      

    事务

    • 保证多条SQL语句要么同时成功,要么同时失败
    • 最常见案例:银行转账
    • 事务api

      try {
          //开启事务
          db.beginTransaction();
          ...........
          //设置事务执行成功
          db.setTransactionSuccessful();
      } finally{
          //关闭事务
          //如果此时已经设置事务执行成功,则sql语句生效,否则不生效
          db.endTransaction();
      }
      

    把数据库的数据显示至屏幕(了解)

    1. 任意插入一些数据

      • 定义业务bean:Person.java
      • 读取数据库的所有数据

        Cursor cs = db.query(“person”, null, null, null, null, null, null);
        while(cs.moveToNext()){
        String name = cs.getString(cs.getColumnIndex(“name”));
        String phone = cs.getString(cs.getColumnIndex(“phone”));
        String money = cs.getString(cs.getColumnIndex(“money”));
        //把读到的数据封装至Person对象
        Person p = new Person(name, phone, money);
        //把person对象保存至集合中
        people.add(p);
        }

      • 把集合中的数据显示至屏幕

        LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
        for(Person p : people){
        //创建TextView,每条数据用一个文本框显示
        TextView tv = new TextView(this);
        tv.setText(p.toString());
        //把文本框设置为ll的子节点
        ll.addView(tv);
        }

      • 分页查询

        Cursor cs = db.query(“person”, null, null, null, null, null, null, “0, 10”);


    ListView(掌握)

    • 用于显示列表
    • MVC结构
      • M:model模型层,要显示的数据 ————people集合
      • V:view视图层,用户看到的界面 ————ListView
      • c:control控制层,操作数据如何显示 ————adapter对象
    • 每一个条目都是一个View对象

    BaseAdapter

    • 必须实现的两个方法

      • 第一个

        //系统调用此方法,用来获知模型层有多少条数据
        @Override
        public int getCount() {
            return people.size();
        }
        
      • 第二个

        //系统调用此方法,获取要显示至ListView的View对象
        //position:是return的View对象所对应的数据在集合中的位置
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            System.out.println("getView方法调用" + position);
            TextView tv = new TextView(MainActivity.this);
            //拿到集合中的元素
            Person p = people.get(position);
            tv.setText(p.toString());
        
            //把TextView的对象返回出去,它会变成ListView的条目
            return tv;
        }
        
    • 屏幕上能显示多少个条目,getView方法就会被调用多少次,屏幕向下滑动时,getView会继续被调用,创建更多的View对象显示至屏幕

    条目的缓存

    • 当条目划出屏幕时,系统会把该条目缓存至内存,当该条目再次进入屏幕,系统在重新调用getView时会把缓存的条目作为convertView参数传入,但是传入的条目不一定是之前被缓存的该条目,即系统有可能在调用getView方法获取第一个条目时,传入任意一个条目的缓存

    ArrayAdapter(熟悉)

    • 在条目中显示一个字符串

      String[] objects = new String[]{
              "张三",
              "李四",
              "王五"
      };
      
      ListView lv = (ListView) findViewById(R.id.lv);
      //arg1:指定要填充的布局文件
      //arg2:指定文本显示至哪一个文本框内
      lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item_array, R.id.tv_name, objects));
      

    SimpleAdapter(熟悉)

    • 可在条目中显示多种数据
    • 要显示的数据封装在List中,集合的每一个元素存放的是一个条目会显示的数据,因为可能会有多种数据,而集合的泛型只能指定一种数据,所以把数据先存放在Map中,在把Map放入List中

       List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
      
       //张三的头像和名字是两种类型的数据,先封装至Map
       Map<String, Object> map1 = new HashMap<String, Object>();
       map1.put("name", "张三");
       map1.put("image", R.drawable.photo1);
       //把Map封装至List
       data.add(map1);
       ...
      
    • 通过两个数组的下标对应指定数据存放入对应的控件中
      lv.setAdapter(new SimpleAdapter(this, data, R.layout.item_array,
      new String[]{“name”, “image”}, new int[]{R.id.tv_name, R.id.iv_photo}));

    网络图片查看器(掌握)

    • 确定图片的网址
    • 发送http请求
      //1.使用网址构造一个URL对象
      URL url = new URL(address);
      //2.获取连接对象,并没有建立连接
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
      //3.设置一些属性
      //设置连接和读取超时
      conn.setConnectTimeout(5000);
      conn.setReadTimeout(5000);
      //设置请求方法,注意必须大写
      conn.setRequestMethod(“GET”);
      //建立连接,发送get请求
      //conn.connect();
      //建立连接,然后获取响应吗,200说明请求成功
      conn.getResponseCode();

    • 服务器的图片是以流的形式返回给浏览器的
      //拿到服务器返回的输入流
      InputStream is = conn.getInputStream();
      //把流里的数据读取出来,并构造成图片
      Bitmap bm = BitmapFactory.decodeStream(is);

    • 把图片设置为ImageView的显示内容

      ImageView iv = (ImageView) findViewById(R.id.iv);
      iv.setImageBitmap(bm);
      
    • 添加权限

    主线程不能被阻塞

    • 在Android中,主线程被阻塞会导致应用不能刷新ui界面,不能响应用户操作,用户体验将非常差
    • 主线程阻塞时间过长,系统会抛出ANR异常
    • ANR:Application Not Response;应用无响应
    • 任何耗时操作都不可以写在主线程
    • 因为网络交互属于耗时操作,如果网速很慢,代码会阻塞,所以网络交互的代码不能运行在主线程

    只有主线程能刷新ui

    • 刷新ui的代码只能运行在主线程,运行在子线程是没有任何效果的
    • 如果需要在子线程中刷新ui,使用消息队列机制
    • 主线程也叫ui线程

    消息队列(重点掌握)

    • 主线程创建时,系统会同时创建消息队列对象(MessageQueue)和消息轮询器对象(Looper)
    • 轮询器的作用,就是不停的检测消息队列中是否有消息(Message)
    • Looper一旦发现Message Queue中有消息,就会把消息取出,然后把消息扔给Handler对象,Handler会调用自己的handleMessage方法来处理这条消息
    • handleMessage方法运行在主线程
    • 主线程创建时,消息队列和轮询器对象就会被创建,但是消息处理器对象,需要使用时,自行创建

      //消息队列
      Handler handler = new Handler(){
          //主线程中有一个消息轮询器looper,不断检测消息队列中是否有新消息,如果发现有新消息,自动调用此方法,注意此方法是在主线程中运行的
          public void handleMessage(android.os.Message msg) {
      
          }
      };
      
    • 在子线程中使用Handler对象往消息队列里发消息

      //创建消息对象
      Message msg = new Message();
      //消息的obj属性可以赋值任何对象,通过这个属性可以携带数据
      msg.obj = bm;
      //what属性相当于一个标签,用于区分出不同的消息,从而运行不能的代码
      msg.what = 1;
      //发送消息
      handler.sendMessage(msg);
      
    • 通过switch语句区分不同的消息

      public void handleMessage(android.os.Message msg) {
          switch (msg.what) {
          //如果是1,说明属于请求成功的消息
          case 1:
              ImageView iv = (ImageView) findViewById(R.id.iv);
              Bitmap bm = (Bitmap) msg.obj;
              iv.setImageBitmap(bm);
              break;
          case 2:
              Toast.makeText(MainActivity.this, "请求失败", 0).show();
              break;
          }       
      }
      

    加入缓存图片的功能(熟悉)

    • 把服务器返回的流里的数据读取出来,然后通过文件输入流写至本地文件

      //1.拿到服务器返回的输入流
      InputStream is = conn.getInputStream();
      //2.把流里的数据读取出来,并构造成图片
      
      FileOutputStream fos = new FileOutputStream(file);
      byte[] b = new byte[1024];
      int len = 0;
      while((len = is.read(b)) != -1){
          fos.write(b, 0, len);
      }
      
    • 创建bitmap对象的代码改成

      Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
      
    • 每次发送请求前检测一下在缓存中是否存在同名图片,如果存在,则读取缓存

    获取开源代码的网站(熟悉)

    • code.google.com
    • github.com
    • 在github搜索smart-image-view
    • 下载开源项目smart-image-view
    • 使用自定义组件时,标签名字要写包名

      <com.loopj.android.image.SmartImageView/>
      
    • SmartImageView的使用

      SmartImageView siv = (SmartImageView) findViewById(R.id.siv);
      siv.setImageUrl("http://192.168.1.102:8080/dd.jpg");
      

    Html源文件查看器(掌握)

    • 发送GET请求

      URL url = new URL(path);
      //获取连接对象
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
      //设置连接属性
      conn.setRequestMethod("GET");
      conn.setConnectTimeout(5000);
      conn.setReadTimeout(5000);
      //建立连接,获取响应吗
      if(conn.getResponseCode() == 200){
      
      }
      
    • 获取服务器返回的流,从流中把html源码读取出来

      byte[] b = new byte[1024];
      int len = 0;
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      while((len = is.read(b)) != -1){
          //把读到的字节先写入字节数组输出流中存起来
          bos.write(b, 0, len);
      }
      //把字节数组输出流中的内容转换成字符串
      //默认使用utf-8
      text = new String(bos.toByteArray());
      

    乱码的处理

    • 乱码的出现是因为服务器和客户端码表不一致导致

      //手动指定码表
      text = new String(bos.toByteArray(), "gb2312");
      

    提交数据(掌握)

    GET方式提交数据

    • get方式提交的数据是直接拼接在url的末尾

      final String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + name + "&pass=" + pass;
      
    • 发送get请求,代码和之前一样

      URL url = new URL(path);
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
      conn.setRequestMethod("GET");
      conn.setReadTimeout(5000);
      conn.setConnectTimeout(5000);
      if(conn.getResponseCode() == 200){
      
      }
      
    • 浏览器在发送请求携带数据时会对数据进行URL编码,我们写代码时也需要为中文进行URL编码

      String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass;
      

    POST方式提交数据

    • post提交数据是用输出流写给服务器的
    • 协议头中多了两个属性

      • Content-Type: application/x-www-form-urlencoded,描述提交的数据的mimetype
      • Content-Length: 32,描述提交的数据的长度

        //给请求头添加post多出来的两个属性
        String data = "name=" + URLEncoder.encode(name) + "&pass=" + pass;
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("Content-Length", data.length() + "");
        
    • 设置允许打开post请求的流

      conn.setDoOutput(true);
      
    • 获取连接对象的输出流,往流里写要提交给服务器的数据

      OutputStream os = conn.getOutputStream();
      os.write(data.getBytes());
      

    HttpClient(掌握)

    发送get请求

    • 创建一个客户端对象

      HttpClient client = new DefaultHttpClient();

    • 创建一个get请求对象

      HttpGet hg = new HttpGet(path);

    • 发送get请求,建立连接,返回响应头对象

      HttpResponse hr = client.execute(hg);

    • 获取状态行对象,获取状态码,如果为200则说明请求成功

      if(hr.getStatusLine().getStatusCode() == 200){
          //拿到服务器返回的输入流
          InputStream is = hr.getEntity().getContent();
          String text = Utils.getTextFromStream(is);
      }
      

    发送post请求

        //创建一个客户端对象
        HttpClient client = new DefaultHttpClient();
        //创建一个post请求对象
        HttpPost hp = new HttpPost(path);
    
    • 往post对象里放入要提交给服务器的数据

      //要提交的数据以键值对的形式存在BasicNameValuePair对象中
      List<NameValuePair> parameters = new ArrayList<NameValuePair>();
      BasicNameValuePair bnvp = new BasicNameValuePair("name", name);
      BasicNameValuePair bnvp2 = new BasicNameValuePair("pass", pass);
      parameters.add(bnvp);
      parameters.add(bnvp2);
      //创建实体对象,指定进行URL编码的码表
      UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "utf-8");
      //为post请求设置实体
      hp.setEntity(entity);
      

    异步HttpClient框架(熟悉)

    发送get请求

        //创建异步的httpclient对象
        AsyncHttpClient ahc = new AsyncHttpClient();
        //发送get请求
        ahc.get(path, new MyHandler());
    

    * 注意AsyncHttpResponseHandler两个方法的调用时机

        class MyHandler extends AsyncHttpResponseHandler{
    
            //http请求成功,返回码为200,系统回调此方法
            @Override
            public void onSuccess(int statusCode, Header[] headers,
                    //responseBody的内容就是服务器返回的数据
                    byte[] responseBody) {
                Toast.makeText(MainActivity.this, new String(responseBody), 0).show();
    
            }
    
            //http请求失败,返回码不为200,系统回调此方法
            @Override
            public void onFailure(int statusCode, Header[] headers,
                    byte[] responseBody, Throwable error) {
                Toast.makeText(MainActivity.this, "返回码不为200", 0).show();
    
            }
    
        }
    

    发送post请求

    • 使用RequestParams对象封装要携带的数据

      //创建异步httpclient对象
      AsyncHttpClient ahc = new AsyncHttpClient();
      //创建RequestParams封装要携带的数据
      RequestParams rp = new RequestParams();
      rp.add("name", name);
      rp.add("pass", pass);
      //发送post请求
      ahc.post(path, rp, new MyHandler());
      

    多线程下载(掌握)

    原理:服务器CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源

    确定每条线程下载多少数据(掌握)

    • 发送http请求至下载地址

      String path = "http://192.168.1.102:8080/editplus.exe";     
      URL url = new URL(path);
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
      conn.setReadTimeout(5000);
      conn.setConnectTimeout(5000);
      conn.setRequestMethod("GET");                   
      
    • 获取文件总长度,然后创建长度一致的临时文件

      if(conn.getResponseCode() == 200){
          //获得服务器流中数据的长度
          int length = conn.getContentLength();
          //创建一个临时文件存储下载的数据
          RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd");
          //设置临时文件的大小
          raf.setLength(length);
          raf.close();
      
    • 确定线程下载多少数据

          //计算每个线程下载多少数据
          int blockSize = length / THREAD_COUNT;
      

    计算每条线程下载数据的开始位置和结束位置(掌握)

        for(int id = 1; id <= 3; id++){
            //计算每个线程下载数据的开始位置和结束位置
            int startIndex = (id - 1) * blockSize;
            int endIndex = id * blockSize - 1;
            if(id == THREAD_COUNT){
                endIndex = length;
            }
    
            //开启线程,按照计算出来的开始结束位置开始下载数据
            new DownLoadThread(startIndex, endIndex, id).start();
        }
    

    再次发送请求至下载地址,请求开始位置至结束位置的数据(掌握)

        String path = "http://192.168.1.102:8080/editplus.exe";
    
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(5000);
        conn.setConnectTimeout(5000);
        conn.setRequestMethod("GET");
    
        //向服务器请求部分数据
        conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
        conn.connect();
    

    * 下载请求到的数据,存放至临时文件中

        if(conn.getResponseCode() == 206){
            InputStream is = conn.getInputStream();
            RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd");
            //指定从哪个位置开始存放数据
            raf.seek(startIndex);
            byte[] b = new byte[1024];
            int len;
            while((len = is.read(b)) != -1){
                raf.write(b, 0, len);
            }
            raf.close();
        }
    

    带断点续传的多线程下载(掌握)

    • 定义一个int变量记录每条线程下载的数据总长度,然后加上该线程的下载开始位置,得到的结果就是下次下载时,该线程的开始位置,把得到的结果存入缓存文件

      //用来记录当前线程总的下载长度
      int total = 0;
      while((len = is.read(b)) != -1){
          raf.write(b, 0, len);
          total += len;
          //每次下载都把新的下载位置写入缓存文本文件
          RandomAccessFile raf2 = new RandomAccessFile(threadId + ".txt", "rwd");
          raf2.write((startIndex + total + "").getBytes());
          raf2.close();
      }
      
    • 下次下载开始时,先读取缓存文件中的值,得到的值就是该线程新的开始位置

      FileInputStream fis = new FileInputStream(file);
      BufferedReader br = new BufferedReader(new InputStreamReader(fis));
      String text = br.readLine();
      int newStartIndex = Integer.parseInt(text);
      //把读到的值作为新的开始位置
      startIndex = newStartIndex;
      fis.close();
      
    • 三条线程都下载完毕之后,删除缓存文件

      RUNNING_THREAD--;
      if(RUNNING_THREAD == 0){
          for(int i = 0; i <= 3; i++){
              File f = new File(i + ".txt");
              f.delete();
          }
      }
      

    手机版的断点续传多线程下载器

    • 把刚才的代码直接粘贴过来就能用,记得在访问文件时的路径要改成Android的目录,添加访问网络和外部存储的路径

    用进度条显示下载进度(掌握)

    • 拿到下载文件总长度时,设置进度条的最大值

      //设置进度条的最大值
      pb.setMax(length);
      
    • 进度条需要显示三条线程的整体下载进度,所以三条线程每下载一次,就要把新下载的长度加入进度条

      • 定义一个int全局变量,记录三条线程的总下载长度

        int progress;
        
      • 刷新进度条

        while((len = is.read(b)) != -1){
            raf.write(b, 0, len);
        
        
            //把当前线程本次下载的长度加到进度条里
            progress += len;
            pb.setProgress(progress);
        
    • 每次断点下载时,从新的开始位置开始下载,进度条也要从新的位置开始显示,在读取缓存文件获取新的下载开始位置时,也要处理进度条进度

      FileInputStream fis = new FileInputStream(file);
      BufferedReader br = new BufferedReader(new InputStreamReader(fis));
      String text = br.readLine();
      int newStartIndex = Integer.parseInt(text);
      
      //新开始位置减去原本的开始位置,得到已经下载的数据长度
      int alreadyDownload = newStartIndex - startIndex;
      //把已经下载的长度设置入进度条
      progress += alreadyDownload;
      

    添加文本框显示百分比进度(熟悉)

        tv.setText(progress * 100 / pb.getMax() + "%");
    

    HttpUtils框架(github上面的开源代码)的使用(熟悉)

    HttpUtils本身就支持多线程断点续传,使用起来非常的方便
    * 创建HttpUtils对象

        HttpUtils http = new HttpUtils();
    

    * 下载文件

        http.download(url, //下载请求的网址
                target, //下载的数据保存路径和文件名
                true, //是否开启断点续传
                true, //如果服务器响应头中包含了文件名,那么下载完毕后自动重命名
                new RequestCallBack<File>() {//侦听下载状态
    
            //下载成功此方法调用
            @Override
            public void onSuccess(ResponseInfo<File> arg0) {
                tv.setText("下载成功" + arg0.result.getPath());
            }
    
            //下载失败此方法调用,比如文件已经下载、没有网络权限、文件访问不到,方法传入一个字符串参数告知失败原因
            @Override
            public void onFailure(HttpException arg0, String arg1) {
                tv.setText("下载失败" + arg1);
            }
    
            //在下载过程中不断的调用,用于刷新进度条
            @Override
            public void onLoading(long total, long current, boolean isUploading) {
                super.onLoading(total, current, isUploading);
                //设置进度条总长度
                pb.setMax((int) total);
                //设置进度条当前进度
                pb.setProgress((int) current);
                tv_progress.setText(current * 100 / total + "%");
            }
        });
    

    创建第二个Activity(掌握)

    • 需要在清单文件中为其配置一个activity标签
    • 标签中如果带有这个子节点,则会在系统中多创建一个快捷图标

       <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
      
    • 一个应用程序可以在桌面创建多个快捷图标。
    • activity的名称、图标可以和应用程序的名称、图标不相同

      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name"
      

    Activity的跳转(掌握)

    Activity的跳转需要创建Intent对象,通过设置intent对象的参数指定要跳转Activity

    通过设置Activity的包名和类名实现跳转,称为显式意图

    通过指定动作实现跳转,称为隐式意图

    显式意图(掌握)

    • 跳转至同一项目下的另一个Activity,直接指定该Activity的字节码即可

      Intent intent = new Intent();
      intent.setClass(this, SecondActivity.class);
      startActivity(intent);
      
    • 跳转至其他应用中的Activity,需要指定该应用的包名和该Activity的类名

      Intent intent = new Intent();
      //启动系统自带的拨号器应用
      intent.setClassName("com.android.dialer", "com.android.dialer.DialtactsActivity");
      startActivity(intent);
      

    隐式意图(掌握)

    • 隐式意图跳转至指定Activity

      Intent intent = new Intent();
      //启动系统自带的拨号器应用
      intent.setAction(Intent.ACTION_DIAL);
      startActivity(intent);
      
    • 要让一个Activity可以被隐式启动,需要在清单文件的activity节点中设置intent-filter子节点

      <intent-filter >
          <action android:name="com.itheima.second"/>
          <data android:scheme="asd" android:mimeType="aa/bb"/>
          <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
      
      • action 指定动作(可以自定义,可以使用系统自带的)
      • data 指定数据(操作什么内容)
      • category 类别 (默认类别,机顶盒,车载电脑)
    • 隐式意图启动Activity,需要为intent设置以上三个属性,且值必须与该Activity在清单文件中对三个属性的定义匹配
    • intent-filter节点及其子节点都可以同时定义多个,隐式启动时只需与任意一个匹配即可
    获取通过setData传递的数据(掌握)
        //获取启动此Activity的intent对象
        Intent intent = getIntent();
        Uri uri = intent.getData();
    

    显式意图和隐式意图的应用场景(掌握)

    • 显式意图用于启动同一应用中的Activity
    • 隐式意图用于启动不同应用中的Activity
      • 如果系统中存在多个Activity的intent-filter同时与你的intent匹配,那么系统会显示一个对话框,列出所有匹配的Activity,由用户选择启动哪一个

    Activity跳转时的数据传递(掌握)

    • Activity通过Intent启动时,可以通过Intent对象携带数据到目标Activity

      Intent intent = new Intent(this, SecondActivity.class);
      intent.putExtra("maleName", maleName);
      intent.putExtra("femaleName", femaleName);
      startActivity(intent);
      
    • 在目标Activity中取出数据

      Intent intent = getIntent();
      String maleName = intent.getStringExtra("maleName");
      String femaleName = intent.getStringExtra("femaleName");
      

    Activity生命周期(掌握)

    void onCreate()

    • Activity已经被创建完毕

    void onStart()

    • Activity已经显示在屏幕,但没有得到焦点

    void onResume()

    • Activity得到焦点,可以与用户交互

    void onPause()

    • Activity失去焦点,无法再与用户交互,但依然可见

    void onStop()

    • Activity不可见,进入后台

    void onDestroy()

    • Activity被销毁

    void onRestart()

    • Activity从不可见变成可见时会执行此方法

    使用场景

    • Activity创建时需要初始化资源,销毁时需要释放资源;或者播放器应用,在界面进入后台时需要自动暂停

    完整生命周期(entire lifetime)

    onCreate–>onStart–>onResume–>onPause–>onStop–>onDestory

    可视生命周期(visible lifetime)

    onStart–>onResume–>onPause–>onStop

    前台生命周期(foreground lifetime)

    onResume–>onPause


    Activity的四种启动模式(掌握)

    每个应用会有一个Activity任务栈,存放已启动的Activity

    Activity的启动模式,修改任务栈的排列情况

    • standard 标准启动模式
    • singleTop 单一顶部模式
      • 如果任务栈的栈顶存在这个要开启的activity,不会重新的创建activity,而是复用已经存在的activity。保证栈顶如果存在,不会重复创建。
      • 应用场景:浏览器的书签
    • singeTask 单一任务栈,在当前任务栈里面只能有一个实例存在

      • 当开启activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的activity,并且把这个activity上面的所有的别的activity都清空,复用这个已经存在的activity。保证整个任务栈里面只有一个实例存在
      • 应用场景:浏览器的activity
      • 如果一个activity的创建需要占用大量的系统资源(cpu,内存)一般配置这个activity为singletask的启动模式。webkit内核 c代码
    • singleInstance启动模式非常特殊, activity会运行在自己的任务栈里面,并且这个任务栈里面只有一个实例存在

      • 如果你要保证一个activity在整个手机操作系统里面只有一个实例存在,使用singleInstance
      • 应用场景: 电话拨打界面

    横竖屏切换的生命周期(熟悉)

    默认情况下 ,横竖屏切换, 销毁当前的activity,重新创建一个新的activity

    快捷键ctrl+F11

    在一些特殊的应用程序常见下,比如游戏,不希望横竖屏切换activity被销毁重新创建
    需求:禁用掉横竖屏切换的生命周期

    1. 横竖屏写死
      android:screenOrientation=”landscape”
      android:screenOrientation=”portrait”

    2. 让系统的环境 不再去敏感横竖屏的切换。

       android:configChanges="orientation|screenSize|keyboardHidden"
      

    掌握开启activity获取返回值(掌握)

    从A界面打开B界面, B界面关闭的时候,返回一个数据给A界面

    步骤:
    1. 开启activity并且获取返回值

      startActivityForResult(intent, 0);
      
    2. 在新开启的界面里面实现设置数据的逻辑

      Intent data = new Intent();
      data.putExtra("phone", phone);
      //设置一个结果数据,数据会返回给调用者
      setResult(0, data);
      finish();//关闭掉当前的activity,才会返回数据
      
    3. 在开启者activity里面实现方法

      //通过data获取返回的数据
      onActivityResult(int requestCode, int resultCode, Intent data) {
      
      }
      
    4. 通过判断请求码和结果码确定返回值的作用

    广播(掌握)

    • 广播的概念
      • 现实:电台通过发送广播发布消息,买个收音机,就能收听
      • Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件。
        Android系统在运行的过程中,会产生很多事件,比如开机、电量改变、收发短信、拨打电话、屏幕解锁

    广播接收者(掌握)

    • 当一条广播被发送出来时,系统是在所有清单文件中遍历,通过匹配意图过滤器找到能接收这条广播的广播接收者

    IP拨号器(掌握)

    原理:接收拨打电话的广播,修改广播内携带的电话号码
    * 定义广播接收者接收打电话广播

    public class CallReceiver extends BroadcastReceiver {
    
        //当广播接收者接收到广播时,此方法会调用
        @Override
        public void onReceive(Context context, Intent intent) {
            //拿到用户拨打的号码
            String number = getResultData();
            //修改广播内的号码
            setResultData("17951" + number);
        }
    }
    

    * 在清单文件中定义该广播接收者接收的广播类型

        <receiver android:name="com.itheima.ipdialer.CallReceiver">
            <intent-filter >
                <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
            </intent-filter>
        </receiver>
    

    * 接收打电话广播需要权限

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

    * 即使广播接收者的进程没有启动,当系统发送的广播可以被该接收者接收时,系统会自动启动该接收者所在的进程


    短信拦截器(熟悉)

    系统收到短信时会产生一条广播,广播中包含了短信的号码和内容

    • 定义广播接收者接收短信广播

      public void onReceive(Context context, Intent intent) {
      //拿到广播里携带的短信内容
      Bundle bundle = intent.getExtras();
      Object[] objects = (Object[]) bundle.get("pdus");
      for(Object ob : objects ){
          //通过object对象创建一个短信对象
          SmsMessage sms = SmsMessage.createFromPdu((byte[])ob);
          System.out.println(sms.getMessageBody());
          System.out.println(sms.getOriginatingAddress());
      }
      

      }

    • 系统创建广播时,把短信存放到一个数组,然后把数据以pdus为key存入bundle,再把bundle存入intent
    • 清单文件中配置广播接收者接收的广播类型,注意要设置优先级属性,要保证优先级高于短信应用,才可以实现拦截

      <receiver android:name="com.itheima.smslistener.SmsReceiver">
          <intent-filter android:priority="1000">
              <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
          </intent-filter>
      </receiver>
      
    • 添加权限

      <uses-permission android:name="android.permission.RECEIVE_SMS"/>
      
    • 4.0之后,广播接收者所在的应用必须启动过一次,才能生效

    • 4.0之后,如果广播接收者所在应用被用户手动关闭了,那么再也不会启动了,直到用户再次手动启动该应用

    监听SD卡状态(掌握)

    • 清单文件中定义广播接收者接收的类型,监听SD卡常见的三种状态,所以广播接收者需要接收三种广播

       <receiver android:name="com.itheima.sdcradlistener.SDCardReceiver">
          <intent-filter >
              <action android:name="android.intent.action.MEDIA_MOUNTED"/>
              <action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
              <action android:name="android.intent.action.MEDIA_REMOVED"/>
              <data android:scheme="file"/>
          </intent-filter>
      </receiver>
      
    • 广播接收者的定义

      public class SDCardReceiver extends BroadcastReceiver {
          @Override
          public void onReceive(Context context, Intent intent) {
              // 区分接收到的是哪个广播
              String action = intent.getAction();
      
              if(action.equals("android.intent.action.MEDIA_MOUNTED")){
                  System.out.println("sd卡就绪");
              }
              else if(action.equals("android.intent.action.MEDIA_UNMOUNTED")){
                  System.out.println("sd卡被移除");
              }
              else if(action.equals("android.intent.action.MEDIA_REMOVED")){
                  System.out.println("sd卡被拔出");
              }
          }
      }
      

    勒索软件(掌握)

    • 接收开机广播,在广播接收者中启动勒索的Activity
    • 清单文件中配置接收开机广播

      <receiver android:name="com.itheima.lesuo.BootReceiver">
          <intent-filter >
              <action android:name="android.intent.action.BOOT_COMPLETED"/>
          </intent-filter>
      </receiver>
      
    • 权限

      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
      
    • 定义广播接收者

      @Override
      public void onReceive(Context context, Intent intent) {
          //开机的时候就启动勒索软件
          Intent it = new Intent(context, MainActivity.class);        
          context.startActivity(it);
      }
      
    • 以上代码还不能启动MainActivity,因为广播接收者的启动,并不会创建任务栈,那么没有任务栈,就无法启动activity
    • 手动设置创建新任务栈的flag

      it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      

    监听应用的安装、卸载、更新(熟悉)

    原理:应用在安装卸载更新时,系统会发送广播,广播里会携带应用的包名
    * 清单文件定义广播接收者接收的类型,因为要监听应用的三个动作,所以需要接收三种广播

        <receiver android:name="com.itheima.app.AppReceiver">
            <intent-filter >
                <action android:name="android.intent.action.PACKAGE_ADDED"/>
                <action android:name="android.intent.action.PACKAGE_REPLACED"/>
                <action android:name="android.intent.action.PACKAGE_REMOVED"/>
                <data android:scheme="package"/>
            </intent-filter>
        </receiver>
    

    * 广播接收者的定义

        public void onReceive(Context context, Intent intent) {
            //区分接收到的是哪种广播
            String action = intent.getAction();
            //获取广播中包含的应用包名
            Uri uri = intent.getData();
            if(action.equals("android.intent.action.PACKAGE_ADDED")){
                System.out.println(uri + "被安装了");
            }
            else if(action.equals("android.intent.action.PACKAGE_REPLACED")){
                System.out.println(uri + "被更新了");
            }
            else if(action.equals("android.intent.action.PACKAGE_REMOVED")){
                System.out.println(uri + "被卸载了");
            }
        }
    

    广播的两种类型(掌握)

    • 无序广播:所有跟广播的intent匹配的广播接收者都可以收到该广播,并且是没有先后顺序(同时收到)
    • 有序广播:所有跟广播的intent匹配的广播接收者都可以收到该广播,但是会按照广播接收者的优先级来决定接收的先后顺序
      • 优先级的定义:-1000~1000
      • 结果接收者:所有广播接收者都接收到广播之后,它才接收,并且一定会接收
      • abortBroadCast:阻止其他接收者接收这条广播,类似拦截,只有有序广播可以被拦截

    Service(掌握)

    • 就是默默运行在后台的组件,可以理解为是没有前台的activity,适合用来运行不需要前台界面的代码
    • 服务可以被手动关闭,不会重启,但是如果被自动关闭,内存充足就会重启
    • startService启动服务的生命周期
      • onCreate-onStartCommand-onDestroy
    • 重复的调用startService会导致onStartCommand被重复调用

    进程优先级(掌握)

    1. 前台进程:拥有一个正在与用户交互的activity(onResume方法被调用)的进程
    2. 可见进程:拥有一个非前台,但是对用户可见的activity(onPause方法被调用)的进程
    3. 服务进程:拥有一个通过startService方法启动的服务的进程
    4. 后台进程:拥有一个后台activity(onStop方法被调用)的进程
    5. 空进程:没有拥有任何活动的应用组件的进程,也就是没有任何服务和activity在运行

    电话窃听器(熟悉)

    • 电话状态:空闲、响铃、接听
    • 获取电话管理器,设置侦听

      TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
      tm.listen(new MyPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE);
      
    • 侦听对象的实现

      class MyPhoneStateListener extends PhoneStateListener{
      
          //当电话状态改变时,此方法调用
          @Override
          public void onCallStateChanged(int state, String incomingNumber) {
              // TODO Auto-generated method stub
              super.onCallStateChanged(state, incomingNumber);
              switch (state) {
              case TelephonyManager.CALL_STATE_IDLE://空闲
                  if(recorder != null){
                      recorder.stop();
                      recorder.release();
                  }
                  break;
              case TelephonyManager.CALL_STATE_OFFHOOK://摘机
                  if(recorder != null){
                      recorder.start();
                  }
                  break;
              case TelephonyManager.CALL_STATE_RINGING://响铃
                  recorder = new MediaRecorder();
                  //设置声音来源
                  recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                  //设置音频文件格式
                  recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                  recorder.setOutputFile("sdcard/haha.3gp");
                  //设置音频文件编码
                  recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                  try {
                      recorder.prepare();
                  } catch (IllegalStateException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  } catch (IOException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
                  break;
              }
          }
      }
      

    服务两种启动方式(掌握)

    • startService
      • 开始服务,会使进程变成为服务进程
      • 启动服务的activity和服务不再有一毛钱关系
    • bindService
      • 绑定服务不会使进程变成服务进程
      • 绑定服务,是activity与服务建立连接,如果activity销毁了,服务也会被解绑并销毁,但是如果服务被销毁,activity不会被销毁
    • 绑定服务和解绑服务的生命周期方法:onCreate->onBind->onUnbind->onDestroy

    找领导办证(掌握)

    • 把服务看成一个领导,服务中有一个banZheng方法,如何才能访问?
    • 绑定服务时,会触发服务的onBind方法,此方法会返回一个Ibinder的对象给MainActivity,通过这个对象访问服务中的方法
    • 绑定服务

      Intent intent = new Intent(this, BanZhengService.class);
      bindService(intent, conn, BIND_AUTO_CREATE);
      
    • 绑定服务时要求传入一个ServiceConnection实现类的对象
    • 定义这个实现类

      class MyServiceconn implements ServiceConnection{
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
          zjr = (PublicBusiness) service;
      }
      @Override
      public void onServiceDisconnected(ComponentName name) { 
      }
      

      }

    • 创建实现类对象

       conn = new MyServiceconn();
      
    • 在服务中定义一个类实现Ibinder接口,以在onBind方法中返回

      class ZhongJianRen extends Binder implements PublicBusiness{
      public void QianXian(){
          //访问服务中的banZheng方法
          BanZheng();
      }   
      public void daMaJiang(){
      
      }
      

      }

    • 把QianXian方法抽取到接口PublicBusiness中定义

    两种启动方法混合使用(掌握)

    • 用服务实现音乐播放时,因为音乐播放必须运行在服务进程中,可是音乐服务中的方法,需要被前台Activity所调用,所以需要混合启动音乐服务
    • 先start,再bind,销毁时先unbind,在stop

    使用服务注册广播接收者(掌握)

    • Android四大组件都要在清单文件中注册
    • 广播接收者可以使用清单文件注册
      • 一旦应用部署,广播接收者就生效了,直到用户手动停止应用或者应用被删除
    • 广播接收者可以使用代码注册
      • 需要广播接收者运行时,使用代码注册,不需要时,可以使用代码解除注册
    • 电量改变、屏幕开关,必须使用代码注册

    • 注册广播接收者

      //创建广播接收者对象
      receiver = new ScreenOnOffReceiver();
      //通过IntentFilter对象指定广播接收者接收什么类型的广播
      IntentFilter filter = new IntentFilter();
      filter.addAction(Intent.ACTION_SCREEN_OFF);
      filter.addAction(Intent.ACTION_SCREEN_ON);
      
      //注册广播接收者
      registerReceiver(receiver, filter);
      
    • 解除注册广播接收者

      unregisterReceiver(receiver);
      
    • 解除注册之后,广播接收者将失去作用

    本地服务:服务和启动它的组件在同一个进程

    远程服务:服务和启动它的组件不在同一个进程

    • 远程服务只能隐式启动,类似隐式启动Activity,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点

    AIDL(掌握)

    • Android interface definition language
    • 安卓接口定义语言
    • 作用:跨进程通信
    • 应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,使用aidl,就可以在其他应用中拿到中间人类所实现的接口

    支付宝远程服务

    1. 定义支付宝的服务,在服务中定义pay方法
    2. 定义中间人对象,把pay方法抽取成接口
    3. 把抽取出来的接口后缀名改成aidl
    4. 中间人对象直接继承Stub对象
    5. 注册这个支付宝服务,定义它的intent-Filter

    需要支付的应用

    1. 把刚才定义好的aidl文件拷贝过来,注意aidl文件所在的包名必须跟原包名一致
    2. 远程绑定支付宝的服务,通过onServiceConnected方法我们可以拿到中间人对象
    3. 把中间人对象通过Stub.asInterface方法强转成定义了pay方法的接口
    4. 调用中间人的pay方法

    五种前台进程(掌握)

    1. activity执行了onresume方法,获得焦点
    2. 拥有一个跟正在与用户交互的activity绑定的服务
    3. 拥有一个服务执行了startForeground()方法
    4. 拥有一个正在执行onCreate()、onStart()或者onDestroy()方法中的任意一个的服务
    5. 拥有一个正在执行onReceive方法的广播接收者

    两种可见进程(掌握)

    1. activity执行了onPause方法,失去焦点,但是可见
    2. 拥有一个跟可见或前台activity绑定的服务

    对话框

    确定取消对话框(掌握)

    • 创建对话框构建器对象,类似工厂模式

    • AlertDialog.Builder builder = new Builder(this);
    • 设置标题和正文

    • builder.setTitle(“警告”);
      builder.setMessage(“若练此功,必先自宫”);
    • 设置确定和取消按钮

      builder.setPositiveButton("现在自宫", new OnClickListener() {
      
          @Override
          public void onClick(DialogInterface dialog, int which) {
              // TODO Auto-generated method stub
              Toast.makeText(MainActivity.this, "恭喜你自宫成功,现在程序退出", 0).show();
          }
      });
      
      builder.setNegativeButton("下次再说", new OnClickListener() {
      
          @Override
          public void onClick(DialogInterface dialog, int which) {
              // TODO Auto-generated method stub
              Toast.makeText(MainActivity.this, "若不自宫,一定不成功", 0).show();
          }
      });
      
    • 使用构建器创建出对话框对象

      AlertDialog ad = builder.create();
      ad.show();
      

    单选对话框(熟悉)

        AlertDialog.Builder builder = new Builder(this);
        builder.setTitle("选择你的性别");
    

    * 定义单选选项
    *
    final String[] items = new String[]{
    “男”, “女”, “其他”
    };
    //-1表示没有默认选择
    //点击侦听的导包要注意别导错
    builder.setSingleChoiceItems(items, -1, new OnClickListener() {

            //which表示点击的是哪一个选项
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(MainActivity.this, "您选择了" + items[which], 0).show();
                //对话框消失
                dialog.dismiss();
            }
        });
    
        builder.show();
    

    多选对话框(熟悉)

        AlertDialog.Builder builder = new Builder(this);
        builder.setTitle("请选择你认为最帅的人");
    

    * 定义多选的选项,因为可以多选,所以需要一个boolean数组来记录哪些选项被选了
    *
    final String[] items = new String[]{
    “赵帅哥”,
    “赵师哥”,
    “赵老师”,
    “侃哥”
    };
    //true表示对应位置的选项被选了
    final boolean[] checkedItems = new boolean[]{
    true,
    false,
    false,
    false,
    };
    builder.setMultiChoiceItems(items, checkedItems, new OnMultiChoiceClickListener() {

            //点击某个选项,如果该选项之前没被选择,那么此时isChecked的值为true
            @Override
            public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                checkedItems[which] = isChecked;
            }
        });
    
        builder.setPositiveButton("确定", new OnClickListener() {
    
            @Override
            public void onClick(DialogInterface dialog, int which) {
                StringBuffer sb = new StringBuffer();
                for(int i = 0;i < items.length; i++){
                    sb.append(checkedItems[i] ? items[i] + " " : "");
                }
                Toast.makeText(MainActivity.this, sb.toString(), 0).show();
            }
        });
        builder.show();
    

    国际化(掌握)


    样式与主题(熟悉)

    • 样式与主题定义方式一样
    • 样式用于布局文件中的组件
    • 主题用于Activity

    多媒体概念(了解)

    • 文字、图片、音频、视频

    计算机图片大小的计算(掌握)

    图片大小 = 图片的总像素 * 每个像素占用的大小

    • 单色图:每个像素占用1/8个字节
    • 16色图:每个像素占用1/2个字节
    • 256色图:每个像素占用1个字节
    • 24位图:每个像素占用3个字节

    加载大图片到内存(掌握)

    Android系统以ARGB表示每个像素,所以每个像素占用4个字节,很容易内存溢出

    对图片进行缩放(掌握)

    • 获取屏幕宽高

      Display dp = getWindowManager().getDefaultDisplay();
      int screenWidth = dp.getWidth();
      int screenHeight = dp.getHeight();

    • 获取图片宽高

      Options opts = new Options();
      //请求图片属性但不申请内存
      opts.inJustDecodeBounds = true;
      BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
      int imageWidth = opts.outWidth;
      int imageHeight = opts.outHeight;
      
    • 图片的宽高除以屏幕宽高,算出宽和高的缩放比例,取较大值作为图片的缩放比例

      int scale = 1;
      int scaleX = imageWidth / screenWidth;
      int scaleY = imageHeight / screenHeight;
      if(scaleX >= scaleY && scaleX > 1){
          scale = scaleX;
      }
      else if(scaleY > scaleX && scaleY > 1){
          scale = scaleY;
      }
      
    • 按缩放比例加载图片

      //设置缩放比例
      opts.inSampleSize = scale;
      //为图片申请内存
      opts.inJustDecodeBounds = false;
      Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
      iv.setImageBitmap(bm);
      

    在内存中创建图片的副本(掌握)

    直接加载的bitmap对象是只读的,无法修改,要修改图片只能在内存中创建出一个一模一样的bitmap副本,然后修改副本

        //加载原图
        Bitmap srcBm = BitmapFactory.decodeFile("sdcard/photo3.jpg");
        iv_src.setImageBitmap(srcBm);
    
        //创建与原图大小一致的空白bitmap
        Bitmap copyBm = Bitmap.createBitmap(srcBm.getWidth(), srcBm.getHeight(), srcBm.getConfig());
        //定义画笔
        Paint paint = new Paint();
        //把纸铺在画版上
        Canvas canvas = new Canvas(copyBm);
        //把srcBm的内容绘制在copyBm上
        canvas.drawBitmap(srcBm, new Matrix(), paint);
    
        iv_copy.setImageBitmap(copyBm);
    

    对图片进行特效处理(熟悉)

    • 首先定义一个矩阵对象

      Matrix mt = new Matrix();
      
    • 缩放效果

      //x轴缩放1倍,y轴缩放0.5倍
      mt.setScale(1, 0.5f);
      
    • 旋转效果

      //以copyBm.getWidth() / 2, copyBm.getHeight() / 2点为轴点,顺时旋转30度
      mt.setRotate(30, copyBm.getWidth() / 2, copyBm.getHeight() / 2);
      
    • 平移

      //x轴坐标+10,y轴坐标+20
      mt.setTranslate(10, 20);
      
    • 镜面

      //把X坐标都变成负数
      mt.setScale(-1, 1);
      //图片整体向右移(要用post,不能用setTranslate,set会复位)
      mt.postTranslate(copyBm.getWidth(), 0);
      
    • 倒影

      //把Y坐标都变成负数
      mt.setScale(1, -1);
      //图片整体向下移
      mt.postTranslate(0, copyBm.getHeight());
      

    画画板(掌握)

    记录用户触摸事件的XY坐标,绘制直线
    * 给ImageView设置触摸侦听,得到用户的触摸事件,并获知用户触摸ImageView的坐标

        iv.setOnTouchListener(new OnTouchListener() {
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                switch (event.getAction()) {
                //触摸屏幕
                case MotionEvent.ACTION_DOWN:
                    //得到触摸屏幕时手指的坐标
                    startX = (int) event.getX();
                    startY = (int) event.getY();
                    break;
                //在屏幕上滑动
                case MotionEvent.ACTION_MOVE:
                    //用户滑动手指,坐标不断的改变,获取最新坐标
                    int newX = (int) event.getX();
                    int newY = (int) event.getY();
                    //用上次onTouch方法得到的坐标和本次得到的坐标绘制直线
                    canvas.drawLine(startX, startY, newX, newY, paint);
                    iv.setImageBitmap(copyBm);
                    startX = newX;
                    startY = newY;
                    break;
    
                }
                return true;
            }
        });
    

    * 刷子效果,加粗画笔

        paint.setStrokeWidth(8);
    

    * 调色板,改变画笔颜色

        paint.setColor(Color.GREEN);
    

    * 保存图片至SD卡

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File("sdcard/dazuo.png"));
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //保存图片
        copyBm.compress(CompressFormat.PNG, 100, fos);
    

    * 系统每次收到SD卡就绪广播时,都会去遍历sd卡的所有文件和文件夹,把遍历到的所有多媒体文件都在MediaStore数据库保存一个索引,这个索引包含多媒体文件的文件名、路径、大小
    * 图库每次打开时,并不会去遍历sd卡获取图片,而是通过内容提供者从MediaStore数据库中获取图片的信息,然后读取该图片
    * 系统开机或者点击加载sd卡按钮时,系统会发送sd卡就绪广播,我们也可以手动发送就绪广播

        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
        intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
        sendBroadcast(intent);
    

    撕衣服(掌握)

    • 原理:把穿内衣和穿外衣的照片重叠显示,内衣照在下面,用户滑动屏幕时,触摸的是外衣照,把手指经过的像素都置为透明,内衣照就显示出来了

       iv.setOnTouchListener(new OnTouchListener() {
      
          @Override
          public boolean onTouch(View v, MotionEvent event) {
              switch (event.getAction()) {
              case MotionEvent.ACTION_MOVE:
                  int newX = (int) event.getX();
                  int newY = (int) event.getY();
                  //把指定的像素变成透明
                  copyBm.setPixel(newX, newY, Color.TRANSPARENT);
                  iv.setImageBitmap(copyBm);
                  break;
      
              }
              return true;
          }
      });
      
    • 每次只设置一个像素点太慢,以触摸的像素为圆心,半径为5画圆,圆内的像素全部置为透明

      for (int i = -5; i < 6; i++) {
          for (int j = -5; j < 6; j++) {
              if(Math.sqrt(i * i + j * j) <= 5)
                  copyBm.setPixel(newX + i, newY + j, Color.TRANSPARENT);
          }
      }
      

    音乐播放器

    播放服务(掌握)

    • 播放音频的代码应该运行在服务中,定义一个播放服务MusicService
    • 服务里定义play、stop、pause、continuePlay等方法

          private void play() {
              // TODO Auto-generated method stub
              player.reset();
              try {
                  player.setDataSource("sdcard/bzj.mp3");
                  player.prepare();
              } catch (Exception e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              } 
              player.start();
      
          }
          private void pause() {
              player.pause();
          }
          private void stop() {
              player.stop();
          }
          private void continuePlay() {
              player.start();
          }
      
    • 把这几个方法抽取成一个接口MusicInterface
    • 定义一个中间人类,继承Binder,实现MusicInterface
    • 先start启动MusicService,再bind

      Intent intent = new Intent(this, MusicService.class);
      startService(intent);
      bindService(intent, conn, BIND_AUTO_CREATE);
      

    根据播放进度设置进度条(掌握)

    • 获取当前的播放时间和当前音频的最长时间

      int currentPosition = player.getCurrentPosition();
      int duration = player.getDuration();
      
    • 播放进度需要不停的获取,不停的刷新进度条,使用计时器每500毫秒获取一次播放进度
    • 发消息至Handler,把播放进度放进Message对象中,在Handler中更新SeekBar的进度

      Timer timer = new Timer();
      timer.schedule(new TimerTask() {
      
          @Override
          public void run() {
              int currentPosition = player.getCurrentPosition();
              int duration = player.getDuration();
              Message msg = Message.obtain();
              //把播放进度存入Message中
              Bundle data = new Bundle();
              data.putInt("currentPosition", currentPosition);
              data.putInt("duration", duration);
              msg.setData(data);
              MainActivity.handler.sendMessage(msg);
          }
      }, 5, 500);
      
    • 在Activity中定义Handler

      static Handler handler = new Handler(){
          public void handleMessage(android.os.Message msg) {
              //取出消息携带的数据
              Bundle data = msg.getData();
              int currentPosition = data.getInt("currentPosition");
              int duration = data.getInt("duration");
      
              //设置播放进度
              sb.setMax(duration);
              sb.setProgress(currentPosition);
          };
      };
      

    拖动进度条改变播放进度

         //给sb设置一个拖动侦听
         sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            //停止拖动时调用
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
                int progress = seekBar.getProgress();
                mi.seekTo(progress);
            }
            //开始拖动时调用           
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
    
            }
            //拖动的时候不断调用         
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                // TODO Auto-generated method stub
    
            }
        }); 
    

    视频播放器(熟悉)

    SurfaceView

    • 对画面的实时更新要求较高
    • 双缓冲技术:内存中有两个画布,A画布显示至屏幕,B画布在内存中绘制下一帧画面,绘制完毕后B显示至屏幕,A在内存中继续绘制下一帧画面
    • 播放视频也是用MediaPlayer,不过跟音频不同,要设置显示在哪个SurfaceView

      SurfaceView sv = (SurfaceView) findViewById(R.id.sv);
      SurfaceHolder sh = sv.getHolder();
      
      MediaPlayer player = new MediaPlayer();
      player.reset();
      try {
          player.setDataSource("sdcard/2.3gp");
          player.setDisplay(sh);
          player.prepare();
      } catch (Exception e) {
          e.printStackTrace();
      }
      player.start();
      
    • SurfaceView是重量级组件,可见时才会创建
    • 给SurfaceHolder设置CallBack,类似于侦听,可以知道SurfaceView的状态

      sh.addCallback(new Callback() {
          //SurfaceView销毁时调用
          @Override
          public void surfaceDestroyed(SurfaceHolder holder) {
              // TODO Auto-generated method stub
      
          }
          //SurfaceView创建时调用
          @Override
          public void surfaceCreated(SurfaceHolder holder) {
              // TODO Auto-generated method stub
      
          }
      
          @Override
          public void surfaceChanged(SurfaceHolder holder, int format, int width,
                  int height) {
              // TODO Auto-generated method stub
      
          }
      });
      
    • SurfaceView一旦不可见,就会被销毁,一旦可见,就会被创建,销毁时停止播放,再次创建时再开始播放

    摄像头(熟悉)

    • 启动系统提供的拍照程序

      //隐式启动系统提供的拍照Activity
      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      //设置照片的保存路径
      File file = new File(Environment.getExternalStorageDirectory(), "haha.jpg"); 
      intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); 
      startActivityForResult(intent, 0);
      
    • 启动系统提供的摄像程序

      Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
      
      File file = new File(Environment.getExternalStorageDirectory(), "haha.3gp"); 
      intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); 
      //设置保存视频文件的质量
      intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
      startActivityForResult(intent, 0);
      

    使用Camera类完成照相(熟悉)

    • 查阅文档

    内容提供者(掌握)

    • 应用的数据库是不允许其他应用访问的
    • 内容提供者的作用就是让别的应用访问到你的私有数据
    • 自定义内容提供者,继承ContentProvider类,重写增删改查方法,在方法中写增删改查数据库的代码,举例增方法

      @Override
      public Uri insert(Uri uri, ContentValues values) {
          db.insert("person", null, values);
          return uri;
      }
      
    • 在清单文件中定义内容提供者的标签,注意必须要有authorities属性,这是内容提供者的主机名,功能类似地址

      <provider android:name="com.itheima.contentprovider.PersonProvider"
          android:authorities="com.itheima.person"
          android:exported="true"
       ></provider>
      
    • 创建一个其他应用,访问自定义的内容提供者,实现对数据库的插入操作

      public void click(View v){
          //得到内容分解器对象
          ContentResolver cr = getContentResolver();
          ContentValues cv = new ContentValues();
          cv.put("name", "小方");
          cv.put("phone", 138856);
          cv.put("money", 3000);
          //url:内容提供者的主机名
          cr.insert(Uri.parse("content://com.itheima.person"), cv);
      }
      

    UriMatcher(掌握)

    • 用于判断一条uri跟指定的多条uri中的哪条匹配
    • 添加匹配规则

      //指定多条uri
      um.addURI("com.itheima.person", "person", PERSON_CODE);
      um.addURI("com.itheima.person", "company", COMPANY_CODE);
      //#号可以代表任意数字
      um.addURI("com.itheima.person", "person/#", QUERY_ONE_PERSON_CODE);
      
    • 通过Uri匹配器可以实现操作不同的表

      @Override
      public Uri insert(Uri uri, ContentValues values) {
          if(um.match(uri) == PERSON_CODE){
              db.insert("person", null, values);
          }
          else if(um.match(uri) == COMPANY_CODE){
              db.insert("company", null, values);
          }
          else{
              throw new IllegalArgumentException();
          }
          return uri;
      }
      
    • 如果路径中带有数字,把数字提取出来的api

      int id = (int) ContentUris.parseId(uri);
      

    短信数据库(掌握)

    • 只需要关注sms表
    • 只需要关注4个字段
      • body:短信内容
      • address:短信的发件人或收件人号码(跟你聊天那哥们的号码)
      • date:短信时间
      • type:1为收到,2为发送

    读取系统短信,首先查询源码获得短信数据库内容提供者的主机名和路径,然后访问内容提供者(掌握)

        ContentResolver cr = getContentResolver();
        Cursor c = cr.query(Uri.parse("content://sms"), new String[]{"body", "date", "address", "type"}, null, null, null);
        while(c.moveToNext()){
            String body = c.getString(0);
            String date = c.getString(1);
            String address = c.getString(2);
            String type = c.getString(3);
            System.out.println(body+";" + date + ";" + address + ";" + type);
        }
    

    插入系统短信(熟悉)

        ContentResolver cr = getContentResolver();
        ContentValues cv = new ContentValues();
        cv.put("body", "您尾号为XXXX的招行储蓄卡收到转账1,000,000人民币");
        cv.put("address", 95555);
        cv.put("type", 1);
        cv.put("date", System.currentTimeMillis());
        cr.insert(Uri.parse("content://sms"), cv);
    

    * 插入查询系统短信需要注册权限


    联系人数据库(掌握)

    • raw_contacts表:
      • contact_id:联系人id
    • data表:联系人的具体信息,一个信息占一行
      • data1:信息的具体内容
      • raw_contact_id:联系人id,描述信息属于哪个联系人
      • mimetype_id:描述信息是属于什么类型
    • mimetypes表:通过mimetype_id到该表查看具体类型

    读取联系人(掌握)

    • 先查询raw_contacts表拿到联系人id

      Cursor cursor = cr.query(Uri.parse("content://com.android.contacts/raw_contacts"), new String[]{"contact_id"}, null, null, null);
      
    • 然后拿着联系人id去data表查询属于该联系人的信息

      Cursor c = cr.query(Uri.parse("content://com.android.contacts/data"), new String[]{"data1", "mimetype"}, "raw_contact_id = ?", new String[]{contactId}, null);
      
    • 得到data1字段的值,就是联系人的信息,通过mimetype判断是什么类型的信息

      while(c.moveToNext()){
          String data1 = c.getString(0);
          String mimetype = c.getString(1);
          if("vnd.android.cursor.item/email_v2".equals(mimetype)){
              contact.setEmail(data1);
          }
          else if("vnd.android.cursor.item/name".equals(mimetype)){
              contact.setName(data1);
          }
          else if("vnd.android.cursor.item/phone_v2".equals(mimetype)){
              contact.setPhone(data1);
          }
      }
      

    插入联系人(熟悉)

    • 先查询raw_contacts表,确定新的联系人的id应该是多少
    • 把确定的联系人id插入raw_contacts表

      cv.put("contact_id", _id);
      cr.insert(Uri.parse("content://com.android.contacts/raw_contacts"), cv);
      
    • 在data表插入数据

      • 插3个字段:data1、mimetype、raw_contact_id

        cv = new ContentValues();
        cv.put("data1", "赵六");
        cv.put("mimetype", "vnd.android.cursor.item/name");
        cv.put("raw_contact_id", _id);
        cr.insert(Uri.parse("content://com.android.contacts/data"), cv);
        
        cv = new ContentValues();
        cv.put("data1", "1596874");
        cv.put("mimetype", "vnd.android.cursor.item/phone_v2");
        cv.put("raw_contact_id", _id);
        cr.insert(Uri.parse("content://com.android.contacts/data"), cv);
        

    内容观察者(掌握)

    • 当数据库数据改变时,内容提供者会发出通知,在内容提供者的uri上注册一个内容观察者,就可以收到数据改变的通知

      cr.registerContentObserver(Uri.parse("content://sms"), true, new MyObserver(new Handler()));
      
      class MyObserver extends ContentObserver{
      
          public MyObserver(Handler handler) {
              super(handler);
              // TODO Auto-generated constructor stub
          }
      
          //内容观察者收到数据库发生改变的通知时,会调用此方法
          @Override
          public void onChange(boolean selfChange) {
      
          }
      
      }
      
    • 在内容提供者中发通知的代码

      ContentResolver cr = getContext().getContentResolver();
      //发出通知,所有注册在这个uri上的内容观察者都可以收到通知
      cr.notifyChange(uri, null);
      

    Fragment(重要)

    • 用途:在一个Activity里切换界面,切换界面时只切换Fragment里面的内容
    • 生命周期方法跟Activity一致,可以理解把其为就是一个Activity
    • fragment切换时会销毁旧的,再创建新的
    • 定义布局文件作为Fragment的显示内容

      //此方法返回的View就会被显示在Fragment上
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
          Bundle savedInstanceState) {
          // TODO Auto-generated method stub
          //用布局文件填充成一个View对象,返回出去,那么就显示在Fragment上了
          View v = inflater.inflate(R.layout.fragment01, null);
          return v;     
      }
      
    • 把Fragment显示至指定ViewGroup中

      //把fragment显示至界面
      //new出fragment对象
      Fragment01 fg = new Fragment01();
      FragmentManager fm = getFragmentManager();
      //开启事务
      FragmentTransaction ft = fm.beginTransaction();
      //把fragment对象显示到指定资源id的组件里面
      ft.replace(R.id.fl, fg);
      ft.commit();
      

    生命周期(重要)

    • fragment切换时旧fragment对象会销毁,新的fragment对象会被创建

    低版本兼容(熟悉)

    • 在support-v4.jar包中有相关api,也就是说fragment可以在低版本模拟器运行

    动画(重要)

    帧动画(重要)

    一张张图片不断的切换,形成动画效果

    • 在drawable目录下定义xml文件,子节点为animation-list,在这里定义要显示的图片和每张图片的显示时长

      <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
          <item android:drawable="@drawable/g1" android:duration="200" />
          <item android:drawable="@drawable/g2" android:duration="200" />
          <item android:drawable="@drawable/g3" android:duration="200" />
      </animation-list>
      
    • 在屏幕上播放帧动画

      ImageView iv = (ImageView) findViewById(R.id.iv);
      //把动画文件设置为imageView的背景
      iv.setBackgroundResource(R.drawable.animations);
      AnimationDrawable ad = (AnimationDrawable) iv.getBackground();
      //播放动画        
      ad.start();
      

    补间动画(重要)

    • 原形态变成新形态时为了过渡变形过程,生成的动画就叫补间动画
    • 位移、旋转、缩放、透明
    位移:
    • 参数10指的是X的起点坐标,但不是指屏幕x坐标为10的位置,而是imageview的 真实X + 10
    • 参数150指的是X的终点坐标,它的值是imageview的 真实X + 150

      //创建为位移动画对象,设置动画的初始位置和结束位置
      TranslateAnimation ta = new TranslateAnimation(10, 150, 20, 140);
      
    • x坐标的起点位置,如果相对于自己,传0.5f,那么起点坐标就是 真实X + 0.5 * iv宽度
    • x坐标的终点位置,如果传入2,那么终点坐标就是 真实X + 2 * iv的宽度
    • y坐标的起点位置,如果传入0.5f,那么起点坐标就是 真实Y + 0.5 * iv高度
    • y坐标的终点位置,如果传入2,那么终点坐标就是 真实Y + 2 * iv高度

      TranslateAnimation ta = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 2);
      
    • 动画播放相关的设置

      //设置动画持续时间
      ta.setDuration(2000);
      //动画重复播放的次数
      ta.setRepeatCount(1);
      //动画重复播放的模式
      ta.setRepeatMode(Animation.REVERSE);
      //动画播放完毕后,组件停留在动画结束的位置上
      ta.setFillAfter(true);
      //播放动画
      iv.startAnimation(ta);
      
    缩放:
    • 参数0.1f表示动画的起始宽度是真实宽度的0.1倍
    • 参数4表示动画的结束宽度是真实宽度的4倍
    • 缩放的中心点在iv左上角

      ScaleAnimation sa = new ScaleAnimation(0.1f, 4, 0.1f, 4);
      
    • 参数0.1f和4意义与上面相同
    • 改变缩放的中心点:传入的两个0.5f,类型都是相对于自己,这两个参数改变了缩放的中心点
    • 中心点x坐标 = 真实X + 0.5 * iv宽度
    • 中心点Y坐标 = 真实Y + 0.5 * iv高度

      ScaleAnimation sa = new ScaleAnimation(0.1f, 4, 0.1f, 4, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

    透明:
    • 0为完全透明,1为完全不透明

      AlphaAnimation aa = new AlphaAnimation(0, 0.5f);
      
    旋转:
    • 20表示动画开始时的iv的角度
    • 360表示动画结束时iv的角度
    • 默认旋转的圆心在iv左上角

      RotateAnimation ra = new RotateAnimation(20, 360);
      
    • 20,360的意义和上面一样
    • 指定圆心坐标,相对于自己,值传入0.5,那么圆心的x坐标:真实X + iv宽度 * 0.5
    • 圆心的Y坐标:真实Y + iv高度 * 0.5

      RotateAnimation ra = new RotateAnimation(20, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
      
    所有动画一起飞
        //创建动画集合
        AnimationSet set = new AnimationSet(false);
        //往集合中添加动画
        set.addAnimation(aa);
        set.addAnimation(sa);
        set.addAnimation(ra);
        iv.startAnimation(set);
    

    属性动画(重要)

    • 补间动画,只是一个动画效果,组件其实还在原来的位置上,xy没有改变

    位移:

    • 第一个参数target指定要显示动画的组件
    • 第二个参数propertyName指定要改变组件的哪个属性
    • 第三个参数values是可变参数,就是赋予属性的新的值
    • 传入0,代表x起始坐标:当前x + 0
    • 传入100,代表x终点坐标:当前x + 100

      //具有get、set方法的成员变量就称为属性
      ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "translationX", 0, 100) ;
      

    缩放:

    • 第三个参数指定缩放的比例
    • 0.1是从原本高度的十分之一开始
    • 2是到原本高度的2倍结束

      ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "scaleY", 0.1f, 2);
      

    透明:

    • 透明度,0是完全透明,1是完全不透明

      ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "alpha", 0.1f, 1);
      

    旋转

    • rotation指定是顺时针旋转
    • 20是起始角度
    • 270是结束角度

      ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "rotation", 20, 270);
      
    • 属性指定为rotationX是竖直翻转
    • 属性指定为rotationY是水平翻转

      ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "rotationY", 20, 180);
      

    可变参数

    • 第三个参数可变参数可以传入多个参数,可以实现往回位移(旋转、缩放、透明)

      ObjectAnimator oa = ObjectAnimator.ofFloat(bt, "translationX", 0, 70, 30, 100) ;
      

    所有动画一起飞

        //创建动画师集合
        AnimatorSet set = new AnimatorSet();
        //设置要播放动画的组件
        set.setTarget(bt);
        //所有动画有先后顺序的播放
        //set.playSequentially(oa, oa2, oa3, oa4);
        //所有动画一起播放
        set.playTogether(oa, oa2, oa3, oa4);
        set.start();
    
    展开全文
  • 记录了作者学习安卓开发过程中见到的各种各样的问题 ,主要是用ANDROID STUDIO 3.2 开发,是学习ANDROID 开发不可多得的学习笔记,作者三个多月的心血,
  • 今天记录一下这段时期学习android笔记,这些笔记比较凌乱,但总归是有用的,以后什么时候有用,可以直接查询来使用。Android沉浸式状态栏如果想要我们的应用打开以后和状态栏的颜色保持一致,或者是我们自己定义的...

    今天记录一下这段时期学习android的笔记,这些笔记比较凌乱,但总归是有用的,以后什么时候有用,可以直接查询来使用。

    通过命令操作contentprovider

    adb shell content insert --uri 的使用
    在命令行下输入adb shell content会提示相关的使用说明

    ubuntu sqlite3可视化

    sudo apt-get install sqlite3
    sqlite3 -version
    sudo apt-get install sqlitebrowser
    sqlitebrowser

    查看某一条提交记录

    git show 9bc2f472e9cf204c737025b5d7101ba16c43b356
    git log --pretty=oneline ./src/com/android/providers/settings/SettingsBackupAgent.java

    google浏览器下载安装
    sudo apt-get install chromium-browser chromium-browser-l10n

    查询修改Settings数据库中的字段值

    adb shell settings get namespace key
    adb shell settings put namespace key value
    adb shell settings delete namespace key
    adb shell settings list namespace 
    

    android studio原码导入

    mmm development/tools/idegen/
    development/tools/idegen/idegen.sh

    反编译遇到的问题

    apktool.jar下载
    https://bitbucket.org/iBotPeaches/apktool/downloads
    将apktool和apktool.jar放置到usr/local/bin目录下,并且设置其为可执行文件
    sudo chmod +x apktool
    sudo chmod +x apktool.jar
    apktool d xxx.apk
    
    反编译的时候可能会出现这样的错误:
    com.googlecode.dex2jar.DexException
    这是因为当前是odex文件,需要执行下面的操作:
    java -jar baksmali-2.0.3.jar -d system/framework -x xxx.odex
    -d 参数就是指定到那个目录下寻找那些所需的jar文件,如果所需的jar包就在当前目录,那么就不需要指定该目录了。
    命令执行完成之后就会在当前目录下生成一个out目录,该目录里面就是对应的smali文件 
    
    这里out目录下存放的是smail文件,我们也可是通过apktool反编译出资源,然后将out替换成我们自己的smaily目录
    java -jar smali-2.0.3.jar  -o classes.dex out

    Ubuntu 使用dpkg安装软件遇到的问题

    使用sudo dpkg -i  **.deb安装的时候,可能会提示有依赖
    dpkg -i 包名 (结果遇到依赖性问题,需要安装其他的包)
    apt-get -f install (使用它就可以自动下载上面所需要的包了)

    android固定手机屏幕

    $ adb shell content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:0
    
    Step2
    从竖屏转到横屏
    $ adb shell content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:1
    由横屏转到竖屏
    $ adb shell content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:0

    git提交patch

    git apply --reject xxx.patch
    git apply xx.patch
    git am xx.patch

    adb无线连接手机

    为避免使用数据线,可通过wifi通信,前提是手机与PC处于同一局域网
    
    启动方法:
    
    adb tcpip 5555  //这一步,必须通过数据线把手机与PC连接后再执行
    adb connect <手机IP>
    
    停止方法:
    
    adb disconnect //断开wifi连接
    adb usb //切换到usb模式

    android模拟用户事件

    文本输入: adb shell input text <string> 例手机端输出demo字符串,相应指令:adb shell input "demo".
    
    键盘事件: input keyevent <KEYCODE>,其中KEYCODE见本文结尾的附表 例点击返回键,相应指令: input keyevent 4.
    
    点击事件: input tap <x> <y> 例点击坐标(500,500),相应指令: input tap 500 500.
    
    滑动事件: input swipe <x1> <y1> <x2> <y2> <time> 例从坐标(300,500)滑动到(100,500),相应指令: input swipe 300 500 100 500. 例200ms时间从坐标(300,500)滑动到(100,500),相应指令: input swipe 300 500 100 500 200.
    

    Android沉浸式状态栏

    如果想要我们的应用打开以后和状态栏的颜色保持一致,或者是我们自己定义的颜色,可以这样来做:

    布局:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ffffff"
        android:orientation="vertical"
        tools:context=".MainActivity" >
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="#009959"
            android:clipToPadding="true"
            android:fitsSystemWindows="true" />
    
        <Button
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:background="#ff669d" />
    
    </LinearLayout>
    

    这里为textView设置了如下两个属性:
    android:clipToPadding=”true”
    android:fitsSystemWindows=”true”
    之所以设置textview是因为我们在改textview中指定了颜色,即状态栏显示的颜色。

    引用布局:

    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);

    这样就完成了一个沉浸式的状态栏。

    android实现截屏的效果

    //构建Bitmap  
    
    WindowManager windowManager = getWindowManager();  
    
    Display display = windowManager.getDefaultDisplay();  
    
    int w = display.getWidth();  
    
    int h = display.getHeight();  
    
    Bitmap Bmp = Bitmap.createBitmap( w, h, Config.ARGB_8888 );      
    
    //获取屏幕  
    
    View decorview = this.getWindow().getDecorView();   
    
    decorview.setDrawingCacheEnabled(true);   
    
    Bmp = decorview.getDrawingCache();   
    

    SpannableString美化文字

    SpannableString string = new SpannableString("test百度");
    string.setSpan(new AbsoluteSizeSpan(50),3,5, Spannable.SPAN_POINT_POINT);
    textView.setText(string);
    
    SpannableString string = new SpannableString("test百度9465fhghfjhg");
    string.setSpan(new StyleSpan(Typeface.ITALIC),3,9, Spannable.SPAN_POINT_POINT);
    textView.setText(string);
    
    SpannableString string = new SpannableString("test百度9465fhghfjhg");
    string.setSpan(new BackgroundColorSpan(Color.argb(187,200,155,200)),3,9, Spannable.SPAN_POINT_POINT);
    textView.setText(string);
    
    SpannableString string = new SpannableString("test百度9465fhghfjhg");
    string.setSpan(new ForegroundColorSpan(Color.argb(187,200,155,200)),3,9, Spannable.SPAN_POINT_POINT);
    
    SpannableString string = new SpannableString("test百度9465fhghfjhg");
    string.setSpan(new URLSpan("百度"),3,9, Spannable.SPAN_POINT_POINT);
    
    SpannableString string = new SpannableString("test百度9465fhghfjhg");
    string.setSpan(new ScaleXSpan(0.618f),3,9, Spannable.SPAN_POINT_POINT);
    
    SpannableString string = new SpannableString("test百度9465fhghfjhg");
    string.setSpan(new QuoteSpan(),3,9, Spannable.SPAN_POINT_POINT);
    
    SpannableString string = new SpannableString("test百度9465fhghfjhg");
    string.setSpan(new StrikethroughSpan(),3,9, Spannable.SPAN_POINT_POINT);
    
    string.setSpan(new RelativeSizeSpan((float) Math.PI),3,9, Spannable.SPAN_POINT_POINT);

    android.media.ExifInterface使用

    android 2.0开始 加入的 android.media.ExifInterface 包中如下方法读取多媒体文件相关信息:

    /**
      *
      * 目前Android SDK定义的Tag有:
      * TAG_DATETIME 时间日期
      * TAG_FLASH 闪光灯
      * TAG_GPS_LATITUDE 纬度
      * TAG_GPS_LATITUDE_REF 纬度参考
      * TAG_GPS_LONGITUDE 经度
      * TAG_GPS_LONGITUDE_REF 经度参考
      * TAG_IMAGE_LENGTH 图片长
      * TAG_IMAGE_WIDTH 图片宽
      * TAG_MAKE 设备制造商
      * TAG_MODEL 设备型号
      * TAG_ORIENTATION 方向
      * TAG_WHITE_BALANCE 白平衡
      */
    String path = "/storage/extSdCard/mayi/fault_images/" + filename;//图片完整路径
    System.out.println(path);
    ExifInterface ext = new ExifInterface(path);//获取图片信息
    int image_length = Integer.parseInt(ext.getAttribute(ExifInterface.TAG_IMAGE_LENGTH));
    int image_width = Integer.parseInt(ext.getAttribute(ExifInterface.TAG_IMAGE_WIDTH));

    manifest的一些属性

    当给某一个activity设置android:excludeFromRecents=”true”的时候,就不会在最近使用的应用里边看到该activity

    android:alwaysRetainTaskState
    是否保留状态不变, 比如切换回home, 再从新打开, activity处于最后的状态

    android:clearTaskOnLanunch
    比如 P 是 activity, Q 是被P 触发的 activity, 然后返回Home, 从新启动 P, 是否显示 Q
    android:finishOnTaskLaunch
    是否关闭已打开的activity当用户重新启动这个任务的时候

    android:onHistory
    是否需要移除这个activity当用户切换到其他屏幕时。 这个属性是 API level 3 中引入的

    android:screenOrientation
    activity显示的模式, “unspecified” 默认值 “landscape” 风景画模式,宽度比高度大一些 “portrait” 肖像模式, 高度比宽度大。 “user” 用户的设置 “behind” “sensor” “nosensor”

    android:allowTaskReparenting(‘true’ or ‘false’)
    是否允许activity更换从属的任务,比如从短信息任务切换到浏览器任务

    创建和删除app快捷方式

    /**    
     * 为程序创建桌面快捷方式    
     */    
    private void addShortcut(){     
            Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");     
    
        //快捷方式的名称     
        shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.app_name));     
        shortcut.putExtra("duplicate", false); //不允许重复创建     
    
        //指定当前的Activity为快捷方式启动的对象: 如 com.everest.video.VideoPlayer     
        //注意: ComponentName的第二个参数必须加上点号(.),否则快捷方式无法启动相应程序     
            ComponentName comp = new ComponentName(this.getPackageName(), "."+this.getLocalClassName());     
            shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_MAIN).setComponent(comp));     
    
        //快捷方式的图标     
        ShortcutIconResource iconRes = Intent.ShortcutIconResource.fromContext(this, R.drawable.icon);     
        shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes);     
        sendBroadcast(shortcut);     
     }  
    
    
    /**    
     * 删除程序的快捷方式    
     */    
    private void delShortcut(){     
          Intent shortcut = new Intent("com.android.launcher.action.UNINSTALL_SHORTCUT");     
    
            //快捷方式的名称     
            shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.app_name));     
    
            //指定当前的Activity为快捷方式启动的对象: 如 com.everest.video.VideoPlayer     
            //注意: ComponentName的第二个参数必须是完整的类名(包名+类名),否则无法删除快捷方式     
            String appClass = this.getPackageName() + "." +this.getLocalClassName();     
            ComponentName comp = new ComponentName(this.getPackageName(), appClass);     
            shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_MAIN).setComponent(comp));     
    
            sendBroadcast(shortcut);     
    
        }   

    另外记得添加以下权限:

    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />     
    <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" /> 

    pm命令的使用

    adb shell pm list package -e  列出手机上可用的所有应用
    pm list packages -d    列出手机上不可用的所有应用、
    pm list packages -s    列出手机上的系统应用
    pm enable com.tct.note   使应用可用
    pm disable com.tct.note   是应用不可用
    pm path com.tct.note   打印出该包名所对应的apk的完整路径
    pm list packages    查看手机上所有的应用的包名
    pm list permission-groups   列出手机上的所有的权限组
    pm list permissions  列出手机上所有的权限
    pm list features   列出手机上的硬件功能
    pm dump com.tct.note > storage/sdcard0/temp/dump.txt  将该应用的相关信息输出到文件
    pm get-install-location com.tct.note  得到应用安装的路径  0:自动  1:内部存储   2:外部存储
    aapt dump badging /local/pertest.apk  列出  中的详细信息
    aapt dump permissions /local/pertest.apk  列出 中使用的权限
    
    adb shell am start –n activityName
    adb shell am kill +package
    adb shell am force-stop +package

    dumpsys命令的使用

    adb shell dumpsys notification
    adb shell dumpsys activity---------------查看ActvityManagerService 所有信息
    adb shell dumpsys activity activities----------查看Activity组件信息
    adb shell dumpsys activity services-----------查看Service组件信息
    adb shell dumpsys activity providers----------产看ContentProvider组件信息
    adb shell dumpsys activity broadcasts--------查看BraodcastReceiver信息
    adb shell dumpsys activity intents--------------查看Intent信息
    adb shell dumpsys activity processes---------查看进程信息
    
    adb shell dumpsys battery
    adb shell dumpsys cpuinfo
    adb shell dumpsys meminfo
    adb shell dumpsys activity
    adb shell dumpsys activity top  获取当前界面的ui信息
    adb shell dumpsys package   获取包的信息
    adb shell dumpsys package com.android.mms 获取某一个包的信息
    adb shell dumpsys notification  获取notification的信息
    adb shell dumpsys power
    adb shell dumpsys wifi
    获取电话信息:
    adb shell dumpsys telephony.registry
    可以获取到电话状态,例如
    mCallState值为0,表示待机状态、1表示来电未接听状态、2表示电话占线状态
    mCallForwarding=false #是否启用呼叫转移
    mDataConnectionState=2 #0:无数据连接 1:正在创建数据连接 2:已连接
    mDataConnectionPossible=true  #是否有数据连接
    mDataConnectionApn=   #APN名称

    onTrimMemory学习

    onTrimMemory()是Android 4.0之后提供的API,系统会根据不同的内存状态来回调。根据不同的内存状态,来响应不同的内存释放策略。

    OnTrimMemory的参数是一个int数值,代表不同的内存状态:

    TRIM_MEMORY_COMPLETE:内存不足,并且该进程在后台进程列表最后一个,马上就要被清理

    TRIM_MEMORY_MODERATE:内存不足,并且该进程在后台进程列表的中部。
    TRIM_MEMORY_BACKGROUND:内存不足,并且该进程是后台进程。
    TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的UI已经不可见了。
    以上4个是4.0增加

    TRIM_MEMORY_RUNNING_CRITICAL:内存不足(后台进程不足3个),并且该进程优先级比较高,需要清理内存

    TRIM_MEMORY_RUNNING_LOW:内存不足(后台进程不足5个),并且该进程优先级比较高,需要清理内存

    TRIM_MEMORY_RUNNING_MODERATE:内存不足(后台进程超过5个),并且该进程优先级比较高,需要清理内存

    setContentView之后获得空间宽和高

    如果在setContentView之后就尝试获得空间的宽和高,此时会得到0,因为此时还没有执行onMeasure方法,可以尝试如下两种解决方案:

    
     方法一:
            int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
            int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
            ssidtext.measure(w, h);
            int width =ssidtext.getMeasuredWidth();
            int height =ssidtext.getMeasuredHeight();
    
    方法二:
            ViewTreeObserver vto = ssidtext.getViewTreeObserver();
            vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                public boolean onPreDraw() {
                    int height = ssidtext.getMeasuredHeight();
                    int width = ssidtext.getMeasuredWidth();
                    return true;
                }
            });

    手机root之后

    android中锁屏密码都是存在:/data/system/ 目录下的
    我们可以查看后缀名是.key的文件,因为有很多种锁屏方案:手势密码,文本密码,PIN密码等,所以会有相对应的文件,我们只要找到对应的文件,然后将其删除即可。

    其实每次连接上的wifi之后,系统会将这些wifi信息存储到指定文件中,在/data/misc/wifi/ 目录下面,然后查看wpa_supplicant.conf文件内容即可

    设置屏幕显示方向

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//通过程序改变屏幕显示的方向 
    
        1.landscape:横屏(风景照) ,显示时宽度大于高度;  
    
         2.portrait:竖屏 (肖像照) , 显示时 高 度大于 宽 度 ;  
    
         3.user:用户当前的首选方向;  
    
         4.behind:继承Activity堆栈中当前Activity下面的那个Activity的方向;  
    
         5.sensor:由物理感应器决定显示方向,它取决于用户如何持有设备,当 设备 被旋转时方向会随之变化——在横屏与竖屏之间;  
    
         6.nosensor:忽略物理感应器——即显示方向与物理感应器无关,不管用户如何旋转设备显示方向都不会随着改变("unspecified"设置除外);  
    
         7.unspecified :未指定,此为默认值,由Android系统自己选择适当的方向,选择策略视具体设备的配置情况而定,因此不同的设备会有不同的方向选择;

    screenrecord的使用

    adb shell screenrecord /sdcard/demo.mp4
    限制录制时间:
    adb shell screenrecord –time-limit 10 /sdcard/demo.mp4
    指定视频分辨率的大小
    adb shell screenrecord –size 1280*720 /sdcard/demo.mp4
    指定视频比特率:比特率越大,视频质量越好
    adb shell screenrecord –bit-rate 6000000 /sdcard/demo.mp4

    scrollView滚动到屏幕底部

    scrollView.fullScroll(ScrollView.FOCUS_DOWN);滚动到底部
    scrollView.fullScroll(ScrollView.FOCUS_UP);滚动到顶部
    需要注意的是,该方法不能直接被调用,因为Android很多函数都是基于消息队列来同步,所以需要一部操作,addView完之后,不等于马上就会显示,而是在队列中等待处理,虽然很快,但是如果立即调用fullScroll, view可能还没有显示出来,所以会失败,应该通过handler在新线程中更新

    handler.post(new Runnable() {  
            @Override  
            public void run() {  
                scrollView.fullScroll(ScrollView.FOCUS_DOWN);  
            }  
        });  
    展开全文
  • Android学习笔记(十)

    2015-08-29 12:19:51
    第一行代码-Android学习笔记(十)

    Android学习笔记(十)

    • Git版本控制工具
      • 忽略文件:在ProviderTest项目的根目录下创建一个名为.gitignore的文件,然后将要忽略的文件或者目录写入文件行。如bin/之后执行添加和上传命令即可。
      • 查看修改内容:在根目录下输入git status可以查看哪个文件发生变化,之后输入git diff 查看所有文件的更改内容。也可以输入igt diff <path>查看指定目录下的修改。
      • 撤销未提交的修改:git checkout < path >,如果要修改已经添加的文件,先通过git reset HEAD <path>撤回提交再执行即可。
      • 查看提交记录:git log,如果想要查看某一条记录,使用git log <id> -1即可。如要查看这条提交记录具体修改使用git log <id> -1 -p即可。
    • 通知(Notification):应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知来实现。
      • 调用Context的getSystemService()方法获取NotificationManager,传入一个字符串参数确定获取系统的服务。
      • 利用Notification的有参构造函数来创建对象。传入三个参数,第一个参数指定通知的图标,第二个参数用于指定通知的ticker内容,第三个参数指定通知被创建的时间,以毫秒为单位
      • 调用Notification的setLatestEventInfo()方法给通知设置一个标准布局。传入四个参数,第一个参数是Context,第二个参数用于指定通知的标题内容,第三个个参数用于指定通知的征文内容,第四个参数暂时不需要使用传入null
      • 调用NotificationManager的notify()方法让通知显示,传入两个参数,第一个参数为id,第二个参数为Notification对象。
      • 使用Intent表达想要启动NotificationActivity的意图,然后将构建好的Intent对象传入PendingIntent的getActivity()方法里,得到PendingIntent的实例,把它作为Notification的setLastestEventInfo()方法的第四个参数。
      • Notification的sound方法用于在通知时播放一段音频。例如:Uri soundUri = Uri.fromFile(new File("/system/media/audio/ringtones/Basic_tone.ogg")); notification.sound = soundUri;
      • Notification的vibrate方法用于控制手机进行震动,想要控制震动需要加入声明。例如long vibrates = {0,1000,1000,1000); notification.vibrate = vibtrates;
    • 接收和发送短信
      • 接收短信:当手机收到一条短信的时候,系统会发出一条直为android.Telephony.SMS_RECEIVED的广播,此时应用程序可以再广播接收器里对其进行监听,收到广播后再从中解析短信的内容即可。
        1. 先从Intent参数中取出一个Bundle对象
        2. 使用pdu密钥来提取一个SMS pdus数组
        3. 再使用SmsMessage的createFromPdu()方法获取发送方号码
        4. 调用getMessageBody()方法就可以获取到短信的内容
        5. 然后将每一个SmsMessage对象中的短信拼接起来,就组成了一条完整的短信。
        6. 声明接收短信的权限
      • 拦截短信
        1. 提高MessageReceiver的优先级
        2. 在onReceive()方法中调用abortBroadcast()方法,终止广播继续传递。
      • 发送短信
        1. 通过SmsManager的getDefault()方法获取SmsManager的实例。
        2. 调用PendingIntent的getBroadcast()方法取得一个PendingIntent对象
        3. 调用它的sendTextMessage()方法发送短信,传入五个参数,第一个用于指定接收人手机号码,第三个参数用于指定短信内容,第四个参数传入PendingIntent对象,用于查看发送状态。其他的暂时传入null
        4. 进行声明android.permission.SEND_SMS
        5. 通过广播接收器的getResultCode()方法,确定发送状态,RESULT_OK即为成功,否则失败
    展开全文
  • Android学习笔记 ( by quqi99 )作者:张华 发表于:2007-12-04 ( http://blog.csdn.net/quqi99 )版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明。 
  • 1、Android系统的四大组件分别是:活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)、内容提供器(Content Provider)。 2、定义一个应用程序名的字符串,有以下两种方式来引用它:
  •  本系列文章仅作为阅读 郭霖大神 的著作《第一行代码——Android》所做的读书笔记,我把其中比较重要的点记录了下来。  写这些笔记文章主要有三个目的: 其一,是对我学习计划的一个监督,可以证明我学了...
  • Android学习笔记系列

    2019-08-06 06:18:02
    近几年,移动领域一直很火,闲来无事,自己凑热闹学习Android一些皮毛,这里记录学习笔记,有不对的地方欢迎留言斧正。 希望列出来的内容我能坚持写完,笔记目录如下: 基础篇 Android学习笔记(1):环境...
  • Android画图学习笔记记录的是在www.moandroid.com上的学习Android画图学习的一些文章 一。类的简介1.如何获取 res 中的资源 数据包package:android.content.res主要类:ResourcesAndroid SDK中的简介:Class for ...
  • 最初虽然对Android开发稍有学习,但涉猎不深,往往项目做完就交接给别人了,没有对机型适配以及UI问题做更多了解。之后从事iOS开发,发现iOS的UI适配真的比Android适配省心好多。由于公司业务的需要,最近研究下...
  • Android studio 入门笔记

    2020-06-03 14:33:40
    Android studio 学习笔记 记录自学Android 后制作的第一个小程序 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法...
  • 闲来无事,看看Android API Guid,了解一下设计意图,记录一些Android API Guid学习笔记 Activities: 1.由于系统在onPause,onStop和onDestory时可能会直接kill掉对应的APP进程,而onPause是第一个状态,所以在...
  • 今天去参加了我大学人生第一次面试,结果却是一败涂地,发现自己在...所以,现在特立一个栏目,记录所学,从头开始学习android基础知识,做一个真正肚子里有书的开发人员。 基本计划为: 每周至少一篇本周的章节知
  • 作为习惯于总结经验的我来说,在研究官方文档之余还是觉得应该写写笔记,作为学习记录。所以接下来一段时间都会写写一些关键记录。 小程序与普通网页开发的区别 ​小程序的主要开发语言是 JavaScript ,小程序的开发...
  • 马克笔记是运行在Android设备上面的一款开源的Markdown笔记,它的功能开发得已经比较完善,已经能够满足大部分用户的需求。现在将其开源到Github上面,用来交流和学习。当然,更希望你能够参与到项目的开发当中,...
  • 张弘扬 2017 Android笔记 开源项目 Android开源项目 Android自学项目 即时通讯 豆芽客户端 简阅客户端 仿Bilibili
  • 前言:建议入门Android的小伙伴作为复习的时候用到,因为涉及比较多的细节知识,不利于你们的前期学习,视频地址:15天精讲精练Android核心技术 Android系统的基本了解 Android整体架构图:Android系统架构—–...
  • 1.android布局原则:  (1)将可复用的组件抽出来并通过include标签使用。  (2)使用viewstub标签来加载一些不常用的布局。  (3)使用merge标签减少布局的嵌套层次。  http://www.imooc.com/video/3229
1 2 3 4 5 ... 20
收藏数 13,367
精华内容 5,346