Android程序猿基本功

这里内容确实很多,可以使用ctrl+f搜索你所需要解决的,基本上都有,这里是笔记。

Android程序猿基本功_第1张图片Jar包的使用

至于依赖库的选择个人认为因人而异,我使用的库如下:


butterknife————View注解库和配套插件android-butterknife-zelezny.
例@BindView(R.id.backdrop)
    ImageView mBackdrop;


retrofit+okhttp————网络请求相关


gson————google推荐的json数据解析库


glide————google推荐的Android图片加载库.包含缓存


RxAndroid+RxJava——Rx函数响应式编程中文文档


compile ‘com.android.support:design:23.0.1’————谷歌Material Design控件库


  //毛玻璃效果
        Glide.with(this)
                .load(MyApplication.currentGirl)
                .bitmapTransform(new BlurTransformation(this, 15))
                .into(mBackdrop);


https://github.com/SuperMan42/MVP


retrofit + okhttp + mvp +material desigin 这一套
做android 开发的人多不




在AND(&&)运算中a()&&b()&&c(),当a为false时,b与c都不再执行
Snackbar 和toast一个效果,从底部弹出 
一个产品开发新版本流程大概是:提出需求->根据需求UI做出效果图->然后产品和UI根据效果图做出小调整->定稿,UI切图->开发根据效果图开发
我们一般开发很多功能都有一个共用的概念,比如程序的标题栏等都是继承一个,如果效果不一样,我们就要单独处理,导致程序可维护性不高。
我理解的好的产品应该是: 设计简单、操作流畅、功能简单易用、稳定性高、用户体验好, 这些都很关键。


所以一些功能从产品经理设计出来的那一刻就注定是失败的,开发多努力都毫无意义...


1.我们尽量要参与需求的制定及讨论,尽量对一些不合理的地方及时从开发的角度提出自己的意见。


2.当需求制定出来的时候,我们拿到效果图,不要着急做,要脑子里面先想想哪里有问题,不合理,整体流程能不能跑通。想通了在做。


3.当产品上线前,尽量不要做一些没有太多意义的小功能。精力都放在处理关键bug,问题上。功能能不加就不加。


4.提前制定好计划,当需求反复改的时候,要及时和上级沟通交流,调整时间计划,避免后期时间计划对应不上。


缓存用的是glide,先缓存网址再用glide显示出来
















安卓四大组件:
Activity 
一个单独的屏幕 它上面可以显示一些控件也可以监听并处理用户的事件做出响应


Service  应用程序中不可见的“工人”


Content Provider 
提供共享的数据存储 Content Provider(内容提供器)用来管理和共享应用程序的数据库


Broadcast Receivers  ----广播
不包含任何用户界面 然而它们可以启动一个Activity以响应接受到的信息 或者通过NotificationManager通知用户


【View 】的基本概念
View是用户接口的基础构件 是所有widget类的基类 Widget类用于创建交互式UI构件(按钮,输入框等)
View 类的ViewGroup子类是layout 的基类 Layout是一个不可见的容器 它保存着View(或ViewGroup)并定义这些View的layout 属性
可以说View类是用户接口类中最重要的一个类


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


一、监听 点击弹出内容


对象.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
       Toast.makeText(当前类.this,"",Toast.LENGTH_SHORT).show();
        }
   });


当前类.this---上下文 ""---弹出的内容 Toast.LENGTH_SHORT---时间长短


二、布局 布局可嵌套 不过尽量少用嵌套


1.LinearLayout 线性布局(最常用)


orientation="vertical" 纵向排序
orientation="horizontal" 横向排序 不写时默认横向
layout_weigh (权重) 占布局的几比几


2.RelativeLayout  相对布局(最常用)


layout_above="传入控件id" 在什么控件的上面
layout_below="传入控件id" 在什么控件的下面
layout_toRightOf="传入控件id" 在什么控件的右边


3.FrameLayout  桢布局 起点左上角 不能去改变


background      可以设置颜色 传图片
backgroundTint  给图片动态设置颜色(着色器)
foregroundGravity  前景位置
foreground     前景(永远在最前面)


4.TableLayout  表格布局  继承LinearLayout  也是一种线性布局


每行都需要用到  自带横向布局


5.AbsoluteLayout  绝对布局(已过时)


layout_x 横坐标
layout_y 纵坐标


三、距离单位及边距


1.px(像素):每个px对应屏幕上的一个点
2.dip或dp:(设备独立像素):一种基于屏幕密度的抽象单位
在每英寸160点的显示器上,1dip=1px
3.sp(比例像素):主要处理字体的大小,可以根据用户的字体大小首选项进行缩放
4.PPI即每英寸所拥有的像素数目,所表示的是每英寸所拥有的像素(pixel)数目
( ppi/160 =getResources().getDisplayMetrics().scaledDensity)


px与dp相互转换公式
px = dp*ppi/160 
dp = px / (ppi / 160)
规定单位转换需要加上0.5f偏移值


四、距离单位及边距


layout_margin   外边距  控件距离父控件边界的距离
padding         内边距  控件内容距离控件边界的距离


五、常用控件 


控件对象.getText().toString().trim()返回一个去掉空格的字符串 trim()去掉前后空格


1.TextView   文本控件


android:textSize="15sp"  设置字体大小 用sp   sp=dp
android:gravity="center" 设置控件内容在什么位置 |后面还可以接位置
android:layout_gravity="center" 设置控件在父布局中的位置


2.ImageView  图片控件


android:src="@mipmap/ic_launcher"  图片的来源


3.EditText   输入框控件


android:inputType="textPassword"   密码可设置不可见
android:hint="手机号"              未输入之前显示的内容 输入后隐藏
android:singleLine="true"          不能换行输入
android:background="@null"         隐藏输入框下划线
android:textCursorDrawable="@null" 光标与字体颜色一致


4.RadioButton 单选框控件 必须用RadioGroup包裹 RadioButton必须指定id


可以通过RadioGroup对象.getCheckedRadioButtonId()获取RadioButton下的id


android:button="@null"  去掉○


   android:layout_width="match_parent"
   android:layout_height="45dp"
   android:orientation="horizontal">  //不写时默认纵向排序(vertical)


               android:id="@+id/radio1"
         android:layout_width="wrap_content"
         android:layout_height="45dp"
         android:text="男" />


               android:id="@+id/radio2"
         android:layout_width="wrap_content"
         android:layout_height="45dp"
         android:text="女" />



5.CheckBox  复选框控件


android:text="我确认接受协议"  写入内容


6.Button   按钮控件


android:text="确认"  写入内容
android:focusable="false"  表示不抢别的控件的监听事件


7.Spinner   下拉框控件


8.ExpandableListView 嵌套listView 类似qq好友列表


适配器实现ExpandableListAdapter


9.SeekBar   可拉拽的进度条 类似手机音量


10.ProgressBar   下载进度条  默认转圈样式


style="@android:style/Widget.ProgressBar.Horizontal" 设置为水平进度条


★自定义适配器:


局部内部类方法:
//自定义Apdater ,继承自BaseAdapter
class SpinnerAdapter extends BaseAdapter{
    //数据显示的数量
    public int getCount() {
       if(list == null){
           return 0;
       }
       return list.size();
    }
    //当前这一行的数据,Spinner通过getSelectedItem()来获取
    public Object getItem(int position) {
       return list.get(position);
    }
    //当前位置
    public long getItemId(int position) {
       return position;
    }
    //spinner每一行显示的布局
    public View getView(int position, View convertView, ViewGroup parent) {
       View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.spinner_item,parent,false);
       TextView textView = (TextView) view.findViewById(R.id.textView1);
       textView.setText(list.get(position));
       return view;
     }
 }
//使用自定义Apdapter实现Spinner的数据关联
SpinnerAdapter adapter = new SpinnerAdapter();
spinner.setAdapter(adapter);


★adapter.notifyDataSetChanged(); 更新数据方法


系统适配器:ArrayAdapter  


Spinner spinner = (Spinner) findViewById(R.id.spinner);//spinner控件


//使用ArrayAdapter来实现Spinner控件
ArrayAdapter adapter = new ArrayAdapter        (MainActivity.this,R.layout.spinner_item,R.id.textView1,list)
上下文,布局文件资源(自己写的布局),布局的id,对象数组


//给Spinner控件设置适配器Adapter
spinner.setAdapter(adapter);


spinner.getSelectedItem().toString() 显示选择的数据内容


android.R.layout.simple_spinner_item
android.R.layout.simple_spinner_dropdown_item //系统自带布局(样式不同)


spinner.setSelection(position);  //根据传入的下标设置初始化位置


◆这种方法很少用:(内容写死了 不能动态修改)
android:entries="@array/city"   显示的内容


内容写到res下values里的strings.xml



   长沙
   北京
   上海
   天津



◆动态注册View,别加载到界面上
    LinearLayout lin = (LinearLayout) findViewById(R.id.lin);
    View view = LayoutInflater.from(this).inflate(R.layout.test2,null);
    View view2 = LayoutInflater.from(this).inflate(R.layout.activity_main,(ViewGroup) view,true);
    lin.addView(view2);


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


【解析XML文件】XmlPullParser工具类可以快速方便的解析xml文件


XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();  //实例化XmlPullParser对象  XmlPullParserFactory工厂类


//把字符串转换成流 加载到XmlPullParser中
xmlPullParser.setInput(new StringReader(String));


//获取当前标签类型 一般以文档头开始
int type = parser.getEventType();


XmlPullParser.START_DOCUMENT  文档头
XmlPullParser.END_DOCUMENT    文档尾
XmlPullParser.START_TAG       开始标签
XmlPullParser.END_TAG         结束标签
XmlPullParser.TEXT            文本


pull.getEventType(); 返回当前解析的内容分类 刚开始指向文档头
pull.getName();  返回当前标签名
pull.getAttributeValue(下标); 返回当前标签指定下标的属性值
pull.nextText();  返回下一段文本
pull.next();  跳到下一解析部分
pull.setInput(FileOutputStream f, String Encode); 设置输入流 编码方式
pull.getText(); 返回当前文本


【Android数据存储】


1.SharedPreferences  它是一个轻量级的存储类 特别适合用于保存软件配置参数


SharedPreferences保存数据 其背后是用xml文件存放数据 
文件存放在/data/data//shared_prefs目录下


可存储数据类型:
long 、boolean、int、float、String、double


初始化:
Context.getSharedPreferences(文件名,打开模式);
getSharedPreferences(name,mode)
方法的第一个参数用于指定该文件的名称 名称不用带后缀 后缀会由Android自动加上
方法的第二个参数指定文件的操作模式 共有四种操作模式


四种操作模式分别为:
1. MODE_APPEND: 追加方式存储
2. MODE_PRIVATE: 私有方式存储,其他应用无法访问  (常用)
3. MODE_WORLD_READABLE: 表示当前文件可以被其他应用读取
4. MODE_WORLD_WRITEABLE: 表示当前文件可以被其他应用写入


常用方法:
abstract boolean contains(String key) 检查文件中是否包含这个key


abstract SharedPreferences.Editor edit()
创建一个Editor对象 通过这个对象可以改变数据 注意的是数据被修改后需要使用函数commint()进行提交
Editor对象.putXX(K,V)  根据类型值储存数据 K--键 V--值


abstract Map getAll()  获取文件中所有的数据


abstract boolean getXX(String key,XX defValue)
查找某个数据(如果没有对应值,返回默认值)


【SQLite数据库】 Android内置了一个轻量级的SqliteDatabase数据库


1.SQLite支持的数据类型
基本数据类型:
 NULL 空值
 INTEGER 带符号的整型 (常用)
 REAL 浮点型
 TEXT 字符串文本      (常用)
 BLOB 二进制对象
但实际上sqlite3也接受如下的数据类型:
 smallint 16 位元的整数
 interger 32 位元的整数
 decimal(p,s) p精确值和 s 大小的十进位整数 精确值p是指全部有几个数(digits)大小值 s是指小数点後有几位数 如果没有特别指定 则系统会设为 p=5; s=0
 float 32位元的实数
 double 64位元的实数


2.SQLite相关类


SQLiteOpenHelper抽象类:通过从此类继承实现用户类 来提供数据库创建、打开、关闭等操作
SQLiteDatabase数据库访问类:执行对数据库的插入记录、查询记录等操作。
SQLiteCursor查询结构操作类:用来访问查询结果中的记录


◆SQLiteOpenHelper
封装了创建和更新数据库使用的逻辑 SQLiteOpenHelper 的子类 一般需要实现如下三个方法


public DBHelper(Context context, String name, CursorFactory factory,int version) {}  //构造方法 可自己写一个构造方法 
例如:


    //数据库的名字
    private static final String sqlName = "test.db";
    //数据库的版本
    private static final int VERSION = 2;


    //构造方法,定义数据库名称和版本
    public MyTestSqlOpenHelper(Context context){
        this(context,sqlName,null,VERSION);
    }


public void onCreate(SQLiteDatabase db) {//使用execSQL()方法执行DDL语句}
//新建表 已存在不会重复 
例如:primary key autoincrement主键自动增长
db.execSQL("create table user(_id integer primary key autoincrement,"name text,age integer)");


public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  //修改表  Version是数据库版本号 只能增不能减
if (oldVersion       db.execSQL("alter table user add address text");
   }
}


◆SQLite插入数据
使用SQLiteDatabase 对象的 insert(), update(), delete() 方法


ContentValues cv = new ContentValues(); //类似Map集合
    cv.put("name", “rose");
    cv.put("address", "ShangHai");
    db.insert(表名, null, cv);  //null表示没写入的地方显示null 返回那行id


◆SQLite查询数据  使用SQLiteDatabase 对象的query()方法


Cursor游标
getCount():获得结果集中有多少记录。
moveToFirst():移动到第一行。
moveToNext():移动到下一行,可遍历所有记录。
isAfterLast():判断是否最后一行。
getColumnNames():返回字段名。
getColumnCount():返回字段号。
getString(),getInt():传入想要获取的字段的下标位置,返回字段值
requery():重新执行查询得到游标。
getColumnIndex() : 传入字段名,返回当前字段下标位置
close():释放游标资源


getString()和getColumnIndex()一般放一起用


例如:
//表名,查询的数据,条件,条件赋值,排序等
Cursor cursor = db.query("user",new String[]{"name","age"},"name=? and age=?",new String[]{"胡桢","50"},null,null,null);


//查询所有可全设为null  
Cursor cursor = db.query("user",null,null,null,null,null,null);
List list = new ArrayList<>();
        //循环游标,获取数据
        while(cursor.moveToNext()){
            Person person = new Person();
            person.setId(cursor.getInt(cursor.getColumnIndex("_id")));
            person.setName(cursor.getString(cursor.getColumnIndex("name")));
            person.setAge(cursor.getInt(cursor.getColumnIndex("age")));
            list.add(person);
        }
        //释放游标,防止内存泄漏
        cursor.close();
        return list;


◆SQLiteDatabase
//插入、更新、删除数据需用到写入权限
SQLiteDatabase db = SQLiteOpenHelper对象.getWritableDatabase();
//查询需用到读取权限 但是读的时候能够获得写入权限
SQLiteDatabase db = SQLiteOpenHelper对象.getReadableDatabase();


【SD卡存储数据】


SD卡路径:
Environment.getExternalStorageDirectory().getAbsolutePath(); 


SD是否可用:
Environment.getExternalStorageState()   获取当前SD卡状态
Environment.MEDIA_MOUNTED SD卡可用


读取SD卡文件和向SD卡写入文件


例如:   与IO流类似


File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "/aaa.txt");
BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
            String str = "你好,--";
            bw.write(str);
            bw.flush();


            BufferedReader br = new BufferedReader(new FileReader(file));
            String str2 = "";
            StringBuffer sb = new StringBuffer();
            while ((str2 = br.readLine()) != null) {
                sb.append(str2);
            }
            textView.setText(sb.toString());


            bw.close();
            br.close();


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


【ListView】一种用于垂直显示的列表控件 如果显示内容过多 则会出现垂直滚动条


android:divider="@color/colorAccent" 线条颜色
android:dividerHeight="1dp"          线条高度
android:cacheColorHint="@null"       防止listview出现缓存导致黑屏现象 
android:scrollbars="none"            设置滚动条
android:listSelector="@color/colorAccent" item选中颜色


ListView列表的显示需要三个元素:
ListVeiw: 用来展示列表的View
适配器:用来把数据映射到ListView上的中介
数据:具体的将被映射的字符串,图片等


ListView在显示数据的时候可以设置布局方式,android系统提供了几种默认的布局:
android.R.layout.simple_list_item_1每个列表项只有一个TextView用来显示数据
android.R.layout.simple_list_item_2每个列表项有两个TextView用来显示数据
android.R.layout.simple_list_item_single_choice每个列表项后都有一个单选按钮
android.R.layout.simple_list_item_multiple_choice每个列表项后都有一个多选按钮
android.R.layout.simple_list_item_checked每个列表项后都有一个CheckedTextView


★ListView事件及监听


单击列表项时触发
public interface OnItemClickListener {
        void onItemClick(AdapterView parent, View view, int position, long id);
    }
列表项被选择时触发
public interface OnItemSelectedListener {
void onItemSelected(AdapterView parent, View view, int position, long id);
void onNothingSelected(AdapterView parent);
    }
position为选中的列表项在ListView中的位置
id为被选中的那一行的id
parent指被单击的ListView
View代表用户选中的那一项


ListView组件不以使用那种布局作为单选或复选的标准,必须使用setChoiceMode()方法设置选取模式以后,单选复选才起作用(搭配系android系统提供的几种默认布局使用)
ListView.CHOICE_MODE_SINGLE  常量为1,表示单选
ListView.CHOICE_MODE_MULTIPLE 常量为2,表示复选
ListView.CHOICE_MODE_NONE 常量为0普通列表,无论使用了何种样式,单选复选都不起作用


★ListView适配器★


ListView适配器需要实现内存重复使用 可降低内存占用 防止内存溢出
前面与spinner适配器一样 差别在最后一个方法


public View getView(int position, View convertView, ViewGroup parent) {


      //定义一个ViewHolder 类对象 赋值为null
      ViewHolder viewHolder = null;


      //当(convertView 为空时 创建一个View
      if (convertView == null){


          //初始化ViewHolder 对象
          viewHolder = new ViewHolder();
          //初始化convertView 
          convertView = LayoutInflater.from(ListViewActivity.this).inflate(R.layout.adapter_item,parent,false);


          //初始化ViewHolder 类里的属性
          viewHolder.textView = (TextView) convertView.findViewById(R.id.text);
          viewHolder.imageView = (ImageView) convertView.findViewById(R.id.image);


          //用convertView.setTag()打包
          convertView.setTag(viewHolder);
      }else {
          //当convertView不为空时 给viewHolder赋值 不然后面会报空指针
          viewHolder = (ViewHolder) convertView.getTag();
      }


      //用viewHolder对象.属性设值
      viewHolder.textView.setText(list.get(position).get("text").toString());
      viewHolder.imageView.setBackgroundResource(Integer.parseInt(list.get(position).get("img").toString()));


      return convertView;
  }


//ViewHolder为了优化ListView内存 防止内存溢出 实现布局控件复用
class ViewHolder {
       TextView textView;
       ImageView imageView;
  }


【GridView】 和ListView一样 多了一个属性


android:numColumns="2"  设置多少列


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


【RecyclerView】


只要用Android提供的LieanerLayoutManager并配以VERTICAL模式 RecyclerView就可以完美达到ListView的基本效果 两者的设计结构也都是数据(Dataset)与视图(View)分离 然后通过适配器(Adapter)来连接的方式


◆强制使用ViewHolder
◆没有OnItemClickListener
  开发者可以定义自己的OnItemClickListner来接受点击事件
◆视图与布局分离 
◆支持子项目层次的动画效果
  ItemAnimator接口


【RecyclerView实现】


◆适配器继承RecyclerView.Adapter
  getView方法不用自己写了 我们只需要写好Viewholder View的复用已经封装好了
◆LayoutManager 
  这个类决定视图被放在画面中哪个位置 简单来说就是管理布局 设置为LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager可以实现ListView,GridView以及流式布局的列表效果 它还可以管理滚动和循环利用
◆ItemAnimator
  这个类可以实现增删动画 而且不想设置的话它的默认效果已经很好了


◆需要配置compile'com.android.support:recyclerview-v7:23.1.1'


【例子】


final MyAdapter adapter = new MyAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);


//adapter调用自定义方法 实现接口的点击事件方法
adapter.setOnItemClickListener(new IOnItemClick() {


    public void onItemClickListener(View view, int position) {
        Toast.makeText(RecyclerActivity.this, "点击了第" + position + "个", Toast.LENGTH_SHORT).show();
    }
});


//adapter调用自定义方法 实现接口的长按事件方法
adapter.setOnItemLongClickListener(new IOnItemLongClick() {


    public void onItemLongClickListener(View view, int position) {
        Toast.makeText(RecyclerActivity.this, "长按了" + list.get(position), Toast.LENGTH_SHORT).show();
    }
});


button.setOnClickListener(new View.OnClickListener() {


    public void onClick(View v) {
        //点击按钮 删除数据
        list.remove(list.size()-1);
        //移除动画效果显示在最末尾
        adapter.notifyItemRemoved(list.size());
    }
});


button2.setOnClickListener(new View.OnClickListener() {


    public void onClick(View v) {
        //点击按钮新增数据
        list.add("i=="+list.size());
        //新增动画效果显示在最末尾
        adapter.notifyItemInserted(list.size());
    }
});
    }


//适配器 <>传入自己写的ViewHolder
class MyAdapter extends RecyclerView.Adapter {
    private IOnItemClick onItemClick;
    private IOnItemLongClick onItemLongClick;


    //返回一个ViewHolder
    public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(RecyclerActivity.this).inflate(R.layout.activity_main_aidl, parent, false);
        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }


    //定义点击监听方法 传入接口
    public void setOnItemClickListener(IOnItemClick onItemClick) {
        this.onItemClick = onItemClick;
    }


    //定义长按监听方法 传入接口
    public void setOnItemLongClickListener(IOnItemLongClick onItemLongClick) {
        this.onItemLongClick = onItemLongClick;
    }


    //绑定ViewHolder 可以给控件设置属性
    public void onBindViewHolder(MyAdapter.MyViewHolder holder, int position) {
        holder.textView.setText(list.get(position));
    }




    public int getItemCount() {
        return list.size();
    }


    //定义一个ViewHolder继承RecyclerView.ViewHolder
    class MyViewHolder extends RecyclerView.ViewHolder {
         private TextView textView;


         //在构造方法里初始化控件
         public MyViewHolder(final View itemView) {
             super(itemView);
             textView = (TextView) itemView.findViewById(R.id.textView);


             //控件设置监听事件
             textView.setOnClickListener(new View.OnClickListener() {


                public void onClick(View v) {
                   //点击事件 调用接口的方法
                   if (onItemClick != null) {
                       onItemClick.onItemClickListener(itemView, getPosition());
                   }
                }
             });


             textView.setOnLongClickListener(new View.OnLongClickListener() {


                public boolean onLongClick(View v) {
                   //长按事件 调用接口的方法
                   onItemLongClick.onItemLongClickListener(itemView, getPosition());
                    return true;
                }
              });
         }
    }
}


//点击事件接口
public interface IOnItemClick {


    void onItemClickListener(View view, int position);
}
//长按事件接口
public interface IOnItemLongClick {


    void onItemLongClickListener(View view, int position);
}
=======================================================================


【Android消息处理机制】
Android是消息驱动的,实现消息驱动有几个要素:


消息的表示:Message
消息队列:MessageQueue
消息循环,用于循环取出消息进行处理:Looper
消息处理,消息循环从消息队列中取出消息后要对消息进行处理:Handler


平时我们最常使用的就是Message与Handler了


Handler、Message、Looper之间的关系:
在线程(Thread)中发送消息(Message) 进入消息队列(Message Queue) 通过Looper对象循环取出消息 并交给Handler进行处理消息


Message的创建:
1.直接new 一个message
2.Message.obtain() 防止重复创建


Looper对象的创建:
1.在子线程中需要手动初始化looper对象:Looper.prepare()启用Looper
public void run() {
    Looper.prepare();  //调用prepare后到调用loop类似于while循环
          
    handler = new Handler() {
       public void handleMessage(Message msg) {
           // process incoming messages here
       }
    };
          
    Looper.loop();
}
Looper.loop() 让Looper开始工作 从消息队列里取消息 处理消息
注意:写在Looper.loop()之后的代码不会被执行 这个函数内部应该是一个循环 当调用mHandler.getLooper().quit()后 loop才会中止 其后的代码才能得以运行


2.在主线程中默认创建Looper对象


【Handler】


handler = new Handler(){
      public void handleMessage(Message msg) {
          super.handleMessage(msg);
          if (msg.what==0){
              Toast.makeText(Main5Activity.this,msg.obj.toString(),Toast.LENGTH_SHORT).show();
          }
      }
};
new Thread(new Runnable() {
      public void run() {
          ★handler 发送消息的各种方式:


          //1、立即发送一个message.what的消息给hanlder
          handler.sendEmptyMessage(0);


          //2、延迟两秒后发送一个message.what的消息给hanlder
          handler.sendEmptyMessageDelayed(0,2000);


          //3、在当前时间+2秒后 发送一个message.what的消息给hanlder
          handler.sendEmptyMessageAtTime(0,System.currentTimeMillis()+2000);
          //4、直接使用Message对象进行封装消息并发送
          Message.obtain(handler,0,"我来自子线程!").sendToTarget();


          //5、直接使用handler对象进行封装消息并发送
          handler.obtainMessage(0,"我来自子线程!").sendToTarget();


          //6、最明了的Handler消息发送
          String str = "我来自子线程!";
          Message msg = new Message();
          msg.obj = str;
          msg.what = 0;
          handler.sendMessage(msg);
      }
}).start();


Handler可能引起内存泄漏:activity有一个onDestroy方法在此removehandler


protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
}


【RunOnUiThread】


调用runOnUiThread前面省略了一个this. 所以在fragment时调用时前面加一个getActivity. 或者getContext. 看情况


new Thread(new Runnable() {
      public void run() {
         while(probar.getProgress()<100){
            try {
               Thread.sleep(1000);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
            count = probar.getProgress();
            count++;


            //在子线程调用runOnUiThread 可以直接在方法中进行ui操作
            runOnUiThread(new Runnable() {
               public void run() {
                  probar.setProgress(count);
                  textView.setText(probar.getProgress()+"");
               }
            });
         }
      }
}).start();


【AsyncTask异步任务】


AsyncTask是个抽象类 使用时需要继承这个类 然后调用execute()方法 
注意继承时需要设定三个泛型Params Progress和Result的类型 如AsyncTask


Params是指调用execute()方法时传入的参数类型和doInBackgound()的参数类型
Progress是指更新进度时传递的参数类型 即publishProgress()和onProgressUpdate()的参数类型
Result是指doInBackground()的返回值类型


上面的说明涉及到几个方法:
doInBackgound()这个方法是继承AsyncTask必须要实现的 运行于后台 耗时的操作可以在这里做
publishProgress()更新进度 给onProgressUpdate()传递进度参数
onProgressUpdate()在publishProgress()调用完被调用 更新进度


例如:


MyAsyncTask async = new MyAsyncTask();
async.execute();


class MyAsyncTask extends AsyncTask {


    //异步任务 该方法运行在doInBackground之前 是在UI线程的
    protected void onPreExecute() {
       super.onPreExecute();
       Toast.makeText(AsyncTaskActivity.this, "开始任务", Toast.LENGTH_SHORT).show();
    }


    //后台运行 该方法主要进行耗时操作 不在UI线程
    protected Void doInBackground(Void... params) {
       while (count < 100) {
           count++;
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           publishProgress(count);
       }
       return null;
    }


    //在UI线程中 在doInBackground运行完后执行
    protected void onPostExecute(Void aVoid) {
       super.onPostExecute(aVoid);
       Toast.makeText(AsyncTaskActivity.this, "结束任务", Toast.LENGTH_SHORT).show();
    }


    //publishProgress()被调用后执行 publishProgress()用于更新
    protected void onProgressUpdate(Integer... values) {
       super.onProgressUpdate(values);
       values[0] = count;
       seekBar.setProgress(values[0]);
       textView.setText("已下载" + values[0] + "" + "%");
    }
}


【AsyncTask和Handler比较】


AsyncTask实现的原理和适用的优缺点:
AsyncTask是android提供的轻量级的异步类 可以直接继承AsyncTask 在类中实现异步操作 并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新) 最后反馈执行的结果给UI主线程
使用的优点:
简单、快捷
过程可控      
使用的缺点:
在使用多个异步操作和并需要进行Ui变更时 就变得复杂起来


Handler异步实现的原理和适用的优缺点:
在Handler 异步实现时 涉及到Handler、Looper、Message、Thread四个对象 实现异步的流程是主线程启动Thread(子线程)运行并生成Message-Looper获取Message并传递给HandlerHandler逐个获取Looper中的Message 并进行UI变更
使用的优点:
结构清晰 功能定义明确
对于多个后台任务时 简单、清晰
使用的缺点:
在单个后台异步处理时 显得代码过多 结构过于复杂(相对性)


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


【Android网络请求】
Android常用的联网方式 包括JDK支持的HttpUrlConnection Apache支持的HttpClient 以及开源的一些联网框架


需要在AndroidManifest.xml注册:



【HttpUrlConnection】


handler = new Handler() {
     public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if (msg.what == 0) {
             String str = msg.obj.toString();
             //解析Json
             GameBean game = analyticJson(str);


        }


     }
};


new Thread(new Runnable() {
   public void run() {
      try {
         //创建URL对象
         URL url = new URL("http://passport.7pa.com/mgame/list?pageSize=20&pageNum=1&cateid=1&typeid=1");
 
         //打开一个HttpURLConnection 连接
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         //设置请求方式
         conn.setRequestMethod("GET");
         //开始连接
         conn.connect();


         int code = conn.getResponseCode();
         //判断是否请求是否成功
         if (code == 200) {
             BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
             String str;
             sb = new StringBuffer();
             while ((str = br.readLine()) != null) {
                   sb.append(str);
                   sb.append("\n");
             }
             br.close();
             handler.obtainMessage(0, sb.toString()).sendToTarget();
          }
      } catch (Exception e) {
          e.printStackTrace();
      }
   }
}).start();


【Json解析】


{} 表示对象        使用JSONObject
[] 表示数组(集合)  使用JSONArray


例如:


JSONObject jo = new JSONObject(json);
gameBean.setCode(jo.getInt("code"));
gameBean.setMsg(jo.getString("msg"));


JSONArray ja = jo.getJSONArray("data");
List list = new ArrayList<>();
for (int i = 0; i < ja.length(); i++) {
     GameBean.DataBean dataBean = new GameBean.DataBean();
     JSONObject object = ja.getJSONObject(i);


     dataBean.setId(object.getString("id"));
     dataBean.setName(object.getString("name"));
}


【第三方的网络请求库】


使用第三方Json解析:
public GameBean analyticJson(String json) {
    Gson gson = new Gson();
    GameBean gameBean = gson.fromJson(json, GameBean.class);
    return gameBean;
}
前面的GameBean 和后面GameBean.class要对应 (实体类类名.class )


★需要导包在libs 在项目右键open module settings→app→dependencies→+libs下面的gson-2.3.1.jar


优秀的第三方库:
https://github.com/square/okhttp                (常用)
https://github.com/kevinsawicki/http-request
https://github.com/wyouflf/xUtils3              (常用)
https://github.com/mcxiaoke/android-volley      (常用)
https://github.com/loopj/android-async-http


例如:使用xUtils3


使用Gradle构建时添加一下依赖即可:
compile 'org.xutils:xutils:3.3.36'


//首先初始化:新建一个MyApplication继承 Application
public class MyApplication extends Application {
    public void onCreate() {
        super.onCreate();
        x.Ext.init(this);
    }
}


需要在AndroidManifest.xml中在application中添加android:name=".MyApplication"


//请求的地址
RequestParams rp = new RequestParams("http://passport.7pa.com/mgame/list?pageSize=20&pageNum=1&cateid=1&typeid=1");


//x.http().get是get方式请求 new一个回调
x.http().get(rp, new Callback.CommonCallback() {
      //请求成功得到Json
      public void onSuccess(String result) {
           //调用解析Json的方法
           bean = analyticJson(result);
           MyAdapter adapter = new MyAdapter();
           listView.setAdapter(adapter);
      }
      //请求出错
      public void onError(Throwable ex, boolean isOnCallback) {


      }
      //取消请求
      public void onCancelled(CancelledException cex) {


      }
      //请求完成
      public void onFinished() {


      }
});


//网络图片加载bean.getData().get(position).getIconurl()相当于图片网址
x.image().bind(imageView, bean.getData().get(position).getIconurl());


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


【Android文件下载】


需要配置:


◆原生态下载文件 写在线程里
new Thread(new Runnable() {


   public void run() {
      String downurl = "下载链接";
         try {


            //文件夹 前面的方法是获取SD卡路径+一个文件夹名
            File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/file");


            //判断文件夹是否存在 不存在则新建
            if (!path.exists()) {
                 path.mkdirs();
            }


            //创建一个文件名字
            File file = new File(path + "/" + bean.getData().get(position).getName() + ".apk");


            //类似IO流的方式下载一个文件 
            URL url = new URL(downurl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            InputStream is = conn.getInputStream();
            FileOutputStream fos = new FileOutputStream(file);


            //文件如果很大可以定的很大一点
            byte[] b = new byte[50 * 1024];
            while (is.read(b) != -1) {
                 fos.write(b);
            }
            fos.flush();
         } catch (Exception e) {
            e.printStackTrace();
         } finally {
            try {
                 if (is != null) {
                     is.close();
                 }
                 if (fos != null) {
                     fos.close();
                 }
            } catch (IOException e) {
                 e.printStackTrace();
            }
          }
    }
}).start();


◆第三方下载文件
RequestParams request = new RequestParams("下载链接");


//设置文件路径和文件名
request.setSaveFilePath("/sdcard/fileGame" + "/" + bean.getData().get(position).getName() + ".apk");


//回调 与网络请求回调有点区别
x.http().get(request, new Callback.ProgressCallback() {


      //暂停下载会进这个方法
      public void onWaiting() {


      }


      //开始下载前会进这个方法
      public void onStarted() {


      }


      //正在下载时会进这个方法 可以写进度条控件
      public void onLoading(long total, long current, boolean isDownloading) {
        total总大小 current下载进度 isDownloading是否正在下载
      }


      //下载成功会进这个方法
      public void onSuccess(File result) {
           Toast.makeText(Json2Activity.this, "下载成功", Toast.LENGTH_SHORT).show();
      }


      //下载出错会进这个方法
      public void onError(Throwable ex, boolean isOnCallback) {


      }


      //取消下载会进这个方法
      public void onCancelled(CancelledException cex) {


      }


      //下载完成会进这个方法
      public void onFinished() {


      }
});


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


【Activity介绍】


Activity是Android的四大组件之一 是应用程序加载布局的容器
一个Activity通常就是一个单独的屏幕 Activity之间通过Intent进行通信


●生命周期:
Activity starts开始时
     ↓
     ↓
进入onCreate()←←←←←←←←←←←←←←←←←←←←←←←←←←←←
     ↓                                                           ↑
     ↓                                                           ↑
进入onStart()←←←←←←←←←←←←←←←←←←←←←←←        ↑
     ↓                                                 ↑        ↑
     ↓                                                 ↑        ↑
进入onResume()   Activity正在运行 如果跳到另一个界面     ↑        ↑
     ↓   ↑                                             ↑        ↑
     ↓   ↑ 如果很快又回到之前界面重新进入onResume()     ↑        ↑
进入onPause()                                           ↑        ↑
     ↓  如果长时间没返回                               ↑        ↑
     ↓                                                 ↑        ↑
进入onStop()→→→之前界面又重新回到当前界面→→→进入onRestart(    ↑
     ↓  ↓  其他程序需要更多内存时会关闭此Activity 如果重新打开时  ↑
     ↓   →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→
     ↓   Activity运行结束
进入onDestroy()
     ↓
     ↓
Activity is shut down 程序关闭


【Activity显式意图界面跳转】


第一个界面:
Intent intent = new Intent(MyActivity.this,JumpTestActivity.class);
intent.putExtra("text","来自第一个界面的数据");
startActivityForResult(intent,0); //需要返回数据 0表示请求标示
startActivity(intent);    //单纯跳转


protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode==0&&resultCode==2){
         Toast.makeText(MyActivity.this,data.getStringExtra("result"),Toast.LENGTH_SHORT).show();
    }
}


第二个界面:
Intent intent = getIntent(); //拿到第一个界面传过来的值 
    if (intent != null){
        textView.setText(intent.getStringExtra("text"));
    }


button.setOnClickListener(new View.OnClickListener() {


    public void onClick(View v) {
        Intent intent2 = new Intent(JumpTestActivity.this,MyActivity.class);
        intent2.putExtra("result","来自第二个界面的返回数据");
        //设置返回标示 和数据
        setResult(2,intent2);
        //关闭当前activity
        finish();
    }
});


配置文件两个Activity都要注册 第一个界面的Activity写在主入口


【Activity隐式意图界面跳转】


第一个程序:
Intent intent = new Intent();
//跳转到第二个程序 传第二个程序的action名字
intent.setAction("action.my.other");
startActivity(intent);


//传类别的名字
intent.addCategory(android.intent.category.HOME);


配置文件:
//设置此程序为主界面 按home键时 模拟系统应用

//这个属性一定要写 表示类别默认



第二个程序的配置文件:




Bundle bundle = new Bundle();
bundle.put("K",V);    //以键值对的形式存数据
bundle.getXX("K","M");  //按照键去找值 没有时返回一个默认M值


【Activity状态保持】


使用onSaveInstanceState和onRestoreInstanceState来进行保存


protected void onSaveInstanceState(Bundle outState) {
     super.onSaveInstanceState(outState);
     outState.putString("key","有数据");
}


protected void onRestoreInstanceState(Bundle savedInstanceState) {
     super.onRestoreInstanceState(savedInstanceState);
     Toast.makeText(this,savedInstanceState.getString("key","没数据"),Toast.LENGTH_SHORT).show();
}


【任务堆栈】


Android中的任务堆栈是指与用户交互的一组Activity的集合 Activity会被按打开顺序安排在一个堆栈里


在配置文件中activity添加一个属性:
android:launchMode=四种模式


标准模式(默认):
●Standard :每次激活Activity时都会创建Activity 并放入任务栈中


独享任务栈顶(浏览器书签):
●SingleTop :如果在任务的栈顶正好存在该Activity的实例就重用该实例 否则就会创建新的实例并放入栈顶(即使栈中已经存在该Activity实例 只要不在栈顶都会创建实例)


独享任务堆栈:
●SingleTask:如果在栈中已经有该Activity的实例就重用该实例(会调用实例的onNewIntent()) 重用时会让该实例回到栈顶 因此在它上面的实例将会被移除栈 如果栈中不存在该实例 将会创建新的实例放入栈中


单例模式:
●SingleInstance:在一个新栈中创建该Activity实例 并让多个应用共享该栈中的该Activity实例 一旦该模式的Activity的实例存在于某个栈中 任何应用再激活该Activity时都会重用该栈中的实例 其效果相当于多个应用程序共享一个应用 不管谁激活该Activity都会进入同一个应用中


如:通话界面:在哪里激活都是同一个界面


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


【Service简介】


Service(服务)是一个没有用户界面的在后台运行执行耗时操作的应用组件


其他应用组件能够启动Service 并且当用户切换到另外的应用场景 Service将持续在后台运行


另外 一个组件能够绑定到一个service与之交互(IPC机制) 例如一个service可能会处理网络操作、播放音乐、操作文件I/O或者与内容提供者(content provider)交互 所有这些活动都是在后台进行


【Service启动方式】


通过startService()启动的服务处于“启动的”状态 一旦启动service就在后台运行 即使启动它的应用组件已经被销毁了 当下载或上传一个文件 当这项操作完成时 service应该停止它本身


还有一种“绑定”状态的service 通过调用bindService()来启动 一个绑定的service提供一个允许组件与service交互的接口 可以发送请求、获取返回结果 还可以通过跨进程通信来交互(IPC) 绑定的service只有当应用组件绑定后才能运行 多个组件可以绑定一个service 当调用unbind()方法时 这个service就会被销毁了
★一个Activity只绑定一次


【Service生命周期】


●启动服务
该服务在其他组件调用 startService() 时创建 然后无限期运行 且必须通过调用 stopSelf() 来自行停止运行 此外其他组件也可以通过调用 stopService() 来停止服务 服务停止后 系统会将其销毁


startService()→→onCreate()→→onStartCommand()→→onDestroy()


●绑定服务
该服务在另一个组件(客户端)调用 bindService() 时创建 然后客户端通过IBinder 接口与服务进行通信 客户端可以通过调用 unbindService() 关闭连接 多个客户端可以绑定到相同服务 而且当所有绑定全部取消后 系统即会销毁该服务(服务不必自行停止运行)


startService()→→onCreate()→→onBind()→→onUnbind()→→onDestroy()


【Service启动】


Intent intent = new Intent(this,MyService.class);
intent.putExtra("key","服务启动");
//启动服务
startService(intent);
//关闭服务
stopService(intent);


//写在Service里
public int onStartCommand(Intent intent, int flags, int startId) {
     //使用startService启动服务会进入该方法
     Toast.makeText(this,intent.getStringExtra("key"),Toast.LENGTH_SHORT).show();
     stopSelf();
     return super.onStartCommand(intent, flags, startId);
}






//启动服务通过bind方式
三种静态标志:
BIND_AUTO_CREATE 表示当收到绑定请求时 如果服务尚未创建 则即刻创建 在系统内存不足 需要先销毁优先级组件来释放内存 且只有驻留该服务的进程成为被销毁对象时 服务才可被销毁


BIND_DEBUG_UNBIND 通常用于调试场景中判断绑定的服务是否正确 但其会引起内存泄漏 因此非调试目的不建议使用


BIND_NOT_FOREGROUND 表示系统将阻止驻留该服务的进程具有前台优先级 仅在后台运行
bindService(intent,conn,BIND_AUTO_CREATE);


ServiceConnection conn = new ServiceConnection() {


   public void onServiceConnected(ComponentName name, IBinder service) {
        //接收服务中onBind方法的返回值
        MyService.MyIBinder binder = (MyService.MyIBinder) service;
            binder.prompt();
   }


   public void onServiceDisconnected(ComponentName name) {


   }
};


protected void onDestroy() {
     super.onDestroy();
     unbindService(conn);
}


//写在Service里
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    //通过bindService启动服务会进入该方法
    //使用Binder对象进行数据的传递
    MyIBinder myIBinder = new MyIBinder();
        return myIBinder;
}


class MyIBinder extends Binder {
    public String prompt(){
        return "启动服务";
    }
}


【IntentService简介】


IntentService是Service类的子类 用来处理异步请求
客户端可以通过startService(Intent)方法传递请求给IntentService 
IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来处理所有Intent请求对象(通过startService的方式发送过来的)所对应的任务 这样以免事务处理阻塞主线程 执行完所一个Intent请求对象所对应的工作之后 如果没有新的Intent请求达到 则自动停止Service 否则执行下一个Intent请求所对应的任务


IntentService在处理事务时 还是采用的Handler方式 创建一个名叫ServiceHandler的内部Handler 并把它直接绑定到HandlerThread所对应的子线程 ServiceHandler把处理一个intent所对应的事务都封装到叫做onHandleIntent的虚函数 因此我们直接实现虚函数onHandleIntent 再在里面根据Intent的不同进行不同的事务处理就可以了


startService(intent);


public class MyIntentService extends IntentService {


    public MyIntentService() {
        super("MyIntentService");
    }


    @Override
    protected void onHandleIntent(Intent intent) {
        //自带线程,可以进行耗时操作
    }


}


【AIDL】


Android系统中的各应用程序都运行在各自的进程中 进程之间通常是无法直接交换数据的
      
Android提供了跨进程调用Service的功能 称为AIDL(android interface define language)Android接口定义语言
      
ADIL相当与两个进程通信的协议 通过这个协议对进程间的通信进行了规范 按照该规范编写代码即可实现进程间的通信


◆AIDL 接口文件
      跨进程调用服务中需要定义接口文件 扩展名为.aidl
      1、在项目的Main文件夹下定义一个AIDL接口文件
      2、AIDL接口的内部语法与Java很相似
      3、.aidl接口文件创建后,需要重新构建Gradle 这样会在build/generated/source/aidl文件下自动生成相应的文件


★只适用于使用bindService()启动的Service


●在一个程序里两个进程:


//AIDL接口
package com.example.administrator.mydemo;


interface IMyAidlInterface {
    String text(); //接口中的方法
}


//Service
public IBinder onBind(Intent intent) {
    return stub;
}
IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {


    public String text() throws RemoteException {
         return "我想回家睡觉= =";
    }
};


//Activity
Intent intent = new Intent(MainAidlActivity.this,MyAidlService.class);
bindService(intent,conn,BIND_AUTO_CREATE);


ServiceConnection conn = new ServiceConnection() {


   public void onServiceConnected(ComponentName name, IBinder service) {
       IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
       try {
           textView.setText(iMyAidlInterface.text());


       } catch (RemoteException e) {
           e.printStackTrace();
       }
   }




   public void onServiceDisconnected(ComponentName name) {


   }
};


//配置文件
    android:name=".MyAidlService"
    android:process=":test">     //表示不在一个进程内容:+自定义



●在两个程序里:


//程序一中的Activity
Intent intent = new Intent("service.application");
bindService(intent,conn,BIND_AUTO_CREATE);


ServiceConnection conn = new ServiceConnection() {


   public void onServiceConnected(ComponentName name, IBinder service) {
       IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
       try {
           textView.setText(iMyAidlInterface.text());


       } catch (RemoteException e) {
           e.printStackTrace();
       }
   }




   public void onServiceDisconnected(ComponentName name) {


   }
};


//程序二中的Service
public IBinder onBind(Intent intent) {
    return stub;
}


IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {


    public String text() throws RemoteException {
        return "我想看机械师2!!!";
    }
};




//程序二中的Service的配置文件
    android:name=".MyService">
   
         
   




★两个程序中的AIDL接口必须一样(方法一致) 包的文件夹名字也必须相同
========================================================================


【ContentProvider内容提供者】
当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。


应该明确以下几点:
1.在系统里不同程序之间共享数据
2.不同程序之间交换数据的标准API
3. ContentProvider以Uri的形式对外提供数据 其他程序使用ContentResolver根据Uri去操作指定的数据
4.四大组件之一 必须要在AndroidManifest.xml进行配置
5.一旦程序通过ContentProvider暴露自己的数据操作接口 不管程序是否启动 其他程序都可以通过该接口访问或操作该程序的内部数据
6.增、删、查、改四种数据操作方式


【Uri类】


Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")
这种URI由3个部分组成: 
“content://”、代表数据的路径、和一个可选的标识数据的ID


URI也可以写成如下形式:
  Uri person = ContentUris.withAppendedId(People.CONTENT_URI,45);




【例子】


◆自己定义一个ContentProvider


private MySql mySql;
//定义一个UriMatcher 类
private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
private static final int USERS = 1;
private static final int USER = 2;


//UriMatcher 类有添加uri的方法
static {
    MATCHER.addURI("com.xiaoairen.test", "user", USERS);
    MATCHER.addURI("com.xiaoairen.test", "user/#", USER);//#占位符 int值
}


插入数据:
public Uri insert(Uri uri, ContentValues values) {
     SQLiteDatabase db = mySql.getWritableDatabase();
     long id = db.insert("user", null, values);


     return ContentUris.withAppendedId(uri, id); //返回一个uri
}


新建数据库:
public boolean onCreate() {
     mySql = new MySql(this.getContext()); //这里上下文需用到getContext()
     return false;
}


查询数据:
public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
     SQLiteDatabase db = mySql.getReadableDatabase();
     Cursor cursor = null;
     //匹配uri
     switch (MATCHER.match(uri)) {
        case USERS:
            cursor = db.query("user", projection, selection, selectionArgs, null, null, sortOrder);
            break;


        case USER:
            //截取字符串 获得最后的int值 可用string方法里的substring()
            long id = ContentUris.parseId(uri);
            //自定义一个条件
            String where = "";
   //如果传过来没有额外条件 只根据int值id去查询数据
            if (selection.equals("")) {
                 where = "_id = " + id;
   //如果传过来有额外条件 根据条件+int值id去查询数据
            } else {
                 where = selection + "and _id = " + id;
            }
            cursor = db.query("user", projection, where, selectionArgs, null, null, sortOrder);
            break;
     }


     return cursor;
}


◆Actitvity里


//调用ContentProvider
ContentResolver resolver = getContentResolver();
//封装uri 向数据库插入数据
Uri uri = Uri.parse("content://com.xiaoairen.test");
ContentValues cv = new ContentValues();
cv.put("name","石佛");
cv.put("age",35);
resolver.insert(uri,cv);


//查询数据
Uri uri2 = Uri.parse("content://com.xiaoairen.test/user");
//不传条件则查询所有
Cursor cursor = resolver.query(uri2,null,null,null,null);
if (cursor != null){
     StringBuffer sb = new StringBuffer();
     while (cursor.moveToNext()){
          sb.append("姓名:");
          sb.append(cursor.getString(cursor.getColumnIndex("name")));
          sb.append("\n");
          sb.append("年龄:");
          sb.append(cursor.getInt(cursor.getColumnIndex("age")));
     }
     textView.setText(sb.toString());
}


//会查询id为1的数据 还可以另加条件
Uri uri3 = Uri.parse("content://com.xiaoairen.test/user/1");
Cursor cs = resolver.query(uri3,null,"",null,null);
if (cs != null){
     StringBuffer sb = new StringBuffer();
     while (cs.moveToNext()){
          sb.append("姓名:");
          sb.append(cs.getString(cs.getColumnIndex("name")));
     }
     Toast.makeText(this,sb.toString(),Toast.LENGTH_SHORT).show();
}


◆配置文件
//建立ContentProvider时会自动生成
      android:name=".MyContentProvider"
      android:authorities="com.xiaoairen.test" >//自己传入的uri



//权限注册




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


【BroadcastReceiver】


本质上就是一个全局的监听器 用于监听系统全局的广播消息 由于BroadcastReceiver是一种全局的监听器 因此它可以方便地实现系统中不同组件之间的通信


程序通过调用Context的sendBroadcast()方法来启动指定的BroadcastReceiver


(1)在程序组件中构建要广播的Intent 使用sendBroadcast()方法发送出去


(2)定义一个自己的广播接收器 这个广播接收器需要继承BroadcastReceiver类 然后重写onReceiver()方法来响应事件


(3)注册广播接收器,可以使用两种方式注册:
//动态注册
IntentFilter filter = new IntentFilter("test.my.receiver");
MyReceiver receiver = new MyReceiver();
registerReceiver(receiver,filter);


//静态注册

   
         
   




【生命周期】


即每次系统广播事件发生后 系统就会创建相应的BroadcastReceiver的实例 并自动触发它的onReceiver()方法 该方法执行完后 BroadcastReceiver的实例就会被销毁


如果BroadcastReceiver的onReceiver()方法不能在10秒内执行完成 Android会认为该程序无响应 所以不要在BroadcastReceiver的onReceiver()方法中执行耗时的操作 否则会弹出ANR的对话框


如果确实需要根据Broadcast来完成比较耗时的任务 则可以考虑通过Intent启动一个Service来完成操作


【两种BroadcastReceiver】


(1)普通广播:普通广播可以在同一时刻被所有接收者接收到 消息传递的效率比较高 但缺点是接收者不能将处理结果传递给下一个接收者 并且无法终止Broadcast Intent的传播


例如:
//Activity
Intent intent = new Intent();
intent.setAction("test.my.receiver");
intent.putExtra("key","广播");
sendBroadcast(intent);


//Receiver
public void onReceive(Context context, Intent intent) {
       
     Toast.makeText(context, intent.getStringExtra("key"), Toast.LENGTH_SHORT).show();


}


(2)有序广播:Ordered Broadcast的接收者将按预先声明的优先级一次接收Broadcast 如:A的优先级高于B B的优先级高于C 那么Broadcast先传给A 再传给B 最后传给C 优先级声明在元素中的android:priority属性中 数越大优先级别越高 取值范围在-1000~1000 优先级别也可以调用IntentFilter对象的setPriority()方法进行设置
Ordered Broadcast接收者可以终止Broadcast Intent的传播 Broadcast Intent的传播一旦终止 后面的接收者就无法收到Broadcast 另外 Ordered Broadcast的接收者可以将数据传递给下一个接收者 如:A得到Broadcast后 可以往它的结果对象中存入数据 当Broadcast传给B时 B可以从A的结果对象中得到A存入的数据


例如:
//Activity
Intent intent = new Intent();
intent.setAction("test.my.receiver");
intent.putExtra("key2","有序广播");
sendOrderedBroadcast(intent,null);


//Receiver
public void onReceive(Context context, Intent intent) {


     Toast.makeText(context, intent.getStringExtra("key"), Toast.LENGTH_SHORT).show();


     Bundle bundle = new Bundle();
     bundle.putString("first","第一个存入数据");
     setResultExtras(bundle);
}


//Receiver2
public void onReceive(Context context, Intent intent) {
     Bundle bundle = getResultExtras(true);
     Toast.makeText(context,intent.getStringExtra("key2")+"\n"+bundle.getString("first"),Toast.LENGTH_SHORT).show();
}


//配置文件

   
       
   





   
       
   




【系统广播】


下面是android系统中定义了很多标准的Broadcast Action来响应系统的广播事件


//表示电池电量低
Intent.ACTION_BATTERY_LO;


//表示电池电量充足
Intent.ACTION_BATTERY_OK;


//在系统启动完成后,这个动作被广播一次(只有一次)。
Intent.ACTION_BOOT_COMPLETED;


//重启设备时的广播
Intent.ACTION_REBOOT;


//屏幕被关闭之后的广播
Intent.ACTION_SCREEN_OFF;


//屏幕被打开之后的广播
Intent.ACTION_SCREEN_ON;


//关闭系统时发出的广播
Intent.ACTION_SHUTDOWN;


例如:
//Activity
Intent intent = new Intent();
intent.setAction("test.my.receiver");
intent.putExtra("key","广播");
sendBroadcast(intent);


//Receiver
public void onReceive(Context context, Intent intent) {
   if ("test.my.receiver".equals(intent.getAction())){
       Toast.makeText(context, intent.getStringExtra("key"), Toast.LENGTH_SHORT).show();
   }else {
       Toast.makeText(context,"开机了",Toast.LENGTH_SHORT).show();
   }
}


//配置文件

   
       
       
   




响应系统广播需要注册相应权限: 



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


【SurfaceView】


它拥有独立的绘图表面 即它不与其宿主窗口共享同一个绘图表面 SurfaceView的UI就可以在一个独立的线程中进行绘制 不会占用主线程资源 SurfaceView一方面可以实现复杂而高效的UI 另一方面又不会导致用户输入得不到及时响应


【自定义】
使用Camera控制拍照的几个步骤:


1、调用Camera的open()打开相机


2、调用Camera的getParameters()获取拍照参数 该方法返回一个Camera.Paremeters对象


3、调用Camera.Parameters对象方法设置拍照的参数


4、调用Camera.startPreview()方法开始预览取景 在预览取景之前需要调用Camera的setPreviewDisplay(SurfaceHolder holder)方法设置使用哪个SurfaceView来显示取景图片


5、调用Camera的takePicture()方法进行拍照


6、结束程序时 调用Camera的stopPreview()结束取景预览 并调用release()方法释放资源


【权限】
















 
【调用系统相机】


//照相
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 3);


protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   //Activity.RESULT_OK =-1 表示请求成功
   if (requestCode == 3 && resultCode == Activity.RESULT_OK) {


      //Bitmap用来加载图片 非常耗内存资源 bitmap.recycle()回收bitmap
      Bitmap bitmap = (Bitmap) data.getExtras().get("data");
      File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/mycamera");
      if (!path.exists()) {
           path.mkdirs();
      }
      SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HHmmss");
      File file = new File(path + "/" + sdf.format(new Date()) + ".jpg");


      try {
           fos = new FileOutputStream(file);
           //把数据写入文件
           bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
      } catch (Exception e) {
           e.printStackTrace();
      } finally {
           try {
               if (fos != null) {
                   fos.close();
               }
           } catch (IOException e) {
               e.printStackTrace();
           }
      }


      Toast.makeText(Activity.this,"拍摄成功", Toast.LENGTH_SHORT).show();
   } else {
      Toast.makeText(Activity.this,"没有拍摄", Toast.LENGTH_SHORT).show();
   }
}


//录视频
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
startActivityForResult(intent, 3);


protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   if (requestCode == 3 && resultCode == Activity.RESULT_OK) {
          
      Uri uri = data.getData();
      ContentResolver resolver = getContentResolver();
      Cursor cursor = resolver.query(uri, null, null, null, null);
      if (cursor.moveToNext()){
         //储存的路径
         String str = cursor.getString(cursor.getColumnIndex("_data"));
         Toast.makeText(Activity.this,"拍摄成功", Toast.LENGTH_SHORT).show();
      }


   } else {
      Toast.makeText(Activity.this,"没有拍摄", Toast.LENGTH_SHORT).show();
   }
}


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


【补间动画(Tween)】
Android 平台提供了两类动画 一类是Tween动画,就是对场景里的对象不断的进行图像变化来产生动画效果(旋转、平移、放缩和渐变)


主要类:
 
Animation   动画
AlphaAnimation 渐变透明度
RotateAnimation 画面旋转
ScaleAnimation 渐变尺寸缩放
TranslateAnimation 位置移动
AnimationSet 动画集


//起始渐变透明 
Animation animation = new AlphaAnimation(0.1f,1.0f);
//起始角度旋转
Animation animation2 = new RotateAnimation(0.0f,360.0f);
//X、Y轴的起始伸缩
Animation animation3 = new ScaleAnimation(1.0f,0.1f,0.1f,1.0f);
//X、Y轴的起始移动
Animation animation4 = new TranslateAnimation(0.1f,200.0f,0.1f,300.0f);


AnimationSet set = new AnimationSet(true);
set.addAnimation(animation);
set.addAnimation(animation2);
set.addAnimation(animation3);
set.addAnimation(animation4);
set.setDuration(3000);
set.setRepeatCount(2);
set.setFillAfter(true);
imageView.startAnimation(set);


Animation.setDuration(3000);//动画持续时间
Animation.setRepeatCount(3);//设置重复次数
Animation.setFillAfter(true);//动画执行完后是否停留在执行完的状态
Animation.setStartOffset(2000);//执行前的等待时间


还可以在XML操作:
在res下创建一个anim文件 然后新建这些xml set标签
     //渐变
    //旋转
     //缩放
 //移动 X轴以下是100%p 以上是-100%p 从底部计算
例如:
Animation animation = AnimationUtils.loadAnimation(上下文,R.anim.alpha);
imageView.startAnimation(animation);


【帧动画(Frame)】


Frame动画 即顺序的播放事先做好的图像 与gif图片原理类似


把图片放在drawable下 新建一个xml  animation-list
//oneshot 表示是否循环一次 false表示一直循环
例如:
    android:oneshot="false">
   
   
   
   
   
   
   



//ImageView控件:android:background="@drawable/animation"
AnimationDrawable drawable = (AnimationDrawable) imageView.getBackground();
drawable.start();


【属性动画】


Property Animation就是通过动画的方式改变对象的属性了 我们首先需要了解几个属性:


相关的类:


ObjectAnimator     动画的执行类
ValueAnimator      动画的执行类 
AnimatorSet        用于控制一组动画的执行:线性,一起,每个动画的先后执行等
AnimatorInflater   用户加载属性动画的xml文件
TypeEvaluator      类型估值 主要用于设置动画操作属性的值
TimeInterpolator   时间插值 定义动画的变化率


总的来说属性动画就是 动画的执行类来设置动画操作的对象的属性、持续时间、开始和结束的属性值、时间差值等 然后系统会根据设置的参数动态的变化对象的属性


// 重复次数 -1表示无限循环
animator.setRepeatCount(-1);


// 重复模式, RESTART: 重新开始 REVERSE:恢复初始状态再开始
animator.setRepeatMode(ValueAnimator.REVERSE);


/**动画变化率 Interpolator
 * DecelerateInterpolator  动画开始时比较慢 然后继续减速
 * AccelerateDecelerateInterpolator 动画开始和结束时比较慢 中间速度较快
 * AccelerateInterpolator  动画开始时比较慢 之后加速
 * CycleInterpolator  传入int参数 决定动画循环几次 速率改变为正弦曲线
 * LinearInterpolator 匀速
 */
anim.setInterpolator(new DecelerateInterpolator());
anim.setInterpolator(new AccelerateDecelerateInterpolator());
anim.setInterpolator(new AccelerateInterpolator());
anim.setInterpolator(new CycleInterpolator(3));
anim.setInterpolator(new LinearInterpolator());


【ObjectAnimator】


//参数二为对象属性 该属性需要有set和get方法
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"rotationX",0.0f,360.0f).setDuration(3000);
animator.start();


animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {


    public void onAnimationUpdate(ValueAnimator animation) {
        //获取转换后0到1的float值
        float fraction = animation.getAnimatedFraction();
        //获取当前设置的值
        float value = (float) animation.getAnimatedValue();
        imageView.setRotationY(value);
        imageView.setAlpha(fraction);
        imageView.setScaleX(fraction);
        imageView.setScaleY(fraction);
        imageView.setTranslationX(value);
    }
});


//写在xml里 在res文件下新建animator文件

            android:duration="1000"
        android:propertyName="rotationY"
        android:valueFrom="0"
        android:valueTo="360" >
   

            android:duration="1000"
        android:propertyName="alpha"
        android:valueFrom="0"
        android:valueTo="1" >
   

            android:duration="1000"
        android:propertyName="translationY"
        android:valueFrom="0"
        android:valueTo="300" >
   




Animator animator = AnimatorInflater.loadAnimator(MainActivity.this,R.animator.animtion);
animator.setTarget(imageView);
animator.start();


【ValueAnimator】
ObjectAnimator和ValueAnimator的区别之处:ValueAnimator并没有在属性上做操作


好处:不需要操作的对象的属性一定要有getter和setter方法 你可以自己根据当前动画的计算值 来操作任何属性


ValueAnimator animator = ValueAnimator.ofFloat(0.1f,330.0f);
animator.setTarget(imageView);
animator.setDuration(3000).start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {


    public void onAnimationUpdate(ValueAnimator animation) {
        float fraction = animation.getAnimatedFraction();
        float value = (float) animation.getAnimatedValue();
        imageView.setTranslationY(value);
        imageView.setTranslationX(value);
        imageView.setRotationY(value);
        imageView.setScaleX(fraction);
        imageView.setScaleY(fraction);
    }
});


【AnimatorSet】


ObjectAnimator anim = ObjectAnimator.ofFloat(imageView, "rotationY", 0.0f, 360.0f);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(imageView, "alpha", 0.0f, 1.0f);
ObjectAnimator anim3 = ObjectAnimator.ofFloat(imageView, "translationY", 0.0f, 300.0f);


AnimatorSet set = new AnimatorSet();
set.setDuration(3000);
set.setTarget(imageView);


//按顺序执行
set.playSequentially(anim, anim2, anim3);


// 同时执行动画
set.playTogether(anim, anim2, anim3);


// 动画anim和anim2同时执行,然后执行anim3,不能写为set.play(anim).with(anim2).after(anim3);
set.play(anim).with(anim2);
set.play(anim3).after(anim2);


// 动画anim和anim2同时执行,然后执行anim3,不能写为set.play(anim).with(anim2).before(anim3);
set.play(anim).with(anim2);
set.play(anim2).before(anim3);


set.start();


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


【Fragment】


Fragment表现Activity中UI的一个行为或者一部分 可以把Fragment想象成Activity的一个模块化区域 有它自己的生命周期 接收属于自己监听事件 并且可以再Activity运行期间添加和删除


Frament必须嵌入到一个Activity中 它的生命周期直接受到其宿主Activity的生命周期影响 当一个Activity正在运行时 可以独立操作每个Fragment 比如添加或者删除


●生命周期
常用的几个生命周期函数:
onAttach(Context) –> 当fragment被绑定到activity上时回调


onCreate(Bundle) –> 当fragment对象创建的时候回调,一般在此方法里做参数接收


onCreateView(LayoutInflater, ViewGroup, Bundle) –> 创建fragment视图时回调


onResume –> 视图前台运行


onDestoryView –> 视图销毁时回调


onDestory –> 销毁fragment时回调


onDetech() –> fragment与activity失去关联时回调


●动态添加Fragment主要分为4步:
1.获取到FragmentManager 在V4包中通过getSupportFragmentManager 在系统中原生的Fragment是通过getFragmentManager获得的


2.开启一个事务 通过调用beginTransaction方法开启


3.向容器内加入Fragment 一般使用add或者replace方法实现 需要传入容器的id和Fragment的实例


4.提交事务 调用commit方法提交


【Fragment管理】


◆要管理activity中的fragments需要使用FragmentManager通过getSupportFragmentManager() 获得


常用的方法有:
manager.findFragmentById();  //根据ID来找到对应的Fragment实例 主要用在静态添加fragment的布局中 因为静态添加的fragment才会有ID  


manager.findFragmentByTag(); //根据TAG找到对应的Fragment实例 主要用于在动态添加的fragment中 根据TAG来找到fragment实例  


manager.getFragments(); //获取所有被ADD进Activity中的Fragment


◆一般用来对当前的Fragment进行管理 包括add,replace,remove;


常用的针对Fragment的方法有:


//将一个fragment实例添加到Activity的最上层  
add(int containerViewId, Fragment fragment, String tag);  


//将一个fragment实例从Activity的fragment队列中删除  
remove(Fragment fragment);  


//替换containerViewId中的fragment实例 注意 它首先把containerViewId中所有fragment删除 然后再add进去当前的fragment  


replace(int containerViewId, Fragment fragment);


◆调用commit方法提交事务后 想要进行别的操作 需重新开启一个新事务


【ViewPager】 一个可以左右滑动的控件 一般与Fragment一起使用


◆ViewPager与Fragment一起使用


Fragment fragment1 = new BlankFragment();
Fragment fragment2 = new Blank2Fragment();
Fragment fragment3 = new Blank3Fragment();
Fragment fragment4 = new Blank4Fragment();


list = new ArrayList<>();
list.add(fragment1);
list.add(fragment2);
list.add(fragment3);
list.add(fragment4);


MyAdapter adapter = new MyAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);


//适配器 与Fragment一起使用需继承FragmentPagerAdapter
class MyAdapter extends FragmentPagerAdapter{


    public MyAdapter(FragmentManager fm) {
         super(fm);
    }


    public Fragment getItem(int position) {
         return list.get(position);
    }


    public int getCount() {
         return list.size();
    }
}


◆ViewPager独自使用时


LayoutInflater inflater = getLayoutInflater();


View view = inflater.inflate(R.layout.fragment_blank,null);
View view2 = inflater.inflate(R.layout.fragment_blank_fragment2,null);
View view3 = inflater.inflate(R.layout.fragment_blank,null);
View view4 = inflater.inflate(R.layout.fragment_blank_fragment2,null);
View view5 = inflater.inflate(R.layout.fragment_blank,null);
list = new ArrayList<>();
list.add(view);
list.add(view2);
list.add(view3);
list.add(view4);
list.add(view5);


viewpager.setAdapter(new MyAdapter());


//单独使用时 继承PagerAdapter
class MyAdapter extends PagerAdapter{


    public int getCount() {
         return list.size();
    }


    public boolean isViewFromObject(View view, Object object) {
         return view==object;
    }


    public Object instantiateItem(ViewGroup container, int position) {
         container.addView(list.get(position));
         return list.get(position);
    }


    public void destroyItem(ViewGroup container, int position, Object object) {
         container.removeView(list.get(position));
    }
}


◆ViewPager监听


viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {


    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {


    }
    //与底部按钮进行一个绑定 类似于微信
    public void onPageSelected(int position) {
        switch (position){
            case 0:
                radioGroup.check(R.id.radio1);
                break;
            case 1:
                radioGroup.check(R.id.radio2);
                break;
            case 2:
                radioGroup.check(R.id.radio3);
                break;
            case 3:
                radioGroup.check(R.id.radio4);
                break;
         }


    }


    public void onPageScrollStateChanged(int state) {


    }
});


//按钮与页面也进行一个绑定
radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {


    public void onCheckedChanged(RadioGroup group, int checkedId) {
        switch (checkedId){
            case R.id.radio1:
                viewPager.setCurrentItem(0);
                break;
            case R.id.radio2:
                viewPager.setCurrentItem(1);
                break;
            case R.id.radio3:
                viewPager.setCurrentItem(2);
                break;
            case R.id.radio4:
                viewPager.setCurrentItem(3);
                break;
        }
    }
});


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


【Dialog】


Dialog是一个非常重要的UI, 它可以方便的给用户提示,用最简洁的方式向用户展示信息


其直接子类和间接子类有: AlertDialog  BottomSheetDialog  DatePickerDialog  TimePickerDialog  ProgressDialog  CharacterPickerDialog


【AlertDialog】


1.创建AlertDialog.Builder对象


2.调用Builder对象的setTitle()设置标题 setIcon设置图标


3.调用Builder对象的相关方法设置内容:
setMessage() : 设置简单文本框的内容
setItems() : 设置简单列表的内容,数组
setSingleChoiceItems() : 设置单选列表的内容 内容参数可以是数组Cursor ListAdapter
setMultiChoiceItems() :设置多选列表项的内容 内容参数可以是数组


4.调用Builder对象的setPositiveButton()和 setNegativeButton()设置按钮和监听器


5.调用Builder对象的create()方法创建AlertDialog对象 再调用AlertDialog对象的show()方法显示对话框


例如:


//初始化弹窗对象
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());


//设置标题图标 不设置标题 图标是不会显示的
builder.setIcon(R.mipmap.ic_launcher);
builder.setTitle("下载");


//设置文本框内容
builder.setMessage("是否下载?");


//确定按钮 有监听事件
builder.setPositiveButton("是",null);


//取消按钮 同样也有监听事件
builder.setNegativeButton("否", new DialogInterface.OnClickListener() {


    public void onClick(DialogInterface dialog, int which) {
        Toast.makeText(getContext(),"逗我呢",Toast.LENGTH_SHORT).show();
    }
});


//必须调用show()方法显示对话框
builder.show();


【DatePickerDialog和TimePickerDialog】


设置日历和时间


Calendar calendar = Calendar.getInstance();
new DatePickerDialog(MainActivity.this, new DatePickerDialog.OnDateSetListener() {


    public void onDateSet(DatePicker view, int year, int month, int day) {
        // TODO Auto-generated method stub
    }
}, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
        calendar.get(Calendar.DAY_OF_MONTH) ).show();




Calendar calendar = Calendar.getInstance();
new TimePickerDialog(MainActivity.this,
        new TimePickerDialog.OnTimeSetListener() {
            @Override
            public void onTimeSet(TimePicker view, int hour, int minute) {
                // TODO Auto-generated method stub


            }
        }, calendar.get(Calendar.HOUR_OF_DAY),
        calendar.get(Calendar.MINUTE), true).show();


【ProgressDialog】


//设置下载进度条弹窗
ProgressDialog dialog1 = new ProgressDialog(getContext());


//设置进度条为水平方向 ProgressDialog.STYLE_SPINNER设置进度条的形式为圆形
dialog1.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);


//设置点击返回键取消窗口显示
dialog1.setCancelable(true);


//设置点击弹窗外边界是否取消进度条窗口
dialog1.setCanceledOnTouchOutside(false);


//设置标题 默认没有
dialog1.setTitle("下载进度");


//设置进度条最大值
dialog1.setMax(100);


// 更新进度条的进度,可以在子线程中更新进度条进度                dialog.incrementProgressBy(1);


//更新进度条进度
dialog1.setProgress(i);


//关闭进度条对话框
dialog1.dismiss();


【自定义Dialog】


//自定义一个类继承Dialog 实现它的构造方法
//定义一个静态的内部类Builder


public static class Builder {
   private Context context;
   private String title, msg, ptext, ntext;
   private OnClickListener onClick, listener;


   public Builder(Context context) {
      this.context = context;
   }


   public Builder setTitle(String title) {
      this.title = title;
      return this;
   }


   public Builder setMessage(String msg) {
      this.msg = msg;
      return this;
   }


   public Builder setPositiveButton(String ptext, OnClickListener onClick) {
      this.ptext = ptext;
      this.onClick = onClick;
      return this;
   }


   public Builder setNegativeButton(String ntext, OnClickListener listener) {
      this.ntext = ntext;
      this.listener = listener;
      return this;
   }


   public MyDialog create() {
      //第二个参数传样式 可以传自定义的
      final MyDialog dialog = new MyDialog(context, R.style.Dialog);
      View view = LayoutInflater.from(context).inflate(R.layout.mydialog, null);
      TextView textView1 = (TextView) view.findViewById(R.id.title);
      TextView textView2 = (TextView) view.findViewById(R.id.message);
      Button button1 = (Button) view.findViewById(R.id.btn1);
      Button button2 = (Button) view.findViewById(R.id.btn2);


      if (title != null && !"".equals(title))
          textView1.setText(title);
      if (msg != null && !"".equals(msg))
          textView2.setText(msg);
      if (ptext != null && !"".equals(ptext))
          button1.setText(ptext);
      if (ntext != null && !"".equals(ntext))
          button2.setText(ntext);
      if (onClick != null)
          button1.setOnClickListener(new View.OnClickListener() {


             public void onClick(View v) {
                onClick.onClick(dialog, 0);
             }
          });
      if (listener != null)
          button2.setOnClickListener(new View.OnClickListener() {


             public void onClick(View v) {
                listener.onClick(dialog, 0);
             }
          });


      //一定要记得加载View
      dialog.setContentView(view);
      return dialog;
   }
}


//在页面上可以调用自定义的Dialog
new MyDialog.Builder(getContext())
     .setTitle("标题")
     .setMessage("请问你是好人吗?")
     .setPositiveButton("是的", new DialogInterface.OnClickListener() {


          public void onClick(DialogInterface dialog, int which) {
             Toast.makeText(getContext(), "", Toast.LENGTH_SHORT).show();
             dialog.dismiss();
          }
     })
     .setNegativeButton("不是", new DialogInterface.OnClickListener() {


          public void onClick(DialogInterface dialog, int which) {
             Toast.makeText(getContext(), "", Toast.LENGTH_SHORT).show();
          }
     })
     .create().show();


//在values里的style 需要配置 parent表示继承Dialog原有的所有属性



【PopWindow】


//需要有自己的view 设置高宽 是否聚焦
PopupWindow pop =  new PopupWindow(view,ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT,true);


//设置窗口可点击 必须添加
pop.setTouchable(true);


//点back键和其他地方使其消失 设置了这个才能触发OnDismisslistener
ColorDrawable cd = new ColorDrawable(0000000000);
pop.setBackgroundDrawable(cd);


//设置在哪个控件下方显示 设置左右偏移量
pop.showAsDropDown(button,0,0);


//设置在底部显示 设置左右偏移量
pop.showAtLocation(button, Gravity.BOTTOM,0,0);


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


【自定义控件】


1.定义一个类继承LinearLayout
2.实现前三个构造方法
3.在主程序的xml里调用自定义控件的xml


【例子】


//实现触摸、监听、输入框监听接口中的方法
public class MyLayout extends LinearLayout implements View.OnTouchListener, View.OnClickListener, TextWatcher {
    private Button button1, button2;
    private EditText edit;
    private int count = 1;
    private boolean isCancel = false;


    //将super改为this 后面传入一个null
    public MyLayout(Context context) {
        this(context, null);
    }


    public MyLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }


    public MyLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View view = View.inflate(context, R.layout.activity_main, this);
        button1 = (Button) view.findViewById(R.id.btn1);
        button2 = (Button) view.findViewById(R.id.btn2);
        edit = (EditText) view.findViewById(R.id.edit);


        //自定义控件设置颜色 在res/values文件下新建attrs文件
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyLayout);
        //第二个参数传默认值
        int bcolor1 = array.getColor(R.styleable.MyLayout_b_color1, getResources().getColor(R.color.colorAccent));
        int bcolor2 = array.getColor(R.styleable.MyLayout_b_color2, getResources().getColor(R.color.colorAccent));
        int ecolor = array.getColor(R.styleable.MyLayout_edit_color, getResources().getColor(R.color.colorAccent));
        //array要回收
        array.recycle();


        if (bcolor1 != 0)
            button1.setBackgroundColor(bcolor1);
        if (bcolor2 != 0)
            button2.setBackgroundColor(bcolor2);
        if (ecolor != 0)
            edit.setBackgroundColor(ecolor);


        //因为实现了接口中的方法 所以这里的监听事件直接传this
        edit.setText(count + "");
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        edit.addTextChangedListener(this);
        button1.setOnTouchListener(this);
        button2.setOnTouchListener(this);
    }


    //触摸时 也就是长按时进入的方法
    public boolean onTouch(View v, MotionEvent event) {
        switch (v.getId()) {
            case R.id.btn1:
                //事件的action等于按下时
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    //设置它的标识符
                    isCancel = true;
                    new Thread(new Runnable() {
                        
                        public void run() {
                            while (isCancel) {
                                //用handler发送消息
                                handler.sendEmptyMessage(0);
                                try {
                                    Thread.sleep(200);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }).start();
                //事件的action等于松开时
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    isCancel = false;
                //事件的action等于取消时
                } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
                    isCancel = false;
                }
                break;
            case R.id.btn2:
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    isCancel = true;
                    new Thread(new Runnable() {
                        
                        public void run() {
                            while (isCancel) {
                                handler.sendEmptyMessage(1);
                                try {
                                    Thread.sleep(200);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }).start();
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    isCancel = false;
                } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
                    isCancel = false;
                }
                break;
        }
        //true为拦截 false为不拦截
        return true;
    }


    Handler handler = new Handler() {
        
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int num = Integer.parseInt(edit.getText().toString());
            if (msg.what == 0) {
                num--;
                edit.setText(num + "");
                //设置输入框的光标在数字后面
                edit.setSelection(edit.getText().length());
            } else if (msg.what == 1) {
                num++;
                edit.setText(num + "");
                edit.setSelection(edit.getText().length());
            }
        }
    };


    //单击按钮时
    public void onClick(View v) {
        int num = Integer.parseInt(edit.getText().toString());
        switch (v.getId()) {
            case R.id.btn1:
                if (num > 0 && num < 100) {
                    num--;
                    edit.setText(num + "");
                    edit.setSelection(edit.getText().length());
                }
                break;
            case R.id.btn2:
                if (num > 0 && num < 100) {
                    num++;
                    edit.setText(num + "");
                    edit.setSelection(edit.getText().length());
                }
                break;
        }
    }


    //对输入框的一个监听
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {


    }


    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        //输入框删除数据为空时 设置值为1 不能改的
        if ("".equals(s.toString())) {
            edit.setText("1");
            edit.setSelection(edit.getText().length());
        } else {
            int num = Integer.parseInt(s.toString());
            //当减到0时也会变为1
            if (num <= 0) {
                edit.setText("1");
                edit.setSelection(edit.getText().length());
            //当加到100时设为99  相当于设置了一个最小值和一个最大值
            } else if (num >= 100) {
                edit.setText("99");
                edit.setSelection(edit.getText().length());
            }
        }
    }


    @Override
    public void afterTextChanged(Editable s) {


    }
}


//模拟一个加载的界面
public class Mylayout2 extends LinearLayout {
    private LinearLayout root,load,flush;
    private ImageView img;
    private Button btn;
    public Mylayout2(Context context) {
        this(context,null);
    }


    public Mylayout2(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }


    public Mylayout2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View view = View.inflate(context,R.layout.load,this);
        root = (LinearLayout) view.findViewById(R.id.root);
        load = (LinearLayout) view.findViewById(R.id.load);
        flush = (LinearLayout) view.findViewById(R.id.flush);
        img = (ImageView) view.findViewById(R.id.img);
        btn = (Button) view.findViewById(R.id.btn);


        //加载一个帧动画
        AnimationDrawable drawable = (AnimationDrawable) img.getBackground();
        drawable.start();
    }


    //加载时 调整布局的显示和隐藏 GONE隐藏 VISIBLE显示
    public void load(){
        flush.setVisibility(GONE);
        load.setVisibility(VISIBLE);
    }
    //完成时 整个大的布局全部隐藏
    public void finish(){
        root.setVisibility(GONE);
    }
    public void flush(final Activity activity){
        flush.setVisibility(VISIBLE);
        load.setVisibility(GONE);
        flush.setOnClickListener(new OnClickListener() {
            
            public void onClick(View v) {
                //重新加载当前activity
                activity.recreate();
            }
        });
    }
}


//Activity
private Mylayout2 layout;
layout = (Mylayout2) findViewById(R.id.layout);


//直接调自定义控件中的加载方法
layout.load();
new Thread(new Runnable() {
    
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //休眠2秒后 调用完成的方法
        runOnUiThread(new Runnable() {
        
            public void run() {
                layout.finish();
            }
        });
    }
}).start();


◆在主程序的xml里运用自定义控件的xml


    //自定义一个命名空间 在res/values中的attrs文件下配置
    xmlns:ll = "http://schemas.android.com/apk/res-auto"
    //设置一个id
    android:id="@+id/layout"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center|top">


    //包名加上项目名称 就可以直接调用
            ll:b_color1="@android:color/holo_blue_light"
        ll:b_color2="@android:color/holo_green_light"
        ll:edit_color="@android:color/holo_orange_light"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">


   




◆自定义控件Mylayout2中的xml文件


    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


            android:id="@+id/load"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center">


                    android:id="@+id/img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/frame"/>


   



            android:id="@+id/flush"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center">


       

你可能感兴趣的:(Android程序猿基本功)