2024年Web前端最全Android基础知识总结,前端开发基础在线学习网站

文末

从转行到现在,差不多两年的时间,虽不能和大佬相比,但也是学了很多东西。我个人在学习的过程中,习惯简单做做笔记,方便自己复习的时候能够快速理解,现在将自己的笔记分享出来,和大家共同学习。

个人将这段时间所学的知识,分为三个阶段:

第一阶段:HTML&CSS&JavaScript基础

第二阶段:移动端开发技术

第三阶段:前端常用框架

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 推荐学习方式:针对某个知识点,可以先简单过一下我的笔记,如果理解,那是最好,可以帮助快速解决问题;

  • 大厂的面试难在,针对一个基础知识点,比如JS的事件循环机制,不会上来就问概念,而是换个角度,从题目入手,看你是否真正掌握。所以对于概念的理解真的很重要。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//Intent 意图:就是启动其他组件,也是组件之间数据传递的重要媒介
//显示跳转
public void btn_intent(View v)
{
/*//创建Intent对象
Intent i=new Intent();
//调用intent的方法,来指定将要启动的  类.class(另一个Activity)
i.setClass(this, OtherActivity.class);
//启动这个Intent意图
startActivity(i);*/

//创建Intent对象,并指定将要启动的  类.class(另一个Activity)
Intent i=new Intent(this,OtherActivity.class);//此句表示显式意图,因为明确设置激活对象为Other类
//启动这个Intent意图
startActivity(i);
}
//隐式跳转
public void btn_i_filter(View v)
{
Intent intent=new Intent();
//调用intent的setAction方法,这个方法的形参为咱们在清单文件中 自定义动作匹配
intent.setAction(“com.cn.intent”);//此句只是指定了Action
//如果自己定义的某个Activity要通过隐式启动,在AndroidManifest.xml那么必须加上android.intent.category.DEFAULT,否则不起作用
intent.addCategory(“android.intent.category.DEFAULT”);
//隐式意图 传递参数
intent.setData(Uri.parse(“meinv:隐式意图传递的参数”));
startActivity(intent);
}
//显示跳转
public void btn_gallery(View v)
{
Intent intent = new Intent();
//会话位置|指定要激活的具体的Activity
intent.setClassName(“com.android.gallery”,“com.android.camera.GalleryPicker”);
startActivity(intent);
}
public void btn_contacts(View v)
{
/*//显示启动,“com.android.contacts.activities.PeopleActivity” sdk API >=17 都可以。
//但是 若在API=10 的,模拟器上 就报错啦,找不到对应的 类
Intent intent = new Intent();
intent.setClassName(“com.android.contacts”,“com.android.contacts.activities.PeopleActivity”);
startActivity(intent);*/

/*//隐式调用拨打电话的界面,没有形参的意图
Intent intent=new Intent();
//通过对应的动作Intent.ACTION_DIAL 启动拨打电话的界面
intent.setAction(Intent.ACTION_CALL);
//传入参数 电话号码
intent.setData(Uri.parse(“tel:”+“13766367322”));
//启动意图
startActivity(intent);*/
//隐式意图  启动浏览器
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(“http://www.baidu.com”));
startActivity(intent);
}
}
OtherActivity.java中的代码:
public class OtherActivity extends Activity {

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
//获取控件
TextView tv=(TextView)findViewById(R.id.tv_other);
//获取启动OtherActivity的intent的意图对象
Intent intent=getIntent();
//获取隐式传递的参数
Uri uri=intent.getData();
String result=uri.toString();
tv.setText(result);
}
}
AndroidManifest.xml里的代码:

<?xml version="1.0" encoding="utf-8"?>


   

   
       
           
               
               
           
       
       
             
               
                   
               
 
               
                 
               
               
           
       
   

  1. Intent意图传参
    MainActivity.java代码:通过intent.putExtra(“names”, name);标示传(保存)值
    /**
     * Intent意图传参
     * @author Administrator
     */
    public class MainActivity extends Activity {
    private EditText et_name,et_age;
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

et_name=(EditText)findViewById(R.id.et_name);
et_age=(EditText)findViewById(R.id.et_age);
}
//按钮的点击
public void clickButton(View v)
{
String name=et_name.getText().toString().trim();
String age=et_age.getText().toString().trim();
int ageInt=Integer.parseInt(age);//Integer.valueOf(age)
//对于一个UI界面中,当判断用户是否输入用户名或密码时,我们常用TextUtils.isEmpty()方法来判断;因为你从EditText返回的是一个变量。
//如果这个变量本身为null值,那么你掉它的equals方法是要报错的。但是如果你调用TextUtils.isEmpty() 把这个变量作为参数传进去。
//只要这个参数为空或者为“”,都会返回真
if(TextUtils.isEmpty(name))//如果没有输入名字
{
Toast.makeText(this, “请输入名字”, Toast.LENGTH_SHORT).show();
}
//跳转到另一个activity,用意图
Intent intent=new Intent(this,OtherActivity.class);
//putExtra(“A”,B)中,AB为键值对,第一个参数为键名,第二个参数为键对应的值。顺便提一下,如果想取出Intent对象中的这些值,
//需要在你的另一个Activity中用getXXXXXExtra方法,注意需要使用对应类型的方法,参数为键名
intent.putExtra(“names”, name);
intent.putExtra(“ages”, age);
//启动意图
startActivity(intent);
}
}
OtherActivity.java里的代码:通过getIntent().getStringExtra(“name”);来获取值
/**
 * Intent意图传参
 * @author Administrator
 */
public class OtherActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
TextView tv_result=(TextView)findViewById(R.id.tv_result);
//得到意图对象
Intent intent=getIntent();
//通过预定好的标示,从intent中得到数据
//String name=getIntent().getStringExtra(“names”);
string name=intent.getStringExtra(“names”);
//如果没有传值,默认值为18
int age=intent.getIntExtra(“ages”, 18);
//通过随机数random来任意定义一个IQ值
Random random=new Random();
int r_iq=random.nextInt(100);
tv_result.setText(“姓名:”+name+“,年龄:”+age+“,IQ:”+r_iq);
}
}

  1. 返回值的传参:下一个Acyivity的参数可以返回给上一个Activity中
    1.//启动意图
    /*startActivityForResult与startActivity的不同之处在于:
    1、startActivity( ) 
    仅仅是跳转到目标页面,若是想跳回当前页面,则必须再使用一次startActivity( )。
    2、startActivityForResult( ) 
    可以一次性完成这项任务,当程序执行到这段代码的时候,假若从T1Activity跳转到下一个Text2Activity, 而当这个Text2Activity
    调用了finish()方法以后,程序会自动跳转回T1Activity,并调用前一个T1Activity中的 onActivityResult( )方法。*/
  2. MainActivity里的代码:
    /**
     * 跳转到目标页面,若是想跳回当前页面并在当前页面获得目标页面中的数据,用startActivityForResult(intent, 100);启动Intent意图
     * @author Administrator */
    public class MainActivity extends Activity {
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

tv=(TextView)findViewById(R.id.tv);
}
public void btn01(View v)
{
//意图
Intent intent=new Intent(this,SecondActivity.class);
//启动意图
/*startActivityForResult与startActivity的不同之处在于:
1、startActivity( ) 
仅仅是跳转到目标页面,若是想跳回当前页面,则必须再使用一次startActivity( )。
2、startActivityForResult( ) 
可以一次性完成这项任务,当程序执行到这段代码的时候,假若从T1Activity跳转到下一个Text2Activity,而当这个Text2Activity
调用了finish()方法以后,程序会自动跳转回T1Activity,并调用前一个T1Activity中的onActivityResult( )方法。*/
//startActivity(intent);
startActivityForResult(intent, 100);
}
public void btn02(View v)
{
//意图
Intent intent=new Intent(this,ThreeActivity.class);
//启动意图
startActivityForResult(intent,101);
}
//重写父类的方法onActivityResult()来处理一些已经绑定了requestCode的activity可能返回的数据
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {//通过第一个参数requestCode来辨别数据来自哪个activity
case 100://通过requestCode=100来筛选出SecondActivity返回的数据
switch (resultCode) {//通过第二个参数resultCode来筛选出具体是SecondActivity哪个功能模块返回的数据
case 0:
//通过第三个参数data可以传递数据,通过intent将其它的activity的数据携带到当前的activity中
String result01=data.getStringExtra(“result”);
tv.setText(result01);
break;
case 1:
String result02=data.getStringExtra(“result01”);
tv.setText(result02);
break;
}
break;
case 101://通过requestCode=101 来筛选出ThreeActivity返回的数据
switch (resultCode) {
case RESULT_OK:
String result03=data.getStringExtra(“result03”);
tv.setText(result03);
break;
default:
break;
}
break;}}}
2. SecondActivity里的代码:
public class SecondActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
}
public void second_btn01(View v)
{
//得到意图
Intent intent=getIntent();
EditText et=(EditText)findViewById(R.id.et);
String res=et.getText().toString();
//putExtra保存值
intent.putExtra(“result”, res);
//传递参数,返回值;返回数据给上一个Activity的
setResult(0, intent);
finish();
}
public void second_btn02(View v)
{
//得到意图
Intent intent=getIntent();
intent.putExtra(“result01”, “好热啊”);
//传递参数,返回值;返回数据给上一个Activity的
setResult(1,intent);
finish();
}
}
2. ThreeActivity代码:
public class ThreeActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_three);
}
public void btn_three(View v)
{
EditText et03=(EditText)findViewById(R.id.et_three);
String res=et03.getText().toString();
Intent intent=getIntent();
//putExtra保存值
intent.putExtra(“result03”, res);
setResult(RESULT_OK, intent);
finish();}
}

⦁ 登录界面之文字变换的事件响应方法onTextChanged()和焦点改变方法onFocusChanged()与输入框清除图标和功能的实现

  1. 自定义输入框(EditTextWithClearButton.java):
    /**
     * 自定义输入框
     * @author Administrator */
    public class EditTextWithClearButton extends EditText {
    // 构造函数
    public EditTextWithClearButton(Context context) {
    super(context);
    }
    public EditTextWithClearButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    }
    public EditTextWithClearButton(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    }
    // 在TextView中集成了接口TextWatcher,并且也重写了文字变换的事件响应方法和焦点改变方法,所以子类EditTextWithClearButton中直接来重写父类中的
    // 文字变化的事件响应方法 onTextChanged和焦点改变方法onFocusChanged()
    // onTextChanged参数解释及实现EditText字数监听,文本框里的内容变化是会触发OnTextChanged事件
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
    super.onTextChanged(text, start, lengthBefore, lengthAfter);
    // getCompoundDrawables()这个方法返回的是一个Drawable[]数组,这个数组有4个元素,分别为TextView的左上右下
    // 4个方向的drawable对象,
    // 通过这些Drawable对象 可以分别为一个TextView的左上右下 这几个方向添加图片
    Drawable[] draw = getCompoundDrawables();
    // 如果输入框里面的文本字符长度不为0,且有焦点,则给这个输入框的右边 动态的添加一个 删除的图标
    if (!getText().toString().equals(“”) && hasFocus()) {
    setCompoundDrawablesWithIntrinsicBounds(draw[0], draw[1],
    getContext().getResources().getDrawable(R.drawable.ic_clear), draw[3]);
    } else {
    // 因为draw数组中无图片
    setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
    }
    // 最后调用invalidate()方法来刷新
    invalidate();
    }
    // onFocusChanged只能在View中重写。该方法是焦点改变的回调方法,当某个控件重写了该方法后,当焦点发生变化时,会自动调用该方法来处理焦点改变事件
    // 参数focused:参数gainFocus表示触发该事件的View是否获得了焦点,当该控件获得焦点时,gainFocus等于true,否则等于false。
    // 参数direction:参数direction表示焦点移动的方向,用数值表示。
    // 参数previouslyFocusedRect:表示触发事件的View的坐标系中,前一个获得焦点的矩形区域,即表示焦点是从哪里来的。如果不可以则为null。
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
    super.onFocusChanged(focused, direction, previouslyFocusedRect);
    // getCompoundDrawables()这个方法返回的是一个Drawable[]数组,这个数组有4个元素,分别为TextView的左上右下
    // 4个方向的drawable对象,
    // 通过这些Drawable对象 可以分别为一个TextView的左上右下 这几个方向添加图片
    Drawable[] draw = getCompoundDrawables();
    // 如果输入框里面的文本字符长度不为0,且有焦点,则给这个输入框的右边 动态的添加一个 删除的图标
    if (!getText().toString().equals(“”) && hasFocus()) {
    setCompoundDrawablesWithIntrinsicBounds(draw[0], draw[1],
    getContext().getResources().getDrawable(R.drawable.ic_clear), draw[3]);
    } else {
    // 因为draw数组中无图片
    setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
    }
    // 最后调用invalidate()方法来刷新
    //invalidate();  — UI线程中
    //postInvalidate(); ---- 非UI线程中
    invalidate();
    }
    // 重写(覆盖)父类中的方法 onTouchEvent(MotionEvent event) ,当用户手指触摸此组件 会触发这个方法,并且 在这里
    // 定义根据业务需求实现的逻辑代码
    public boolean onTouchEvent(MotionEvent event) {
    boolean re = super.onTouchEvent(event);
    // 当你在屏幕上 操作了,类MotionEvent能够知道你的 具体动作
    switch (event.getAction()) {// event.getAction()得到你的具体动作状态
    case MotionEvent.ACTION_DOWN:// 当触摸动作按下的时候,啥都不做
    break;
    case MotionEvent.ACTION_UP:// 当触摸动作松开的时候
    //左上右下 这几个方向添加图片
    Drawable[] drawable = getCompoundDrawables();
    // 如果右边的drawable不为null,说明这个删除图标 是存在的,正在显示在组件上;点击这个 删除图标 应该是输入框中的内容清空,且 删除图片 消失
    if(drawable[2]!=null)
    {
    /*思路:
    第一步:确定这个删除图标的 位置区域范围(这个图标 是有面积,上下左右 四个边界值 不同)
    第二步:根据MotionEvent的方法 getX 和 getY 来确定 在屏幕上 你的触摸点的(x ,y)
    第三步:若在屏幕上,你的触摸点(x,y)在 删除图片的区域范围内,则 清空文本字符串 和 让删除图标 消失。
    定义是 删除图标 的左边距*/
    int left=getWidth()-getPaddingRight()-drawable[2].getIntrinsicWidth();
    //定义是 删除图标 的右边距
    int right=getWidth()-getPaddingRight();
    //定义是 删除图标 的上边距
    int top=getPaddingTop();
    //定义是 删除图标 的下边距
    int bottom=getHeight()-getPaddingTop();
    //若在 删除图片的范围内,则说明触摸了 这个删除图标
    if(event.getX()<right&&event.getX()>left&&event.getY()>top&&event.getY()<bottom)
    {
    setText(“”);
    }
    }
    break;
    default:
    break;
    }
    return re;}}
  2. MainActivity里的代码:
    public class MainActivity extends Activity {
    private TextView title, left_btn;
    private Button login_sign_in;
    private CheckBox login_keep_password;
    private Context context;
    private Spinner mSpinnerIdType;
    private EditTextWithClearButton login_number;
    private EditTextWithClearButton login_password;
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.member_layout);
    context = getApplicationContext();
    // 获取组件
    title = (TextView) findViewById(R.id.title_txt);
    // 给组件设值
    title.setText(R.string.login_tab_title);
    //返回组件
    left_btn = (TextView) findViewById(R.id.left_btn);
    left_btn.setVisibility(View.VISIBLE);
    left_btn.setOnClickListener(mOnClick);
    //登录组件
    login_sign_in = (Button)findViewById(R.id.login_sign_in);
    login_sign_in.setOnClickListener(mOnClick);
    //证件输入框
    login_number = (EditTextWithClearButton)findViewById(R.id.login_id_number);
    //密码输入框
    login_password = (EditTextWithClearButton)findViewById(R.id.login_id_password);
    //忘记密码点击事件
    findViewById(R.id.login_forget_password).setOnClickListener(mOnClick);
    //下拉菜单组件
    mSpinnerIdType=(Spinner)findViewById(R.id.spinner_id);
    //OnTouchListener()方法,触摸事件;是获取某一个控件某一个View的点击监控。
    mSpinnerIdType.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
    //打开输入法窗口
    //InputMethodManager imm=((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE));
    return false;
    }
    });
    //自定义微调框(下拉菜单)
    //适配器  //R.array.id_types从arrays.xml文件里获取数据源
    //R.array.id_types 数据源的标识符
    //R.layout.my_simple_spinner_item 用于创建视图的布局的标识符
    ArrayAdapter adapter=ArrayAdapter.createFromResource(context, R.array.id_types, 
    R.layout.my_simple_spinner_item);
    //setDropDownViewResource可以设置下拉菜单的显示方式,将该xml定义在res/layout目录下,可针对下拉菜单中的TextView进行设置
    //R.layout.my_simple_spinner_dropdown_item 定义下拉视图的布局资源
    adapter.setDropDownViewResource(R.layout.my_simple_spinner_dropdown_item);
    //给下拉菜单设值适配器
    mSpinnerIdType.setAdapter(adapter);
    //给下拉菜设值监听事件
    mSpinnerIdType.setOnItemSelectedListener(new OnItemSelectedListener(){
    //下拉菜单的选项的点击事件
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { login\_password.setText(""); } @Override public void onNothingSelected(AdapterView<?> parent) {
    }
    }); }
    //监听器
    OnClickListener mOnClick = new OnClickListener() {
    public void onClick(View v) {
    switch (v.getId()) {
    case R.id.login_sign_in:
    String name=login_number.getText().toString().trim();
    String pwd=login_password.getText().toString().trim();
    if (name.equals(“dawn”)&&pwd.equals(“123456”)) {
    User u=new User();
    //设置值
    u.setUser_name(name);
    u.setUser_pwd(pwd);
    //Intent意图,通过意图来跳转页面和传值
    Intent intent=new Intent(MainActivity.this,OtherActivity.class);
    //putExtra(“A”,B)中,AB为键值对,第一个参数为键名,第二个参数为键对应的值。顺便提一下,如果想取出Intent对象中的这些值,
    //需要在你的另一个Activity中用getXXXXXExtra方法,注意需要使用对应类型的方法,参数为键名
    intent.putExtra(“User”,u);
    //设置意图
    startActivity(intent);
    }else
    {
    Toast.makeText(context, “账号(dawn)和密码(123456)不正确,请重新输入”, Toast.LENGTH_SHORT).show();
    }
    break;
    case R.id.left_btn:
    Toast.makeText(context, “返回”, Toast.LENGTH_SHORT).show();
    finish();
    break;
    case R.id.login_forget_password:
    Toast.makeText(context, “忘记密码”, Toast.LENGTH_SHORT).show();
    break;
    default:
    break;
    }}};}
  3. OtherActivity里的代码:
    public class OtherActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_other);
    //获得组件
    TextView result=(TextView)findViewById(R.id.tv_result);
    //获取意图
    Intent intent=getIntent();
    //通过意图获取序列化的对象
    User user=(User)intent.getSerializableExtra(“User”);
    //赋值
    result.setText(“姓名:”+user.getUser_name()+“,密码:”+user.getUser_pwd());
    } }
  4. OtherActivity里的代码:
    public class OtherActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_other);
    //获得组件
    TextView result=(TextView)findViewById(R.id.tv_result);
    //获取意图
    Intent intent=getIntent();
    //通过意图获取序列化的对象
    User user=(User)intent.getSerializableExtra(“User”);
    //赋值
    result.setText(“姓名:”+user.getUser_name()+“,密码:”+user.getUser_pwd());
    }}
  5. User.java里的代码
    /**
     * 实现了Serializable序列化
     * @author Administrator */
    public class User implements Serializable {
    private String user_name;
    private String user_pwd;
    public String getUser_name() {
    return user_name;
    }
    public void setUser_name(String user_name) {
    this.user_name = user_name;
    }
    public String getUser_pwd() {
    return user_pwd;
    }
    public void setUser_pwd(String user_pwd) {
    this.user_pwd = user_pwd;
    }}

⦁ 引导页和引导点的制作

  1. Activity_main.xml里的代码:
    <RelativeLayout 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”
        tools:context=“ r e l a t i v e P a c k a g e . {relativePackage}. relativePackage.{activityClass}” >
       
        <TextView
            android:id=“@+id/tv_main”
            android:layout_width=“wrap_content”
            android:layout_height=“wrap_content”
            android:text=“@string/hello_world” />
       
        <RelativeLayout
            android:id=“@+id/index_index”
            android:layout_width=“match_parent”
            android:layout_height=“match_parent”
            android:background=“@color/transparent”
            android:orientation=“vertical” >
           
            <android.support.v4.view.ViewPager   
                android:id=“@+id/viewPager_index”
                android:layout_width=“match_parent”
                android:layout_height=“match_parent”/>
           
            <com.example.android09_gfbank_guidepages.PageControlView
                 android:id=“@+id/pageControlView_index”
                android:layout_width=“wrap_content”
                android:layout_height=“wrap_content”
                android:layout_alignParentBottom=“true”
                android:layout_centerHorizontal=“true”
                android:layout_marginBottom=“30dp”/>
           
       
  2. Item_image_layout.xml里的代码:
<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android=“http://schemas.android.com/apk/res/android”
    android:layout_width=“match_parent”
    android:layout_height=“match_parent” >
   
    <FrameLayout
        android:id=“@+id/start_view”
        android:layout_width=“match_parent”
        android:layout_height=“match_parent”
        android:visibility=“visible” >
       
        <ImageView
            android:id=“@+id/image”
            android:layout_width=“match_parent”
            android:layout_height=“match_parent”
            android:layout_gravity=“center”
            android:contentDescription=“@null” />
       
        <TextView
            android:id=“@+id/start_btn”
            android:layout_width=“wrap_content”
            android:layout_height=“wrap_content”
            android:layout_gravity=“center_horizontal|bottom”
            android:layout_marginBottom=“105dp”
            android:contentDescription=“@null”
            android:text=“开启财富人生”
            android:textSize=“30sp”
            android:visibility=“gone” />
   

  1. 控制引导点PageControlView.java:
    /**
     * 控制引导点*/
    public class PageControlView extends LinearLayout {
    //上下文
    private Context context;
    //设置引导点的个数
    public int count;
    //定义引导点变换的颜色(实际上是图片)
    public int[] image={R.drawable.index_page_control_u,R.drawable.index_page_control_s};;
    //定义构造器
    public PageControlView(Context context) {
    super(context);
    this.context=context;
    }

public PageControlView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context=context;
}
//控制页面滑动时,引导点颜色的变换;该方法的形参currentPage 其实指向是 ViewPager的索引 position
public void generalPageControl(int currentPage)
{
//清空所有组件
this.removeAllViews();
for (int i = 0; i < count; i++) {
//创建ImageView对象
ImageView imageView=new ImageView(context);
//当传入ViewPager的position与i 相等,则让 指示点 引用一张图片(或该引导点变色)
if(i==currentPage)
{
imageView.setImageResource(image[1]);
}else
{
imageView.setImageResource(image[0]);
}
//LayoutParams相当于一个Layout的信息包,它封装了Layout的位置、高、宽等信息。假设在屏幕上一块区域是由一个Layout占领的,
//如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去。
LayoutParams lp=new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
//设置组件四周的空白,左    上   右   下
lp.setMargins(0, 0, 10, 0);
//给组件添加布局
imageView.setLayoutParams(lp);
//把view添加到Activity中
this.addView(imageView);
}
}
}

  1. 自定义CustomPagerAdapter:
    /**
     * 自定义PageAdapter*/
    public class CustomPagerAdapter extends PagerAdapter {
    private int[] images;// 放图片
    private Activity context;// 上下文
    private PageControlView page_control;
    // FrameLayout布局
    FrameLayout imageLayout;
    private TextView start_btn;
    private FrameLayout start_view;
    private ImageView imageView;
    // 构造方法传值
    public CustomPagerAdapter(Activity context, int[] images, PageControlView page_control) {
    this.context = context;
    this.images = images;
    this.page_control = page_control;
    }
    // 返回要左右滑动的view的个数
    public int getCount() {
    return images.length;
    }
    @Override //一般来说,destroyitem在viewpager移除一个item时调用。 viewpage一般都会缓冲3个item,
    //即一开始就会调用3次instantiateItem, 当向右滑动,到第3页时,第1页的item会被调用到destroyitem。
    public void destroyItem(ViewGroup container, int position, Object object) {
    container.removeView((View) object);
    }
    // isViewFromObject方法是用来判断pager的一个view是否和instantiateItem方法返回的object有关联
    // 可以供外面的人调用,用来判断你传入的view和那个object是等价的
    public boolean isViewFromObject(View arg0, Object arg1) {
    return arg0.equals(arg1);
    }
    // 为位置为position的item创建view;instantiateItem是初始化item用的
    public Object instantiateItem(View view, int position) {
    imageLayout = (FrameLayout)context.getLayoutInflater().inflate(R.layout.item_image_layout, null);
    imageView = (ImageView) imageLayout.findViewById(R.id.image);
    start_btn = (TextView) imageLayout.findViewById(R.id.start_btn);
    // 监听器
    start_btn.setOnClickListener(listener);
    // 当切换到 最后一个item 的时候,让start_btn 展现
    if(position==images.length-1)
    {
    start_btn.setVisibility(View.VISIBLE);
    }
    //设置每一个item的图片
    imageView.setImageResource(images[position]);
    // 把view添加到ViewPager中
    ((ViewPager) view).addView(imageLayout, 0);
    return imageLayout;
    }
    //监听事件
    OnClickListener listener = new OnClickListener() {
    public void onClick(View v) {
    penDoor();
    }
    };
    protected void penDoor() {
    Toast.makeText(context, “开门,展现里面的MainActivity”, Toast.LENGTH_SHORT).show();
    }
    }
  2. MainActivity里的代码:
    /**
     * 引导页的制作 */
    public class MainActivity extends Activity {
    private TextView tv_main;
    //声明ViewPager对象
    private ViewPager viewPager;
    //声明指导页面的自定义PageControlView,PageControlView是引导页下面的引导点
    private PageControlView page_control;
    //声明适配器的对象
    private CustomPagerAdapter adapter;
    //定义引导页的 几张图片
    private int[] images={R.drawable.index_01,R.drawable.index_02,R.drawable.index_03,R.drawable.index_04};
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv_main=(TextView)findViewById(R.id.tv_main);
    //这个app是否第一次打开,若第一个打开则应当显示引导页。否则不显示引导页;方法getIndexFirst() 得到是否应该显示引导页。
    if (getIndexFirst()) {
    //初始化主界面的数据
    initContent();
    /*得到导引页里面的组件的对象,并且设置一些引导页的业务逻辑*/
    //获得控件对象
    viewPager=(ViewPager)findViewById(R.id.viewPager_index);
    page_control=(PageControlView)findViewById(R.id.pageControlView_index);
    //设置ViewPager当前的展示页若不设置,默认是position=0 的展示页)
    viewPager.setCurrentItem(0);
    //重新对page_control.iamge赋值
    page_control.image = new int[]{R.drawable.gray_dot,R.drawable.black_dot};
    //设置引导点的个数
    page_control.count=images.length;
    //初始化第一个点为黑色
    page_control.generalPageControl(0);
    //创建自定义适配器的对象,并传值
    adapter=new CustomPagerAdapter(this,images,page_control);
    //设置配置器
    viewPager.setAdapter(adapter);
    //调用ViewPager的item切换的监听方法,并且通过这个方法指示引导页下面的点同步改动。
    viewPager.setOnPageChangeListener(listener);
    }else
    {
    //初始化主界面的数据
    initContent();
    }
    }
    //监听事件
    OnPageChangeListener listener=new OnPageChangeListener(){
    //此方法是在状态改变的时候调用,其中arg0这个参数有三种状态(0,1,2)。arg0 1的时候表示正在滑动,arg02的时候表示滑动完毕了,
    //arg0==0的时候表示什么都没做。当页面开始滑动的时候,三种状态的变化顺序为(1,2,0)
    public void onPageScrollStateChanged(int arg0) {
    // TODO Auto-generated method stub
    }
    //当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:arg0 :当前页面,及你点击滑动的页面,
    //arg1:当前页面偏移的百分比,arg2:当前页面偏移的像素位置   
    public void onPageScrolled(int arg0, float arg1, int arg2) {
    // TODO Auto-generated method stub
    }
    //指的是当前选择的是哪个页面
    public void onPageSelected(int position) {
    //就是通过这句代码,才能让引导页的点 和ViewPager切换
    page_control.generalPageControl(position);
    }
    };
    //模拟初始化主界面的数据
    private void initContent() {
    tv_main.setText(R.string.app_name);
    }
    //模拟是否是第一次登陆。若是第一次登陆,返回true;否则啊 false 不展示引导页
    private boolean getIndexFirst() {
    return true;

}}

⦁ ViewPager:功能就是可以使视图左右滑动
package com.example.android09_viewpager;
/**
 * ViewPager的功能就是可以使视图滑动
 */
public class MainActivity extends Activity {
// 声明3个view,将对应对应好的3个xml布局
private View view01, view02, view03;
// 声明一个集合对象来装着上view
private List listView;
// 声明ViewPager对象
private ViewPager viewPager;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取控件
viewPager=(ViewPager)findViewById(R.id.viewPager);
// 将xml布局转换为view对象
// LayoutInflater它的作用类似于findViewById()。不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化
LayoutInflater inflater = getLayoutInflater();
// inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById()的区别,inflate是加载一个布局文件,而findViewById则是从布局文件中查找一个控件
view01 = inflater.inflate(R.layout.pager01_layout, null);
view02 = inflater.inflate(R.layout.pager02_layout, null);
view03 = inflater.inflate(R.layout.pager03_layout, null);
// 将左右滑动的View添加到集合中,这样数据源就准备好了
listView = new ArrayList();
listView.add(view01);
listView.add(view02);
listView.add(view03);
// 设置适配器
viewPager.setAdapter(pagerAdapter);
}
/*因为pagerAdapter是默认预加载前后一张的,所以当加载第一页时,调用了两次instantiateItem方法;第一次是加载本来的第一页,第二次是预加载第二页。当滑动到第二页时,只调用了一次instantiateItem方法;是因为本页已经在之前预加载过了,没有再调用instantiateItem方法,而是直接从ViewGroup中直接取出第二页用于显示;然后进行预加载第三页,所以这里会调用一次instantiateItem方法。接着滑动到第三页,由于第三节页也已经预加载过了,所以只是从ViewGroup取出第三页显示而不调用instantiateItem;但是由于预加载默认是前后一张,所以这时会从ViewGroup中取出第一页销毁;直到从第三页滑到第二页时才会再预加载第一页。*/
// 对于ViewPager有一个专用的适配器PagerAdapter
PagerAdapter pagerAdapter = new PagerAdapter() {
// 返回要左右滑动的view的个数
public int getCount() {
return listView.size();
}
// 从当前container中删除指定位置的position的view
//destroyitem在viewpager移除一个item时调用。 viewpage一般都会缓冲3个item,即一开始就会调用3次instantiateItem, 当向右滑动,到第3页时,第1页的item会被调用到destroyitem
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(listView.get(position));
}
// 为位置为position的item创建view;instantiateItem是初始化item用的
public Object instantiateItem(ViewGroup container, int position) {
container.addView(listView.get(position));
return listView.get(position);
}
//isViewFromObject方法是用来判断pager的一个view是否和instantiateItem方法返回的object有关联
//可以供外面的人调用,用来判断你传入的view和那个object是等价的
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0==arg1;
}};}

⦁ GestureDetector手势:可用于图片的滑动播放
当用户触摸屏幕的时候,会产生许多手势,例如down,up,scroll,filing等等。
一般情况下,我们知道View类有个View.OnTouchListener内部接口,通过重写他的onTouch(View v, MotionEvent event)方法,我们可以处理一些touch事件,但是这个方法太过简单,如果需要处理一些复杂的手势,用这个接口就会很麻烦(因为我们要自己根据用户触摸的轨迹去判断是什么手势)。
Android sdk给我们提供了GestureDetector(Gesture:手势Detector:识别)类,通过这个类我们可以识别很多的手势,主要是通过他的onTouchEvent(event)方法完成了不同手势的识别。虽然他能识别手势,但是不同的手势要怎么处理,应该是提供给程序员实现的。
public class MainActivity extends Activity implements OnGestureListener{
//声明手势检测器实例
GestureDetector detector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建手势检测器对象。通过GestureDetector构造器来创建对象
//GestureDetector(Context context, OnGestureListener listener)
//第一个参数 是上下文,第二个参数是接口 OnGestureListener对象
detector=new GestureDetector(this, this);
}
//用户的手指触动Activity的时候,会触发 key、touch等的事件处理方法,现在咱们来重写Activity的OnTouchEvent方法
@Override//重写Activity类里的onTouchEvent事件方法
//如果MainActivity 实现OnTouchListener接口,这里重写OnTouchListener里的onTouch方法
//public boolean onTouch(View v, MotionEvent event)
public boolean onTouchEvent(MotionEvent event) {
//将这个Activity上面的触碰事件交给GestureDetector手势处理类 来处理,或者说手势检测类截获了touch事件
//这一步,必须写。否则,你的动作无法触发对应的 手势
return detector.onTouchEvent(event);
}
//当触摸事件按下时触发这个方法
@Override
public boolean onDown(MotionEvent e) {
Log.e(“Down”, “onDown”);
return false;
}
//当用户在屏幕上按下,而且 还未移动和松开时候 触发这个方法
@Override
public void onShowPress(MotionEvent e) {
Log.e(“ShowPress”, “onShowPress”);
}
//当用户在触摸屏上 轻击 时候 会触发这个方法
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.e(“SingleTapUp”, “onSingleTapUp”);
return false;
}
//当用户在屏幕上滚动时触发的方法
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.e(“Scroll”, “onScroll”);
return false;
}

//用户在屏幕上长按时触动这个方法
@Override
public void onLongPress(MotionEvent e) {
Log.e(“LongPress”, “onLongPress”);
}

//当用户在触摸屏上“拖过…  滑动…” 时 触发这个方法。velocityX, velocityY  代表 滑动 动作在横向或 纵向上的速度
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.e(“Fling”, " onFling");
return false;
}
}
⦁ ViewFlipper图片的自动循环播放

  1. 自动播放:
    activity_main.xml:代码
    <RelativeLayout 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”
        tools:context=“ r e l a t i v e P a c k a g e . {relativePackage}. relativePackage.{activityClass}” >

<ViewFlipper
        android:id=“@+id/flipper”
        android:layout_width=“wrap_content”
        android:layout_height=“wrap_content”
        android:layout_alignParentTop=“true”
        android:layout_marginTop=“10dp” >
   

MainActivity代码:
/**
 * ViewFlipper图片的自动循环播放,不在XML里添加图片,在java里动态添加图片
 * @author Administrator */
public class MainActivity extends Activity {
ViewFlipper flipper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flipper=(ViewFlipper)findViewById(R.id.flipper);
//自动添加图片
flipper.addView(getImageView(R.drawable.img_1));
flipper.addView(getImageView(R.drawable.img_2));
flipper.addView(getImageView(R.drawable.img_3));
flipper.addView(getImageView(R.drawable.img_4));
//设置自动循环
flipper.setFlipInterval(2000);//view切换的时间间隔
//开始进行view的切换
flipper.startFlipping();
}
//需要将项目中的img_1.jpg等图片通过ViewFlippter的addView动态添加,而addView的形参是view,
//所以呢  需要写一个方法 将图片id 转化为 View
private ImageView getImageView(int id)
{
ImageView imageView=new ImageView(this);
imageView.setImageResource(id);
return imageView;
}
}

  1. 滑动播放
    activity_main.xml:代码和上面的一样
    MainActivity代码:
    /**
     * ViewFlipper图片的自动循环播放,左右滑动展示图片
     * @author Administrator
     */
    public class MainActivity extends Activity implements OnTouchListener,OnGestureListener{
    //声明图片的自动循环播放
    ViewFlipper flipper;
    //声明手势
    GestureDetector detector;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //第一个参数 是上下文,第二个参数是接口 OnGestureListener对象
    detector=new GestureDetector(this,this);

flipper=(ViewFlipper)findViewById(R.id.flipper);
//自动添加图片
flipper.addView(getImageView(R.drawable.img_1));
flipper.addView(getImageView(R.drawable.img_2));
flipper.addView(getImageView(R.drawable.img_3));
flipper.addView(getImageView(R.drawable.img_4));
//设置监听器
flipper.setOnTouchListener(this);
//允许长按住ViewFlipper,这样才能识别拖动等收拾
flipper.setLongClickable(true);
}

//需要将项目中的img_1.jpg等图片通过ViewFlippter的addView动态添加,而addView的形参是view,
//所以呢  需要写一个方法 将图片id 转化为 View
private ImageView getImageView(int id)
{
ImageView imageView=new ImageView(this);
imageView.setImageResource(id);
return imageView;
}

用户的手指触动Activity的时候,会触发 key、touch等的事件处理方法,现在咱们来重写Activity的OnTouchEvent方法
@Override // 重写OnTouchListener里的onTouch方法
public boolean onTouch(View v, MotionEvent event) {
// 将这个Activity上面的触碰事件交给GestureDetector手势处理类 来处理,或者说手势检测类截获了touch事件
// 这一步,必须写。否则,你的动作无法触发对应的 手势
return detector.onTouchEvent(event);
}
//当触摸事件按下时触发这个方法
@Override
public boolean onDown(MotionEvent e) {
return false;
}
//当用户在屏幕上按下,而且 还未移动和松开时候 触发这个方法
@Override
public void onShowPress(MotionEvent e) {
}
//当用户在触摸屏上 轻击 时候 会触发这个方法
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
//当用户在屏幕上滚动时触发的方法
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
//用户在屏幕上长按时触动这个方法
@Override
public void onLongPress(MotionEvent e) {
}
//当用户在触摸屏上“拖过…  滑动…” 时 触发这个方法。velocityX, velocityY  代表 滑动 动作在横向或 纵向上的速度
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//设置拖动距离大于100时,图片改变
//手指向左滑动,终点(e2)在起点(e1)的左侧,e2.getX() - e1.getX 小于 0
if (e1.getX()-e2.getX()>100 && Math.abs(velocityX)>100) {
//左滑显示下一张图片
flipper.showNext();
}else if(e2.getX()-e1.getX()>100 && Math.abs(velocityX)>100)
{//手指向右滑动,终点(e2)在起点(e1)的右侧,e2.getX() - e1.getX 大于 0
//右滑显示上一张图片
flipper.showPrevious();
}
return false;
}
}

⦁ LayoutParams的用法:LayoutParams继承于Android.View.ViewGroup.LayoutParams.
  LayoutParams相当于一个Layout的信息包,它封装了Layout的位置、高、宽等信息。假设在屏幕上一块区域是由一个Layout占领的,如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去。
       可以这样去形容LayoutParams,在象棋的棋盘上,每个棋子都占据一个位置,也就是每个棋子都有一个位置的信息,如这个棋子在4行4列,这里的“4行4列”就是棋子的LayoutParams。

但LayoutParams类也只是简单的描述了宽高,宽和高都可以设置成三种值:
       1,一个确定的值;
       2,FILL_PARENT,即填满(和父容器一样大小);
       3,WRAP_CONTENT,即包裹住组件就好。

  1. FrameLayout下动态设置子控件居中,动态用JAVA代码要这样实现:
    LayoutParams lp=new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
    lp.setMargins(0, 0, 10, 0);//设置组件四周的空白,左    上   右   下
    imageView.setLayoutParams(lp);//给组件添加布局
  2. RelativeLayout下动态设置子控件居中:
    RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); 
    lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE); 
    lp.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); 
    btn1.setLayoutParams(lp);

⦁ IO流数据存储

  1. IO流:相对内存来说的,数据存入内存中是输入流,从内存中保存在文本文件等中是输出流。
  2. 写入和读取
    public class MainActivity extends Activity {
    //定义一个文件名,这个示例中的数据 存储和读取都是从这个文件中来操作
    final String FILE_NAME=“cm.txt”;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    /*//使用StringBuilder+append来拼接一个字符串
    String sb=new StringBuilder(“a”).append(“b”).append(“b”).toString();
    Toast.makeText(this, sb, Toast.LENGTH_SHORT).show();*/
    //得到两个按钮组件对象
    Button read=(Button)findViewById(R.id.read);
    Button write=(Button)findViewById(R.id.write);
    //得到两个文本框的对象
    final EditText edit01=(EditText)findViewById(R.id.edit01);
    final EditText edit02=(EditText)findViewById(R.id.edit02);
    //为按钮绑定监听事件
    write.setOnClickListener(new OnClickListener(){
    //点击
    public void onClick(View v) {
    write(edit01.getText().toString());
    edit01.setText(“”);
    }
    });
    read.setOnClickListener(new OnClickListener(){
    //点击
    public void onClick(View v) {
    edit02.setText(read());
    }
    });
    }
    private void write(String context)
    {
    try {
    //以追加的形式打开文件输出流
    //这个文件的路径  /data/data/包名/files/cm.txt
    //Context.MODE_APPEND以追加方式打开这个文件,应用程序可以向这个文件中追加内容
    FileOutputStream fos=openFileOutput(FILE_NAME,Context.MODE_APPEND);
    //将字节流FileOutputStream包装成处理流PrintStream
    PrintStream ps=new PrintStream(fos);

ps.println(context);
ps.close();

} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private String read()
{
try {
//打开文件输入流
//文件的路径  /data/data/包名/files/cm.txt
FileInputStream fis=openFileInput(FILE_NAME);
//定义一个缓冲区
byte[] buff=new byte[1024];
int hasRead=0;
StringBuilder sb=new StringBuilder(“”);
//读取文件内容
while(((hasRead=fis.read(buff))>0))
{
//不断的循环遍历读取这个输入流
//将字符数据转成字符串new String(buff,0,hasRead)
//使用StringBuilder + append 来凭借字符串
sb.append(new String(buff,0,hasRead));
}
fis.close();
return sb.toString();

} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return null;

}
}

  1. 写入数据之内容的覆盖和追加
    public class MainActivity extends Activity {
    private EditText et_userName;
    private EditText et_userPwd;
    private CheckBox cb_remember_pwd;
    private RadioGroup rg_mode;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

et_userName = (EditText)findViewById(R.id.et_name);
et_userPwd = (EditText)findViewById(R.id.et_password);
//密码是否勾选框
cb_remember_pwd = (CheckBox)findViewById(R.id.cb_remember_pwd);
//单选框组
rg_mode = (RadioGroup)findViewById(R.id.rg_mode);
}

//按钮的点击事件
public void login(View v)
{
String name=et_userName.getText().toString();
String pwd=et_userPwd.getText().toString();

//若用户名或密码字符串,其中有一个为null 或者长度为零,则返回为true
if(TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd))
{
Toast.makeText(this, “用户名和密码不能为空”, Toast.LENGTH_LONG).show();
}else
{
if (“cm”.equals(name) && “123”.equals(pwd)) {
Toast.makeText(this, “登陆成功”, Toast.LENGTH_LONG).show();
//判断 记住用户名密码 是否勾选
if (cb_remember_pwd.isChecked()) {
boolean result=false;
//获取单选框组下边的单选框的id
int id=this.rg_mode.getCheckedRadioButtonId();
switch (id) {
case R.id.rb_private://私有模式,新建一个文件覆盖原文件,内容覆盖原文件内容
result=FilePermissionService.saveInfo(this, name, pwd, 0);
break;
case R.id.rb_append://对原文件续写,内容追加在原内容后
result=FilePermissionService.saveInfo(this, name, pwd, 1);
break;
}
}
}else
{
Toast.makeText(this, “用户名或密码不正确”, Toast.LENGTH_LONG).show();
}
}
}
}
FilePermissionService.java里写覆盖或追加
/**
 * 保存用户名和密码到一个文件中
 * @author Administrator
 */
public class FilePermissionService {
// 保存用户名和密码到一个文件中
public static boolean saveInfo(Context context, String name, String password, int mode) {
// 文件输出流
FileOutputStream fos = null;
try {
// 定义一个switch分支语句,根据传入的参数mode来判定执行哪个 写文件mode
switch (mode) {
case 0:// 当参数mode等0,代表文件login.txt的输入流,且模式为私有模式
// Context.MODE_PRIVATE 若路径下没有同名文件,则新建;若有同名文件,则覆盖之前的文件。
fos = context.openFileOutput(“login.txt”, context.MODE_PRIVATE);
break;
case 1:// 当参数mode等1,代表文件login.txt的输入流,且模式 追加模式
// Context.MODE_APPEND 以追加方式打开这个文件,应用程序可以向这个文件中 追加内容。
fos = context.openFileOutput(“login.txt”, context.MODE_APPEND);
break;
}
// 使用输出流写数据
fos.write((name + “=” + password).getBytes());
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

⦁ SharedPreferences:进行数据存储

  1. LoginServer 
    /**
     * 使用SharedPreferences来定义一个存储用户名和密码的功能模块
     * @author Administrator
     */
    public class LoginServer {
    //使用SharedPreferences来定义一个存储用户名和密码的功能模块
    public static void saveInfo(Context context,String name,String pwd)
    {
    //通过上下文Context对象的getSharedPreferences方法来得到SharedPreferences对象
    SharedPreferences sp=context.getSharedPreferences(“config”, Context.MODE_PRIVATE);
    //调用SharedPreferences的edit()方法得到Editor对象
    Editor editor=sp.edit();
    //使用对象editor来存储数据
    editor.putString(“username”, name);
    editor.putString(“password”, pwd);
    //最后一步,调用commit方法,数据保存成功
    editor.commit();
    }
    public static void saveInfo02(Context context)
    {
    SharedPreferences sp=context.getSharedPreferences(“config”, Context.MODE_PRIVATE);
    Editor editor=sp.edit();
    editor.putString(“a”,“123”);
    editor.putString(“b”,“123”);
    editor.commit();
    }
    //删除所有数据
    public static void delAllData(Context context)
    {
    SharedPreferences sp=context.getSharedPreferences(“config”, Context.MODE_PRIVATE);
    Editor editor=sp.edit();
    //删除所有的editor存储的数据
    editor.clear();
    editor.commit();
    }
    //删除Key键值对应的数据
    public static void delKeyData(Context context)
    {
    SharedPreferences sp=context.getSharedPreferences(“config”, Context.MODE_PRIVATE);
    Editor editor=sp.edit();
    //删除Key键值对应的editor里存储的数据
    editor.remove(“a”);
    editor.commit();//同步提交
    //editor.apply();//异步提交,2.3版本后增加的
    }
    }
  2. 存储、读取和删除
    /**
     * SharedPreferences进行数据存储 */
    public class MainActivity extends Activity {
    private EditText et_username;
    private EditText et_password;
    private CheckBox cb_rememberPwd;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_username = (EditText) findViewById(R.id.et_username);
et_password = (EditText) findViewById(R.id.et_userpwd);
cb_rememberPwd = (CheckBox) findViewById(R.id.cb_remember_pwd);
SharedPreferences sp = this.getSharedPreferences(“config”,MODE_PRIVATE);
// 通过对象sp来获取之前保存的数据
et_username.setText(sp.getString(“username”, “默认值”));
et_password.setText(sp.getString(“password”, “默认值”));
}
// 按钮点击事件
public void login(View v) {
String name = et_username.getText().toString();
String pwd = et_password.getText().toString();

if (TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd)) {
Toast.makeText(this, “用户名或密码不能为空”, Toast.LENGTH_SHORT).show();
} else {
if (“cm”.equals(name) && “123”.equals(pwd)) {
Toast.makeText(this, “登录成功”, Toast.LENGTH_SHORT).show();
if (cb_rememberPwd.isChecked()) {
LoginServer.saveInfo(this, name, pwd);
Toast.makeText(this, “保存成功”, Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(this, “用户名或密码不正确”, Toast.LENGTH_SHORT).show();
}
}
}
// 删除所有数据
public void delAll(View v) {
LoginServer.delAllData(this);
}
// 添加数据
public void addData(View v) {
LoginServer.saveInfo02(this);
}
// 删除Key键值对应的数据
public void delKey(View v) {
LoginServer.delKeyData(this);
}
}

⦁ 统计进入该模块或启动app的次数(类似友盟插件)
/**
 * 统计进入该模块或启动app的次数(类似友盟插件) */
public class MainActivity extends Activity {
//声明SharedPreferences对象
SharedPreferences shared;
//程序每次启动,都会调用这个方法onCreate ,在这个方法中 先得到之前保存的count值,这个count值 表示此应用已经打开的次数。
//然后,在对这个count值 自加1,表示当前打开的次数
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取SharedPreferences对象
shared=getSharedPreferences(“count”,MODE_PRIVATE);
//读取shared里面的count_key对应的value值,若“count”不存在返回0
int count=shared.getInt(“count_key”, 0);
Toast.makeText(this,"程序已经被使用了 : " + (count+1) + “次”,Toast.LENGTH_SHORT).show();;
//获取拥有存储数据的Editor对象
Editor editor=shared.edit();
//存储数据
editor.putInt(“count_key”, ++count);
//提交数据
editor.commit();
}

⦁ 文件存储—得到文件或文件夹的大小(Android12_Shared04)

  1. 定义得到大小的方法
    /*
     * 定义路径文件大小的工具类
     */
    public class FileSizeUtil {
    // 定义四个全局静态常量
    public static final int SIZETYPE_B = 1;
    public static final int SIZETYPE_KB = 2;
    public static final int SIZETYPE_MB = 3;
    public static final int SIZETYPE_GB = 4;
    // 获取指定文件的大小
    private static long getFilesSize(File file) {
    // 定义一个变量表示文件的大小
    long size = 0;
    // 声明一个输入流对象,通过这个流获取文件大小
    FileInputStream fis = null;

try {
/*
* if (file.exists())// 若路径是存在的 { // 得到这个抽象路径的文件输入流对象fis fis = new
* FileInputStream(file); // 调用方法available得到文件的大小,数据类型为int size =
* fis.available();//当文件容量大于1.999G的时候,此方法无效 }
*/
if (file.exists() && file.isFile()) {// 若这个抽象路径是存在的 且 是文件
size = file.length();
} else {
file.createNewFile();
Log.e(“获取文件大小”, “文件不存在”);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return size;
}
// 根据文件大小的方法 来定义 文件夹的大小
private static long getFilesSizes(File file) {
// 定义一个变量size 表示指定路径的大小。这里的这个变量 size 还起到了临时存储的作用
long size = 0;
// 当这个抽象路径File表示一个文件夹的时候,首先调用方法listFiles得到一个File类型的数组,
// 当这个抽象路径File表示一个文件,调用listFile()得到是null
// 那么 在调用方法listFile之前,应该对这个抽象路径File做一个是文件 或是 文件夹的判断。
File[] fList = file.listFiles();
// for循环遍历这个File类型的数组
for (int i = 0; i < fList.length; i++) {
if (fList[i].isDirectory()) {// 若是文件夹
// 使用递归方法再次遍历文件夹
size = size + getFilesSizes(fList[i]);
} else {
// 若是文件,则得到文件的大小并文件大小累加
size = size + getFilesSize(fList[i]);
}
}
return size;
}
// 暴露给外界使用
// 定义一个方法:得到这个路径下的文件夹或文件的大小后,转化为期望的单位B,KB,MB,GB
public static double getSize(String filePath, int sizeType) {
// 强目录字符串包装成抽象路径FIle
File file = new File(filePath);
// 定义一个表示目录大小的变量
long blockSize = 0;
if (file.isDirectory())// 若是文件夹
{
blockSize = getFilesSizes(file);
} else// 若是文件
{
blockSize = getFilesSize(file);
}
// 格式化文件大小数据单位转换文件大小成指定的数据类型,返回值为double类型
return formatFileSize(blockSize, sizeType);
}
// 格式化文件大小数据单位转换文件大小成指定的数据类型,返回值为double类型
private static double formatFileSize(long blockSize, int sizeType) {
// DecimalFormat是NumberFormat的一个具体的实现子类,用于格式化 十进制
// 数字。使用DecimalFormat确保数据精确到 小数点 后两位。
DecimalFormat df = new DecimalFormat(“#.00”);
double fileSizeLong = 0;
switch (sizeType) {
case SIZETYPE_B:// 返回单位 为 B
fileSizeLong = Double.valueOf(df.format((double) blockSize));
// df.format((double)blockSize)— 数据格式化
// Double.valueOf()— 将String类型的强制类型转化为double类型
break;
case SIZETYPE_KB:// 返回单位 为KB
fileSizeLong = Double.parseDouble(df.format((double) blockSize / 1024));
break;
case SIZETYPE_MB:
fileSizeLong = Double.valueOf(df.format((double) blockSize / (1024 * 1024)));
break;
case SIZETYPE_GB:
fileSizeLong = Double.parseDouble(df.format((double) blockSize / (1024 * 1024 * 1024)));
break;
default:
break;
}
return fileSizeLong;
}

// 方法重构
private static String formatFileSize(long blockSize) {
// DecimalFormat是NumberFormat的一个具体的实现子类,用于格式化 十进制
// 数字。使用DecimalFormat确保数据精确到 小数点 后两位。
DecimalFormat df = new DecimalFormat(“#.00”);
String fileSizeString  = “”;
String wrongSize=“0B”;
if (blockSize==0) {
return wrongSize;
}
if(blockSize < 1024){
fileSizeString = df.format((double)blockSize) + “B”;
}else if(blockSize < 1024*1024){
fileSizeString = df.format((double)blockSize/1024) + “KB”;
}else if(blockSize < 1024*1024*1024){
fileSizeString = df.format((double)blockSize/(1024*1024)) + “MB”;
}else{
fileSizeString = df.format((double)blockSize/(1024*1024*1024)) + “GB”;
}
return fileSizeString;
}

// 调用这个方法 自定计算指定文件或文件夹的大小,返回的数据 自动匹配单位 B KB MB GB
public static String getAutoFileOrFilesSize(String filePath) {
// 强目录字符串包装成抽象路径FIle
File file = new File(filePath);
// 定义一个表示目录大小的变量
long blockSize = 0;
if (file.isDirectory())// 若是文件夹
{
blockSize = getFilesSizes(file);
} else// 若是文件
{
blockSize = getFilesSize(file);
}
// 格式化文件大小数据单位,根据数字大小 单位分别为 B KB MB GB,返回值类型为String
return formatFileSize(blockSize);
}
}

⦁ 打电话和发短信(Android13_PhoneAndShortMessage)
1.自定义dialog控件

⦁ 用Bitmap保存图片到内部存储和从内部存储获取图片(Android14_BitmapPic)

  1. 保存图片到内部存储和从内部存储获取图片(GetDirUtil)
    public class GetDirUtil {
    // 得到/data/data/包名/app_XXX 下的一张图片的bitmap
    public static Bitmap loadImageFromDir(Context c, String filename) {
    try {
    // 将 目录+文件名 包装成一个抽象路径
    File file = new File(getDir©, filename);
    // 根据 抽象路径f 生成一个IO
    InputStream is = new FileInputStream(file);
    // 将一个IO流转化为 Bitmap
    Bitmap bmp = BitmapFactory.decodeStream(is);
    return bmp;
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    }
    return null;
    }

private static String getDir(Context c) {
// 定义存储目录data/data/包名/app_my_dir
File file = c.getDir(“my_dir”, Context.MODE_PRIVATE);
String path = file.getPath();
return path;
}

// /data/data/包名/app_my_dir----c.getDir(“my_dir”, Context.MODE_PRIVATE)

/**
* 保存图片在内部存储中
*/
public static void saveFile(Context c, Bitmap bmp, String fileName) {
try {
// 将 目录+文件名 包装成一个抽象路径
File myFile = new File(getDir©, fileName);
if (!myFile.exists())// myFile.exists()存在这个路径
{
// 路径不存在会创建
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myFile));
//质量压缩方法,把压缩后的数据存放到bos流中
//第一个参数是图片压缩后的格式;第二个参数是压缩率(100表示不压缩),图片形状大小不变,清晰度和所占空间改变;第三个参数是流
bmp.compress(Bitmap.CompressFormat.JPEG, 80, bos);

bos.flush();
bos.close();
} else {
Log.e(“dir–”, fileName + “已经存在了,我不再重新创建”);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

  1. 通过Bitmap得到项目资源下的图片并保存在内部存储中
    public class MainActivity extends Activity {
    //内部存储下文件名
    final String fileName = “user_01.jpg”;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //把图片保存在内部存储中
    copyPicToIns();
    }
    //把图片保存在内部存储中
    private void copyPicToIns() {
    // 调用上下文的getResoueces方法得到 系统资源对象Resource
    //.png、.9.png、.jpg文件,它们被编译进以下的Drawable资源子类型中:要获得这种类型的一个资源,可以 使用Resource.getDrawable(id)
    //为了获取资源类型,使用mContext.getResources().getDrawable(R.drawable.imageId)
    Resources res=getResources();
    //通过BitmapFactory类 将图片资源R.drawable.user 转化为bitmp
    Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.user);
    // 保存图片至 内部存储路径中
    GetDirUtil.saveFile(this, bmp, fileName);
    }
    //点击按钮显示图片
    public void getPic(View v)
    {
    //获取/data/data/包名/app_XXX 下的一张图片
    Bitmap bmp=GetDirUtil.loadImageFromDir(this, fileName);
    //得到控件
    ImageView image = (ImageView)findViewById(R.id.image);
    //图片绑定在控件上显示
    image.setImageBitmap(bmp);
    }
    }

⦁ Sdcard外部存储–其实也是文件存储

  1. LoginService向sdcard中读数据写数据的工具类
    /**
     * 向sdcard中 读数据 写数据 的工具类 */
    public class LoginService {
    /**
    * 向sdcard中写数据
    */
    public static boolean saveInfo(Context context, String name, String pwd) {
    try {
    // 得到SDCARD的状态 是否加载 是否可用 是否卸载…
    // Environment是一个提供访问环境变量的接口类,常使用Environment类去获取外部存储目录,在访问外部存储之前一定要先判断外部存储是否已经是可使用(已挂载&可使用)状态,
    String state = Environment.getExternalStorageState();
    // Environment.MEDIA_MOUNTED—返回值 “mounted”。若SDCARD的状态值等于 “mounted”
    // 则说明SDCARD已经准备好了,可以用了
    if (Environment.MEDIA_MOUNTED.equals(state)) {
    // Environment.getExternalStorageDirectory()得到的是抽象路径
    // 存储地址:抽象路径+文件名
    File file = new File(Environment.getExternalStorageDirectory(), “info.txt”);
    // 通过抽象路径 得到输出流
    FileOutputStream fos = new FileOutputStream(file);
    // 输出流 写 数据
    fos.write((name + “=” + pwd).getBytes());
    fos.flush();
    fos.close();
    return true;
    } else {
    Toast.makeText(context, “sdcard不可用”, Toast.LENGTH_SHORT).show();
    return false;
    }
    } catch (Exception e) {
    e.printStackTrace();
    Toast.makeText(context, “SDCARD异常”, Toast.LENGTH_SHORT).show();
    return false;
    }
    }
    /**
    * 从sdcard中读数据
    */
    public static HashMap<String,String> getInfo(Context context)
    {
    try {
    //得到文件info.txt的抽象路径
    File file=new File(Environment.getExternalStorageDirectory()+“info.txt”);
    //创建文件输入流
    FileInputStream fis= new FileInputStream(file);

//字符流包装 文件输入流,再  缓冲流 包装字符流
BufferedReader br=new BufferedReader(new InputStreamReader(fis));
//读一行,根据“=” 划分String类型的数组
String[] result=br.readLine().split(“=”);
HashMap<String,String> hm=new HashMap<String,String>();
hm.put(“name”, result[0]);
hm.put(“pwd”, result[1]);
br.close();
fis.close();
return hm;
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(context, “无法读取数据”, Toast.LENGTH_SHORT).show();
}
return null;
}
}

  1. 存储和获得数据
    /**
     * 外部存储:sdcard
     */
    public class MainActivity extends Activity {

private EditText et_username;
private EditText et_password;
private CheckBox cb_remember_psw;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

this.et_username = (EditText) this.findViewById(R.id.et_username);
this.et_password = (EditText) this.findViewById(R.id.et_password);
this.cb_remember_psw = (CheckBox) this.findViewById(R.id.cb_remember_psw);
// 获取已经保存的数据
HashMap<String, String> info = LoginService.getInfo(getBaseContext());
if (info != null) {
this.et_username.setText(info.get(“name”));
this.et_password.setText(info.get(“pwd”));
}
}

// 响应按钮的响应事件
public void login(View v) {
String name=et_username.getText().toString().trim();
String pwd=et_password.getText().toString().trim();
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd)) {
Toast.makeText(this, “用户名和密码都不能为空”, Toast.LENGTH_SHORT).show();
}else
{
//模拟 用户登陆 成功
if (“cm”.equals(name) && “123”.equals(pwd)) {
Toast.makeText(this, “登录成功”, Toast.LENGTH_SHORT).show();

//checkbox 是否被勾选
if(cb_remember_psw.isChecked())
{
//保存数据
boolean result=LoginService.saveInfo(this, name, pwd);
if (result) {
Toast.makeText(this, “数据保存成功”, Toast.LENGTH_SHORT).show();
}
}
}else{
Toast.makeText(this, “登录失败”, Toast.LENGTH_SHORT).show();
}
}

}
}

⦁ Handler(Android16_Handler)

  1. 什么是Handler:主线程和子线程之间的信使
  2. Handler用什么用:
    Android 中主线程也叫 UI 线程,那么从名字上我们也知道主线程主要是用来创建、更新 UI 的,而其他耗时操作,比如网络访问,或者文件处理,多媒体处理等都需要在子线程中操作,之所以在子线程中操作是为了保证 UI 的流畅程度,手机显示的刷新频率是 60Hz,也就是一秒钟刷新 60 次,每16.67 毫秒刷新一次,为了不丢帧,那么主线程处理代码最好不要超过 16 毫秒。当子线程处理完数据后,为了防止 UI 处理逻辑的混乱,Android 只允许主线程修改 UI,那么这时候就需要 Handler来充当子线程和主线程之间的桥梁了。
    我们通常将 Handler 声明在 Activity 中,然后重写 Handler 中的 handleMessage 方法,当子线程调用 handler.sendMessage()方法后 handleMessage 方法就会在主线程中执行。
    这里面除了 Handler、Message 外还有隐藏的 Looper 和 MessageQueue 对象。
    在主线程中 Android 默认已经调用了 Looper.preper()方法,调用该方法的目的是在 Looper 中创建 MessageQueue 成员变量并把 Looper 对象绑定到当前线程中。当调用 Handler 的sendMessage(对象)方法的时候就将 Message 对象添加到了 Looper 创建的 MessageQueue队列中,同时给 Message 指定了 target 对象,其实这个 target 对象就是 Handler 对象。主线程默认执行了 Looper.looper ()方法,该方法从 Looper 的成员变量 MessageQueue 中取出 Message,然后调用 Message 的 target 对象的 handleMessage()方法。这样就完成了整个消息机制。
  3. 案例1
    public class MainActivity extends Activity {
    final static int BITMAP_UI = 0;
    static final int EXCEPTION = 1;
    private ImageView iv_beauty;
    private EditText et_path;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    this.iv_beauty = (ImageView) this.findViewById(R.id.iv_beauty);
    this.et_path = (EditText) this.findViewById(R.id.et_path);
    }
    // 主线程创建消息处理器
    Handler handler=new Handler(){
    // 消息处理时 调用,并处理这个消息
    public void handleMessage(Message msg)
    {
    if(msg.whatBITMAP_UI)
    {
    // 获取消息对象,位图bitmap
    Bitmap map=(Bitmap)msg.obj;
    //在UI线程中更新UI
    iv_beauty.setImageBitmap(map);
    }else  //else if(msg.what
    EXECPTION)
    {
    Toast.makeText(getApplicationContext(),“图片获取失败”, Toast.LENGTH_SHORT).show();
    }
    }
    };
    //按钮
    public void watch(View v)
    {
    final String path=et_path.getText().toString().trim();
    if(TextUtils.isEmpty(path))
    {
    Toast.makeText(this, “按毛啊”, Toast.LENGTH_SHORT).show();
    }else {
    new Thread(){
    public void run()
    {
    try {
    URL url=new URL(path);
    //HttpURLConnection网络连接;要开发与Internet连接的程序,最基础的还是使用HttpURLConnection
    HttpURLConnection connection=(HttpURLConnection)url.openConnection();
    //设置请求方式
    connection.setRequestMethod(“GET”);
    //设置超时时间
    connection.setConnectTimeout(10000);
    //服务器返回 请求 状态值
    int code=connection.getResponseCode();
    if(code==200)
    {
    //获取流
    InputStream is=connection.getInputStream();
    //把流转换为位图
    Bitmap bmp=BitmapFactory.decodeStream(is);
    // 告诉主线程一个消息,帮我更新 UI, 内容:bitmap
    Message msg=new Message();
    // 消息的代号,是一个int类型
    msg.what=BITMAP_UI;
    msg.obj=bmp;
    // 利用handler发送Message
    handler.sendMessage(msg);
    }
    } catch (MalformedURLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }.start();
    }
    }
    }
  4. 案例2—TimerTask计时器
    public class MainActivity extends Activity {
    // 定义 周期性显示的图片的id 数组
    int[] images = new int[] { R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d };
    // 定义变量 当前图片的id值
    private int currentImageId;
    // 显示图片的ImageView
    private ImageView show;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

show = (ImageView) findViewById(R.id.show);
// 定义一个计时器,让这个计时器周期性的执行指定任务
// TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务。
// 第一个参数是 TimerTask 类,在包:import Java.util.TimerTask.使用者要继承该类,并实现 public void run() 方法;
// 第二个参数"0"的意思是:(0就表示无延迟),当你调用该方法后,该方法必然会调用 TimerTask 类 TimerTask 类 中的
// run() 方法,
// 这个参数就是这两者之间的差值,转换成汉语的意思就是说,用户调用 schedule() 方法后,要等待这么长的时间才可以第一次执行
// run() 方法;
// 第三个参数意思是:第一次调用之后,从第二次开始每隔多长的时间调用一次 run()方法
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// 发送空消息(在子线程中)
myHandler.sendEmptyMessage(0x123);
}
}, 1000, 3000);// 调用方法后延迟1秒后调用图片,每隔3秒显示下一张图片
}
// 在主线程中创建一个Handler对象,用于接收子线程的通知
Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
// 如果这个消息是本程序的那个模块发送的
if (msg.what == 0x123) {
// 定义一个变量i,循环的表示出 数组imageIds中每个元素的索引
int i = currentImageId++ % images.length;
// 在主线程中,动态的显示图片
show.setImageResource(images[i]);
}
}
};
}

⦁ AsyncHttpClient异步请求网络数据(Android17_AsyncHttpClient)

  1. Android开源框架AsyncHttpClient (android-async-http)使用
    android-async-http 开源框架可以使我们轻松地获取网络数据或者向服务器发送数据,最关键的是,它是异步框架,在底层使用线程池处理并发请求,效率很高,使用又特别简单。
    * 通过上面3个简单的方法看出 使用开源框架 get或post请求服务器的步骤非常简单。
    * 1.创建异步请求AsyncHttpClient对象
    * 2.封装请求参数(有就封,没就不封装)
    * 3.get 或 post 请求,这个方法中一般包括 请求的url地址、参数、处理返回结果的handler
    * 4.在成功请求的 回调方法里面,根据status 状态响应吗 head 响应头信息 responseBody响应内容的字节 数组,再进行相应操作
  2. 使用AsyncHttpClient 异步请求传输数据和下载
    public class MainActivity extends Activity {
    private TextView text01, text02;
    private EditText et_username, et_password;
    private ImageView image;
    private String str_username;
    private String str_password;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    text01 = (TextView) findViewById(R.id.text01);
    text02 = (TextView) findViewById(R.id.text02);
    et_username = (EditText) findViewById(R.id.et_username);
    et_password = (EditText) findViewById(R.id.et_password);
    image = (ImageView) findViewById(R.id.image);
    }

public void get01(View v) {
// 创建异步请求的对象(相当于 HttpClient)
AsyncHttpClient client = new AsyncHttpClient();
String url = “http://www.sina.com”;
// 然后 调用get或者 post来请求服务器端、这个方法的调用 就直接在UI线程中,第一个参数 是
// URL,第二个参数是AsyncHttpResponseHandler对象
// ,这个对象就是接受请求结果的,一般咱们习惯使用匿名内部类的方式来创建这个对象
client.get(url, new AsyncHttpResponseHandler() {
// 重写父类中的几个方法。一般都会重写onSuccess 和 onFailure

// 可以在这里进行一些前置的 操作,比如开启 加载对话框
@Override
public void onStart() {
}

// 与服务器交互过程中,可以在这里进行 显示进度条
@Override
public void onProgress(int bytesWritten, int totalSize) {
super.onProgress(bytesWritten, totalSize);
}

// 如果网络请求成功了,会回调这个方法。
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
// statusCode(此时是200)
// headers 头部请求信息
// responseBody 服务器成功返回的数据
text01.setText(“statusCode=” + statusCode + "\r\nheaders = " + headers);
text02.setText(“responseBody=” + new String(responseBody));
}

// 网络请求失败
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
text01.setText("请求失败了,错误码 = " + statusCode);
text02.setText("responseBody = " + new String(responseBody));
}

@Override
public void onRetry() {
// TODO Auto-generated method stub
super.onRetry();
}

});
}

// 使用get,通过RequestParams来携带参数,
public void get02(View v) {
str_username = this.et_username.getText().toString().trim();
str_password = this.et_password.getText().toString().trim();
// 创建异步请求对象
AsyncHttpClient client = new AsyncHttpClient();
// 网址
String url = “http://www.sina.com”;
// RequestParams对象封装参数
RequestParams params = new RequestParams();
// “username”—从哪里来的?与服务器端约定好的 一个KEY值,服务器端程序员 就是靠 这个KEY来得到你的用户名
params.put(“username”, str_username);
params.put(“password”, str_password);
client.get(url, params, new AsyncHttpResponseHandler() {

@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
text01.setText("statusCode = " + statusCode + "\r\nheaders = " + headers);
text02.setText("responseBody = " + new String(responseBody));
}

@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
text01.setText("请求失败了,错误码 = " + statusCode);
text02.setText("responseBody = " + new String(responseBody));
}

});
}

// 同样可以使用post,通过RequestParams来携带参数
public void post01(View v) {
str_username = this.et_username.getText().toString().trim();
str_password = this.et_password.getText().toString().trim();
// 创建异步请求对象
AsyncHttpClient client = new AsyncHttpClient();
String url = “http://www.sina.com”;
// RequestParams对象
RequestParams params = new RequestParams();
params.put(“username”, str_username);
params.put(“password”, str_password);
client.post(url, params, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
text01.setText("statusCode = " + statusCode + "\r\nheaders = " + headers);
text02.setText("responseBody = " + new String(responseBody));
}

@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
text01.setText("请求失败了,错误码 = " + statusCode);
text02.setText("responseBody = " + new String(responseBody));
}
});
}

/*
* 通过上面3个简单的方法看出 使用开源框架 get或post请求服务器的步骤 非常简单。
* 1.创建异步请求AsyncHttpClient对象
* 2.封装请求参数(有就封,没就不封装)
* 3.get 或 post 请求,这个方法中一般包括 请求的url地址、参数、处理返回结果的handler
* 4.在成功请求的 回调方法里面,根据status 状态响应吗 head 响应头信息 responseBody响应内容的字节 数组,再进行相应操作

* AsyncHttpClient进行网络交互的时候,还可以携带Cookie,若各位以后再工作中 涉及到了cookie的时候,
* 可以去AsyncHttpClient官网 http:loopj.com/android-async-http/ 查询相关的 demo

* 除此之外,还可以向服务器端 上传一个文件吧。 这个参数 常用的是 File对象 或者 IO流
*/
// 参数为File对象,上传文件 File形式
public void upload_01(View v) {
// 判断外部存储是否在状态(可用)
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// 确定本地中 需要上传的文件的路径
File file = new File(Environment.getExternalStorageDirectory(), “2.txt”);
// 文件上传的网址
String url = “http://www.sina.com”;

if (!file.exists())// 文件不存在
{
// 创建文件
try {
file.createNewFile();
Toast.makeText(getApplicationContext(), “文件创建成功”, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}

// 先判断 本地文件文件是否存在
if (file.exists() && file.length() > 0) {
// 创建异步请求对象
AsyncHttpClient client = new AsyncHttpClient();
// 设置 参数—文件形式
RequestParams params = new RequestParams();
try {
params.put(“file_http”, file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
client.post(url, params, new AsyncHttpResponseHandler() {
// 文件上传成功时 回调这个方法
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Toast.makeText(getApplicationContext(), “文件上传成功了”, Toast.LENGTH_SHORT).show();
}

// 文件上传失败时 回调这个方法
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Toast.makeText(getApplicationContext(), “文件上传失败了!错误码=” + statusCode, Toast.LENGTH_SHORT).show();
}
});
} else {
Toast.makeText(getApplicationContext(), “本地文件 的 路径找不到或文件为空”, Toast.LENGTH_SHORT).show();
}
}
}

// 将一个本地文件的抽象路径转化为IO流 ,然后上传;首先 在工程目录assets中放入一张图片 1.png,然后 将此图片转化为 输入流
// 再以流的形式将文件上传入服务器
public void upload_02(View v) {
// 文件上传的网址
final String url = “http://www.baidu.com”;

try {
/*
* assets文件夹里面的文件都是保持原始的文件格式,需要用AssetManager以字节流的形式读取文件。 1.
* 先在Activity里面调用getAssets() 来获取AssetManager引用。 2.
* 再用AssetManager的open(String fileName, int accessMode)
* 方法则指定读取的文件以及访问模式就能得到输入流InputStream。 3. 然后就是用已经open file
* 的inputStream读取文件,读取完成后记得inputStream.close() 。
* 4.调用AssetManager.close() 关闭AssetManager。
* 需要注意的是,来自Resources和Assets 中的文件只可以读取而不能进行写的操作
*/
// InputStream is = this.getResources().getAssets().open(“a.jpg”);
// InputStream is = getResources().getAssets().open(“a.jpg”);
InputStream is = getAssets().open(“a.jpg”);

// 上传文件
if (is != null) {
// 创建异步请求对象
AsyncHttpClient client = new AsyncHttpClient();
// 设置 参数—文件形式
RequestParams params = new RequestParams();
// 第一个参数 – 约定好的一个key; 第二参数-- 需要上传文件的输入流;第三个参数 – 为这个流 命名
params.put(“io_http”, is, “a.jpg”);

client.post(url, params, new AsyncHttpResponseHandler() {
// 文件上传成功时 回调这个方法
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Toast.makeText(getApplicationContext(), “文件上传成功了”, Toast.LENGTH_SHORT).show();
}

// 文件上传失败时 回调这个方法
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Toast.makeText(getApplicationContext(), “文件上传失败了!错误码=” + statusCode, Toast.LENGTH_SHORT).show();
}

});
} else {
Toast.makeText(getApplicationContext(), “文件IO为null”, Toast.LENGTH_SHORT).show();
}

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

// 除了上传,还有下载!!! 从百度手机助手上 下载 唯品会app
public void download(View v) {
final String DOWN_URL = “http://p.gdown.baidu.com/335968baaf3fd2e53e86ea63059c89c2f858fb97c917484548d6b5586ed137a437abce04”;
downLoadAPP(DOWN_URL, null, fileHandler);
}
// 下载
private void downLoadAPP(String url, RequestParams param, FileAsyncHttpResponseHandler file) {
// 创建异步请求对象
AsyncHttpClient client = new AsyncHttpClient();
client.get(url, param, file);
}

// 文件路径
String app_my = Environment.getExternalStorageDirectory() + File.separator + “weipinhui.apk”;
File app_file = new File(app_my);

// FileAsyncHttpResponseHandler是 AsyncHttpResponseHandler 子类,可用于下载功能
FileAsyncHttpResponseHandler fileHandler = new FileAsyncHttpResponseHandler(app_file) {
// 文件下载成功时 回调这个方法
@Override
public void onSuccess(int statusCode, File file) {
Toast.makeText(getApplicationContext(), “文件下载成功了” + statusCode, Toast.LENGTH_SHORT).show();
}
// 文件上传失败时 回调这个方法
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error){
Toast.makeText(getApplicationContext(), “文件下载失败了!错误码=” + statusCode, Toast.LENGTH_SHORT).show();
}
};
}

  1. 阿萨

⦁ Fragment

  1. Fragment的产生与介:http://blog.csdn.net/lmj623565791/article/details/37992017网址里有详解
    Android 官方推荐 : DialogFragment 创建对话框(http://blog.csdn.net/lmj623565791/article/details/37815413)
    Android运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视。针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布局以适应平板神马超级大屏的。难道无法做到一个App可以同时适应手机和平板么,当然了,必须有啊。Fragment的出现就是为了解决这样的问题。你可以把Fragment当成Activity的一个界面的一个组成部分,甚至Activity的界面可以完全有不同的Fragment组成,更帅气的是Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个Fragment。
    2、Fragment的生命周期

Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:

可以看到Fragment比Activity多了几个额外的生命周期回调方法:
onAttach(Activity)
当Fragment与Activity发生关联时调用。
onCreateView(LayoutInflater, ViewGroup,Bundle)
创建该Fragment的视图
onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用
onDestoryView()
与onCreateView想对应,当该Fragment的视图被移除时调用
onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用
注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现,
3、静态的使用Fragment(Android23_Fragment01)
嘿嘿,终于到使用的时刻了~~
这是使用Fragment最简单的一种方式,把Fragment当成普通的控件,直接写在Activity的布局文件中。步骤:
1、继承Fragment,重写onCreateView决定Fragemnt的布局
2、在Activity中声明此Fragment,就当和普通的View一样
下面展示一个例子(我使用2个Fragment作为Activity的布局,一个Fragment用于标题布局,一个Fragment用于内容布局):

是不是把Fragment当成普通的View一样声明在Activity的布局文件中,然后所有控件的事件处理等代码都由各自的Fragment去处理,瞬间觉得Activity好干净有木有代码的可读性、复用性以及可维护性是不是瞬间提升了~下面看下效果图:

4、动态的使用Fragment(Android23_Fragment02)

上面已经演示了,最简单的使用Fragment的方式~下面介绍如何动态的添加、更新、以及删除Fragment

为了动态使用Fragment,我们修改一下Actvity的布局文件,中间使用一个FrameLayout,下面添加四个按钮~嘿嘿不是微信的按钮- -!

5、Fragment家族常用的API
Fragment常用的三个类:
android.app.Fragment 主要用于定义Fragment
android.app.FragmentManager 主要用于在Activity中操作Fragment
android.app.FragmentTransaction 保证一些列Fragment操作的原子性,熟悉事务这个词,一定能明白~
a、获取FragmentManage的方式:
getFragmentManager() // v4中,getSupportFragmentManager
b、主要的操作都是FragmentTransaction的方法
FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
transaction.add() 
往Activity中添加一个Fragment
transaction.remove() 
从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
transaction.replace()
使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~
transaction.hide()
隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
transaction.show()
显示之前隐藏的Fragment
detach()
会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
attach()
重建view视图,附加到UI上并显示。
transatcion.commit()//提交一个事务
注意:常用Fragment的哥们,可能会经常遇到这样Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。
上述,基本是操作Fragment的所有的方式了,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。
值得注意的是:如果你喜欢使用Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。
a、比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。
b、再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。
c、remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach。

⦁ Android Volley完全解析(一),初识Volley的基本用法(Android29_RequestQueueFromVolley)

  1. Volley简介
    我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据。Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高。
    不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码。于是乎,一些Android网络通信框架也就应运而生,比如说AsyncHttpClient,它把HTTP所有的通信细节全部封装在了内部,我们只需要简单调用几行代码就可以完成通信操作了。再比如Universal-Image-Loader,它使得在界面上显示网络图片的操作变得极度简单,开发者不用关心如何从网络上获取图片,也不用关心开启线程、回收图片资源等细节,Universal-Image-Loader已经把一切都做好了。
    Android开发团队也是意识到了有必要将HTTP的通信操作再进行简单化,于是在2013年Google I/O大会上推出了一个新的网络通信框架——Volley。Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载网络上的图片。除了简单易用之外,Volley在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。
    下图所示的这些应用都是属于数据量不大,但网络通信频繁的,因此非常适合使用Volley。
  2. 下载Volley
    介绍了这么多理论的东西,下面我们就准备开始进行实战了,首先需要将Volley的jar包准备好,如果你的电脑上装有Git,可以使用如下命令下载Volley的源码:
    git clone https://android.googlesource.com/platform/frameworks/volley
    下载完成后将它导入到你的Eclipse工程里,然后再导出一个jar包就可以了。如果你的电脑上没有Git,那么也可以直接使用我导出好的jar包,下载地址是:http://download.csdn.net/detail/sinyu890807/7152015 。
    新建一个Android项目,将volley.jar文件复制到libs目录下,这样准备工作就算是做好了。
  3. StringRequest的用法
    前面已经说过,Volley的用法非常简单,那么我们就从最基本的HTTP通信开始学习吧,即发起一条HTTP请求,然后接收HTTP响应。首先需要获取到一个RequestQueue对象,可以调用如下方法获取到:
    RequestQueue mQueue = Volley.newRequestQueue(context);
    注意这里拿到的RequestQueue是一个请求队列对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。RequestQueue内部的设计就是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的,基本上在每一个需要和网络交互的Activity中创建一个RequestQueue对象就足够了。
    接下来为了要发出一条HTTP请求,我们还需要创建一个StringRequest对象,如下所示:
    StringRequest stringRequest = new StringRequest(“http://www.baidu.com”,  
                            new Response.Listener() {  
                                @Override  
                                public void onResponse(String response) {  
                                    Log.d(“TAG”, response);  
                                }  
                            }, new Response.ErrorListener() {  
                                @Override  
                                public void onErrorResponse(VolleyError error) {  
                                    Log.e(“TAG”, error.getMessage(), error);  
                                }  
                            });  
    可以看到,这里new出了一个StringRequest对象,StringRequest的构造函数需要传入三个参数,第一个参数就是目标服务器的URL地址,第二个参数是服务器响应成功的回调,第三个参数是服务器响应失败的回调。其中,目标服务器地址我们填写的是百度的首页,然后在响应成功的回调里打印出服务器返回的内容,在响应失败的回调里打印出失败的详细信息。
    最后,将这个StringRequest对象添加到RequestQueue里面就可以了,如下所示:
    mQueue.add(stringRequest); 
    另外,由于Volley是要访问网络的,因此不要忘记在你的AndroidManifest.xml中添加如下权限:
     
    好了,就是这么简单,如果你现在运行一下程序,并发出这样一条HTTP请求,就会看到LogCat中会打印出如下图所示的数据。
     
    没错,百度返回给我们的就是这样一长串的HTML代码,虽然我们看起来会有些吃力,但是浏览器却可以轻松地对这段HTML代码进行解析,然后将百度的首页展现出来。
    这样的话,一个最基本的HTTP发送与响应的功能就完成了。你会发现根本还没写几行代码就轻易实现了这个功能,主要就是进行了以下三步操作:
  4. 创建一个RequestQueue对象。
  5. 创建一个StringRequest对象。
  6. 将StringRequest对象添加到RequestQueue里面。
    不过大家都知道,HTTP的请求类型通常有两种,GET和POST,刚才我们使用的明显是一个GET请求,那么如果想要发出一条POST请求应该怎么做呢?StringRequest中还提供了另外一种四个参数的构造函数,其中第一个参数就是指定请求类型的,我们可以使用如下方式进行指定:
    StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener); 
    可是这只是指定了HTTP请求方式是POST,那么我们要提交给服务器的参数又该怎么设置呢?很遗憾,StringRequest中并没有提供设置POST参数的方法,但是当发出POST请求的时候,Volley会尝试调用StringRequest的父类——Request中的getParams()方法来获取POST参数,那么解决方法自然也就有了,我们只需要在StringRequest的匿名类中重写getParams()方法,在这里设置POST参数就可以了,代码如下所示:
    StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener) {  
        @Override  
        protected Map<String, String> getParams() throws AuthFailureError {  
            Map<String, String> map = new HashMap<String, String>();  
            map.put(“params1”, “value1”);  
            map.put(“params2”, “value2”);  
            return map;  
        }  
    };

你可能会说,每次都这样用起来岂不是很累?连个设置POST参数的方法都没有。但是不要忘记,Volley是开源的,只要你愿意,你可以自由地在里面添加和修改任何的方法,轻松就能定制出一个属于你自己的Volley版本。
4. JsonRequest的用法
学完了最基本的StringRequest的用法,我们再来进阶学习一下JsonRequest的用法。类似于StringRequest,JsonRequest也是继承自Request类的,不过由于JsonRequest是一个抽象类,因此我们无法直接创建它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和JsonArrayRequest,从名字上你应该能就看出它们的区别了吧?一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。
至于它们的用法也基本上没有什么特殊之处,先new出一个JsonObjectRequest对象,如下所示:
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(“http://m.weather.com.cn/data/101010100.html”, null,  
        new Response.Listener() {  
            @Override  
            public void onResponse(JSONObject response) {  
                Log.d(“TAG”, response.toString());  
            }  
        }, new Response.ErrorListener() {  
            @Override  
            public void onErrorResponse(VolleyError error) {  
                Log.e(“TAG”, error.getMessage(), error);  
            }  
        });  
可以看到,这里我们填写的URL地址是http://m.weather.com.cn/data/101010100.html,这是中国天气网提供的一个查询天气信息的接口,响应的数据就是以JSON格式返回的,然后我们在onResponse()方法中将返回的数据打印出来。
最后再将这个JsonObjectRequest对象添加到RequestQueue里就可以了,如下所示:
mQueue.add(jsonObjectRequest);  
这样当HTTP通信完成之后,服务器响应的天气信息就会回调到onResponse()方法中,并打印出来。现在运行一下程序,发出这样一条HTTP请求,就会看到LogCat中会打印出如下图所示的数据。
 
由此可以看出,服务器返回给我们的数据确实是JSON格式的,并且onResponse()方法中携带的参数也正是一个JSONObject对象,之后只需要从JSONObject对象取出我们想要得到的那部分数据就可以了。
你应该发现了吧,JsonObjectRequest的用法和StringRequest的用法基本上是完全一样的,Volley的易用之处也在这里体现出来了,会了一种就可以让你举一反三,因此关于JsonArrayRequest的用法相信已经不需要我再去讲解了吧。

⦁ Gson :Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库。可以将一个 JSON 字符串转成一个 Java 对象,或者反过来。Gson解析json数据。
实体类:
⦁ public class Student {  
⦁     private int id;  
⦁     private String name;  
⦁     private Date birthDay;  
⦁     public int getId() {  
⦁         return id;  
⦁     }  
⦁     public void setId(int id) {  
⦁         this.id = id;  
⦁     }  
⦁     public String getName() {  
⦁         return name;  
⦁     }  
⦁     public void setName(String name) {  
⦁         this.name = name;  
⦁     }  
⦁     public Date getBirthDay() {  
⦁         return birthDay;  
⦁     }  
⦁     public void setBirthDay(Date birthDay) {  
⦁         this.birthDay = birthDay;  
⦁     }   
⦁     public String toString() {  
⦁         return “Student [birthDay=” + birthDay + “, id=” + id + “, name=”  
⦁                 + name + “]”;  
⦁     }  
⦁ }

测试类:
⦁ import java.util.ArrayList;  
⦁ import java.util.Date;  
⦁ import java.util.List;  
⦁   
⦁ import com.google.gson.Gson;  
⦁ import com.google.gson.reflect.TypeToken;  
⦁ public class GsonTest1 {  
⦁     public static void main(String[] args) {  
⦁         Gson gson = new Gson();  
⦁         Student student1 = new Student();  
⦁         student1.setId(1);  
⦁         student1.setName(“李坤”);  
⦁         student1.setBirthDay(new Date());  
⦁   
⦁         // //  
⦁         System.out.println(“----------简单对象之间的转化-------------”);  
⦁         // 简单的bean转为json  
⦁         String s1 = gson.toJson(student1);  
⦁         System.out.println(“简单Bean转化为Json===” + s1);  
⦁   
⦁         // json转为简单Bean  
⦁         Student student = gson.fromJson(s1, Student.class);  
⦁         System.out.println(“Json转为简单Bean===” + student);  
⦁         // 结果:  
⦁         // 简单Bean转化为Json==={“id”:1,“name”:“李坤”,“birthDay”:“Jun 22, 2012 8:27:52 AM”}  
⦁         // Json转为简单Bean=Student [birthDay=Fri Jun 22 08:27:52 CST 2012, id=1,  
⦁         // name=李坤]  
⦁         // //  
⦁         Student student2 = new Student();  
⦁         student2.setId(2);  
⦁         student2.setName(“曹贵生”);  
⦁         student2.setBirthDay(new Date());  
⦁   
⦁         Student student3 = new Student();  
⦁         student3.setId(3);  
⦁         student3.setName(“柳波”);  
⦁         student3.setBirthDay(new Date());  
⦁   
⦁         List list = new ArrayList();  
⦁         list.add(student1);  
⦁         list.add(student2);  
⦁         list.add(student3);  
⦁   
⦁         System.out.println(“----------带泛型的List之间的转化-------------”);  
⦁         // 带泛型的list转化为json  
⦁         String s2 = gson.toJson(list);  
⦁         System.out.println("带泛型的list转化为json
" + s2);  
⦁   
⦁         // json转为带泛型的list  
⦁         List retList = gson.fromJson(s2,  
⦁                 new TypeToken<List>() {  
⦁                 }.getType());  
⦁         for (Student stu : retList) {  
⦁             System.out.println(stu);  
⦁         }  
⦁   
⦁         // 结果:  
⦁         // 带泛型的list转化为json==[{“id”:1,“name”:“李坤”,“birthDay”:“Jun 22, 2012 8:28:52 AM”},{“id”:2,“name”:“曹贵生”,“birthDay”:“Jun 22, 2012 8:28:52 AM”},{“id”:3,“name”:“柳波”,“birthDay”:“Jun 22, 2012 8:28:52 AM”}]  
⦁         // Student [birthDay=Fri Jun 22 08:28:52 CST 2012, id=1, name=李坤]  
⦁         // Student [birthDay=Fri Jun 22 08:28:52 CST 2012, id=2, name=曹贵生]  
⦁         // Student [birthDay=Fri Jun 22 08:28:52 CST 2012, id=3, name=柳波]  
⦁   
⦁     }  
⦁ }

执行结果:
⦁ ----------简单对象之间的转化-------------  
⦁ 简单Bean转化为Json==={“id”:1,“name”:“李坤”,“birthDay”:“Jun 22, 2012 9:10:31 PM”}  
⦁ Json转为简单Bean=Student [birthDay=Fri Jun 22 21:10:31 CST 2012, id=1, name=李坤]  
⦁ ----------带泛型的List之间的转化-------------  
⦁ 带泛型的list转化为json
[{“id”:1,“name”:“李坤”,“birthDay”:“Jun 22, 2012 9:10:31 PM”},{“id”:2,“name”:“曹贵生”,“birthDay”:“Jun 22, 2012 9:10:31 PM”},{“id”:3,“name”:“柳波”,“birthDay”:“Jun 22, 2012 9:10:31 PM”}]  
⦁ Student [birthDay=Fri Jun 22 21:10:31 CST 2012, id=1, name=李坤]  
⦁ Student [birthDay=Fri Jun 22 21:10:31 CST 2012, id=2, name=曹贵生]  
⦁ Student [birthDay=Fri Jun 22 21:10:31 CST 2012, id=3, name=柳波]

⦁ Xml之Pull解析和xml存储(Android24_XML_Pull)

  1. xml文件(books.xml)
<?xml version="1.0" encoding="UTF-8"?>              鬼吹灯         36                 JAVA秘籍         56                 Android宝典         66       1. 解析和保存的工具类 public class BookMgr { public static final String BOOKS="books"; public static final String BOOK="book"; public static final String NAME="name"; public static final String PRICE="price"; public static final String ID="id";

/** 解析xml文件
* @param is :输入流
* @return ArrayList <HashMap<String, Object>> 
*/
public static ArrayList<HashMap<String, Object>> parserXML(InputStream is) throws Exception{
//取得一个xml解析器
XmlPullParser parser=Xml.newPullParser();
//设置要解析的内容
parser.setInput(is, “UTF-8”);
//获取事件类型
int event=parser.getEventType();
ArrayList<HashMap<String,Object>> data=null;
HashMap<String, Object> map=null;
//当还没到文档的尾部时
while(event!=XmlPullParser.END_DOCUMENT){
switch (event) {
case XmlPullParser.START_TAG://比较开始标签
String name=parser.getName();//获取标签名
if(BOOKS.equals(name)){
//new 一个ArrayList
data=new ArrayList<HashMap<String,Object>>();
}else if(BOOK.equals(name)){
//new 一个map
map=new HashMap<String, Object>();
//往map中添加值
map.put(ID, parser.getAttributeValue(0));
}else if(NAME.equals(name)||PRICE.equals(name)){
map.put(name, parser.nextText());
}
break;
case XmlPullParser.END_TAG:
if(BOOK.equals(parser.getName())){
//把map添加到list中
data.add(map);
}

default:
break;
}
//开始下一个解析动作,并且获取解析动作的类型
event=parser.next();
}
//关闭输入流
is.close();
return data;
}

/**
* 把数据存在.xml文件中
* @param data  :输入的数据列表
* @param outstream  :把输入的列表数据转换为输出流,进行输出
*/
public static void exportXml
(ArrayList<HashMap<String, Object>> inputdata, OutputStream outstream) throws Exception {
//获取xml构建器
XmlSerializer serializer=Xml.newSerializer();
//设置要输出到哪里去,格式是什么
serializer.setOutput(outstream, “UTF-8”);
//写xml开始 节点
serializer.startDocument(“UTF-8”, true);
//写books开始标签
serializer.startTag(null, BOOKS);
//for 循环,遍历出所有的 book ,写到xml中
for(HashMap<String, Object> map:inputdata){
//写book开始标签
serializer.startTag(null, BOOK);
//写 book属性
serializer.attribute(null, ID, map.get(ID).toString());
//写name开始标签
serializer.startTag(null, NAME);
serializer.text(map.get(NAME).toString());
//写name结束标签
serializer.endTag(null, NAME);
//写price开始标签
serializer.startTag(null, PRICE);
//写price值
serializer.text(map.get(PRICE).toString());
//写price结束标签
serializer.endTag(null, PRICE);
//写book结束标签
serializer.endTag(null, BOOK);
}
//写books结束标签
serializer.endTag(null, BOOKS);
//写xml结束节点
serializer.endDocument();
outstream.close();
}
}

  1. Activity中实现
    /**
     * XML之Pull解析
     * @author Administrator
     *
     */
    public class PullActivity extends Activity {
    ArrayList<HashMap<String,Object>> data;

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pull);
        //每一项的item的样子  ,初始化的数据
        ListView bookList=(ListView) findViewById(R.id.book_list);
        
        //传参数
        InputStream is=getClassLoader().getResourceAsStream(“books.xml”);
        try {
        //数据从books.xml解析中来
data=BookMgr.parserXML(is);
} catch (Exception e) {
e.printStackTrace();
}
        //给适配器装配数据
        SimpleAdapter simpleAdapter=new SimpleAdapter(this, data, 
        R.layout.book_item,
        new String[]{BookMgr.NAME,BookMgr.PRICE},
        new int[]{R.id.book_name,R.id.book_price});
        //绑定适配器
        bookList.setAdapter(simpleAdapter);
        
    }
    
    //把解析到的数据保存到sdcard中
    public void exportXML(View v){
    //把数据输出到xml文件中
    //定义一个输出流,写入到sdcard中
    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
   
    File file1=new File(Environment.getExternalStorageDirectory(), “ff”);
    //创建多个目录
    file1.mkdirs();
//    File rootFile=Environment.getExternalStorageDirectory();
    File file=new File(file1, “books.xml”);
    try {
BookMgr.exportXml(data,new FileOutputStream(file));

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

⦁ 自定义拍照之SurfaceView(Android25_customPhone)

  1. SurfaceView介绍:SurfaceView由于可以直接从内存或者DMA等硬件接口取得图像数据,因此是个非常重要的绘图容器
    详解地址:http://www.360doc.com/content/13/0103/14/7724936_257842268.shtml
  2. 布局
    <RelativeLayout 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:orientation=“vertical” >
       
        <SurfaceView 
            android:id=“@+id/surfaceView”
            android:layout_width=“match_parent”
            android:layout_height=“match_parent”
            />
        <TextView
            android:id=“@+id/takePhoto”
            android:layout_width=“wrap_content”
            android:layout_height=“wrap_content”
            android:layout_alignParentBottom=“true”
            android:layout_centerHorizontal=“true”
            android:layout_marginBottom=“10dp”
            android:background=“#cccccc”
            android:padding=“10dp”
            android:text=“拍照” />
  3. 代码
    /**
     * 自定义拍照 SurfaceView由于可以直接从内存或者DMA等硬件接口取得图像数据,因此是个非常重要的绘图容器;
     * Camera.Parameters:相机的服务设置。使相机参数生效,应用程序必须调用setparameters(相机参数。)。
     */
    @SuppressLint(“NewApi”)
    public class MainActivity extends Activity implements OnClickListener {
    private Camera camera;
    // 相机的服务设置
    private Camera.Parameters parameters;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.takePhoto).setOnClickListener(this);
// surfaceView是在一个新起的单独线程中可以重新绘制画面,当使用surfaceView
// 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。
// 但这也带来了另外一个问题,就是事件同步
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
// 设置SurfaceView分辨率
surfaceView.getHolder().setFixedSize(176, 144);
// 保持屏幕常亮
surfaceView.getHolder().setKeepScreenOn(true);
// 为SurfaceView添加一个回调函数
surfaceView.getHolder().addCallback(new SurfaceViewCallback());
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.takePhoto:// 拍照
camera.takePicture(null, null, new PictureCallback() {

@Override // 第一个参数就是图片数据。
public void onPictureTaken(byte[] data, Camera camera) {
// 把数据保存到sd卡中
saveSD(data);
}
});
break;
}
}

// 把数据保存到sd卡中
protected void saveSD(byte[] data) {
String fileName = System.currentTimeMillis() + “.jpg”;
File file = new File(Environment.getExternalStorageDirectory() + “/files/”);
if (!file.exists()) {
file.mkdirs();
}
File jpgFile = new File(file, fileName);

try {
// 文件输出流
FileOutputStream fos = new FileOutputStream(jpgFile);
// 写入sd卡
fos.write(data);
// 关闭输入流
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* SurfaceView的回调函数
*/
private class SurfaceViewCallback implements Callback {
// SurfaceView创建时调用
@Override
public void surfaceCreated(SurfaceHolder holder) {

try {
// 打开摄像头
camera = Camera.open();
// 设置SurfaceView显示Camera
camera.setPreviewDisplay(holder);
// 设置拍照方向
camera.setDisplayOrientation(getPreviewDegree());
// 开始预览
camera.startPreview();
// 在自定义相机的代码中,调用
// camera.takePicture(null,rawCallback,jpegCallback); 方法完成拍照时会发现
// 无论系统的音量时震动、静音还是有声音都无法关闭自定义相机的拍照声音。
// camera.takePicture(null,rawCallback,jpegCallback);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

// SurfaceView拍照状态改变时调用
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// 获取camera的各项参数
parameters = camera.getParameters();
// 图片格式
parameters.setPictureFormat(PixelFormat.JPEG);
// 设置预览大小
parameters.setPreviewSize(width, height);
// 设置每秒显示的帧数
parameters.setPreviewFpsRange(4, 6);
// 设置图片保存尺寸
parameters.setPictureSize(width, height);
// 设置图片质量
parameters.setJpegQuality(100);
}

// SurfaceView销毁时调用
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (camera != null) {
// 释放照相机
camera.release();
camera = null;
}
}
}

// 根据activity方向获取carame的方法
private int getPreviewDegree() {
// 获取屏幕方向
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int degree = 0;
// 根据手机屏幕的方向,计算照相机的方法
switch (rotation) {
case Surface.ROTATION_0:
degree = 90;
break;
case Surface.ROTATION_90:
degree = 0;
break;
case Surface.ROTATION_180:
degree = 270;
break;
case Surface.ROTATION_270:
degree = 180;
break;
}
return degree;
}
}

⦁ 拍照之调用本机的拍照软件(Android25_photoUri)

  1. 代码:
    **
     * 拍照
     */
    public class MainActivity extends Activity implements OnClickListener {

//图片保存路径
private Uri photoUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

findViewById(R.id.takePhoto).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.takePhoto:
//使用前,判断sd卡是否存在
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
Intent intent=new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//键值对管理类,使用ContentValues存储图片路径,可以获取图片的原图
ContentValues values=new ContentValues();
// 插入图片路径到数据库
photoUri=getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
//把图片路径存入photoUri:content://media/external/images/media/375
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent, 1);
}else
{
Toast.makeText(getApplicationContext(), “sd卡不存在”, Toast.LENGTH_SHORT).show();
}
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//获取图片绝对路径 picPath:/storage/emulated/0/DCIM/Camera/1464849143598.jpg
if(requestCodeActivity.RESULT_OK)
{
//图片在数据库中的列名
String[] projection={MediaStore.Images.Media.DATA};
//游标,读取数据时,数据的位置信息
Cursor cursor=getContentResolver().query(photoUri, projection, null, null, null);
String picPath=“”;
if(resultCode
1)
{
if (data==null) {
//使用之前,判断data是不是为空,在有些手机会出现错误
Toast.makeText(getApplicationContext(), “选择图片出错”, Toast.LENGTH_SHORT).show();
finish();
}
if(cursor!=null)
{
// 根据列名,获取查询数据的下标
int columnIndex=cursor.getColumnIndexOrThrow(projection[0]);
//使用游标时,必须把游标移动到最前面
cursor.moveToFirst();
picPath=cursor.getString(columnIndex);
cursor.close();
}
}
}
}
}

⦁ 音频播放之SoundPool(Android25_SoundPool)
SoundPool —— 适合短促且对反应速度比较高的情况(游戏音效或按键声等)

  1. 详情地址:http://blog.csdn.net/qduningning/article/details/8680575
  2. 代码
    /**
     * 音频播放:SoundPool */
    public class MainActivity extends Activity implements OnClickListener {

private SoundPool player;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

findViewById(R.id.play).setOnClickListener(this);
findViewById(R.id.pause).setOnClickListener(this);
findViewById(R.id.stopPlayer).setOnClickListener(this);
第一个参数:播放几个音频文件
// 第二个:声音类型
// 第三个:声音品质
player = new SoundPool(2, AudioManager.STREAM_MUSIC, 100);
player.load(this, R.raw.a_sos, 0);
player.load(this, R.raw.beep, 1);

}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.play://开始播放
player.play(1, 0.5f, 0.5f, 0, 0, 1f);
player.play(2, 0.5f, 0.5f, 0, 0, 1f);
break;
case R.id.pause://暂停
player.pause(1);
player.pause(2);
//继续播放
//player.resume(2);
break;
case R.id.stopPlayer://停止播放
player.stop(1);
player.stop(2);
//释放资源
player.release();
break;
default:
break;
}
}
}

⦁ 拍照完后显示显示照片在界面上(Android26_CameraAndPhoto)

  1. 展示:

⦁ 从网络上获取图片(Android26_CompressImage)

⦁ 图片加载之 LruCache图片缓存技术(Android26_PicLruCache)

  1. LruCache:图片缓存之内存缓存技术.内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。 其中最核心的类是LruCache (此类在android-support-v4的包中提供) 。这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。 它有一个特点,当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。
    步骤:(1)要先设置缓存图片的内存大小,我这里设置为手机内存的1/4, 手机内存的获取方式:
    int MAXMEMONRY = (int)(Runtime.getRuntime() .maxMemory() / 1024);
    (2)LruCache里面的键值对分别是URL和对应的图片 (3)重写了一个叫做sizeOf的方法,返回的是图片数量。
  2. 代码
    //图片缓存
    public class MainActivity extends Activity {
    // 图片网址
    private String url = “http://g.hiphotos.baidu.com/image/h%3D200/sign=9d57666cf8039245beb5e60fb795a4a8/4b90f603738da977678b168ab451f8198718e3ef.jpg”;
    // 内存类
    private LruCache<String, Bitmap> mLruCache;
    // 图片保存的文件夹
    private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + “/files/”;
    private ImageView pic1, pic2, pic3;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

pic1 = (ImageView) findViewById(R.id.pic1);
pic2 = (ImageView) findViewById(R.id.pic2);
pic3 = (ImageView) findViewById(R.id.pic3);

// 初始化内存设置
initLruCache();
// 从网络获取数据
getNetImage();

// pic1的监听器
pic1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 加载内存中的图片
if (getCacheBitmap(url) != null) {
pic2.setImageBitmap(getCacheBitmap(url));
}
}
});
pic2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//加载sd卡中的图片
if (readSD(url) != null) {
pic3.setImageBitmap(readSD(url));
}
}
});

}

// 读取sd卡图片
protected Bitmap readSD(String url) {
Bitmap bitmap = null;
String imageSDPath = path + url.substring(url.lastIndexOf(“/”) + 1, url.length()).toLowerCase();
File file = new File(imageSDPath);
// 判断文件是否存在
if (!file.exists()) {
return null;
}
bitmap = BitmapFactory.decodeFile(imageSDPath);
if (bitmap != null || bitmap.toString().length() > 5) {
return bitmap;
} else {
return null;
}
}

// 获取内存中的bitmap
protected Bitmap getCacheBitmap(String key) {
return mLruCache.get(key);
}

// 从网络获取数据
private void getNetImage() {
new Thread() {
public void run() {
HttpClient httpClient = new DefaultHttpClient();
HttpGet get = new HttpGet(url);
try {
HttpResponse response = httpClient.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
// 获取返回结果的实体类
HttpEntity httpEntity = response.getEntity();
InputStream is = httpEntity.getContent();
Bitmap bmp = BitmapFactory.decodeStream(is);
handler.obtainMessage(1, bmp).sendToTarget();
}
} catch (Exception e) {
e.printStackTrace();
}

}
}.start();
}

// 主线程
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Bitmap bmp = (Bitmap) msg.obj;
// 保存到内存中
addCache(url, bmp);
// 保存到sd卡中
saveSD(url, bmp);
pic1.setImageBitmap(bmp);
break;
}
}
};

// 初始化内存设置
private void initLruCache() {
// 获取程序可用的最大内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
// 设置缓存图片的内存大小
int cacheSize = maxMemory / 4;
// 实例化内存类
mLruCache = new LruCache<String, Bitmap>(cacheSize);
}

// 保存bitmap到sd卡
protected void saveSD(String url, Bitmap bmp) {
try {
final String fileName = url.substring(url.lastIndexOf(“/”) + 1, url.length()).toLowerCase();
File file = new File(path + fileName);
// 判断sd卡是否存在
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// 判断文件夹是否存在
File mm = new File(path);
if (!mm.exists()) {
mm.mkdirs();
}
// 保存bitmap到sd卡
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
// 把bitmap写入文件中
bmp.compress(Bitmap.CompressFormat.JPEG, 100, bos);
// 清空缓存
bos.flush();
bos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 把图片添加到内存
protected void addCache(String url, Bitmap bmp) {
if (getCacheBitmap(url) == null) {
if (bmp != null) {
mLruCache.put(url, bmp);
}
}
}
}

⦁ 通过线程池加载图片(Android26_ThreadPool)

  1. 线程池:线程池是预先创建线程的一种技术。线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中。这些线程都是处于睡眠状态,即均为启动,不消耗CPU,而只是占用较小的内存空间。当请求到来之后,缓冲池给这次请求分配一个空闲线程,把请求传入此线程中运行,进行处理。当预先创建的线程都处于运行状态,即预制线程不够,线程池可以自由创建一定数量的新线程,用于处理更多的请求。当系统比较闲的时候,也可以通过移除一部分一直处于停用状态的线程。
  2. 通过线程池获取图片
    /**
     * 获取图片,线程池
     */
    public class ThreadPoolImageLoader {
    // 创建线程池,参数:有多少个线程可以同时运行
    private ExecutorService executorService = Executors.newFixedThreadPool(3);
    private Handler handler=new Handler();

//加载网络图片
public void loadImage(final String url,final Callback callback)
{
//Runnable:新开一个线程
executorService.submit(new Runnable(){
@Override
public void run() {
Drawable drawable=loadImageUrl(url);
//讲drawable转换成bitmap
BitmapDrawable bd=(BitmapDrawable)drawable;
final Bitmap bmp=bd.getBitmap();
//Runnable:不会新开一个线程;把线程内的数据传递到主线程
handler.post(new Runnable(){
@Override
public void run() {
callback.result(bmp);
}
});
}
});
}
//从网络上下载图片
private Drawable loadImageUrl(String url)
{
try {
//获取输入流
InputStream is=new URL(url).openStream();
//将输入流转换为drawable
return Drawable.createFromStream(is, “image.png”);
} catch (Exception e) {
return null;
}
}
interface Callback{
void result(Bitmap bitmap);
}
}

  1. MainActivity
    /**
     * 通过线程池加载图片 */
    public class MainActivity extends Activity {
    private String url = “http://g.hiphotos.baidu.com/image/h%3D200/sign=9d57666cf8039245beb5e60fb795a4a8/4b90f603738da977678b168ab451f8198718e3ef.jpg”;
    private ImageView pic1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

pic1 =(ImageView)findViewById(R.id.pic1);
//实例化对象
ThreadPoolImageLoader tpoolImage=new ThreadPoolImageLoader();
tpoolImage.loadImage(url, new Callback(){
@Override
public void result(Bitmap bitmap) {
pic1.setImageBitmap(bitmap);
}
});
L.i(“ss”);
}
}

⦁ AIDL:为使应用程序之间能够彼此通信,Android提供了IPC (Inter Process Communication,进程间通信)的一种独特实现: AIDL (Android Interface Definition Language, Android接口定义语言)。
⦁ AIDL是IPC的一个轻量级实现,用了对于Java开发者来说很熟悉的语法。Android也提供了一个工具,可以自动创建Stub(类构架,类骨架)。当我们需要在应用间通信时,我们需要按以下几步走:

  1. 定义一个AIDL接口
  2. 为远程服务(Service)实现对应Stub
  3. 将服务“暴露”给客户程序使用
    ⦁ 用例: HelloSumAIDL
    AIDL的语法很类似Java的接口(Interface),只需要定义方法的签名。
    AIDL支持的数据类型与Java接口支持的数据类型有些不同
  4. 所有基础类型(int, char, 等)
  5. String,List,Map,CharSequence等类
  6. 其他AIDL接口类型
  7. 所有Parcelable的类
    ⦁ 阿魏啊

    ⦁ JNI:JNI是JAVA标准平台中的一个重要功能,它弥补了JAVA的与平台无关这一重大优点的不足,在JAVA实现跨平台的同时,也能与其它语言(如C、C++)的动态库进行交互,给其它语言发挥优势的机会。JNI提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。

有了JAVA标准平台的支持,使JNI模式更加易于实现和使用。在此总结了下面这个知识图:

⦁ Android中系统日期时间的获取
⦁ import    java.text.SimpleDateFormat;       
⦁        
⦁ SimpleDateFormat    formatter    =   new    SimpleDateFormat    ("yyyy年MM月dd日    HH:mm:ss     ");       
⦁ Date    curDate    =   new    Date(System.currentTimeMillis());//获取当前时间       
⦁ String    str    =    formatter.format(curDate);       
 
可以获取当前的年月时分,也可以分开写:
[java] view plain copy print?
⦁ SimpleDateFormat    sDateFormat    =   new    SimpleDateFormat(“yyyy-MM-dd    hh:mm:ss”);       
⦁ String    date    =    sDateFormat.format(new    java.util.Date());    
 
如果想获取当前的年月,则可以这样写(只获取时间或秒种一样):
Java代码
[java] view plain copy print?
⦁ SimpleDateFormat sdf=new SimpleDateFormat(“yyyy-MM”);    
⦁ String date=sdf.format(new Java.util.Date());    
当然还有就是可以指定时区的时间(待):
[java] view plain copy print?
⦁ df=DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL,Locale.CHINA);  
⦁ System.out.println(df.format(new Date()));  
 
 
如何获取Android系统时间是24小时制还是12小时制
       
⦁ ContentResolver cv = this.getContentResolver();  
⦁        String strTimeFormat = Android.provider.Settings.System.getString(cv,  
⦁                                           android.provider.Settings.System.TIME_12_24);  
⦁         
⦁        if(strTimeFormat.equals(“24”))  
⦁       {  
⦁               Log.i(“activity”,“24”);  
⦁        }

利用Calendar获取
⦁ Calendar c = Calendar.getInstance();  
⦁ 取得系统日期:year = c.get(Calendar.YEAR)  
⦁                month = c.get(Calendar.MONTH)  
⦁                day = c.get(Calendar.DAY_OF_MONTH)  
⦁ 取得系统时间:hour = c.get(Calendar.HOUR_OF_DAY);  
⦁                   minute = c.get(Calendar.MINUTE)  
⦁                   int second=c.get(Calendar.SECOND);
利用Time获取
⦁ Time t=new Time(); // or Time t=new Time(“GMT+8”); 加上Time Zone资料。  
⦁ t.setToNow(); // 取得系统时间。  
⦁ int year = t.year;  
⦁ int month = t.month;  
⦁ int date = t.monthDay;  
⦁ int hour = t.hour; // 0-23  
⦁ int minute = t.minute;  
⦁ int second = t.second;

唯一不足是取出时间只有24小时模式.

⦁ 自定义ListView让一个页面可以同时展示几个ListView(Android28_WorkListView)

  1. 自定义ListView:
    /**
     * 重新测量Listview的宽度和高度 */
    public class MyListView extends ListView {

public MyListView(Context context) {
super(context);
}

public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
//重新测量listview的宽度和高度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 重新测量gridview的宽度和高度
int height=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, height);
}
}

  1. Xml
    <ScrollView 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:orientation=“vertical”
        android:scrollbars=“vertical” >
        <LinearLayout
            android:layout_width=“match_parent”
            android:layout_height=“match_parent”
            android:orientation=“vertical” >
           
           
            <com.cm.android28_worklistview.MyListView
                android:id=“@+id/mylist1”
                android:layout_width=“fill_parent”
                android:layout_height=“wrap_content” />
            <com.cm.android28_worklistview.MyListView
                android:id=“@+id/mylist2”
                android:layout_width=“fill_parent”
                android:layout_height=“wrap_content” />
            <com.cm.android28_worklistview.MyListView
                android:id=“@+id/mylist3”
                android:layout_width=“fill_parent”
                android:layout_height=“wrap_content” />
       
  2. MainActivity
    public class MainActivity extends Activity {
    // 定义信息
    private int[] images={R.drawable.jingjing,R.drawable.ruodan,R.drawable.yifei};
    private String[] names = { “刘静静”, “刘洛丹”, “胡一菲” };
    private String[] years = { “33岁”, “29岁”, “28岁” };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ArrayList<HashMap<String,String>> listItem01=new ArrayList<HashMap<String,String>>();
    //把信息保存在list集合中
    for (int i = 0; i < names.length; i++) {
    HashMap<String,String> map01=new HashMap<String, String>();
    //把数据放在Map集合中
    map01.put(“name”, names[i]);
    map01.put(“year”, years[i]);
    //把数据加入到List集合中
    listItem01.add(map01);
    }
    ArrayList<HashMap<String,Object>> listItem02=new ArrayList<HashMap<String,Object>>();
    for (int i = 0; i < names.length; i++) {
    HashMap<String,Object> map02=new HashMap<String, Object>();
    //把数据放在Map集合中
    map02.put(“image”,images[i]);
    map02.put(“name”, names[i]);
    map02.put(“year”, years[i]);
    //把数据加入到List集合中
    listItem02.add(map02);
    }
    // 获得ListView组件
    ListView list01 = (ListView) findViewById(R.id.mylist1);
    ListView list02 = (ListView) findViewById(R.id.mylist2);
    ListView list03 = (ListView) findViewById(R.id.mylist3);
    // 适配器
    ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_single_choice, names);
    list01.setAdapter(adapter);
    //适配器
    String [] from={“name”,“year”};
    int[] to={R.id.names,R.id.years};
    SimpleAdapter simpleAdapter=new SimpleAdapter(this, listItem01, R.layout.rl02_layout, from, to);
    //设置适配器
    list02.setAdapter(simpleAdapter);
    //自定义适配器
    MyCustomAdapter customAdapter=new MyCustomAdapter(this, listItem02);
    list03.setAdapter(customAdapter);
    }

}

⦁ 图片加载框架ImageLoader(Android29_ImageLoaderConfiguration)

  1. Universal-Image-Loader这个开源框架又来给我们造福了,它是一个图片加载框架,主要强大在于可以用于网络等图片源的加载,并且有多重缓存机制。 多线程的图像加载的可能性的宽调谐对ImageLoader的配置(线程池的大小,HTTP选项,内存和光盘高速缓存,显示图像,以及其他)的图像的可能性中的缓存存储器和/或设备的文件器系统(或SD卡)
  2. 图片的异步加载和双缓存
    /*
     *DisplayImageOptions 图片的异步加载和双缓存
     * ImageLoaderConfiguration 设置缓存 */
    public class MainActivity extends Activity {
    private String url2 = “http://att.x2.hiapk.com/album/201203/30/134620xxxftxszc0apaiff.jpg”;
    private ImageView pic;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

pic = (ImageView) findViewById(R.id.pic);
// ImageLoader配置信息,图片的异步加载和双缓存
DisplayImageOptions options = new DisplayImageOptions.Builder()
.cacheInMemory(true)// 设置内存缓存
.cacheOnDisc(true)// 设置sd卡缓存
.build();
//设置缓存
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).defaultDisplayImageOptions(options)
.diskCacheSize(200 * 1024 * 1024)// sd缓存大小
.discCacheFileCount(200)// sd卡缓存数量
.memoryCacheSize(50 * 1024 * 1024)// 内存大小
.writeDebugLogs()// 日志信息
.build();
// 初始化ImageLoader
ImageLoader.getInstance().init(config);
//ImageLoader加载图片
ImageLoader.getInstance().displayImage(url2, pic);
}
}

⦁ OkHttp:网络访问(Android29_oKHttp)
现在Android网络方面的第三方库很多,volley,Retrofit,OKHttp等,各有各自的特点,这边博客就来简单介绍下如何使用OKHttp。
梗概
OKHttp是一款高效的HTTP客户端,支持连接同一地址的链接共享同一个socket,通过连接池来减小响应延迟,还有透明的GZIP压缩,请求缓存等优势
详情地址:http://blog.csdn.net/lmj623565791/article/details/47911083
一、概述
最近在群里听到各种讨论okhttp的话题,可见okhttp的口碑相当好了。再加上Google貌似在6.0版本里面删除了HttpClient相关API,对于这个行为不做评价。为了更好的在应对网络访问,学习下okhttp还是蛮必要的,本篇博客首先介绍okhttp的简单使用,主要包含:
⦁ 一般的get请求
⦁ 一般的post请求
⦁ 基于Http的文件上传
⦁ 文件下载
⦁ 加载图片
⦁ 支持请求回调,直接返回对象、对象集合
⦁ 支持session的保持
⦁ 最后会对上述几个功能进行封装,完整的封装类的地址见:https://github.com/hongyangAndroid/okhttp-utils
二、使用教程
(一)Http Get
对了网络加载库,那么最常见的肯定就是http get请求了,比如获取一个网页的内容。
(二) Http Post 携带参数
看来上面的简单的get请求,基本上整个的用法也就掌握了,比如post携带参数,也仅仅是Request的构造的不同。
三、封装
由于按照上述的代码,写多个请求肯定包含大量的重复代码,所以我希望封装后的代码调用是这样的:
四、整合Gson
很多人提出项目中使用时,服务端返回的是Json字符串,希望客户端回调可以直接拿到对象,于是整合进入了Gson,完善该功能。

⦁ Sensor传感器、感应器(Android29_Sensor)

  1. Sensor Type
          重力感应/加速度传感器 (G-Sensor)
          光感应   (Light-Sensor) 
          温度感应
          方向感应
          磁场、
          临近性
    2.如何实现Sensor编程
         a.获取系统服务(SENSOR_SERVICE)返回一个SensorManager 对象
               sensormanager = (SensorManager)getSystemSeriver(SENSOR_SERVICE);
         b.通过SensorManager对象获取相应的Sensor类型的对象
               sensorObject = sensormanager.getDefaultSensor(sensor Type);
         c.声明一个SensorEventListener 对象用于侦听Sensor 事件,并重载onSensorChanged方法
                SensorEventListener sensorListener = new SensorEventListener(){
                 };
         d.注册相应的SensorService
                 sensormanager.registerListener(sensorListener, sensorObject, Sensor TYPE);
        e.销毁相应的SensorService
                sensormanager.unregisterListener(sensorListener, sensorObject);
     
        f: SensorListener 接口是传感器应用程序的中心。它包括两个必需方法:
              onSensorChanged(int sensor,float values[]) 方法在传感器值更改时调用。
              该方法只对受此应用程序监视的传感器调用(更多内容见下文)。该方法的参数包括:一个整数,指示更改的传感器;一个浮点值数组,表示传感器数据本身。有些传感器只提供一个数据值,另一些则提供三个浮点值。方向和加速表传感器都提供三个数据值。
          当传感器的准确性更改时,将调用 onAccuracyChanged(int sensor,int accuracy) 方法。参数包括两个整数:一个表示传感器,另一个表示该传感器新的准确值。
  2. 检测手机又哪些传感器
    /**
     * 检测手机又哪些传感器
     */
    public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //获取传感器服务
    SensorManager sensorManager=(SensorManager)getSystemService(Context.SENSOR_SERVICE);
    //获取所有传感器列表
    List list=sensorManager.getSensorList(Sensor.TYPE_ALL);
    for (int i = 0; i <list.size(); i++) {
    Log.e(“阿萨德”, list.get(i).getName());
    }
    }
    }
  3. 震动传感器
    a. 一个检测手机摇晃的监听器
    /**
     * 一个检测手机摇晃的监听器 *
     */
    public class ShakeListener implements SensorEventListener {
    // 获取传感器服务
    private SensorManager sensorManager;
    private Context context;
    // 传感器
    private Sensor sensor;
    // 重力感应监听器 ,接口
    private OnShakeListener listener;
    // 手机上一个位置时重力感应坐标
    private float x1 = 0;
    private float y1 = 0;
    private float z1 = 0;
    private long time;
    private long interval_time;
    public ShakeListener(Context context) {
    this.context = context;
    }
    public void setListener(OnShakeListener listener) {
    this.listener = listener;
    }
    public void start() {
    // 获取传感服务器
    sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
    // 获取加速传感器
    sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    // 注册传感器,第三个参数:反应速率,根据情况设定
    sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
    }
    public void stop() {
    // 注销传感器
    sensorManager.unregisterListener(this);
    }
    // 加速传感器感应获取数据变化的调用
    @Override
    public void onSensorChanged(SensorEvent event) {
    // 获取当前时间
    long curr = System.currentTimeMillis();
    // 判断时间间隔是否大于10秒
    interval_time = curr - time;
    if (interval_time < 100) {
    return;
    }
    // 当前时间给time
    time = curr;
    // xyz坐标
    float x = event.values[0];
    float y = event.values[1];
    float z = event.values[2];
    // 摇一摇的速度
    double speed = Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1) - (z - z1) * (z - z1)) / interval_time
    * 1000;
    Log.e(“阿萨德”, “speed:” + speed);
    // 达到速度阀值,发出提示
    if (speed > 200) {
    listener.onShake();
    }
    x1 = x;
    y1 = y;
    z1 = z;

}
// 传感器精度改变时调用
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
public interface OnShakeListener {
void onShake();
}
}
a. 震动
/**
 * Sensor 传感器、感应器
 */
public class MainActivity2 extends Activity {
private ShakeListener shake;
// 震动器
private Vibrator vibrator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取震动的服务
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
shake = new ShakeListener(this);
shake.setListener(new OnShakeListener() {

@Override
public void onShake() {
/*Vibrator.vibrate()方法:
只有1个参数的时候,第一个参数用来指定振动的毫秒数。
要传递2个参数的时候,第1个参数用来指定振动时间的样本,第2个参数用来指定是否需要循环。 
振动时间的样本是指振动时间和等待时间的交互指定的数组。
※下面的例子,在程序起动后等待3秒后,振动1秒,再等待2秒后,振动5秒,再等待3秒后,振动1秒
long[] pattern = {3000, 1000, 2000, 5000, 3000, 1000}; // OFF/ON/OFF/ON…
  vibrator.vibrate(pattern, -1);*/
vibrator.vibrate(1000);
// 异步延时
new Handler().postDelayed(new Runnable() {

@Override
public void run() {
// 取消震动
vibrator.cancel();
}
}, 1000);
}
});
shake.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
shake.stop();
}
}

⦁ 地图的制作

  1. 百度地图:Android30_BaiMap
  2. 高德地图:Android30_GaoDeMap

⦁ BroadcastReceiver广播接收器

  1. 广播的两种注册:动态注册广播和静态注册广播(Android31_BroadcastReceive01)
    a. MainActovity中动态注册
    public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

//方法一:动态注册广播;方法二:在清单文件中静态注册广播
/*//动态注册 广播
IntentFilter filter=new IntentFilter();
filter.addAction(“com.cn.ABC”);
//将 广播接受者 和 广播标识 绑定了
registerReceiver(new MyReceiver(), filter);*/
}

public void tvClick(View v)
{
//定义广播Intent  然后将这个intent发出去
Intent intent =new Intent();
//setAction里面的参数"com.cn.ABC" 就是广播匹配BroadcastReceiver的标识
intent.setAction(“com.cn.ABC”);
//携带了参数
intent.putExtra(“AABB”, “你们问我,软件外包是什么。我解释了几句还没明白,随想了一下,包工头知道吧?顿悟”);
//发送广播
sendBroadcast(intent);
}
}
a. 清单文件中静态注册:

<?xml version="1.0" encoding="utf-8"?>


   
   
       
           
               
               
           
       
       
         
       
           
               
           
       
   

a. 接收广播
/**
 * 接收广播
 * @author Administrator
 *
 */
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, “广播的匹配标识action为” + intent.getAction()+ “; 携带的数据:” +
intent.getStringExtra(“AABB”), Toast.LENGTH_LONG).show();
}
}
2. 广播的优先级(Android31_BroadcastReceive02)
a. MainActivity
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void tvClick(View v){
//定义广播Intent  然后将这个intent发出去
Intent intent=new Intent();
//setAction里面的参数"com.cn.BROADCAST" 就是广播匹配BroadcastReceiver的标识
intent.setAction(“com.cn.BROADCAST”);
//携带了参数
intent.putExtra(“broadcast”, “更快 更强 更好”);
//发送有序广播
sendOrderedBroadcast(intent,null);
}
}
a. myReceriver01
/**
 * Bundle类用作携带数据,它类似于Map,用于存放key-value名值对形式的值。相对于Map,它提供了各种常用类型的putXxx()/getXxx()方法,如:putString()/getString()和putInt()/getInt(),putXxx()用于往Bundle对象放入数据,getXxx()方法用于从Bundle对象里获取数据。Bundle的内部实际上是使用了HashMap类型的变量来存放putXxx()方法放入的值
 */
public class myReceriver01 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, “广播的匹配标识action为” + intent.getAction()+ “; 携带的数据:” +
intent.getStringExtra(“broadcast”), Toast.LENGTH_LONG).show();
Bundle bundle=new Bundle();
bundle.putString(“first”, “我叫牛破天,我牛气冲天”);
//将bundle放入广播中
setResultExtras(bundle);
}
}
a. myReceriver02
public class myReceriver02 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle=getResultExtras(true);
String first=bundle.getString(“first”,“妞妞,我还小”);

Toast.makeText(context, “上一个广播接收者 赋的值:” + first, Toast.LENGTH_LONG).show();
}
}
a. 清单文件设置优先级

<?xml version="1.0" encoding="utf-8"?>


   
   
       
           
               


           
       
       
       
           
           
               
           
       
      
         
           
           
               
           
       
   

⦁ SQLite数据库(Android34_SQLite)

  1. SQLite数据库 只能存储适量的数据 SQLite数据库 就是一个方便操作的可以读写数据的文件
    SQLite数据库 支持哪些操作方式:
    a.支持绝大部分的SQL92语法  和  SQL语句来操作 SQLite数据库
    b.Android系统的SDK在SQL语句的基础上封装了 一些常用API 增删改查…
  2. SQLiteDatabase数据库的操作类
    SQLiteDatabase  openDatabse(path, SQLiteDatabase.CursorFactory,int flag)
    SQLiteDatabase  openOrCreateDatabse(path, SQLiteDatabase.CursorFactory,int flag)   打开或者创建(如果不存在)path所代表的SQLite数据库
    SQLiteDatabase  openOrCreateDatabse(File, SQLiteDatabase.CursorFactory,int flag)

String path = getFileDir() + “/mydb.db3”

  1. SQL语言
    四大类:
    数据查询语言DQL  select<字段名表>子句  from<表>子句  where<查询条件>子句 
    数据操纵语句DML  insert  update   delete
    数据定义语言DDL  create table mytable
    数据控制语言DCL…
    使用SQL语句来操作 SQLite数据库,通过 SQLiteDatabase对象的方法execSQL(sql语句)
    execSQL(String sql)—sql 语句 。没有占位符
    execSQL(string sql, Object[] object)  执行带有占位符的sql语句

insert into 表名 values (值01, 值02…)
Insert into 表名 (列1,列2…) valsues (值01, 值02…))
rawQuery(Strint sql,String[] str)  执行带有占位符的sq语句   返回值类型为Cursor对象  JDBC  ResultSet
Cursor对象  getXxx 得到这行的指定列的数据

  1. 使用Android系统提供的api操作SQLite数据库
    SQLiteDatabase里面的简单操作数据库的方法
    1、使用insert方法插入记录
    SQLiteDatabase的insert方法的签名为long insert(String table,String nullColumnHack,ContentValues values),这个插入方法的参数说明如下:
    table:代表想插入数据的表名。
    nullColumnHack:代表强行插入null值的数据列的列名。
    values:代表一行记录的数据。
    insert方法插入的一行记录使用ContentValues存放,ContentValues类似于Map,它提供了put(String key,Xxx value)(其中key为数据列的列名)方法用于存入数据、getAsXxx(String key)方法用于取出数据。
    例如如下语句:
    ContentValues values=new ContentValues();
    values.put(“name”,“孙悟空”):
    values.put(“age”,500);
    //返回新添记录的行号,该行号是一个内部直,与主键id无关,发生错误返回-1
    long rowid=db.insert(“person_inf”,null,values);
    2、使用update方法更新数据
    SQLiteDatabase的update方法签名为update(String table,ContentValues values,String whereClause,String[] whereArgs),这个更新方法的参数说明如下:
    table:代表想要更新数据的表名。
    values:代表想要更新的数据。
    whereClause:满足该whereClause子句的记录将会被更新。
    whereArgs:用于为whereArgs子句传递参数。
    例如我们想要更新person_inf表中所有主键大于20的人的人名,可调用如下方法:
    ContentValues values=new ContentValues();
    //存放更新后的人名
    values.put(“name”,“新人名”);
    int result=db.update(“person_inf”,values,“_id>?”,new Integer[]{20});
    3、使用delete方法删除记录
    SQLiteDatabase的delete方法签名为delete(String table,String whereClause,String[] whereArgs),这个删除的参数说明如下:
    table:代表想删除数据的表名。
    whereClause:满足该whereClause子句的记录将会被删除。
    whereArgs:用于为whereArgs子句传入参数。
    删除person_inf表中所有人名以孙开头的记录
    int result=db.delete(“person_inf”,“person_name like ?”,new String[]{“孙_”});
    4、使用query方法查询记录
    SQLiteDatabase的query方法签名为Cursor query(boolean distinct,String table,String[] columns,String selection,String[] selectionArgs,String groupBy,String having,String orderBy,String limit),这个query方法的参数说明如下。
    distinct:指定是否去除重复记录。
    table:执行查询数据的表名。
    columns:要查询出来的列名。
    selection:查询条件子句。
    selectionArgs:用于为selection子句中占位符传入参数值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常。
    groupBy:用于控制分组。
    having:用于对分组进行过滤。
    orderBy:用于对记录进行排序。
    limit:用于进行分页。
    例如查询出person_inf表中人名以孙开头的数据
    Cursor cursor=db.query(“person_inf”,new String[]{“_id,name,age”},“name like ?”,new String []{“孙%”},null,null,“personid desc”,“5,10”);
    cursor.close();
  2. 查询数据db.query()和db.rawQuery(sql, selectionArgs)的区别
    Cursor cursor = db.rawQuery(“select name from *** where id=?”, new String[]{“1”});
    Cursor cursor = db.query(“***”, new String[]{“name”}, “id=?”, new String[]{“1”}, null, null, null);
    上面是两个分别是query和rawQuery的查询语句,主要区别是rawQuery是直接使用SQL语句进行查询的,也就是第一个参数字符串,在字符串内的“?”会被后面的String[]数组逐一对换掉;而query函数是Android自己封装的查询API.
    而后者query对比前者来讲就有一个好处,前者rawQuery你在写入SQL语句的时候,有可能写错了或者写漏了什么单词拼写错误的时候他会出错,而后者相对来讲出错的机率就比较小
  3. 创建表和添加数据
    public class MainActivity extends Activity {
    // 声明数据库对象
    SQLiteDatabase db;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 在路径getFilesDir()+“/mydb.db”
    String path = this.getFilesDir() + “/mydb.db”;
    //创建数据库
    db = SQLiteDatabase.openOrCreateDatabase(path, null);

// user_info表存在就删除该表
db.execSQL(“DROP TABLE IF EXISTS user_info”);

// 定义建表的Sql语句,没有占位符
String sql = “create table user_info(user_id integer primary key,”

  • “user_name varchar(255),user_pass varchar(255))”;
    // 数据库最小执行SQL语句
    db.execSQL(sql);

/*// 向表中插入数据
String sql_insert = “insert into user_info(user_id,user_name,user_pass)”

  • " values(null,‘haha’,‘123456’)";
    // 数据库对象执行SQL插入语句
    db.execSQL(sql_insert);*/

String name=“haha”;
String pass=“12345”;
//带占位符的sql语句
String sqlz=“insert into user_info values(null,?,?)”;
//执行语句
db.execSQL(sqlz, new String[]{name,pass});

}
}

  1. 创建数据库、插入数据和展现数据
    public class MainActivity2 extends Activity implements OnClickListener {

// 声明数据库对象
SQLiteDatabase db;
Button bn ;
ListView listView;
EditText name,pass;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);

listView = (ListView)findViewById(R.id.showsql);
bn = (Button)findViewById(R.id.btn);
bn.setOnClickListener(this);
name = (EditText)findViewById(R.id.name);
pass = (EditText)findViewById(R.id.pass);

//创建或打开数据库
db = SQLiteDatabase.openOrCreateDatabase(this.getFilesDir().getPath() + “/mydb.db3”, null);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn:
String et_name = name.getText().toString();//若不输入内容  得到是长度为0的空字符串
String et_pass = pass.getText().toString();
try {
/*创建数据表不应该放在这,当第二次插入数据是会报错,说创建了表
* // 执行DDL语句创建数据表 的 表结构
db.execSQL(“create table users_info(user_id integer primary key autoincrement,”

  • “user_name varchar(50),user_pass varchar(255))”);*/
    // 执行insert语句 插入数据;使用带 占位符的 sql语句
    db.execSQL(“insert into users_info values(null,?,?)”, new String[] { et_name, et_pass });
    // 执行 查询
    Cursor cursor=db.rawQuery(“select * from users_info”, null);

前端面试题汇总

JavaScript

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

性能

linux

前端资料汇总

  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值