Android Java小知识点集锦

来源:互联网 发布:juniper network mac 编辑:程序博客网 时间:2024/05/17 13:40

1.在JAVA中怎么清空StringBuffer变量内容

StringBuffer my_StringBuffer = new StringBuffer();

my_StringBuffer.append(‘helloworld’); //添加字符串到StringBuffer中

int sb_length = my_StringBuffer.length();// 取得字符串的长度
my_StringBuffer.delete(0,sb_length); //删除字符串从0~sb_length-1处的内容 (这个方法就是用来清除StringBuffer中的内容的)

还有另外一种方式
my_StringBuffer.setLength(0); //设置StringBuffer变量的长度为0

2.相对布局和帧布局都默认子控件在左上方
3.getX、getRawX、getTranslationX意义的文字描述

具体参看博客:http://blog.csdn.net/dmk877/article/details/51550031

event.getX():表示的是触摸的点距离自身左边界的距离
event.getY():表示的是触摸的点距离自身上边界的距离
event.getRawX:表示的是触摸点距离屏幕左边界的距离
event.getRawY:表示的是触摸点距离屏幕上边界的距离
View.getWidth():表示的是当前控件的宽度,即getRight()-getLeft()

View.getHeight():表示的是当前控件的高度,即getBottom()-getTop()
View.getTop():子View的顶部到父View顶部的距离
View.getRight():子View的右边界到父View的左边界的距离
View.getBottom():子View的底部到父View的顶部的距离
View.getLeft():子View的左边界到父View的左边界的距离
View.getTranslationX()计算的是该View在X轴的偏移量。初始值为0,向左偏移值为负,向右偏移值为正。
View.getTranslationY()计算的是该View在Y轴的偏移量。初始值为0,向上偏移为负,向下偏移为证。

4.Button中设置的drawerleft距离文字太远的解决方法:
可以通过当中的属性来设置。
Android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1”
android:text=”小鸡炖蘑菇”
android:textAlignment=”textStart”
android:layout_gravity=”start”
android:drawableLeft=”@android:drawable/ic_menu_search”
在 17 版本 之前
需要 在button中 添加
android:textAlignment=”textStart”
android:layout_gravity=”start”
在 17 版本之后
android:textAlignment=”textStart”

5.android中可以在代码中对控件设置setPadding,但是不能设置margin,根据margin的理解,需要是参考父控件的原理,所以我们不能孩子姐设置,需要通过下面的方式:
Android的view中有setPadding,但是没有直接的setMargin方法。如果要在代码中设置该怎么做呢?

可以通过设置view里面的 LayoutParams 设置,而这个LayoutParams是根据该view在不同的GroupView而不同的。

ImageView image = (ImageView) findViewById(R.id.main_image);  RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(image.getLayoutParams());  //这个具体是看当前控件的父控件是什么布局来决定的lp.setMargins(50, 100, 0, 0);  image.setLayoutParams(lp);  

这里的RelativeLayout是说明该view在一个RelativeLayout里面。

可以把设置margin的方式封装成方法,只要是GroupView里面的LayoutParams 即可。

[java] view plain copy

public static void setMargins (View v, int l, int t, int r, int b) {      if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {          ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();          p.setMargins(l, t, r, b);          v.requestLayout();      }  }  

顺便说下
Padding 为内边框,指该控件内部内容,如文本/图片距离该控件的边距

Margin 为外边框,指该控件距离边父控件的边距
6.用newInstance与用new是区别的,区别在于创建对象的方式不一样,前者是使用类加载机制,那么为什么会有两种创建对象方式?这个就要从可伸缩、可扩展,可重用等软件思想上解释了。Java中工厂模式经常使用newInstance来创建对象,因此从为什么要使用工厂模式上也可以找到具体答案。
例如:
Class c = Class.forName(“A”);
factory = (AInterface)c.newInstance();
其中AInterface是A的接口,如果下面这样写,你可能会理解:
String className = “A”;
Class c = Class.forName(className);
factory = (AInterface)c.newInstance();
进一步,如果下面写,你可能会理解:
String className = readfromXMlConfig;//从xml 配置文件中获得字符串
Class c = Class.forName(className);factory = (AInterface)c.newInstance();
上面代码就消灭了A类名称,优点:无论A类怎么变化,上述代码不变,甚至可以更换A的兄弟类B , C , D….等,只要他们继承Ainterface就可以。
从jvm的角度看,我们使用new的时候,这个要new的类可以没有加载;
但是使用newInstance时候,就必须保证:1、这个类已经加载;2、这个类已经连接了。而完成上面两个步骤的正是class的静态方法forName()方法,这个静态方法调用了启动类加载器(就是加载javaAPI的那个加载器)。
有了上面jvm上的理解,那么我们可以这样说,newInstance实际上是把new这个方式分解为两步,即,首先调用class的加载方法加载某个类,然后实例化。
这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了我们降耦的手段。

[补充:]
newInstance: 弱类型。低效率。只能调用无参构造。
new: 强类型。相对高效。能调用任何public构造。
newInstance()是实现IOC、反射、依赖倒置 等技术方法的必然选择,new 只能实现具体类的实例化,不适合于接口编程。类里面就是通过这个类的默认构造函数构建了一个对象,如果没有默认构造函数就抛出InstantiationException, 如果没有访问默认构造函数的权限就抛出IllegalAccessException

7.startActivityForResult与startActivity的不同之处在于:
1、startActivity( )
仅仅是跳转到目标页面,若是想跳回当前页面,则必须再使用一次startActivity( )。
2、startActivityForResult( )
可以一次性完成这项任务,当程序执行到这段代码的时候,假若从T1Activity跳转到下一个Text2Activity,而当这个Text2Activity调用了finish()方法以后,程序会自动跳转回T1Activity,并调用前一个T1Activity中的onActivityResult( )方法。

相关函数:
startActivityForResult(Intent intent, Int requestCode)
setResut(int resultCode, Intent intent)
onActivityResult(int requestCode, int resultCode, Intent intent)

简单例子介绍:

1.跳转的时候不是采用startActivity(intent) 这个方法,而是startActivityForResult(intent, 0)
复制代码

Intent intent=new Intent();
intent.setClass(A.this, B.class);
Bundle bundle=new Bundle();
String str1=”aaaaaa”;
bundle.putString(“str1”, str1);
intent.putExtras(bundle);
startActivityForResult(intent, 0);//这里采用startActivityForResult来做跳转,此处的0为一个依据,可以写其他的值,但一定要>=0

复制代码

2.重写onActivityResult方法,用来接收B回传的数据。
复制代码

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (resultCode) { //resultCode为回传的标记,我在B中回传的是RESULT_OK
case RESULT_OK:
Bundle b=data.getExtras(); //data为B中回传的Intent
String str=b.getString(“str1”);//str即为回传的值
break;
default:
break;
}
}

复制代码

3.在B中回传数据时采用setResult方法,并且之后要调用finish方法。

setResult(RESULT_OK, intent); //intent为A传来的带有Bundle的intent,当然也可以自己定义新的Bundle
finish();//此处一定要调用finish()方法

Android activity的setResult()在什么时候调用(重点也是难点)

如果在startActivityForResult起来的Activity里面设置setResult,结果并不会马上返回给parent的Activity,只有当前Activity被finish,结果才会被发送给parent的onActivityResult去处理!

如果一个activity要返回数据到启动它的那个activity,可以调用setResult()方法。那什么时候去调用setResult()方法返回数据呢?
看一下源码就明白了:
复制代码

public final void setResult(int resultCode, Intent data) {
synchronized (this) {
mResultCode = resultCode;
mResultData = data;
}
}

public void finish() {    if (mParent == null) {        int resultCode;        Intent resultData;        synchronized (this) {            resultCode = mResultCode;            resultData = mResultData;        }        if (Config.LOGV) Log.v(TAG, "Finishing self: token=" + mToken);        try {            if (ActivityManagerNative.getDefault()                .finishActivity(mToken, resultCode, resultData)) {                mFinished = true;            }        } catch (RemoteException e) {            // Empty        }    } else {        mParent.finishFromChild(this);    }}

复制代码

这段代码可以看出activity返回result是在被finish的时候,也就是说调用setResult()方法必须在finish()之前。
那么如果在如下方法中调用setResult()也有可能不会返回成功: onPause(), onStop(), onDestroy(),
因为这些方法调用不一定是在finish之前的,当然在onCreate()就调用setResult肯定是在finish之前的

按BACK键从一个Activity退出来的,一按BACK,android就会自动调用Activity的finish()方法,然后设置resultCode为RESULT_CANCELED,也就不会返回任何数据了 .
解决方法就是在Activity里面捕获按BACK的事件,捕获到之后先setResult,然后自己来调用finish,就搞定了……把BACK事件直接自己给吞了
复制代码

@Override
public void onBackPressed() {
Log.i(TAG, “onBackPressed”);
setResult(Const.LIVE_OK);
super.onBackPressed();
}

复制代码

当然还可以在onCreate()就调用setResult,不过我觉得这种方法没有重写onBackPressed()方法好.

8.从路径中提取文件名:
public String getFileName(String pathandname){

    int start=pathandname.lastIndexOf("/");        int end=pathandname.lastIndexOf(".");        if(start!=-1 && end!=-1){            return pathandname.substring(start+1,end);          }else{            return null;        }    }   

9.Bitmap:

 Bitmap是Android系统中的图像处理的最重要类之一。用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。

重要函数

 public void recycle() // 回收位图占用的内存空间,把位图标记为Dead public final boolean isRecycled() //判断位图内存是否已释放   public final int getWidth()//获取位图的宽度  public final int getHeight()//获取位图的高度 public final boolean isMutable()//图片是否可修改  public int getScaledWidth(Canvas canvas)//获取指定密度转换后的图像的宽度  public int getScaledHeight(Canvas canvas)//获取指定密度转换后的图像的高度 public boolean compress(CompressFormat format, int quality, OutputStream stream)//按指定的图片格式以及画质,将图片转换为输出流。 format:Bitmap.CompressFormat.PNG或Bitmap.CompressFormat.JPEG quality:画质,0-100.0表示最低画质压缩,100以最高画质压缩。对于PNG等无损格式的图片,会忽略此项设置。public static Bitmap createBitmap(Bitmap src) //以src为原图生成不可变得新图像 public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)//以src为原图,创建新的图像,指定新图像的高宽以及是否可变。 public static Bitmap createBitmap(int width, int height, Config config)——创建指定格式、大小的位图 public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height)以source为原图,创建新的图片,指定起始坐标以及新图像的高宽。

BitmapFactory工厂类:
Option 参数类:

public boolean inJustDecodeBounds//如果设置为true,不获取图片,不分配内存,但会返回图片的高度宽度信息。public int inSampleSize//图片缩放的倍数public int outWidth//获取图片的宽度值public int outHeight//获取图片的高度值 public int inDensity//用于位图的像素压缩比 public int inTargetDensity//用于目标位图的像素压缩比(要生成的位图) public byte[] inTempStorage //创建临时文件,将图片存储public boolean inScaled//设置为true时进行图片压缩,从inDensity到inTargetDensitypublic boolean inDither //如果为true,解码器尝试抖动解码public Bitmap.Config inPreferredConfig //设置解码器public String outMimeType //设置解码图像public boolean inPurgeable//当存储Pixel的内存空间在系统内存不足时是否可以被回收public boolean inInputShareable //inPurgeable为true情况下才生效,是否可以共享一个InputStreampublic boolean inPreferQualityOverSpeed  //为true则优先保证Bitmap质量其次是解码速度public boolean inMutable //配置Bitmap是否可以更改,比如:在Bitmap上隔几个像素加一条线段public int inScreenDensity //当前屏幕的像素密度

工厂方法:

public static Bitmap decodeFile(String pathName, Options opts) //从文件读取图片 public static Bitmap decodeFile(String pathName)public static Bitmap decodeStream(InputStream is) //从输入流读取图片public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)public static Bitmap decodeResource(Resources res, int id) //从资源文件读取图片public static Bitmap decodeResource(Resources res, int id, Options opts) public static Bitmap decodeByteArray(byte[] data, int offset, int length) //从数组读取图片public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)public static Bitmap decodeFileDescriptor(FileDescriptor fd)//从文件读取文件 与decodeFile不同的是这个直接调用JNI函数进行读取 效率比较高public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

Bitmap.Config inPreferredConfig :

 枚举变量 (位图位数越高代表其可以存储的颜色信息越多,图像越逼真,占用内存越大)public static final Bitmap.Config ALPHA_8 //代表8位Alpha位图        每个像素占用1byte内存public static final Bitmap.Config ARGB_4444 //代表16位ARGB位图  每个像素占用2byte内存public static final Bitmap.Config ARGB_8888 //代表32位ARGB位图  每个像素占用4byte内存public static final Bitmap.Config RGB_565 //代表8位RGB位图          每个像素占用2byte内存 Android中一张图片(BitMap)占用的内存主要和以下几个因数有关:图片长度,图片宽度,单位像素占用的字节数。一张图片(BitMap)占用的内存=图片长度*图片宽度*单位像素占用的字节数

图片读取实例:
1.)从文件读取方式一
复制代码

/**
* 获取缩放后的本地图片
*
* @param filePath 文件路径
* @param width 宽
* @param height 高
* @return
*/
public static Bitmap readBitmapFromFile(String filePath, int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
int inSampleSize = 1;

    if (srcHeight > height || srcWidth > width) {        if (srcWidth > srcHeight) {            inSampleSize = Math.round(srcHeight / height);        } else {            inSampleSize = Math.round(srcWidth / width);        }    }    options.inJustDecodeBounds = false;    options.inSampleSize = inSampleSize;    return BitmapFactory.decodeFile(filePath, options);}

复制代码
2.)从文件读取方式二 效率高于方式一
复制代码

/**
* 获取缩放后的本地图片
*
* @param filePath 文件路径
* @param width 宽
* @param height 高
* @return
*/
public static Bitmap readBitmapFromFileDescriptor(String filePath, int width, int height) {
try {
FileInputStream fis = new FileInputStream(filePath);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {            if (srcWidth > srcHeight) {                inSampleSize = Math.round(srcHeight / height);            } else {                inSampleSize = Math.round(srcWidth / width);            }        }        options.inJustDecodeBounds = false;        options.inSampleSize = inSampleSize;        return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);    } catch (Exception ex) {    }    return null;}

复制代码

测试同样生成10张图片两种方式耗时比较 cpu使用以及内存占用两者相差无几 第二种方式效率高一点 所以建议优先采用第二种方式
复制代码

  start = System.currentTimeMillis();    for (int i = 0; i < testMaxCount; i++) {        BitmapUtils.readBitmapFromFile(filePath, 400, 400);    }    end = System.currentTimeMillis();    Log.e(TAG, "BitmapFactory decodeFile--time-->" + (end - start));    start = System.currentTimeMillis();    for (int i = 0; i < testMaxCount; i++) {       BitmapUtils.readBitmapFromFileDescriptor(filePath, 400, 400);    }    end = System.currentTimeMillis();    Log.e(TAG, "BitmapFactory decodeFileDescriptor--time-->" + (end - start));

复制代码

3.)从输入流中读取文件
复制代码

/**
* 获取缩放后的本地图片
*
* @param ins 输入流
* @param width 宽
* @param height 高
* @return
*/
public static Bitmap readBitmapFromInputStream(InputStream ins, int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(ins, null, options);
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
int inSampleSize = 1;

    if (srcHeight > height || srcWidth > width) {        if (srcWidth > srcHeight) {            inSampleSize = Math.round(srcHeight / height);        } else {            inSampleSize = Math.round(srcWidth / width);        }    }    options.inJustDecodeBounds = false;    options.inSampleSize = inSampleSize;    return BitmapFactory.decodeStream(ins, null, options);}

复制代码
4.)从资源文件中读取文件
复制代码

public static Bitmap readBitmapFromResource(Resources resources, int resourcesId, int width, int height) {    BitmapFactory.Options options = new BitmapFactory.Options();    options.inJustDecodeBounds = true;    BitmapFactory.decodeResource(resources, resourcesId, options);    float srcWidth = options.outWidth;    float srcHeight = options.outHeight;    int inSampleSize = 1;    if (srcHeight > height || srcWidth > width) {        if (srcWidth > srcHeight) {            inSampleSize = Math.round(srcHeight / height);        } else {            inSampleSize = Math.round(srcWidth / width);        }    }    options.inJustDecodeBounds = false;    options.inSampleSize = inSampleSize;    return BitmapFactory.decodeResource(resources, resourcesId, options);}

复制代码

此种方式相当的耗费内存 建议采用decodeStream代替decodeResource 可以如下形式
复制代码

public static Bitmap readBitmapFromResource(Resources resources, int resourcesId, int width, int height) {    InputStream ins = resources.openRawResource(resourcesId);    BitmapFactory.Options options = new BitmapFactory.Options();    options.inJustDecodeBounds = true;    BitmapFactory.decodeStream(ins, null, options);    float srcWidth = options.outWidth;    float srcHeight = options.outHeight;    int inSampleSize = 1;    if (srcHeight > height || srcWidth > width) {        if (srcWidth > srcHeight) {            inSampleSize = Math.round(srcHeight / height);        } else {            inSampleSize = Math.round(srcWidth / width);        }    }    options.inJustDecodeBounds = false;    options.inSampleSize = inSampleSize;    return BitmapFactory.decodeStream(ins, null, options);}

复制代码

decodeStream、decodeResource占用内存对比:
复制代码

start = System.currentTimeMillis();
for (int i = 0; i < testMaxCount; i++) {
BitmapUtils.readBitmapFromResource(getResources(), R.mipmap.ic_app_center_banner, 400, 400);
Log.e(TAG, “BitmapFactory decodeResource–num–>” + i);
}
end = System.currentTimeMillis();
Log.e(TAG, “BitmapFactory decodeResource–time–>” + (end - start));

    start = System.currentTimeMillis();    for (int i = 0; i < testMaxCount; i++) {        BitmapUtils.readBitmapFromResource1(getResources(), R.mipmap.ic_app_center_banner, 400, 400);        Log.e(TAG, "BitmapFactory decodeStream--num-->" + i);    }    end = System.currentTimeMillis();    Log.e(TAG, "BitmapFactory decodeStream--time-->" + (end - start));

复制代码

BitmapFactory.decodeResource 加载的图片可能会经过缩放,该缩放目前是放在 java 层做的,效率比较低,而且需要消耗 java 层的内存。因此,如果大量使用该接口加载图片,容易导致OOM错误

BitmapFactory.decodeStream 不会对所加载的图片进行缩放,相比之下占用内存少,效率更高。

这两个接口各有用处,如果对性能要求较高,则应该使用 decodeStream;如果对性能要求不高,且需要 Android 自带的图片自适应缩放功能,则可以使用 decodeResource。
5. )从二进制数据读取图片
复制代码

public static Bitmap readBitmapFromByteArray(byte[] data, int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, options);
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
int inSampleSize = 1;

    if (srcHeight > height || srcWidth > width) {        if (srcWidth > srcHeight) {            inSampleSize = Math.round(srcHeight / height);        } else {            inSampleSize = Math.round(srcWidth / width);        }    }    options.inJustDecodeBounds = false;    options.inSampleSize = inSampleSize;    return BitmapFactory.decodeByteArray(data, 0, data.length, options);}

复制代码
6.)从assets文件读取图片
复制代码

/**
* 获取缩放后的本地图片
*
* @param filePath 文件路径
* @return
*/
public static Bitmap readBitmapFromAssetsFile(Context context, String filePath) {
Bitmap image = null;
AssetManager am = context.getResources().getAssets();
try {
InputStream is = am.open(filePath);
image = BitmapFactory.decodeStream(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return image;
}

复制代码
图片保存文件:
复制代码

public static void writeBitmapToFile(String filePath, Bitmap b, int quality) {    try {        File desFile = new File(filePath);        FileOutputStream fos = new FileOutputStream(desFile);        BufferedOutputStream bos = new BufferedOutputStream(fos);        b.compress(Bitmap.CompressFormat.JPEG, quality, bos);        bos.flush();        bos.close();    } catch (IOException e) {        e.printStackTrace();    }}

复制代码
图片压缩:
复制代码

private static Bitmap compressImage(Bitmap image) {    if (image == null) {        return null;    }    ByteArrayOutputStream baos = null;    try {        baos = new ByteArrayOutputStream();        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);        byte[] bytes = baos.toByteArray();        ByteArrayInputStream isBm = new ByteArrayInputStream(bytes);        Bitmap bitmap = BitmapFactory.decodeStream(isBm);        return bitmap;    } catch (OutOfMemoryError e) {    } finally {        try {            if (baos != null) {                baos.close();            }        } catch (IOException e) {        }    }    return null;}

复制代码
图片缩放:
复制代码

/** * 根据scale生成一张图片 * * @param bitmap * @param scale  等比缩放值 * @return */public static Bitmap bitmapScale(Bitmap bitmap, float scale) {    Matrix matrix = new Matrix();    matrix.postScale(scale, scale); // 长和宽放大缩小的比例    Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);    return resizeBmp;}

复制代码
获取图片旋转角度:
复制代码

/**
* 读取照片exif信息中的旋转角度
*
* @param path 照片路径
* @return角度
*/
private static int readPictureDegree(String path) {
if (TextUtils.isEmpty(path)) {
return 0;
}
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (Exception e) {
}
return degree;
}

复制代码
图片旋转角度:
复制代码

private static Bitmap rotateBitmap(Bitmap b, float rotateDegree) {    if (b == null) {        return null;    }    Matrix matrix = new Matrix();    matrix.postRotate(rotateDegree);    Bitmap rotaBitmap = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true);    return rotaBitmap;}

复制代码
图片转二进制:

public byte[] bitmap2Bytes(Bitmap bm) {    ByteArrayOutputStream baos = new ByteArrayOutputStream();    bm.compress(Bitmap.CompressFormat.PNG, 100, baos);    return baos.toByteArray();}

Bitmap转Drawable

public static Drawable bitmapToDrawable(Resources resources, Bitmap bm) {
Drawable drawable = new BitmapDrawable(resources, bm);
return drawable;
}

Drawable转Bitmap
复制代码

public static Bitmap drawableToBitmap(Drawable drawable) {    Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);    Canvas canvas = new Canvas(bitmap);    drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());    drawable.draw(canvas);    return bitmap;}

复制代码
Drawable、Bitmap占用内存探讨

之前一直使用过Afinal 和Xutils 熟悉这两框架的都知道,两者出自同一人,Xutils是Afina的升级版,AFinal中的图片内存缓存使用的是Bitmap 而后来为何Xutils将内存缓存的对象改成了Drawable了呢?我们一探究竟

写个测试程序:
复制代码

    List<Bitmap> bitmaps = new ArrayList<>();    start = System.currentTimeMillis();    for (int i = 0; i < testMaxCount; i++) {        Bitmap bitmap = BitmapUtils.readBitMap(this, R.mipmap.ic_app_center_banner);        bitmaps.add(bitmap);        Log.e(TAG, "BitmapFactory Bitmap--num-->" + i);    }    end = System.currentTimeMillis();    Log.e(TAG, "BitmapFactory Bitmap--time-->" + (end - start));    List<Drawable> drawables = new ArrayList<>();    start = System.currentTimeMillis();    for (int i = 0; i < testMaxCount; i++) {        Drawable drawable = getResources().getDrawable(R.mipmap.ic_app_center_banner);        drawables.add(drawable);        Log.e(TAG, "BitmapFactory Drawable--num-->" + i);    }    end = System.currentTimeMillis();    Log.e(TAG, "BitmapFactory Drawable--time-->" + (end - start));

复制代码

测试数据1000 同一张图片

Bitmap 直接70条数据的时候挂掉

Drawable 轻松1000条数据通过

从测试说明Drawable 相对Bitmap有很大的内存占用优势。这也是为啥现在主流的图片缓存框架内存缓存那一层采用Drawable作为缓存对象的原因。

小结:

图片处理就暂时学习到这里,以后再做补充。

干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!

9.自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fragment谈上关系,做什么都要问下Fragment能实现不~哈哈,是不是有点过~

本篇博客力求为大家说明Fragment如何产生,什么是Fragment,Fragment生命周期,如何静态和动态的使用Fragment,Fragment回退栈,Fragment事务;以及Fragment的一些特殊用途,例如:没有布局的Fragment有何用处?Fragment如何与Activity交互?Fragment如何创建对话框?Fragment如何与ActionBar集成等等。
1、Fragment的产生与介绍

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

嘿嘿,终于到使用的时刻了~~

这是使用Fragment最简单的一种方式,把Fragment当成普通的控件,直接写在Activity的布局文件中。步骤:

1、继承Fragment,重写onCreateView决定Fragemnt的布局

2、在Activity中声明此Fragment,就当和普通的View一样

下面展示一个例子(我使用2个Fragment作为Activity的布局,一个Fragment用于标题布局,一个Fragment用于内容布局):

TitleFragment的布局文件:

[html] view plain copy

<?xml version="1.0" encoding="utf-8"?>  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="match_parent"      android:layout_height="45dp"      android:background="@drawable/title_bar" >      <ImageButton          android:id="@+id/id_title_left_btn"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:layout_centerVertical="true"          android:layout_marginLeft="3dp"          android:background="@drawable/showleft_selector" />      <TextView          android:layout_width="fill_parent"          android:layout_height="fill_parent"          android:gravity="center"          android:text="我不是微信"          android:textColor="#fff"          android:textSize="20sp"          android:textStyle="bold" />  </RelativeLayout>  

TitleFragment

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View;  import android.view.View.OnClickListener;  import android.view.ViewGroup;  import android.widget.ImageButton;  import android.widget.Toast;  public class TitleFragment extends Fragment  {      private ImageButton mLeftMenu;      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          View view = inflater.inflate(R.layout.fragment_title, container, false);          mLeftMenu = (ImageButton) view.findViewById(R.id.id_title_left_btn);          mLeftMenu.setOnClickListener(new OnClickListener()          {              @Override              public void onClick(View v)              {                  Toast.makeText(getActivity(),                          "i am an ImageButton in TitleFragment ! ",                          Toast.LENGTH_SHORT).show();              }          });          return view;      }  }  

同理还有ContentFragment的其布局文件:

[html] view plain copy

<?xml version="1.0" encoding="utf-8"?>  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:orientation="vertical" >      <TextView          android:layout_width="fill_parent"          android:layout_height="fill_parent"          android:gravity="center"          android:text="使用Fragment做主面板"          android:textSize="20sp"          android:textStyle="bold" />  </LinearLayout>  

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  public class ContentFragment extends Fragment  {      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          return inflater.inflate(R.layout.fragment_content, container, false);      }  }  

MainActivity

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Activity;  import android.os.Bundle;  import android.view.Window;  public class MainActivity extends Activity  {      @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          requestWindowFeature(Window.FEATURE_NO_TITLE);          setContentView(R.layout.activity_main);      }  }  

Activity的布局文件:

[java] view plain copy

<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" >      <fragment          android:id="@+id/id_fragment_title"          android:name="com.zhy.zhy_fragments.TitleFragment"          android:layout_width="fill_parent"          android:layout_height="45dp" />      <fragment          android:layout_below="@id/id_fragment_title"          android:id="@+id/id_fragment_content"          android:name="com.zhy.zhy_fragments.ContentFragment"          android:layout_width="fill_parent"          android:layout_height="fill_parent" />  </RelativeLayout>  

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

4、动态的使用Fragment

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

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

[html] view plain copy

<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" >      <fragment          android:id="@+id/id_fragment_title"          android:name="com.zhy.zhy_fragments.TitleFragment"          android:layout_width="fill_parent"          android:layout_height="45dp" />      <include          android:id="@+id/id_ly_bottombar"          android:layout_width="fill_parent"          android:layout_height="55dp"          android:layout_alignParentBottom="true"          layout="@layout/bottombar" />      <FrameLayout          android:id="@+id/id_content"          android:layout_width="fill_parent"          android:layout_height="fill_parent"          android:layout_above="@id/id_ly_bottombar"          android:layout_below="@id/id_fragment_title" />  </RelativeLayout>  

底部四个按钮的布局就不贴了,到时看效果图就明白了~~

下面主Activity

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Activity;  import android.app.FragmentManager;  import android.app.FragmentTransaction;  import android.os.Bundle;  import android.view.View;  import android.view.View.OnClickListener;  import android.view.Window;  import android.widget.LinearLayout;  public class MainActivity extends Activity implements OnClickListener  {      private LinearLayout mTabWeixin;      private LinearLayout mTabFriend;      private ContentFragment mWeixin;      private FriendFragment mFriend;      @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          requestWindowFeature(Window.FEATURE_NO_TITLE);          setContentView(R.layout.activity_main);          // 初始化控件和声明事件          mTabWeixin = (LinearLayout) findViewById(R.id.tab_bottom_weixin);          mTabFriend = (LinearLayout) findViewById(R.id.tab_bottom_friend);          mTabWeixin.setOnClickListener(this);          mTabFriend.setOnClickListener(this);          // 设置默认的Fragment          setDefaultFragment();      }      private void setDefaultFragment()      {          FragmentManager fm = getFragmentManager();          FragmentTransaction transaction = fm.beginTransaction();          mWeixin = new ContentFragment();          transaction.replace(R.id.id_content, mWeixin);          transaction.commit();      }      @Override      public void onClick(View v)      {          FragmentManager fm = getFragmentManager();          // 开启Fragment事务          FragmentTransaction transaction = fm.beginTransaction();          switch (v.getId())          {          case R.id.tab_bottom_weixin:              if (mWeixin == null)              {                  mWeixin = new ContentFragment();              }              // 使用当前Fragment的布局替代id_content的控件              transaction.replace(R.id.id_content, mWeixin);              break;          case R.id.tab_bottom_friend:              if (mFriend == null)              {                  mFriend = new FriendFragment();              }              transaction.replace(R.id.id_content, mFriend);              break;          }          // transaction.addToBackStack();          // 事务提交          transaction.commit();      }  }  

可以看到我们使用FragmentManager对Fragment进行了动态的加载,这里使用的是replace方法~~下一节我会详细介绍FragmentManager的常用API。

注:如果使用Android3.0以下的版本,需要引入v4的包,然后Activity继承FragmentActivity,然后通过getSupportFragmentManager获得FragmentManager。不过还是建议版Menifest文件的uses-sdk的minSdkVersion和targetSdkVersion都改为11以上,这样就不必引入v4包了。

代码中间还有两个Fragment的子类,ContentFragment上面已经见过,FriendFragment其实类似:

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  public class FriendFragment extends Fragment  {      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          return inflater.inflate(R.layout.fragment_friend, container, false);      }  }  

效果图:

可以看到很好的实现了效果,其实这个效果以前的博客中也出现过,在博客:Android项目Tab类型主界面大总结 Fragment+TabPageIndicator+ViewPager,有兴趣可以看看。ps:为了代码的简洁,就不添加按钮的点击变化什么的了,主要讲解功能了~~~
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。

上述已经介绍完成了Fragment常用的一些方法,相信看完,大家一定清楚了Fragment的产生理由,以及如何使用Fragment,再根据API的讲解,也能明白,曾经为何觉得Fragment会出现一些列乱七八槽的问题,终究是因为没有弄清楚其生命周期。

由于篇幅原因,剩下的内容留到下一篇了。在下一篇,会介绍:

1、如何管理Fragment回退栈

2、Fragment如何与Activity交互

3、Fragment与Activity交互的最佳实践

4、没有视图的Fragment的用处

5、使用Fragment创建对话框

6、如何与ActionBar,MenuItem集成等~~

本篇将介绍上篇博客提到的:如何管理Fragment回退栈,Fragment如何与Activity交互,Fragment与Activity交互的最佳实践,没有视图的Fragment的用处,使用Fragment创建对话框,如何与ActionBar,MenuItem集成等~~
1、管理Fragment回退栈

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

看这样一个效果图:

点击第一个按钮,切换到第二个界面,点击第二个按钮,切换到第三个界面,然后点击Back键依次回退。这像不像初学android时的Activity跳转,当然了,这里肯定不是,不然我就跪了。这里是Fragment实现的,用户点击Back,实际是Fragment回退栈不断的弹栈。

如何添加一个Fragment事务到回退栈:

FragmentTransaction.addToBackStack(String)

下面讲解代码:很明显一共是3个Fragment和一个Activity.

先看Activity的布局文件:

[html] view plain copy

<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" >      <FrameLayout          android:id="@+id/id_content"          android:layout_width="fill_parent"          android:layout_height="fill_parent" >      </FrameLayout>  </RelativeLayout>  

不同的Fragment就在这个FrameLayout中显示。

MainActivity.java

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Activity;  import android.app.FragmentManager;  import android.app.FragmentTransaction;  import android.os.Bundle;  import android.view.Window;  public class MainActivity extends Activity  {      @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          requestWindowFeature(Window.FEATURE_NO_TITLE);          setContentView(R.layout.activity_main);          FragmentManager fm = getFragmentManager();          FragmentTransaction tx = fm.beginTransaction();          tx.add(R.id.id_content, new FragmentOne(),"ONE");          tx.commit();      }  }  

很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.

下面是FragmentOne

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.app.FragmentManager;  import android.app.FragmentTransaction;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View;  import android.view.View.OnClickListener;  import android.view.ViewGroup;  import android.widget.Button;  public class FragmentOne extends Fragment implements OnClickListener  {      private Button mBtn;      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          View view = inflater.inflate(R.layout.fragment_one, container, false);          mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);          mBtn.setOnClickListener(this);          return view;      }      @Override      public void onClick(View v)      {          FragmentTwo fTwo = new FragmentTwo();          FragmentManager fm = getFragmentManager();          FragmentTransaction tx = fm.beginTransaction();          tx.replace(R.id.id_content, fTwo, "TWO");          tx.addToBackStack(null);          tx.commit();      }  }  

我们在点击FragmentOne中的按钮时,使用了replace方法,如果你看了前一篇博客,一定记得replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用tx.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,证据就是:仔细看上面的效果图,我们在跳转前在文本框输入的内容,在用户Back得到第一个界面的时候不见了。

接下来FragmentTwo

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.app.FragmentManager;  import android.app.FragmentTransaction;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View;  import android.view.View.OnClickListener;  import android.view.ViewGroup;  import android.widget.Button;  public class FragmentTwo extends Fragment implements OnClickListener  {      private Button mBtn ;      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          View view = inflater.inflate(R.layout.fragment_two, container, false);          mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);          mBtn.setOnClickListener(this);          return view ;       }      @Override      public void onClick(View v)      {          FragmentThree fThree = new FragmentThree();          FragmentManager fm = getFragmentManager();          FragmentTransaction tx = fm.beginTransaction();          tx.hide(this);          tx.add(R.id.id_content , fThree, "THREE");  //      tx.replace(R.id.id_content, fThree, "THREE");          tx.addToBackStack(null);          tx.commit();      }  }  

这里点击时,我们没有使用replace,而是先隐藏了当前的Fragment,然后添加了FragmentThree的实例,最后将事务添加到回退栈。这样做的目的是为了给大家提供一种方案:如果不希望视图重绘该怎么做,请再次仔细看效果图,我们在FragmentTwo的EditText填写的内容,用户Back回来时,数据还在~~~

最后FragmentThree就是简单的Toast了:

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View;  import android.view.View.OnClickListener;  import android.view.ViewGroup;  import android.widget.Button;  import android.widget.Toast;  public class FragmentThree extends Fragment implements OnClickListener  {      private Button mBtn;      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          View view = inflater.inflate(R.layout.fragment_three, container, false);          mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);          mBtn.setOnClickListener(this);          return view;      }      @Override      public void onClick(View v)      {          Toast.makeText(getActivity(), " i am a btn in Fragment three",                  Toast.LENGTH_SHORT).show();      }  }  

好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。

这里极其注意一点:上面的整体代码不具有任何参考价值,纯粹为了显示回退栈,在后面讲解了Fragment与Activity通信以后,会重构上面的代码!
2、Fragment与Activity通信

因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。
3、Fragment与Activity通信的最佳实践

因为要考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。

下面我通过两种方式的代码,分别重构,FragmentOne和FragmentTwo的点击事件,以及Activity对点击事件的响应:

首先看FragmentOne

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View;  import android.view.View.OnClickListener;  import android.view.ViewGroup;  import android.widget.Button;  public class FragmentOne extends Fragment implements OnClickListener  {      private Button mBtn;      /**      * 设置按钮点击的回调      * @author zhy      *      */      public interface FOneBtnClickListener      {          void onFOneBtnClick();      }      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          View view = inflater.inflate(R.layout.fragment_one, container, false);          mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);          mBtn.setOnClickListener(this);          return view;      }      /**      * 交给宿主Activity处理,如果它希望处理      */      @Override      public void onClick(View v)      {          if (getActivity() instanceof FOneBtnClickListener)          {              ((FOneBtnClickListener) getActivity()).onFOneBtnClick();          }      }  }  

可以看到现在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;并且我们声明了一个接口,来回调其点击事件,想要管理其点击事件的Activity实现此接口就即可。可以看到我们在onClick中首先判断了当前绑定的Activity是否实现了该接口,如果实现了则调用。

再看FragmentTwo

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View;  import android.view.View.OnClickListener;  import android.view.ViewGroup;  import android.widget.Button;  public class FragmentTwo extends Fragment implements OnClickListener  {      private Button mBtn ;      private FTwoBtnClickListener fTwoBtnClickListener ;      public interface FTwoBtnClickListener      {          void onFTwoBtnClick();      }      //设置回调接口      public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)      {          this.fTwoBtnClickListener = fTwoBtnClickListener;      }      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          View view = inflater.inflate(R.layout.fragment_two, container, false);          mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);          mBtn.setOnClickListener(this);          return view ;       }      @Override      public void onClick(View v)      {          if(fTwoBtnClickListener != null)          {              fTwoBtnClickListener.onFTwoBtnClick();          }      }  }  

与FragmentOne极其类似,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。

最后看Activity :

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Activity;  import android.app.FragmentManager;  import android.app.FragmentTransaction;  import android.os.Bundle;  import android.view.Window;  import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;  import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;  public class MainActivity extends Activity implements FOneBtnClickListener,          FTwoBtnClickListener  {      private FragmentOne mFOne;      private FragmentTwo mFTwo;      private FragmentThree mFThree;      @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          requestWindowFeature(Window.FEATURE_NO_TITLE);          setContentView(R.layout.activity_main);          mFOne = new FragmentOne();          FragmentManager fm = getFragmentManager();          FragmentTransaction tx = fm.beginTransaction();          tx.add(R.id.id_content, mFOne, "ONE");          tx.commit();      }      /**      * FragmentOne 按钮点击时的回调      */      @Override      public void onFOneBtnClick()      {          if (mFTwo == null)          {              mFTwo = new FragmentTwo();              mFTwo.setfTwoBtnClickListener(this);          }          FragmentManager fm = getFragmentManager();          FragmentTransaction tx = fm.beginTransaction();          tx.replace(R.id.id_content, mFTwo, "TWO");          tx.addToBackStack(null);          tx.commit();      }      /**      * FragmentTwo 按钮点击时的回调      */      @Override      public void onFTwoBtnClick()      {          if (mFThree == null)          {              mFThree = new FragmentThree();          }          FragmentManager fm = getFragmentManager();          FragmentTransaction tx = fm.beginTransaction();          tx.hide(mFTwo);          tx.add(R.id.id_content, mFThree, "THREE");          // tx.replace(R.id.id_content, fThree, "THREE");          tx.addToBackStack(null);          tx.commit();      }  }  

代码重构结束,与开始的效果一模一样。上面两种通信方式都是值得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然Fragment和Activity可以通过getActivity与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。

4、如何处理运行时配置发生变化

运行时配置发生变化,最常见的就是屏幕发生旋转,如果你不知道如何处理屏幕变化可以参考:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

这里提一下:很多人觉得强制设置屏幕的方向就可以了,但是有一点,当你的应用被至于后台(例如用户点击了home),长时间没有返回的时候,你的应用也会被重新启动。比如上例:如果你把上面的例子你至于FragmentThree界面,然后处于后台状态,长时间后你会发现当你再次通过home打开时,上面FragmentThree与FragmentOne叠加在一起,这就是因为你的Activity重新启动,在原来的FragmentThree上又绘制了一个FragmentOne。

好了,下面看一段代码:

Activity:

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Activity;  import android.app.FragmentManager;  import android.app.FragmentTransaction;  import android.os.Bundle;  import android.view.Window;  public class MainActivity extends Activity  {      private FragmentOne mFOne;      @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          requestWindowFeature(Window.FEATURE_NO_TITLE);          setContentView(R.layout.activity_main);          mFOne = new FragmentOne();          FragmentManager fm = getFragmentManager();          FragmentTransaction tx = fm.beginTransaction();          tx.add(R.id.id_content, mFOne, "ONE");          tx.commit();      }  }  

Fragment

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.os.Bundle;  import android.util.Log;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  public class FragmentOne extends Fragment  {      private static final String TAG = "FragmentOne";      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          Log.e(TAG, "onCreateView");          View view = inflater.inflate(R.layout.fragment_one, container, false);          return view;      }      @Override      public void onCreate(Bundle savedInstanceState)      {          // TODO Auto-generated method stub          super.onCreate(savedInstanceState);          Log.e(TAG, "onCreate");      }      @Override      public void onDestroyView()      {          // TODO Auto-generated method stub          super.onDestroyView();          Log.e(TAG, "onDestroyView");      }      @Override      public void onDestroy()      {          // TODO Auto-generated method stub          super.onDestroy();          Log.e(TAG, "onDestroy");      }  }  

很简单的代码,当你运行之后,不断的旋转屏幕,你会发现每旋转一次屏幕,屏幕上就多了一个FragmentOne的实例,并且后台log会打印出许多套生命周期的回调。

类似:

[html] view plain copy

07-20 08:18:46.651: E/FragmentOne(1633): onCreate  07-20 08:18:46.651: E/FragmentOne(1633): onCreate  07-20 08:18:46.651: E/FragmentOne(1633): onCreate  07-20 08:18:46.681: E/FragmentOne(1633): onCreateView  07-20 08:18:46.831: E/FragmentOne(1633): onCreateView  07-20 08:18:46.891: E/FragmentOne(1633): onCreateView  

这是为什么呢,因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建;这样造成当旋转的时候,本身存在的Fragment会重新启动,然后当执行Activity的onCreate时,又会再次实例化一个新的Fragment,这就是出现的原因。

那么如何解决呢:

其实通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建:

默认的savedInstanceState会存储一些数据,包括Fragment的实例:通过打印可以看出:

[java] view plain copy

07-20 08:23:12.952: E/FragmentOne(1782): Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8, android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721, android:views=android.util.SparseArray@40d0af68}]}]  

所以,我们简单改一下代码,只有在savedInstanceState==null时,才进行创建Fragment实例:

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Activity;  import android.app.FragmentManager;  import android.app.FragmentTransaction;  import android.os.Bundle;  import android.util.Log;  import android.view.Window;  public class MainActivity extends Activity  {      private static final String TAG = "FragmentOne";      private FragmentOne mFOne;      @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          requestWindowFeature(Window.FEATURE_NO_TITLE);          setContentView(R.layout.activity_main);          Log.e(TAG, savedInstanceState+"");          if(savedInstanceState == null)          {              mFOne = new FragmentOne();              FragmentManager fm = getFragmentManager();              FragmentTransaction tx = fm.beginTransaction();              tx.add(R.id.id_content, mFOne, "ONE");              tx.commit();          }      }  }  

现在无论进行多次旋转都只会有一个Fragment实例在Activity中。

现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持?

其实和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。

由于篇幅原因,就不贴测试代码了。
5、Fragmeny与ActionBar和MenuItem集成

Fragment可以添加自己的MenuItem到Activity的ActionBar或者可选菜单中。

a、在Fragment的onCreate中调用 setHasOptionsMenu(true);

b、然后在Fragment子类中实现onCreateOptionsMenu

c、如果希望在Fragment中处理MenuItem的点击,也可以实现onOptionsItemSelected;当然了Activity也可以直接处理该MenuItem的点击事件。

代码:

Fragment

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Fragment;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.Menu;  import android.view.MenuInflater;  import android.view.MenuItem;  import android.view.View;  import android.view.ViewGroup;  import android.widget.Toast;  public class FragmentOne extends Fragment  {      @Override      public void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          setHasOptionsMenu(true);      }      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup container,              Bundle savedInstanceState)      {          View view = inflater.inflate(R.layout.fragment_one, container, false);          return view;      }      @Override      public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)      {          inflater.inflate(R.menu.fragment_menu, menu);      }      @Override      public boolean onOptionsItemSelected(MenuItem item)      {          switch (item.getItemId())          {          case R.id.id_menu_fra_test:              Toast.makeText(getActivity(), "FragmentMenuItem1", Toast.LENGTH_SHORT).show();              break;          }          return true;      }  }  

Activity

[java] view plain copy

package com.zhy.zhy_fragments;  import android.app.Activity;  import android.app.FragmentManager;  import android.app.FragmentTransaction;  import android.os.Bundle;  import android.util.Log;  import android.view.Menu;  import android.view.MenuItem;  import android.view.Window;  import android.widget.Toast;  public class MainActivity extends Activity  {      private static final String TAG = "FragmentOne";      private FragmentOne mFOne;      @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          requestWindowFeature(Window.FEATURE_NO_TITLE);          setContentView(R.layout.activity_main);          Log.e(TAG, savedInstanceState + "");          if (savedInstanceState == null)          {              mFOne = new FragmentOne();              FragmentManager fm = getFragmentManager();              FragmentTransaction tx = fm.beginTransaction();              tx.add(R.id.id_content, mFOne, "ONE");              tx.commit();          }      }      @Override      public boolean onCreateOptionsMenu(Menu menu)      {          super.onCreateOptionsMenu(menu);          getMenuInflater().inflate(R.menu.main, menu);          return true;      }      @Override      public boolean onOptionsItemSelected(MenuItem item)      {          switch (item.getItemId())          {          case R.id.action_settings:              Toast.makeText(this, "setting", Toast.LENGTH_SHORT).show();              return true;          default:              //如果希望Fragment自己处理MenuItem点击事件,一定不要忘了调用super.xxx              return super.onOptionsItemSelected(item);          }      }  }  

效果图:

好了,可以很好的看到,Fragment可以添加MenuItem,也可以自己处理点击~~~
6、没有布局的Fragment的作用

没有布局文件Fragment实际上是为了保存,当Activity重启时,保存大量数据准备的

请参考博客:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

7、使用Fragment创建对话框

这是Google推荐的方式,我也单独写过博客介绍,请参考:Android 官方推荐 : DialogFragment 创建对话框

好了,终于把Fragment相关的联系到一起了,上述基本包含了Fragment所有的用法~相信大家如果能够看完,一定有不少的收获~

有任何问题,欢迎留言~~~

两篇结束,相信你对Fragment已经有了一定的了解,那么在项目中的最佳实践是什么呢?请移步:Android Fragment 你应该知道的一切

10.fragment与Framelayout的区别:
FrameLayout与Fragment标签都是用来放Fragment的,区别是,Fragment标签只能设置固定的Fragment,而FrameLayout中可以通过FragmentManager更换Fragment

11.自定义View1:
转载请标明出处:http://blog.csdn.NET/lmj623565791/article/details/24252901

很多的Android入门程序猿来说对于android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章。先总结下自定义View的步骤:

1、自定义View的属性

2、在View的构造方法中获得我们自定义的属性

[ 3、重写onMesure ]

4、重写onDraw

我把3用[]标出了,所以说3不一定是必须的,当然了大部分情况下还是需要重写的。

1、自定义View的属性,首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。

[html] view plain copy

<?xml version="1.0" encoding="utf-8"?>  <resources>      <attr name="titleText" format="string" />      <attr name="titleTextColor" format="color" />      <attr name="titleTextSize" format="dimension" />      <declare-styleable name="CustomTitleView">          <attr name="titleText" />          <attr name="titleTextColor" />          <attr name="titleTextSize" />      </declare-styleable>  </resources>  

我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:

一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;不清楚的可以google一把。

然后在布局中声明我们的自定义View

[objc] view plain copy

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:tools="http://schemas.android.com/tools"      xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"      android:layout_width="match_parent"      android:layout_height="match_parent" >      <com.example.customview01.view.CustomTitleView          android:layout_width="200dp"          android:layout_height="100dp"          custom:titleText="3712"          custom:titleTextColor="#ff0000"          custom:titleTextSize="40sp" />  </RelativeLayout>  

一定要引入 xmlns:custom=”http://schemas.android.com/apk/res/com.example.customview01”我们的命名空间,后面的包路径指的是项目的package

2、在View的构造方法中,获得我们的自定义的样式

[java] view plain copy

/**      * 文本      */      private String mTitleText;      /**      * 文本的颜色      */      private int mTitleTextColor;      /**      * 文本的大小      */      private int mTitleTextSize;      /**      * 绘制时控制文本绘制的范围      */      private Rect mBound;      private Paint mPaint;      public CustomTitleView(Context context, AttributeSet attrs)      {          this(context, attrs, 0);      }      public CustomTitleView(Context context)      {          this(context, null);      }      /**      * 获得我自定义的样式属性      *       * @param context      * @param attrs      * @param defStyle      */      public CustomTitleView(Context context, AttributeSet attrs, int defStyle)      {          super(context, attrs, defStyle);          /**          * 获得我们所定义的自定义样式属性          */          TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);          int n = a.getIndexCount();          for (int i = 0; i < n; i++)          {              int attr = a.getIndex(i);              switch (attr)              {              case R.styleable.CustomTitleView_titleText:                  mTitleText = a.getString(attr);                  break;              case R.styleable.CustomTitleView_titleTextColor:                  // 默认颜色设置为黑色                  mTitleTextColor = a.getColor(attr, Color.BLACK);                  break;              case R.styleable.CustomTitleView_titleTextSize:                  // 默认设置为16sp,TypeValue也可以把sp转化为px                  mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(                          TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));                  break;              }          }          a.recycle();          /**          * 获得绘制文本的宽和高          */          mPaint = new Paint();          mPaint.setTextSize(mTitleTextSize);          // mPaint.setColor(mTitleTextColor);          mBound = new Rect();          mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);      }  

我们重写了3个构造方法,默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造调用我们的三个参数的构造,我们在三个参数的构造中获得自定义属性。

3、我们重写onDraw,onMesure调用系统提供的:

[java] view plain copy

@Override      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)      {          super.onMeasure(widthMeasureSpec, heightMeasureSpec);      }      @Override      protected void onDraw(Canvas canvas)      {          mPaint.setColor(Color.YELLOW);          canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);          mPaint.setColor(mTitleTextColor);          canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);      }  

此时的效果是:

是不是觉得还不错,基本已经实现了自定义View。但是此时如果我们把布局文件的宽和高写成wrap_content,会发现效果并不是我们的预期:

系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。

所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:

重写之前先了解MeasureSpec的specMode,一共三种类型:

EXACTLY:一般是设置了明确的值或者是MATCH_PARENT

AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT

UNSPECIFIED:表示子布局想要多大就多大,很少使用

下面是我们重写onMeasure代码:

[java] view plain copy

@Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  {      int widthMode = MeasureSpec.getMode(widthMeasureSpec);      int widthSize = MeasureSpec.getSize(widthMeasureSpec);      int heightMode = MeasureSpec.getMode(heightMeasureSpec);      int heightSize = MeasureSpec.getSize(heightMeasureSpec);      int width;      int height ;      if (widthMode == MeasureSpec.EXACTLY)      {          width = widthSize;      } else      {          mPaint.setTextSize(mTitleTextSize);          mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);          float textWidth = mBounds.width();          int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());          width = desired;      }      if (heightMode == MeasureSpec.EXACTLY)      {          height = heightSize;      } else      {          mPaint.setTextSize(mTitleTextSize);          mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);          float textHeight = mBounds.height();          int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());          height = desired;      }      setMeasuredDimension(width, height);  }  

现在我们修改下布局文件:

[html] view plain copy

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:tools="http://schemas.android.com/tools"      xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"      android:layout_width="match_parent"      android:layout_height="match_parent" >      <com.example.customview01.view.CustomTitleView          android:layout_width="wrap_content"          android:layout_height="wrap_content"          custom:titleText="3712"          android:padding="10dp"          custom:titleTextColor="#ff0000"          android:layout_centerInParent="true"          custom:titleTextSize="40sp" />  </RelativeLayout>  

现在的效果是:

完全复合我们的预期,现在我们可以对高度、宽度进行随便的设置了,基本可以满足我们的需求。

当然了,这样下来我们这个自定义View与TextView相比岂不是没什么优势,所有我们觉得给自定义View添加一个事件:

在构造中添加:

[java] view plain copy

this.setOnClickListener(new OnClickListener()          {              @Override              public void onClick(View v)              {                  mTitleText = randomText();                  postInvalidate();              }          });  

[java] view plain copy

private String randomText()      {          Random random = new Random();          Set<Integer> set = new HashSet<Integer>();          while (set.size() < 4)          {              int randomInt = random.nextInt(10);              set.add(randomInt);          }          StringBuffer sb = new StringBuffer();          for (Integer i : set)          {              sb.append("" + i);          }          return sb.toString();      }  

下面再来运行:

我们添加了一个点击事件,每次让它随机生成一个4位的随机数,有兴趣的可以在onDraw中添加一点噪点,然后改写为验证码,是不是感觉很不错。
自定义View2:
转载请标明出处:http://blog.csdn.NET/lmj623565791/article/details/24300125

继续自定义View之旅,前面已经介绍过一个自定义View的基础的例子,Android 自定义View (一),如果你还对自定义View不了解可以去看看。今天给大家带来一个稍微复杂点的例子。

自定义View显示一张图片,下面包含图片的文本介绍,类似相片介绍什么的,不过不重要,主要是学习自定义View的用法么。

还记得上一篇讲的4个步骤么:

1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ]
4、重写onDraw

直接切入正题:

1、在res/values/attr.xml

[html] view plain copy

<?xml version="1.0" encoding="utf-8"?>  <resources>      <attr name="titleText" format="string" />      <attr name="titleTextSize" format="dimension" />      <attr name="titleTextColor" format="color" />      <attr name="image" format="reference" />      <attr name="imageScaleType">          <enum name="fillXY" value="0" />          <enum name="center" value="1" />      </attr>      <declare-styleable name="CustomImageView">          <attr name="titleText" />          <attr name="titleTextSize" />          <attr name="titleTextColor" />          <attr name="image" />          <attr name="imageScaleType" />      </declare-styleable>  </resources>  

2、在构造中获得我们的自定义属性:

[java] view plain copy

/**      * 初始化所特有自定义类型      *       * @param context      * @param attrs      * @param defStyle      */      public CustomImageView(Context context, AttributeSet attrs, int defStyle)      {          super(context, attrs, defStyle);          TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyle, 0);          int n = a.getIndexCount();          for (int i = 0; i < n; i++)          {              int attr = a.getIndex(i);              switch (attr)              {              case R.styleable.CustomImageView_image:                  mImage = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0));                  break;              case R.styleable.CustomImageView_imageScaleType:                  mImageScale = a.getInt(attr, 0);                  break;              case R.styleable.CustomImageView_titleText:                  mTitle = a.getString(attr);                  break;              case R.styleable.CustomImageView_titleTextColor:                  mTextColor = a.getColor(attr, Color.BLACK);                  break;              case R.styleable.CustomImageView_titleTextSize:                  mTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,                          16, getResources().getDisplayMetrics()));                  break;              }          }          a.recycle();          rect = new Rect();          mPaint = new Paint();          mTextBound = new Rect();          mPaint.setTextSize(mTextSize);          // 计算了描绘字体需要的范围          mPaint.getTextBounds(mTitle, 0, mTitle.length(), mTextBound);      }  

3、重写onMeasure

[java] view plain copy

@Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  {      // super.onMeasure(widthMeasureSpec, heightMeasureSpec);      /**      * 设置宽度      */      int specMode = MeasureSpec.getMode(widthMeasureSpec);      int specSize = MeasureSpec.getSize(widthMeasureSpec);      if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate      {          Log.e("xxx", "EXACTLY");          mWidth = specSize;      } else      {          // 由图片决定的宽          int desireByImg = getPaddingLeft() + getPaddingRight() + mImage.getWidth();          // 由字体决定的宽          int desireByTitle = getPaddingLeft() + getPaddingRight() + mTextBound.width();          if (specMode == MeasureSpec.AT_MOST)// wrap_content          {              int desire = Math.max(desireByImg, desireByTitle);              mWidth = Math.min(desire, specSize);              Log.e("xxx", "AT_MOST");          }      }      /***      * 设置高度      */      specMode = MeasureSpec.getMode(heightMeasureSpec);      specSize = MeasureSpec.getSize(heightMeasureSpec);      if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate      {          mHeight = specSize;      } else      {          int desire = getPaddingTop() + getPaddingBottom() + mImage.getHeight() + mTextBound.height();          if (specMode == MeasureSpec.AT_MOST)// wrap_content          {              mHeight = Math.min(desire, specSize);          }      }      setMeasuredDimension(mWidth, mHeight);  }  

4、重写onDraw

[java] view plain copy

@Override      protected void onDraw(Canvas canvas)      {          // super.onDraw(canvas);          /**          * 边框          */          mPaint.setStrokeWidth(4);          mPaint.setStyle(Paint.Style.STROKE);          mPaint.setColor(Color.CYAN);          canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);          rect.left = getPaddingLeft();          rect.right = mWidth - getPaddingRight();          rect.top = getPaddingTop();          rect.bottom = mHeight - getPaddingBottom();          mPaint.setColor(mTextColor);          mPaint.setStyle(Style.FILL);          /**          * 当前设置的宽度小于字体需要的宽度,将字体改为xxx...          */          if (mTextBound.width() > mWidth)          {              TextPaint paint = new TextPaint(mPaint);              String msg = TextUtils.ellipsize(mTitle, paint, (float) mWidth - getPaddingLeft() - getPaddingRight(),                      TextUtils.TruncateAt.END).toString();              canvas.drawText(msg, getPaddingLeft(), mHeight - getPaddingBottom(), mPaint);          } else          {              //正常情况,将字体居中              canvas.drawText(mTitle, mWidth / 2 - mTextBound.width() * 1.0f / 2, mHeight - getPaddingBottom(), mPaint);          }          //取消使用掉的快          rect.bottom -= mTextBound.height();          if (mImageScale == IMAGE_SCALE_FITXY)          {              canvas.drawBitmap(mImage, null, rect, mPaint);          } else          {              //计算居中的矩形范围              rect.left = mWidth / 2 - mImage.getWidth() / 2;              rect.right = mWidth / 2 + mImage.getWidth() / 2;              rect.top = (mHeight - mTextBound.height()) / 2 - mImage.getHeight() / 2;              rect.bottom = (mHeight - mTextBound.height()) / 2 + mImage.getHeight() / 2;              canvas.drawBitmap(mImage, null, rect, mPaint);          }      }  

代码,结合注释和第一篇View的使用,应该可以看懂,不明白的留言。下面我们引入我们的自定义View:

[html] view plain copy

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:tools="http://schemas.android.com/tools"      xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.customview02"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:orientation="vertical" >      <com.zhy.customview02.view.CustomImageView          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:layout_margin="10dp"          android:padding="10dp"          zhy:image="@drawable/ic_launcher"          zhy:imageScaleType="center"          zhy:titleText="hello andorid ! "          zhy:titleTextColor="#ff0000"          zhy:titleTextSize="30sp" />      <com.zhy.customview02.view.CustomImageView          android:layout_width="100dp"          android:layout_height="wrap_content"          android:layout_margin="10dp"          android:padding="10dp"          zhy:image="@drawable/ic_launcher"          zhy:imageScaleType="center"          zhy:titleText="helloworldwelcome"          zhy:titleTextColor="#00ff00"          zhy:titleTextSize="20sp" />      <com.zhy.customview02.view.CustomImageView          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:layout_margin="10dp"          android:padding="10dp"          zhy:image="@drawable/lmj"          zhy:imageScaleType="center"          zhy:titleText="妹子~"          zhy:titleTextColor="#ff0000"          zhy:titleTextSize="12sp" />  </LinearLayout>  

我特意让显示出现3中情况:

1、字体的宽度大于图片,且View宽度设置为wrap_content

2、View宽度设置为精确值,字体的长度大于此宽度

3、图片的宽度大于字体,且View宽度设置为wrap_content

11.IO
1.什么是IO

  Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。Java的I/O流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法。    Java.io是大多数面向数据流的输入/输出类的主要软件包。此外,Java也对块传输提供支持,在核心库 java.nio中采用的便是块IO。

  流IO的好处是简单易用,缺点是效率较低。块IO效率很高,但编程比较复杂。
Java IO模型 :
Java的IO模型设计非常优秀,它使用Decorator模式,按功能划分Stream,您可以动态装配这些Stream,以便获得您需要的功能。例如,您需要一个具有缓冲的文件输入流,则应当组合使用FileInputStream和BufferedInputStream。

2.数据流的基本概念

    数据流是一串连续不断的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。   “流是磁盘或其它外围设备中存储的数据的源点或终点。”在电脑上的数据有三种存储方式,一种是外存,一种是内存,一种是缓存。比如电脑上的硬盘,磁盘,U盘等都是外存,在电脑上有内存条,缓存是在CPU里面的。外存的存储量最大,其次是内存,最后是缓存,但是外存的数据的读取最慢,其次是内存,缓存最快。这里总结从外存读取数据到内存以及将数据从内存写到外存中。对于内存和外存的理解,我们可以简单的理解为容器,即外存是一个容器,内存又是另外一个容器。那又怎样把放在外存这个容器内的数据读取到内存这个容器以及怎么把内存这个容器里的数据存到外存中呢? 在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:     标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流等等,java中将输入输出抽象称为流,就好像水管,将两个容器连接起来。将数据冲外存中读取到内存中的称为输入流,将数据从内存写入外存中的称为输出流。流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。  总结的基本概念如下:

1) 数据流:

一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

2) 输入流(Input Stream):

  程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道

3) 输出流:
程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。

采用数据流的目的就是使得输出输入独立于设备。

Input Stream不关心数据源来自何种设备(键盘,文件,网络)
Output Stream不关心数据的目的是何种设备(键盘,文件,网络)
4 数据流分类:
流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。因此Java中的流分为两种:
1) 字节流:数据流中最小的数据单元是字节
2) 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

  1. 标准I/O

    Java程序可通过命令行参数与外界进行简短的信息交换,同时,也规定了与标准输入、输出设备,如键盘、显示器进行信息交换的方式。而通过文件可以与外界进行任意数据形式的信息交换。

  2. 命令行参数

[java] view plain copy
print?

public class TestArgs {      public static void main(String[] args) {          for (int i = 0; i < args.length; i++) {              System.out.println("args[" + i + "] is <" + args[i] + ">");          }      }  }  

运行命令:java Java C VB

运行结果:

args[0] is

args[1] is

args[2] is

  1. 标准输入,输出数据流

java系统自带的标准数据流:java.lang.System:

[java] view plain copy
print?

java.lang.System   public final class System  extends Object{      static  PrintStream  err;//标准错误流(输出)     static  InputStream  in;//标准输入(键盘输入流)     static  PrintStream  out;//标准输出流(显示器输出流)  }  

注意:
(1)System类不能创建对象,只能直接使用它的三个静态成员。
(2)每当main方法被执行时,就自动生成上述三个对象。

1) 标准输出流 System.out

System.out向标准输出设备输出数据,其数据类型为PrintStream。方法:

  Void print(参数)  Void println(参数)

2)标准输入流 System.in

System.in读取标准输入设备数据(从标准输入获取数据,一般是键盘),其数 据类型为InputStream。方法:    int read()  //返回ASCII码。若,返回值=-1,说明没有读取到任何字节读取工作结束。     int read(byte[] b)//读入多个字节到缓冲区b中返回值是读入的字节数

例如:
[java] view plain copy
print?

import java.io.*;  public class StandardInputOutput {      public static void main(String args[]) {          int b;          try {              System.out.println("please Input:");              while ((b = System.in.read()) != -1) {                  System.out.print((char) b);              }          } catch (IOException e) {              System.out.println(e.toString());          }      }  }  

等待键盘输入,键盘输入什么,就打印出什么:

3)标准错误流

System.err输出标准错误,其数据类型为PrintStream。可查阅API获得详细说明。

标准输出通过System.out调用println方法输出参数并换行,而print方法输出参数但不换行。println或print方法都通 过重载实现了输出基本数据类型的多个方法,包括输出参数类型为boolean、char、int、long、float和double。同时,也重载实现 了输出参数类型为char[]、String和Object的方法。其中,print(Object)和println(Object)方法在运行时将调 用参数Object的toString方法。

[java] view plain copy
print?

import java.io.BufferedReader;  import java.io.IOException;  import java.io.InputStreamReader;  public class StandardInputOutput {      public static void main(String args[]) {          String s;          // 创建缓冲区阅读器从键盘逐行读入数据          InputStreamReader ir = new InputStreamReader(System.in);          BufferedReader in = new BufferedReader(ir);          System.out.println("Unix系统: ctrl-d 或 ctrl-c 退出"                  + "\nWindows系统: ctrl-z 退出");          try {              // 读一行数据,并标准输出至显示器              s = in.readLine();              // readLine()方法运行时若发生I/O错误,将抛出IOException异常              while (s != null) {                  System.out.println("Read: " + s);                  s = in.readLine();              }              // 关闭缓冲阅读器              in.close();          } catch (IOException e) { // Catch any IO exceptions.              e.printStackTrace();          }      }  }  

4.java.IO层次体系结构

 在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable.掌握了这些IO的核心操作那么对于Java中的IO体系也就有了一个初步的认识了  Java I/O主要包括如下几个层次,包含三个部分:

1.流式部分――IO的主体部分;

2.非流式部分――主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;

3.其他类–文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。

主要的类如下:

 1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。 2. InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。 3. OutputStream(二进制格式操作):抽象类。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。 Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。 4. Reader(文件格式操作):抽象类,基于字符的输入操作。 5. Writer(文件格式操作):抽象类,基于字符的输出操作。 6. RandomAccessFile(随机文件操作):它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。 Java中IO流的体系结构如图:
  1. 非流式文件类–File类

在Java语言的java.io包中,由File类提供了描述文件和目录的操作与管理方法。但File类不是InputStream、OutputStream或Reader、Writer的子类,因为它不负责数据的输入输出,而专门用来管理磁盘文件与目录。

作用:File类主要用于命名文件、查询文件属性和处理文件目录。
[java] view plain copy
print?

public    class   File   extends Object       implements Serializable,Comparable  {}  

File类共提供了三个不同的构造函数,以不同的参数形式灵活地接收文件和目录名信息。构造函数:
1)File (String pathname)
例:File f1=new File(“FileTest1.txt”); //创建文件对象f1,f1所指的文件是在当前目录下创建的FileTest1.txt
2)File (String parent , String child)
例:File f2=new File(“D:\dir1”,”FileTest2.txt”) ;// 注意:D:\dir1目录事先必须存在,否则异常
3)File (File parent , String child)
例:File f4=new File(“\dir3”);
File f5=new File(f4,”FileTest5.txt”); //在如果 \dir3目录不存在使用f4.mkdir()先创建
一个对应于某磁盘文件或目录的File对象一经创建, 就可以通过调用它的方法来获得文件或目录的属性。
1)public boolean exists( ) 判断文件或目录是否存在
2)public boolean isFile( ) 判断是文件还是目录
3)public boolean isDirectory( ) 判断是文件还是目录
4)public String getName( ) 返回文件名或目录名
5)public String getPath( ) 返回文件或目录的路径。
6)public long length( ) 获取文件的长度
7)public String[ ] list ( ) 将目录中所有文件名保存在字符串数组中返回。
File类中还定义了一些对文件或目录进行管理、操作的方法,常用的方法有:
1) public boolean renameTo( File newFile ); 重命名文件
2) public void delete( ); 删除文件
3) public boolean mkdir( ); 创建目录

例子:

[java] view plain copy
print?

import java.io.File;  import java.io.IOException;  public class TestFile {      public static void main(String args[]) throws IOException {          File dir = new File("\\root");          File f1 = new File(dir, "fileOne.txt");          File f2 = new File(dir, "fileTwo.java");          // 文件对象创建后,指定的文件或目录不一定物理上存在          if (!dir.exists())              dir.mkdir();          if (!f1.exists())              f1.createNewFile();          if (!f2.exists())              f2.createNewFile();          System.out.println("f1's AbsolutePath=  " + f1.getAbsolutePath());          System.out.println("f1 Canread=" + f1.canRead());          System.out.println("f1's len= " + f1.length());          String[] FL;          int count = 0;          FL = dir.list();          for (int i = 0; i < FL.length; i++) {              count++;              System.out.println(FL[i] + "is in \\root");          }          System.out.println("there are" + count + "file in //root");      }  }  

说明:File类的方法:
(1) exists()测试磁盘中指定的文件或目录是否存在
(2) mkdir()创建文件对象指定的目录(单层目录)
(3) createNewFile()创建文件对象指定的文件

(4) list()返回目录中所有文件名字符串

  1. Java.IO流类库
  2. io流的四个基本类

    java.io包中包含了流式I/O所需要的所有类。在java.io包中有四个基本类:InputStream、OutputStream及Reader、Writer类,它们分别处理字节流和字符流:

基本数据流的I/O

输入/输出

字节流

字符流

输入流

Inputstream

Reader

输出流

OutputStream

Writer

IO框架:

Java中其他多种多样变化的流均是由它们派生出来的:

  JDK1.4版本开始引入了新I/O类库,它位于java.nio包中,新I/O类库利用通道和缓冲区等来提高I/O操作的效率。  在java.io包中, java.io.InputStream 表示字节输入流, java.io.OutputStream表示字节输出流,处于java.io包最顶层。这两个类均为抽象类,也就是说它们不能被实例化,必须生成子类之后才能实现一定的功能。
  1. io流的具体分类

一、按I/O类型来总体分类:

 1. Memory 1)从/向内存数组读写数据: CharArrayReader、 CharArrayWriter、ByteArrayInputStream、ByteArrayOutputStream               2)从/向内存字符串读写数据 StringReader、StringWriter、StringBufferInputStream 2.Pipe管道  实现管道的输入和输出(进程间通信): PipedReader、PipedWriter、PipedInputStream、PipedOutputStream 3.File 文件流。对文件进行读、写操作 :FileReader、FileWriter、FileInputStream、FileOutputStream 4. ObjectSerialization 对象输入、输出 :ObjectInputStream、ObjectOutputStream 5.DataConversion数据流 按基本数据类型读、写(处理的数据是Java的基本类型(如布尔型,字节,整数和浮点数)):DataInputStream、DataOutputStream 6.Printing 包含方便的打印方法 :PrintWriter、PrintStream 7.Buffering缓冲  在读入或写出时,对数据进行缓存,以减少I/O的次数:BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream 8.Filtering 滤流,在数据进行读或写时进行过滤:FilterReader、FilterWriter、FilterInputStream、FilterOutputStream过 9.Concatenation合并输入 把多个输入流连接成一个输入流 :SequenceInputStream10.Counting计数  在读入数据时对行记数 :LineNumberReader、LineNumberInputStream11.Peeking Ahead 通过缓存机制,进行预读 :PushbackReader、PushbackInputStream12.Converting between Bytes and Characters 按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader,Writer的转换类):InputStreamReader、OutputStreamWriter

二、按数据来源(去向)分类:
1、File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter
2、byte[]:ByteArrayInputStream, ByteArrayOutputStream
3、Char[]: CharArrayReader, CharArrayWriter
4、String: StringBufferInputStream, StringReader, StringWriter
5、网络数据流:InputStream, OutputStream, Reader, Writer

  1. 字节流InputStream/OutputStream

    1. InputStream抽象类

      InputStream 为字节输入流,它本身为一个抽象类,必须依靠其子类实现各种功能,此抽象类是表示字节输入流的所有类的超类。 继承自InputStream 的流都是向程序中输入数据的,且数据单位为字节(8bit);

InputStream是输入字节数据用的类,所以InputStream类提供了3种重载的read方法.Inputstream类中的常用方法:
  (1) public abstract int read( ):读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工作结束。
  (2) public int read(byte b[ ]):读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法实际上是调用下一个方法实现的
  (3) public int read(byte b[ ], int off, int len):从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。
  (4) public int available( ):返回输入流中可以读取的字节数。注意:若输入阻塞,当前线程将被挂起,如果InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用,
  (5) public long skip(long n):忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取
  (6) public int close( ) :我们在使用完后,必须对我们打开的流进行关闭.

     主要的子类:     1) FileInputStream把一个文件作为InputStream,实现对文件的读取操作     

   2) ByteArrayInputStream:把内存中的一个缓冲区作为InputStream使用
   3) StringBufferInputStream:把一个String对象作为InputStream
   4) PipedInputStream:实现了pipe的概念,主要在线程中使用
   5) SequenceInputStream:把多个InputStream合并为一个InputStream

2.OutputStream抽象类
  OutputStream提供了3个write方法来做数据的输出,这个是和InputStream是相对应的。
  1. public void write(byte b[ ]):将参数b中的字节写到输出流。
  2. public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流。
  3. public abstract void write(int b) :先将int转换为byte类型,把低字节写入到输出流中。
  4. public void flush( ) : 将数据缓冲区中数据全部输出,并清空缓冲区。
  5. public void close( ) : 关闭输出流并释放与流相关的系统资源。

   主要的子类:  1) ByteArrayOutputStream:把信息存入内存中的一个缓冲区中   2) FileOutputStream:把信息存入文件中   3) PipedOutputStream:实现了pipe的概念,主要在线程中使用   4) SequenceOutputStream:把多个OutStream合并为一个OutStream 

流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时。
3. 文件输入流: FileInputStream类

  FileInputStream可以使用read()方法一次读入一个字节,并以int类型返回,或者是使用read()方法时读入至一个byte数组,byte数组的元素有多少个,就读入多少个字节。在将整个文件读取完成或写入完毕的过程中,这么一个byte数组通常被当作缓冲区,因为这么一个byte数组通常扮演承接数据的中间角色。

作用:以文件作为数据输入源的数据流。或者说是打开文件,从文件读数据到内存的类。
使用方法(1)
File fin=new File(“d:/abc.txt”);
FileInputStream in=new FileInputStream( fin);

使用方法(2)
FileInputStream in=new FileInputStream(“d: /abc.txt”);

程序举例:
将InputFromFile.java的程序的内容显示在显示器上

[java] view plain copy
print?

import java.io.IOException;  import java.io.FileInputStream;  ;  public class TestFile {      public static void main(String args[]) throws IOException {          try{                     FileInputStream rf=new   FileInputStream("InputFromFile.java");                 int n=512;   byte  buffer[]=new  byte[n];                    while((rf.read(buffer,0,n)!=-1)&&(n>0)){                     System.out.println(new String(buffer) );                  }                  System.out.println();                  rf.close();          } catch(IOException  IOe){                      System.out.println(IOe.toString());          }      }  }  

4.文件输出流:FileOutputStream类
  作用:用来处理以文件作为数据输出目的数据流;或者说是从内存区读数据入文件

  FileOutputStream类用来处理以文件作为数据输出目的数据流;一个表示文件名的字符串,也可以是File或FileDescriptor对象。 

  创建一个文件流对象有两种方法:
  方式1:
   File f=new File (“d:/myjava/write.txt “);
FileOutputStream out= new FileOutputStream (f);
  方式2:
  FileOutputStream out=new FileOutputStream(“d:/myjava/write.txt “);
  方式3:构造函数将 FileDescriptor()对象作为其参数。
  FileDescriptor() fd=new FileDescriptor();
  FileOutputStream f2=new FileOutputStream(fd);
  方式4:构造函数将文件名作为其第一参数,将布尔值作为第二参数。
  FileOutputStream f=new FileOutputStream(“d:/abc.txt”,true);
  注意: (1)文件中写数据时,若文件已经存在,则覆盖存在的文件;(2)的读/写操作结束时,应调用close方法关闭流。

程序举例:使用键盘输入一段文章,将文章保存在文件write.txt中

[java] view plain copy
print?

import java.io.IOException;  import java.io.FileOutputStream;  public class TestFile {      public static void main(String args[]) throws IOException {          try {              System.out.println("please Input from      Keyboard");              int count, n = 512;              byte buffer[] = new byte[n];              count = System.in.read(buffer);              FileOutputStream wf = new FileOutputStream("d:/myjava/write.txt");              wf.write(buffer, 0, count);              wf.close(); // 当流写操作结束时,调用close方法关闭流。              System.out.println("Save to the write.txt");          } catch (IOException IOe) {              System.out.println("File Write Error!");          }      }  }  
  1. FileInputStream流和FileOutputStream的应用
    利用程序将文件file1.txt 拷贝到file2.txt中。
    [java] view plain copy
    print?

    import java.io.File;
    import java.io.IOException;
    import java.io.FileOutputStream;
    import java.io.FileInputStream;

    public class TestFile {
    public static void main(String args[]) throws IOException {
    try {
    File inFile = new File(“copy.java”);
    File outFile = new File(“copy2.java”);
    FileInputStream finS = new FileInputStream(inFile);
    FileOutputStream foutS = new FileOutputStream(outFile);
    int c;
    while ((c = finS.read()) != -1) {
    foutS.write(c);
    }
    finS.close();
    foutS.close();
    } catch (IOException e) {
    System.err.println(“FileStreamsTest: ” + e);
    }
    }

    }

  2. 缓冲输入输出流 BufferedInputStream/ BufferedOutputStream

    计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大。为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制。缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存。这样可以减少访问硬盘的次数,提高传输效率。

BufferedInputStream:当向缓冲流写入数据时候,数据先写到缓冲区,待缓冲区写满后,系统一次性将数据发送给输出设备。

BufferedOutputStream :当从向缓冲流读取数据时候,系统先从缓冲区读出数据,待缓冲区为空时,系统再从输入设备读取数据到缓冲区。
1)将文件读入内存:

将BufferedInputStream与FileInputStream相接

FileInputStream in=new FileInputStream( “file1.txt ” );

BufferedInputStream bin=new BufferedInputStream( in);

2)将内存写入文件:

将BufferedOutputStream与 FileOutputStream相接

FileOutputStreamout=new FileOutputStream(“file1.txt”);

BufferedOutputStream bin=new BufferedInputStream(out);

3)键盘输入流读到内存
将BufferedReader与标准的数据流相接
InputStreamReader sin=new InputStreamReader (System.in) ;
BufferedReader bin=new BufferedReader(sin);
[java] view plain copy
print?

import java.io.*;  public class ReadWriteToFile {      public static void main(String args[]) throws IOException {          InputStreamReader sin = new InputStreamReader(System.in);          BufferedReader bin = new BufferedReader(sin);          FileWriter out = new FileWriter("myfile.txt");          BufferedWriter bout = new BufferedWriter(out);          String s;          while ((s = bin.readLine()).length() > 0) {              bout.write(s, 0, s.length());          }      }  }  

程序说明:
从键盘读入字符,并写入到文件中BufferedReader类的方法:String readLine()
作用:读一行字符串,以回车符为结束。
BufferedWriter类的方法:bout.write(String s,offset,len)
作用:从缓冲区将字符串s从offset开始,len长度的字符串写到某处。

  1. 字符流Writer/Reader

    Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。
  2. Reader抽象类

    用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

    1) FileReader :与FileInputStream对应     主要用来读取字符文件,使用缺省的字符编码,有三种构造函数: 

       (1)将文件名作为字符串 :FileReader f=new FileReader(“c:/temp.txt”);
       (2)构造函数将File对象作为其参数。
       File f=new file(“c:/temp.txt”);
       FileReader f1=new FileReader(f);
       (3) 构造函数将FileDescriptor对象作为参数
       FileDescriptor() fd=new FileDescriptor()
       FileReader f2=new FileReader(fd);
    (1) 用指定字符数组作为参数:CharArrayReader(char[])
    (2) 将字符数组作为输入流:CharArrayReader(char[], int, int)
      读取字符串,构造函数如下: public StringReader(String s);
    2) CharArrayReader:与ByteArrayInputStream对应
      3) StringReader : 与StringBufferInputStream对应
      4) InputStreamReader
       从输入流读取字节,在将它们转换成字符:Public inputstreamReader(inputstream is);
      5) FilterReader: 允许过滤字符流
       protected filterReader(Reader r);
      6) BufferReader :接受Reader对象作为参数,并对其添加字符缓冲器,使用readline()方法可以读取一行。
       Public BufferReader(Reader r);

    主要方法:

    (1) public int read() throws IOException; //读取一个字符,返回值为读取的字符

      (2) public int read(char cbuf[]) throws IOException; /读取一系列字符到数组cbuf[]中,返回值为实际读取的字符的数量/
      (3) public abstract int read(char cbuf[],int off,int len) throws IOException;
      /读取len个字符,从数组cbuf[]的下标off处开始存放,返回值为实际读取的字符数量,该方法必须由子类实现/

  3. Writer抽象类

    写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。 其子类如下:

    1) FileWrite: 与FileOutputStream对应
      将字符类型数据写入文件,使用缺省字符编码和缓冲器大小。
      Public FileWrite(file f);
      2) chararrayWrite:与ByteArrayOutputStream对应 ,将字符缓冲器用作输出。
       Public CharArrayWrite();
      3) PrintWrite:生成格式化输出
       public PrintWriter(outputstream os);
      4) filterWriter:用于写入过滤字符流
       protected FilterWriter(Writer w);
      5) PipedWriter:与PipedOutputStream对应

    6) StringWriter:无与之对应的以字节为导向的stream

    主要方法:

      (1) public void write(int c) throws IOException; //将整型值c的低16位写入输出流
      (2) public void write(char cbuf[]) throws IOException; //将字符数组cbuf[]写入输出流
      (3) public abstract void write(char cbuf[],int off,int len) throws IOException; //将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流
      (4) public void write(String str) throws IOException; //将字符串str中的字符写入输出流
      (5) public void write(String str,int off,int len) throws IOException; //将字符串str 中从索引off开始处的len个字符写入输出流
      (6) flush( ) //刷空输出流,并输出所有被缓存的字节。
      (7)close() 关闭流 public abstract void close() throws IOException

3 .InputStream与Reader差别 OutputStream与Writer差别

InputStream和OutputStream类处理的是字节流,数据流中的最小单位是字节(8个bit)
Reader与Writer处理的是字符流,在处理字符流时涉及了字符编码的转换问题

[java] view plain copy
print?

import java.io.*;  public class EncodeTest {      private static void readBuff(byte [] buff) throws IOException {         ByteArrayInputStream in =new ByteArrayInputStream(buff);          int data;          while((data=in.read())!=-1)   System.out.print(data+"  ");          System.out.println();     in.close();     }     public static void main(String args[]) throws IOException {         System.out.println("内存中采用unicode字符编码:" );         char   c='好';         int lowBit=c&0xFF;     int highBit=(c&0xFF00)>>8;         System.out.println(""+lowBit+"   "+highBit);         String s="好";         System.out.println("本地操作系统默认字符编码:");         readBuff(s.getBytes());         System.out.println("采用GBK字符编码:");         readBuff(s.getBytes("GBK"));         System.out.println("采用UTF-8字符编码:");               readBuff(s.getBytes("UTF-8"));      }  }  

Reader类能够将输入流中采用其他编码类型的字符转换为Unicode字符,然后在内存中为其分配内存

Writer类能够将内存中的Unicode字符转换为其他编码类型的字符,再写到输出流中。

  1. FileWriter类(字符输出流类)

构造方法:FileWriter fw = new FileWriter(String fileName);//创建字符输出流类对象和已存在的文件相关联。文件不存在的话,并创建。

           如: FileWriter fw = new FileWriter("C:\\1.txt");             FileWriter fw = new FileWriter(String fileName,boolean append);//创建字符输出流类对象和已存在的文件相关联,并设置该该流对文件的操作是否为续写。           如:FileWriter fw = new FileWriter("C:\\1.txt",ture); //表示在fw对文件再次写入时,会在该文件的结尾续写,并不会覆盖掉。

主要方法:

    void write(String str)   //写入字符串。当执行完此方法后,字符数据还并没有写入到目的文件中去。此时字符数据会保存在缓冲区中。此时在使用刷新方法就可以使数据保存到目的文件中去。    viod flush()                //刷新该流中的缓冲。将缓冲区中的字符数据保存到目的文件中去。    viod close()               //关闭此流。在关闭前会先刷新此流的缓冲区。在关闭后,再写入或者刷新的话,会抛IOException异常。
  1. 如何选择IO流

    1)确定是数据源和数据目的(输入还是输出)

          源:输入流 InputStream Reader      目的:输出流 OutputStream Writer

    2)明确操作的数据对象是否是纯文本

         是:字符流Reader,Writer     否:字节流InputStream,OutputStream

    3)明确具体的设备。

         是硬盘文件:File++:     读取:FileInputStream,, FileReader,       写入:FileOutputStream,FileWriter     是内存用数组          byte[]:ByteArrayInputStream, ByteArrayOutputStream          是char[]:CharArrayReader, CharArrayWriter     是String:StringBufferInputStream(已过时,因为其只能用于String的每个字符都是8位的字符串), StringReader, StringWriter     是网络用Socket流     是键盘:用System.in(是一个InputStream对象)读取,用System.out(是一个OutoutStream对象)打印

    3)是否需要转换流

        是,就使用转换流,从Stream转化为Reader,Writer:InputStreamReader,OutputStreamWriter 

    4)是否需要缓冲提高效率

    是就加上Buffered:BufferedInputStream, BufferedOuputStream, BuffereaReader, BufferedWriter
    5)是否需要格式化输出

例:将一个文本文件中数据存储到另一个文件中。
1)数据源和数据目的:读取流,InputStream Reader 输出:OutputStream Writer
2)是否纯文本:是!这时就可以选择Reader Writer。
3)设备:是硬盘文件。Reader体系中可以操作文件的对象是 FileReader FileWriter。

        FileReader fr = new FileReader("a.txt");           FileWriter fw = new FileWriter("b.txt");   4)是否需要提高效率:是,加Buffer         BufferedReader bfr = new BufferedReader(new FileReader("a.txt");  );           BufferedWriter bfw = new BufferedWriter(new FileWriter("b.txt");  );  
  1. IOException异常类的子类

1.public class EOFException :
非正常到达文件尾或输入流尾时,抛出这种类型的异常。
2.public class FileNotFoundException:
当文件找不到时,抛出的异常。
3.public class InterruptedIOException:
当I/O操作被中断时,抛出这种类型的异常

11.android中的组件Activity在manifest.xml文件中可以指定参数android:ConfigChanges,用于捕获手机状态的改变。
在Activity中添加了android:configChanges属性,在当所指定属性(Configuration Changes)发生改变时,通知程序调用onConfigurationChanged()函数。

设置方法:将下列字段用“|”符号分隔开,例如:“locale|navigation|orientation”

“mcc“ 移动国家号码,由三位数字组成,每个国家都有自己独立的MCC,可以识别手机用户所属国家。
“mnc“ 移动网号,在一个国家或者地区中,用于区分手机用户的服务商。
“locale“ 所在地区发生变化。
“touchscreen“ 触摸屏已经改变。(这不应该常发生。)
“keyboard“ 键盘模式发生变化,例如:用户接入外部键盘输入。
“keyboardHidden“ 用户打开手机硬件键盘
“navigation“ 导航型发生了变化。(这不应该常发生。)
“orientation“ 设备旋转,横向显示和竖向显示模式切换。
“fontScale“ 全局字体大小缩放发生改变

对Android:configChanges属性,一般认为有以下几点:
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

2、设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

3、设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

但是,自从Android 3.2(API 13),在设置Activity的android:configChanges=”orientation|keyboardHidden”后,还是一样会重新调用各个生命周期的。因为screen size也开始跟着设备的横竖切换而改变。所以,在AndroidManifest.xml里设置的MiniSdkVersion和 TargetSdkVersion属性大于等于13的情况下,如果你想阻止程序在运行时重新加载Activity,除了设置”orientation”,你还必须设置”ScreenSize”。
解决方法:
AndroidManifest.xml中设置android:configChanges=”orientation|screenSize“

12.message.obj传递多个参数
Bean.java:

public class Bean {    public Bitmap bitmap;    public HashMap<String, String> map;}

MainActivity.java:

 private ImageView imageView;    Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            if (msg.what == 1) {                Bean obj = (Bean) msg.obj;                imageView.setImageBitmap(obj.bitmap);                Toast.makeText(MainActivity.this, obj.map.get("a"), Toast.LENGTH_LONG).show();            }        }    };    private HashMap<String, String> map = new HashMap<>(0);    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        imageView = (ImageView) findViewById(R.id.imageView);        map.put("a", "1");        map.put("b", "2");        map.put("c", "3");        map.put("d", "4");        map.put("e", "5");        new Thread(new Runnable() {            @Override            public void run() {                Bean bean = new Bean();                bean.bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);                bean.map = map;                Message msg = handler.obtainMessage();                msg.what = 1;                msg.obj = bean;                handler.sendMessage(msg);            }        }).start();    }
  1. InputStream中read()与read(byte[] b)
    http://blog.csdn.net/snihcel/article/details/7893097

read()与read(byte[] b)这两个方法在抽象类InputStream中前者是作为抽象方法存在的,后者不是,JDK API中是这样描述两者的:

1:read() :
从输入流中读取数据的下一个字节,返回0到255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回-1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

2:read(byte[] b) :
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。如果 b 的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b 中。将读取的第一个字节存储在元素 b[0] 中,下一个存储在 b[1] 中,依次类推。读取的字节数最多等于 b 的长度。设 k 为实际读取的字节数;这些字节将存储在 b[0] 到 b[k-1] 的元素中,不影响 b[k] 到 b[b.length-1] 的元素。

由帮助文档中的解释可知,read()方法每次只能读取一个字节,所以也只能读取由ASCII码范围内的一些字符。这些字符主要用于显示现代英语和其他西欧语言。而对于汉字等unicode中的字符则不能正常读取。只能以乱码的形式显示。

对于read()方法的上述缺点,在read(byte[] b)中则得到了解决,就拿汉字来举例,一个汉字占有两个字节,则可以把参数数组b定义为大小为2的数组即可正常读取汉字了。当然b也可以定义为更大,比如如果b=new byte[4]的话,则每次可以读取两个汉字字符了,但是需要注意的是,如果此处定义b 的大小为3或7等奇数,则对于全是汉字的一篇文档则不能全部正常读写了。

下面用实例来演示一下二者的用法:
实例说明:类InputStreamTest1.java 来演示read()方法的使用。类InputStreamTest2.java来演示read(byte[] b)的使用。两个类的主要任务都是通过文件输入流FileInputStream来读取文本文档xuzhimo.txt中的内容,并且输出到控制台上显示。

先看一下xuzhimo.txt文档的内容

InputStreamTest1.java
Java代码 复制代码 收藏代码

/**   * User: liuwentao   * Time: 12-1-25 上午10:11   */  public class InputStreamTest1 {       public static void main(String[] args){           String path = "D:\\project\\opensouce\\opensouce_demo\\base_java\\classes\\demo\\java\\inputstream\\";           File file = new File(path + "xuzhimo.txt");           InputStream inputStream = null;           int i=0;           try {               inputStream = new FileInputStream(file);               while ((i = inputStream.read())!=-1){                   System.out.print((char)i + "");               }           }catch (FileNotFoundException e) {               e.printStackTrace();           } catch (IOException e) {               e.printStackTrace();           }       }   }  

[java] view plaincopy

/**  * User: liuwentao  * Time: 12-1-25 上午10:11  */  public class InputStreamTest1 {      public static void main(String[] args){          String path = "D:\\project\\opensouce\\opensouce_demo\\base_java\\classes\\demo\\java\\inputstream\\";          File file = new File(path + "xuzhimo.txt");          InputStream inputStream = null;          int i=0;          try {              inputStream = new FileInputStream(file);              while ((i = inputStream.read())!=-1){                  System.out.print((char)i + "");              }          }catch (FileNotFoundException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }      }  }  

执行结果:

如果将while循环中的 (char)去掉,即改成:
引用
System.out.print(i + “”);

则执行结果:

InputStreamTest2.java
Java代码 复制代码 收藏代码

/**   * User: liuwentao   * Time: 12-1-25 上午10:11   */  public class InputStreamTest2 {       public static void main(String[] args){           String path = "D:\\project\\opensouce\\opensouce_demo\\base_java\\src\\demo\\java\\inputstream\\";           File file = new File(path + "xuzhimo.txt");           InputStream inputStream = null;           int i=0;           try {               inputStream = new FileInputStream(file);               byte[] bytes = new byte[16];               while ((i = inputStream.read(bytes))!=-1){                   String str = new String(bytes);                   System.out.print(str);               }           }catch (FileNotFoundException e) {               e.printStackTrace();           } catch (IOException e) {               e.printStackTrace();           }       }   }  

[java] view plaincopy

/**  * User: liuwentao  * Time: 12-1-25 上午10:11  */  public class InputStreamTest2 {      public static void main(String[] args){          String path = "D:\\project\\opensouce\\opensouce_demo\\base_java\\src\\demo\\java\\inputstream\\";          File file = new File(path + "xuzhimo.txt");          InputStream inputStream = null;          int i=0;          try {              inputStream = new FileInputStream(file);              byte[] bytes = new byte[16];              while ((i = inputStream.read(bytes))!=-1){                  String str = new String(bytes);                  System.out.print(str);              }          }catch (FileNotFoundException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }      }  }  

执行结果:

遗憾的是,还是有乱码,解决办法可以参见下面教程
http://wentao365.iteye.com/blog/1183951

修改后的代码:
Java代码 复制代码 收藏代码

/**   * User: liuwentao   * Time: 12-1-25 上午10:11   */  public class InputStreamTest3 {       public static void main(String[] args) {           String path = "D:\\project\\opensouce\\opensouce_demo\\base_java\\src\\demo\\java\\inputstream\\";           File file = new File(path + "xuzhimo.txt");           InputStream inputStream = null;           String line;           StringBuffer stringBuffer = new StringBuffer();           try {               //InputStream :1)抽象类,2)面向字节形式的I/O操作(8 位字节流) 。               inputStream = new FileInputStream(file);               //Reader :1)抽象类,2)面向字符的 I/O操作(16 位的Unicode字符) 。               Reader reader = new InputStreamReader(inputStream, "UTF-8");               //增加缓冲功能               BufferedReader bufferedReader = new BufferedReader(reader);               while ((line = bufferedReader.readLine()) != null) {                   stringBuffer.append(line);               }               if (bufferedReader != null) {                   bufferedReader.close();               }               String content = stringBuffer.toString();               System.out.print(content);           } catch (FileNotFoundException e) {               e.printStackTrace();           } catch (IOException e) {               e.printStackTrace();           }       }   }  

[java] view plaincopy

/**  * User: liuwentao  * Time: 12-1-25 上午10:11  */  public class InputStreamTest3 {      public static void main(String[] args) {          String path = "D:\\project\\opensouce\\opensouce_demo\\base_java\\src\\demo\\java\\inputstream\\";          File file = new File(path + "xuzhimo.txt");          InputStream inputStream = null;          String line;          StringBuffer stringBuffer = new StringBuffer();          try {              //InputStream :1)抽象类,2)面向字节形式的I/O操作(8 位字节流) 。              inputStream = new FileInputStream(file);              //Reader :1)抽象类,2)面向字符的 I/O操作(16 位的Unicode字符) 。              Reader reader = new InputStreamReader(inputStream, "UTF-8");              //增加缓冲功能              BufferedReader bufferedReader = new BufferedReader(reader);              while ((line = bufferedReader.readLine()) != null) {                  stringBuffer.append(line);              }              if (bufferedReader != null) {                  bufferedReader.close();              }              String content = stringBuffer.toString();              System.out.print(content);          } catch (FileNotFoundException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }      }  }  

执行结果:

还是遗憾,没有换行。

解决办法,通过 commons-io-*.jar
Java代码 复制代码 收藏代码

/**   * User: liuwentao   * Time: 12-1-25 上午10:11   */  public class InputStreamTest4 {       public static void main(String[] args) {           String path = "D:\\project\\opensouce\\opensouce_demo\\base_java\\src\\demo\\java\\inputstream\\";           File file = new File(path + "xuzhimo.txt");           String content = null;           try {               content = FileUtils.readFileToString(file, "utf-8");           } catch (IOException e) {               e.printStackTrace();           }           System.out.println("content:" + content);       }   }  

[java] view plaincopy

/**  * User: liuwentao  * Time: 12-1-25 上午10:11  */  public class InputStreamTest4 {      public static void main(String[] args) {          String path = "D:\\project\\opensouce\\opensouce_demo\\base_java\\src\\demo\\java\\inputstream\\";          File file = new File(path + "xuzhimo.txt");          String content = null;          try {              content = FileUtils.readFileToString(file, "utf-8");          } catch (IOException e) {              e.printStackTrace();          }          System.out.println("content:" + content);      }  }  

执行结果:

java InputStream读取数据问题

http://blog.csdn.net/happyq/article/details/7992885

  1. 关于InputStream.read()
    在从数据流里读取数据时,为图简单,经常用InputStream.read()方法。这个方法是从流里每次只读取读取一个字节,效率会非常低。 更好的方法是用InputStream.read(byte[] b)或者InputStream.read(byte[] b,int off,int len)方法,一次读取多个字节。

  2. 关于InputStream类的available()方法
    要一次读取多个字节时,经常用到InputStream.available()方法,这个方法可以在读写操作前先得知数据流里有多少个字节可以读取。需要注意的是,如果这个方法用在从本
    地文件读取数据时,一般不会遇到问题,但如果是用于网络操作,就经常会遇到一些麻烦。比如,Socket通讯时,对方明明发来了1000个字节,但是自己的程序调用available()方法却只得到900,或者100,甚至是0,感觉有点莫名其妙,怎么也找不到原因。其实,这是因为网络通讯往往是间断性的,一串字节往往分几批进行发送。本地程序调用available()方法有时得到0,这可能是对方还没有响应,也可能是对方已经响应了,但是数据还没有送达本地。对方发送了1000个字节给你,也许分成3批到达,这你就要调用3次available()方法才能将数据总数全部得到。
    如果这样写代码:
    int count = in.available();
    byte[] b = new byte[count];
    in.read(b);
    在进行网络操作时往往出错,因为你调用available()方法时,对发发送的数据可能还没有到达,你得到的count是0。
    需要改成这样:
    int count = 0;
    while (count == 0) {
    count = in.available();
    }
    byte[] b = new byte[count];
    in.read(b);

  3. 关于InputStream.read(byte[] b)和InputStream.read(byte[] b,int off,int len)这两个方法都是用来从流里读取多个字节的,有经验的程序员就会发现,这两个方法经常 读取不到自己想要读取的个数的字节。比如第一个方法,程序员往往希望程序能读取到b.length个字节,而实际情况是,系统往往读取不了这么多。仔细阅读Java的API说明就发现了,这个方法 并不保证能读取这么多个字节,它只能保证最多读取这么多个字节(最少1个)。因此,如果要让程序读取count个字节,最好用以下代码:
    byte[] b = new byte[count];
    int readCount = 0; // 已经成功读取的字节的个数
    while (readCount < count) {
    readCount += in.read(bytes, readCount, count - readCount);
    }
    用这段代码可以保证读取count个字节,除非中途遇到IO异常或者到了数据流的结尾(EOFException)

字节是存储容量的基本单位,1字节=8个二进制位。
字符是指字母、数字、汉字和各种符号。一个字符在计算机中用若干个字节的二进制数表示,西文字符1个字节,中文字符2个字节。

在Java中,下述三个类经常用于处理数据流,下面介绍一下三个类的不同之处以及各自的用法。

InputStream : 是所有字节输入流的超类,一般使用它的子类:FileInputStream等,它能输出字节流;InputStreamReader : 是字节流与字符流之间的桥梁,能将字节流输出为字符流,并且能为字节流指定字符集,可输出一个个的字符;BufferedReader : 提供通用的缓冲方式文本读取,readLine读取一个文本行, 从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。

安卓MVC模式
MVC是框架模式,框架通常是对代码的重用,而设计是对一系列相同结构代码的重用,架构介于框架和设计之间。
MVC优点:成本较低易于维护和修改。耦合性不高,表现层和业务层分离实现各司其职。
MVC缺点:完全理解MVC不是很容易,花费时间将MVC运用到项目。
在Android中的运用:
M:对数据的处理和网络获取,bean,适配器。
V:view试图,xml和自定义控件。
C:controler控制器,由activity充当。
Activity控制器的主要作用就是解耦,将视图view和模型model分离,两者在activity中进行绑定或完成其他逻辑。mvc适合大项目,比如android的ui框架。

MVP应用架构模式
Model view presenter 在安卓开发中越来越重要。
Presenter-交互中间人:主要作为沟通view和model的桥梁,他从model层检索数据后,返回给View层,是的view和model之间没有耦合,也将业务逻辑从view中抽离出来。
View-用户界面:指的是activity和fragment或者某个view控件,通常view需要实现一个逻辑接口,定义一个presenter作为成员变量,具体实现转交给presenter处理完成后返回数
据给view,最后presenter调用view的接口将结果返回给view。
model-数据的存取:presenter需要通过model进行数据的存取,具体表现在:model是封装了数据库dao和网络获取数据的角色。

泛型:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html

内存优化:
http://blog.csdn.net/a396901990/article/details/37914465
http://blog.csdn.net/a396901990/article/details/38707007/

Bitmap继承Parcelable,是一个可以跨进程传输的对象,BitmapDrawable继承Drawable,可Drawable只是一个抽象类,此类是一个存放数据流的载体。

1.使用情况:如果想绑定imageView之类的控件,两者都可以用,而想要将图片数据转换成其它对象,Bitmap功能更强大,而BitmapDrawable只是一个流的载体,所以一般获取src资源文件的时候用得多,而想要把资源图片截入到Bitmap需要转换后才可得到Bitmap对象。
2.BitmapDrawable就是封装了一个位图。直接以文件的方式,就是封装了一个原始的位图。以Xml方式,可以对原始的位图进行一系列的处理,比如说抗锯齿,拉伸,对齐等等。
3.要了解BitmapDrawable的使用,还需要明白Bitmap、BitmapFactory等类。Bitmap代表了一个原始的位图,并且可以对位图进行一系列的变换操作。BitmapFactory提供一系列的方法用于产生一个Bitmap对象。多用在Canvas中。

EventBus使用:
http://blog.csdn.net/harvic880925/article/details/40660137

retrofit2.0 和rxjava2.0

原创粉丝点击