学习andriod开发之 自己动手写图片查看器(二)

      大家好 我是akira 上一讲 我们说了利用httpClinet发送网络请求 来获取流去拿到图片这么一个过程 

我们可以回顾一下具体是开一个线程 然后利用httpClient 然后发一个get请求过来 得到响应吗 如果为200说明成功

得到一个流 同时保存在sd卡中 然后写一个方法 根据url格式 来获取到name (利用path的substring的lastIndexof+1)

同时拿到bitmap之后调用compress方法 保存在文件中 然后发一个消息 展示。

        OK 回顾完之后 有个问题就是如果加载大图的时候 这里必定会出现OOM异常 也就是通常所说的内存过大

说说图片内存具体怎么算 我们先要有一张图 下面我随便找了一张

  看下分辨率

      

嗯 确实是够大 

加载后出现


OK 再来找下日志


这里有个12582924的申请字节 我们可以算下

除两个1024后 发现是12M 有人说 andriod不是16M或 24M内存么 12M不超啊 

这个看法有两点错误 

1 加入12M仅仅为了处理一个图片 那么剩下的4M 或者12M 处理太多的东西 虚拟机受不了

2 andriod系统在设计本初 就给处理图给了8M空间 如果这么一对比肯定是超了 

所以 这里所谓的OOM就是处理图片内存>8M

好 知道了这个之后 我们看下怎么解决

效果如下

缓存之后效果


看下代码如何实现的

Bitmap bits ;
	/**
	 * 加载数据(关键方法)
	 * @param path
	 */
	private void loadData(final String path) {
		 if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
		 file = new File(Environment.getExternalStorageDirectory(),getPicName(path));
		    if(file.exists()){
		    	pic.setImageURI(Uri.fromFile(file));
		    
	    	}
		 	else{
				 
		    	new Thread(){
					public void run() {
						     
						try {
							HttpClient client = new DefaultHttpClient();
							HttpGet get = new HttpGet(path);
							HttpResponse httpResponse = client.execute(get);
							if(httpResponse.getStatusLine().getStatusCode()==200){
								InputStream is = httpResponse.getEntity().getContent();  
								bits =   scalePic(is, pic.getHeight(), pic.getWidth());
								OutputStream os = new FileOutputStream(file);
								bits.compress(CompressFormat.JPEG, 80, os);
								 Message msg = Message.obtain();  
		                        msg.obj =bits;  
		                        msg.what = GETPIC;  
		                        mHandler.sendMessage(msg);  
							}
							
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						
					};
					
				}.start();
		 }
		 }
		
	
	}

我们发现这里加了一个新方法是scalePic 里面我传了三个参数 因为是流 所以传一个input 再传宽高 OK 

然后看下缩放pic的方法

/**
	 * 缩放图片
	 * @param bit
	 * @param width
	 * @param height
	 */
	protected Bitmap scalePic(InputStream is, int imageHeight, int imageWidth) {
		Options opts = new Options();
		opts.inJustDecodeBounds = true;
        opts.outHeight = imageHeight;
        opts.outWidth = imageWidth;
        opts.inSampleSize =4;// 16分之一
        opts.inJustDecodeBounds = false;
		Bitmap smallbit = BitmapFactory.decodeStream(is, null, opts);
		return smallbit;
	}

这里吐槽一下 以前大多数做法是利用bitmap的decodeFile 但这样效率很低 

而decodeStream的三参方法很少人用 试了一下效果很好 

这里的思路仍然是缩放 利用关键的类是options 这是bitmap的一个类 专门设置位图参数使用

我们看到 利用opts的outHeight或outWidth拿到宽高 然后去进行缩放宽高的4分之一 

所以整体缩放就是16分之1 inJustDecodeBounds这个方法的作用是设置成true 就先不加载进内存 方便调用bitmap身上的参数

然后加载的时候再给false 最后我们返回的是缩小后的bitmap

PS:这里要吐槽一下 网上很多代码都是互抄一点使用价值也没有 强烈鄙视

但是这里有个问题 你会发现有趣的resovle failedXXX这么一个异常 这个原因找了一段时间 发现原来是文件名的问题 

原来我们的图片文件名不宜过长 否则就无法解析 

OK 我们修改获取名字的代码

/**
	 * 得到图片名字
	 * @param path
	 * @return
	 */
	private String getPicName(String path){
		String newpath = path.substring(path.lastIndexOf("/")+1);
		if(newpath.length()>=16){
			newpath = newpath.substring(16, newpath.length());
		}
		return newpath;
	}

这里 我们进行判断 如果长度超过16就截 说白了 OK 然后再运行就没问题了


这里截了一张效果图 

原图片路径

http://imgsrc.baidu.com/forum/pic/item/4f55880a19d8bc3e1c82e62a838ba61eaad34598.jpg

最后 我们改下周围的布局 利用一个新控件ScrollView 这个就可以实现上下拉动啦 

布局代码

<ScrollView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
    android:layout_height="match_parent"
     >
<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".MainActivity" 
    android:orientation="vertical"
    >
    
    <ImageView 
        android:id="@+id/pic"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:contentDescription="@null"
        />
	
    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
       <TextView
         android:layout_centerHorizontal="true"
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
       	android:text="@string/hello_world" />
       
<!--        <Button  -->
<!--            android:id="@+id/bt_next" -->
<!--            android:layout_width="wrap_content" -->
<!--            android:layout_height="wrap_content" -->
<!--            android:text="next" -->
<!--            /> -->
       </RelativeLayout>
   
    
    <EditText 
        android:text="http://imgsrc.baidu.com/forum/pic/item/4f55880a19d8bc3e1c82e62a838ba61eaad34598.jpg"
        android:id="@+id/et_address"
        android:hint="@string/typeuri"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
       <Button
        android:layout_centerHorizontal="true"
        android:id="@+id/bt_load"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
       	android:text="@string/load" />
       </RelativeLayout>

</LinearLayout>
</ScrollView>
但新问题来了 我们发现通常加载的时候 最好给下提示来个进度条什么的 

这个时候 我们也做一个 来了一个新朋友叫做progressBar 好的 我们利用代码加上进度条


ProgressBar pb;
	private RelativeLayout rl;
	/**
	 * 显示
	 * @param ct
	 */
	private void showProgress(Context ct){
		
		if(pb==null){
		ProgressBar pb = new ProgressBar(ct);
		rl.addView(pb);
		pb.setVisibility(View.VISIBLE);
		}
		
	}
	/**
	 * 隐藏
	 */
	private void hideProgress(){
		
		pb.setVisibility(View.GONE);
	}

这里加上两个方法 显示和隐藏 利用相对布局的addView方法 加载进去 

分别在加载和显示后 调用showXXX和hideXXX

if(httpResponse.getStatusLine().getStatusCode()==200){
								InputStream is = httpResponse.getEntity().getContent();
								showProgress(MainActivity.this);
								bits =   scalePic(is, pic.getHeight(), pic.getWidth());
								OutputStream os = new FileOutputStream(file);
								bits.compress(CompressFormat.JPEG, 80, os);
								 Message msg = Message.obtain();  
		                        msg.obj =bits;  
		                        msg.what = GETPIC;  
		                        mHandler.sendMessage(msg);  
							}


case GETPIC:
				//已经拿到图片
				Bitmap bit = (Bitmap) msg.obj;
				pic.setImageBitmap(bit);
				name.setText(getPicName(path));
				hideProgress();
				showToast(MainActivity.this, "成功获取", Toast.LENGTH_SHORT);
				break;

但是利用MVC思想的话 这里应该是分开业务类来写 而且我们发现加载一个图利用了这么多方法 

而有种框架几行代码就能搞定全部 

那么这框架究竟是什么框架呢 请看下讲 

学习andriod开发之 自己动手写图片查看器(三)

本期代码下载

http://download.csdn.net/detail/mtgodd/8088709

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值