`

Android学习之瀑布流效果

阅读更多

FormFile.java:

 

package com.example.photowallfallsdemo;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

/**
 * 上传文件
 */
public class FormFile {
	/* 上传文件的数据 */
	private byte[] data;
	private InputStream inStream;
	private File file;
	/* 文件名称 */
	private String filname;
	/* 请求参数名称*/
	private String parameterName;
	/* 内容类型 */
	private String contentType = "application/octet-stream";
	
	/**
	 * 此函数用来传输小文件
	 * @param filname
	 * @param data
	 * @param parameterName
	 * @param contentType
	 */
	public FormFile(String filname, byte[] data, String parameterName, String contentType) {
		this.data = data;
		this.filname = filname;
		this.parameterName = parameterName;
		if(contentType!=null) this.contentType = contentType;
	}
	/**
	 * 此函数用来传输大文件
	 * @param filname
	 * @param file
	 * @param parameterName
	 * @param contentType
	 */
	public FormFile(String filname, File file, String parameterName, String contentType) {
		this.filname = filname;
		this.parameterName = parameterName;
		this.file = file;
		try {
			this.inStream = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		if(contentType!=null) this.contentType = contentType;
	}
	
	public File getFile() {
		return file;
	}

	public InputStream getInStream() {
		return inStream;
	}

	public byte[] getData() {
		return data;
	}

	public String getFilname() {
		return filname;
	}

	public void setFilname(String filname) {
		this.filname = filname;
	}

	public String getParameterName() {
		return parameterName;
	}

	public void setParameterName(String parameterName) {
		this.parameterName = parameterName;
	}

	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}
	
}

 

 

 

 

 

HttpRequestUtil.java:

 

package com.example.photowallfallsdemo;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/*
 * 此类用来发送HTTP请求
 * */
public class HttpRequestUtil {
	/**
	 * 发送GET请求
	 * @param url
	 * @param params
	 * @param headers
	 * @return
	 * @throws Exception
	 */
	public static URLConnection sendGetRequest(String url,
			Map<String, String> params, Map<String, String> headers)
			throws Exception {
		StringBuilder buf = new StringBuilder(url);
		Set<Entry<String, String>> entrys = null;
		// 如果是GET请求,则请求参数在URL中
		if (params != null && !params.isEmpty()) {
			buf.append("?");
			entrys = params.entrySet();
			for (Map.Entry<String, String> entry : entrys) {
				buf.append(entry.getKey()).append("=")
						.append(URLEncoder.encode(entry.getValue(), "UTF-8"))
						.append("&");
			}
			buf.deleteCharAt(buf.length() - 1);
		}
		URL url1 = new URL(buf.toString());
		HttpURLConnection conn = (HttpURLConnection) url1.openConnection();
		conn.setRequestMethod("GET");
		// 设置请求头
		if (headers != null && !headers.isEmpty()) {
			entrys = headers.entrySet();
			for (Map.Entry<String, String> entry : entrys) {
				conn.setRequestProperty(entry.getKey(), entry.getValue());
			}
		}
		conn.getResponseCode();
		return conn;
	}
	/**
	 * 发送POST请求
	 * @param url	
	 * @param params
	 * @param headers
	 * @return 
	 * @throws Exception
	 */
	public static URLConnection sendPostRequest(String url,
			Map<String, String> params, Map<String, String> headers)
			throws Exception {
		StringBuilder buf = new StringBuilder();
		Set<Entry<String, String>> entrys = null;
		// 如果存在参数,则放在HTTP请求体,形如name=aaa&age=10
		if (params != null && !params.isEmpty()) {
			entrys = params.entrySet();
			for (Map.Entry<String, String> entry : entrys) {
				buf.append(entry.getKey()).append("=")
						.append(URLEncoder.encode(entry.getValue(), "UTF-8"))
						.append("&");
			}
			buf.deleteCharAt(buf.length() - 1);
		}
		URL url1 = new URL(url);
		HttpURLConnection conn = (HttpURLConnection) url1.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoOutput(true);
		OutputStream out = conn.getOutputStream();
		out.write(buf.toString().getBytes("UTF-8"));
		if (headers != null && !headers.isEmpty()) {
			entrys = headers.entrySet();
			for (Map.Entry<String, String> entry : entrys) {
				conn.setRequestProperty(entry.getKey(), entry.getValue());
			}
		}
		conn.getResponseCode(); // 为了发送成功
		return conn;
	}
	/**
	 * 直接通过HTTP协议提交数据到服务器,实现如下面表单提交功能:
	 *   <FORM METHOD=POST ACTION="http://192.168.0.200:8080/ssi/fileload/test.do" enctype="multipart/form-data">
			<INPUT TYPE="text" NAME="name">
			<INPUT TYPE="text" NAME="id">
			<input type="file" name="imagefile"/>
		    <input type="file" name="zip"/>
		 </FORM>
	 * @param path 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.itcast.cn或http://192.168.1.10:8080这样的路径测试)
	 * @param params 请求参数 key为参数名,value为参数值
	 * @param file 上传文件
	 */
	public static boolean uploadFiles(String path, Map<String, String> params, FormFile[] files) throws Exception{     
        final String BOUNDARY = "---------------------------7da2137580612"; //数据分隔线
        final String endline = "--" + BOUNDARY + "--\r\n";//数据结束标志
        
        int fileDataLength = 0;
        if(files!=null&&files.length!=0){
	        for(FormFile uploadFile : files){//得到文件类型数据的总长度
	        	StringBuilder fileExplain = new StringBuilder();
	 	        fileExplain.append("--");
	 	        fileExplain.append(BOUNDARY);
	 	        fileExplain.append("\r\n");
	 	        fileExplain.append("Content-Disposition: form-data;name=\""+ uploadFile.getParameterName()+"\";filename=\""+ uploadFile.getFilname() + "\"\r\n");
	 	        fileExplain.append("Content-Type: "+ uploadFile.getContentType()+"\r\n\r\n");
	 	        fileExplain.append("\r\n");
	 	        fileDataLength += fileExplain.length();
	        	if(uploadFile.getInStream()!=null){
	        		fileDataLength += uploadFile.getFile().length();
		 	    }else{
		 	    	fileDataLength += uploadFile.getData().length;
		 	    }
	        }
        }
        StringBuilder textEntity = new StringBuilder();
        if(params!=null&&!params.isEmpty()){
	        for (Map.Entry<String, String> entry : params.entrySet()) {//构造文本类型参数的实体数据
	            textEntity.append("--");
	            textEntity.append(BOUNDARY);
	            textEntity.append("\r\n");
	            textEntity.append("Content-Disposition: form-data; name=\""+ entry.getKey() + "\"\r\n\r\n");
	            textEntity.append(entry.getValue());
	            textEntity.append("\r\n");
	        }
        }
        //计算传输给服务器的实体数据总长度
        int dataLength = textEntity.toString().getBytes().length + fileDataLength +  endline.getBytes().length;
        
        URL url = new URL(path);
        int port = url.getPort()==-1 ? 80 : url.getPort();
        Socket socket = new Socket(InetAddress.getByName(url.getHost()), port);	       
        OutputStream outStream = socket.getOutputStream();
        //下面完成HTTP请求头的发送
        String requestmethod = "POST "+ url.getPath()+" HTTP/1.1\r\n";
        outStream.write(requestmethod.getBytes());
        String accept = "Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n";
        outStream.write(accept.getBytes());
        String language = "Accept-Language: zh-CN\r\n";
        outStream.write(language.getBytes());
        String contenttype = "Content-Type: multipart/form-data; boundary="+ BOUNDARY+ "\r\n";
        outStream.write(contenttype.getBytes());
        String contentlength = "Content-Length: "+ dataLength + "\r\n";
        outStream.write(contentlength.getBytes());
        String alive = "Connection: Keep-Alive\r\n";
        outStream.write(alive.getBytes());
        String host = "Host: "+ url.getHost() +":"+ port +"\r\n";
        outStream.write(host.getBytes());
        //写完HTTP请求头后根据HTTP协议再写一个回车换行
        outStream.write("\r\n".getBytes());
        //把所有文本类型的实体数据发送出来
        outStream.write(textEntity.toString().getBytes());	       
        //把所有文件类型的实体数据发送出来
        if(files!=null&&files.length!=0){
	        for(FormFile uploadFile : files){
	        	StringBuilder fileEntity = new StringBuilder();
	 	        fileEntity.append("--");
	 	        fileEntity.append(BOUNDARY);
	 	        fileEntity.append("\r\n");
	 	        fileEntity.append("Content-Disposition: form-data;name=\""+ uploadFile.getParameterName()+"\";filename=\""+ uploadFile.getFilname() + "\"\r\n");
	 	        fileEntity.append("Content-Type: "+ uploadFile.getContentType()+"\r\n\r\n");
	 	        outStream.write(fileEntity.toString().getBytes());
	 	        if(uploadFile.getInStream()!=null){
	 	        	byte[] buffer = new byte[1024];
	 	        	int len = 0;
	 	        	while((len = uploadFile.getInStream().read(buffer, 0, 1024))!=-1){
	 	        		outStream.write(buffer, 0, len);
	 	        	}
	 	        	uploadFile.getInStream().close();
	 	        }else{
	 	        	outStream.write(uploadFile.getData(), 0, uploadFile.getData().length);
	 	        }
	 	        outStream.write("\r\n".getBytes());
	        }
        }
        //下面发送数据结束标志,表示数据已经结束
        outStream.write(endline.getBytes());
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        if(reader.readLine().indexOf("200")==-1){//读取web服务器返回的数据,判断请求码是否为200,如果不是200,代表请求失败
        	return false;
        }
        outStream.flush();
        outStream.close();
        reader.close();
        socket.close();
        return true;
	}
	/**
	 * 提交数据到服务器
	 * @param path 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.itcast.cn或http://192.168.1.10:8080这样的路径测试)
	 * @param params 请求参数 key为参数名,value为参数值
	 * @param file 上传文件
	 */
	public static boolean uploadFile(String path, Map<String, String> params, FormFile file) throws Exception{
	   return uploadFiles(path, params, new FormFile[]{file});
	}
	/**
	 * 将输入流转为字节数组
	 * @param inStream
	 * @return
	 * @throws Exception
	 */
	public static byte[] read2Byte(InputStream inStream)throws Exception{
		ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = 0;
		while( (len = inStream.read(buffer)) !=-1 ){
			outSteam.write(buffer, 0, len);
		}
		outSteam.close();
		inStream.close();
		return outSteam.toByteArray();
	}
	/**
	 * 将输入流转为字符串
	 * @param inStream
	 * @return
	 * @throws Exception
	 */
	public static String read2String(InputStream inStream)throws Exception{
		ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = 0;
		while( (len = inStream.read(buffer)) !=-1 ){
			outSteam.write(buffer, 0, len);
		}
		outSteam.close();
		inStream.close();
		return new String(outSteam.toByteArray(),"UTF-8");
	}
	/**
	 * 发送xml数据
	 * @param path 请求地址
	 * @param xml xml数据
	 * @param encoding 编码
	 * @return
	 * @throws Exception
	 */
	public static byte[] postXml(String path, String xml, String encoding) throws Exception{
		byte[] data = xml.getBytes(encoding);
		URL url = new URL(path);
		HttpURLConnection conn = (HttpURLConnection)url.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoOutput(true);
		conn.setRequestProperty("Content-Type", "text/xml; charset="+ encoding);
		conn.setRequestProperty("Content-Length", String.valueOf(data.length));
		conn.setConnectTimeout(5 * 1000);
		OutputStream outStream = conn.getOutputStream();
		outStream.write(data);
		outStream.flush();
		outStream.close();
		if(conn.getResponseCode()==200){
			return read2Byte(conn.getInputStream());
		}
		return null;
	}
	//测试函数
	public static void main(String args[]) throws Exception {
		Map<String, String> params = new HashMap<String, String>();
		params.put("name", "xiazdong");
		params.put("age", "10");
		HttpURLConnection conn = (HttpURLConnection) HttpRequestUtil
				.sendGetRequest(
						"http://192.168.0.103:8080/Server/PrintServlet",
						params, null);
		int code = conn.getResponseCode();
		InputStream in = conn.getInputStream();
		byte[]data = read2Byte(in);
		
		//FormFile formFile = new FormFile(file.getName(), file, "document", "text/plain");
		//boolean isSuccess = HttpRequestUtil.uploadFile("http://192.168.0.103:8080/Server/FileServlet", null, formFile);
	}
}

 

 

 

 

 

ImageLoader.java:

 

package com.example.photowallfallsdemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;

/**
 * 对图片进行管理的工具类。
 * 
 * @author Tony
 */
public class ImageLoader {

	/**
	 * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
	 */
	private static LruCache<String, Bitmap> mMemoryCache;

	/**
	 * ImageLoader的实例。
	 */
	private static ImageLoader mImageLoader;

	private ImageLoader() {
		// 获取应用程序最大可用内存
		int maxMemory = (int) Runtime.getRuntime().maxMemory();
		int cacheSize = maxMemory / 8;
		// 设置图片缓存大小为程序最大可用内存的1/8
		mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
			@Override
			protected int sizeOf(String key, Bitmap bitmap) {
				return bitmap.getByteCount();
			}
		};
	}

	/**
	 * 获取ImageLoader的实例。
	 * 
	 * @return ImageLoader的实例。
	 */
	public static ImageLoader getInstance() {
		if (mImageLoader == null) {
			mImageLoader = new ImageLoader();
		}
		return mImageLoader;
	}

	/**
	 * 将一张图片存储到LruCache中。
	 * 
	 * @param key
	 *            LruCache的键,这里传入图片的URL地址。
	 * @param bitmap
	 *            LruCache的键,这里传入从网络上下载的Bitmap对象。
	 */
	public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
		if (getBitmapFromMemoryCache(key) == null) {
			mMemoryCache.put(key, bitmap);
		}
	}

	/**
	 * 从LruCache中获取一张图片,如果不存在就返回null。
	 * 
	 * @param key
	 *            LruCache的键,这里传入图片的URL地址。
	 * @return 对应传入键的Bitmap对象,或者null。
	 */
	public Bitmap getBitmapFromMemoryCache(String key) {
		return mMemoryCache.get(key);
	}

	public static int calculateInSampleSize(BitmapFactory.Options options,
			int reqWidth) {
		// 源图片的宽度
		final int width = options.outWidth;
		int inSampleSize = 1;
		if (width > reqWidth) {
			// 计算出实际宽度和目标宽度的比率
			final int widthRatio = Math.round((float) width / (float) reqWidth);
			inSampleSize = widthRatio;
		}
		return inSampleSize;
	}

	public static Bitmap decodeSampledBitmapFromResource(String pathName,
			int reqWidth) {
		// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(pathName, options);
		// 调用上面定义的方法计算inSampleSize值
		options.inSampleSize = calculateInSampleSize(options, reqWidth);
		// 使用获取到的inSampleSize值再次解析图片
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeFile(pathName, options);
	}

}

 

 

 

 

 

MainActivity.java:

 

package com.example.photowallfallsdemo;

import android.os.Bundle;
import android.view.Window;
import android.app.Activity;

public class MainActivity extends Activity {

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

}

 

 

 

 

 

MyScrollView.java:

 

package com.example.photowallfallsdemo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * 自定义的ScrollView,在其中动态地对图片进行添加。
 * 
 * @author guolin
 */
public class MyScrollView extends ScrollView implements OnTouchListener {

	/**
	 * 每页要加载的图片数量
	 */
	public static final int PAGE_SIZE = 10;

	/**
	 * 记录当前已加载到第几页
	 */
	private int page=1;

	/**
	 * 每一列的宽度
	 */
	private int columnWidth;

	/**
	 * 当前第一列的高度
	 */
	private int firstColumnHeight;

	/**
	 * 当前第二列的高度
	 */
	private int secondColumnHeight;

	/**
	 * 当前第三列的高度
	 */
	private int thirdColumnHeight;

	/**
	 * 是否已加载过一次layout,这里onLayout中的初始化只需加载一次
	 */
	private boolean loadOnce;

	/**
	 * 对图片进行管理的工具类
	 */
	private ImageLoader imageLoader;

	/**
	 * 第一列的布局
	 */
	private LinearLayout firstColumn;

	/**
	 * 第二列的布局
	 */
	private LinearLayout secondColumn;

	/**
	 * 第三列的布局
	 */
	private LinearLayout thirdColumn;

	/**
	 * 记录所有正在下载或等待下载的任务。
	 */
	private static Set<LoadImageTask> taskCollection;

	/**
	 * MyScrollView下的直接子布局。
	 */
	private static View scrollLayout;

	/**
	 * MyScrollView布局的高度。
	 */
	private static int scrollViewHeight;

	/**
	 * 记录上垂直方向的滚动距离。
	 */
	private static int lastScrollY = -1;

	/**
	 * 记录所有界面上的图片,用以可以随时控制对图片的释放。
	 */
	private List<ImageView> imageViewList = new ArrayList<ImageView>();

	/**
	 * 在Handler中进行图片可见性检查的判断,以及加载更多图片的操作。
	 */
	private static Handler handler = new Handler() {

		public void handleMessage(android.os.Message msg) {
			MyScrollView myScrollView = (MyScrollView) msg.obj;
			int scrollY = myScrollView.getScrollY();
			// 如果当前的滚动位置和上次相同,表示已停止滚动
			if (scrollY == lastScrollY) {
				// 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片
				if (scrollViewHeight + scrollY >= scrollLayout.getHeight()
						&& taskCollection.isEmpty()) {
					myScrollView.doMySendTest();
				}
				myScrollView.checkVisibility();
			} else {
				lastScrollY = scrollY;
				Message message = new Message();
				message.obj = myScrollView;
				// 5毫秒后再次对滚动位置进行判断
				handler.sendMessageDelayed(message, 5);
			}
		};

	};
	
	private Handler handler2 = new Handler() {
		public void handleMessage(android.os.Message msg) {
			if (msg.what == 0) {
				String resultStr=(String) msg.obj;
				loadMoreImages(resultStr);
			}
		};
	};

	/**
	 * MyScrollView的构造函数。
	 * 
	 * @param context
	 * @param attrs
	 */
	public MyScrollView(Context context, AttributeSet attrs) {
		super(context, attrs);
		imageLoader = ImageLoader.getInstance();
		taskCollection = new HashSet<LoadImageTask>();
		setOnTouchListener(this);
	}

	/**
	 * 进行一些关键性的初始化操作,获取MyScrollView的高度,以及得到第一列的宽度值。并在这里开始加载第一页的图片。
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if (changed && !loadOnce) {
			scrollViewHeight = getHeight();
			scrollLayout = getChildAt(0);
			firstColumn = (LinearLayout) findViewById(R.id.first_column);
			secondColumn = (LinearLayout) findViewById(R.id.second_column);
			thirdColumn = (LinearLayout) findViewById(R.id.third_column);
			columnWidth = firstColumn.getWidth();
			loadOnce = true;
			doMySendTest();
		}
	}

	/**
	 * 监听用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。
	 */
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		if (event.getAction() == MotionEvent.ACTION_UP) {
			Message message = new Message();
			message.obj = this;
			handler.sendMessageDelayed(message, 5);
		}
		return false;
	}
	
	//获取数据信息
	public String getMyDataInfo(){
		String resultstr="";
		String murl="http://192.168.1.100/search.do";
		System.out.println("getMyDataInfo==="+"执行getMyDataInfo.....");
		Map<String, String> params = new HashMap<String, String>();  
        params.put("cid", "1971558");  
        params.put("page", page+"");  
        HttpURLConnection conn;
		try {
//			conn = (HttpURLConnection) HttpRequestUtil  
//			        .sendGetRequest(  
//			                murl,  
//			                params, null);
			conn = (HttpURLConnection) HttpRequestUtil  
			        .sendPostRequest(  
			                murl,  
			                params, null);
			int code = conn.getResponseCode();  
	        InputStream in = conn.getInputStream();  
	        byte[]data = HttpRequestUtil.read2Byte(in);  
	        resultstr = new String(data, "utf-8");
	        System.out.println("resultstr==="+resultstr);
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return resultstr;
	}
	
	//执行远程请求
	public void doMySendTest(){
		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				String temp=getMyDataInfo();
				Message msg = new Message();
				msg.what = 0;
				msg.obj = temp;
				handler2.sendMessage(msg);
			}
		}.start();
	}

	/**
	 * 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。
	 */
	public void loadMoreImages(String jsonString) {
		JSONObject demoJson;
		JSONArray mprodcutsList = null;
		int totalSize=0;
		try {
			demoJson = new JSONObject(jsonString);
			totalSize =Integer.parseInt(demoJson.getString("totalSize"));
			mprodcutsList = demoJson.getJSONArray("mprodcuts");
			if (hasSDCard()) {
				int startIndex = (page-1) * PAGE_SIZE;
				//int endIndex = page * PAGE_SIZE + PAGE_SIZE;
				if (startIndex < totalSize) {
					Toast.makeText(getContext(), "正在加载...", Toast.LENGTH_SHORT)
							.show();
					for (int i = 0; i < mprodcutsList.length(); i++) {
						LoadImageTask task = new LoadImageTask();
						taskCollection.add(task);
						task.execute(mprodcutsList.getJSONObject(i).getString("bigpicture"));
					}
					page++;
				} else {
					Toast.makeText(getContext(), "已没有更多图片", Toast.LENGTH_SHORT)
							.show();
				}
			} else {
				Toast.makeText(getContext(), "未发现SD卡", Toast.LENGTH_SHORT)
						.show();
			}
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 遍历imageViewList中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。
	 */
	public void checkVisibility() {
		for (int i = 0; i < imageViewList.size(); i++) {
			ImageView imageView = imageViewList.get(i);
			int borderTop = (Integer) imageView.getTag(R.string.border_top);
			int borderBottom = (Integer) imageView
					.getTag(R.string.border_bottom);
			if (borderBottom > getScrollY()
					&& borderTop < getScrollY() + scrollViewHeight) {
				String imageUrl = (String) imageView.getTag(R.string.image_url);
				Bitmap bitmap = imageLoader.getBitmapFromMemoryCache(imageUrl);
				if (bitmap != null) {
					imageView.setImageBitmap(bitmap);
				} else {
					LoadImageTask task = new LoadImageTask(imageView);
					task.execute(imageUrl);
				}
			} else {
				imageView.setImageResource(R.drawable.empty_photo);
			}
		}
	}

	/**
	 * 判断手机是否有SD卡。
	 * 
	 * @return 有SD卡返回true,没有返回false。
	 */
	private boolean hasSDCard() {
		return Environment.MEDIA_MOUNTED.equals(Environment
				.getExternalStorageState());
	}

	/**
	 * 异步下载图片的任务。
	 * 
	 * @author guolin
	 */
	class LoadImageTask extends AsyncTask<String, Void, Bitmap> {

		/**
		 * 图片的URL地址
		 */
		private String mImageUrl;

		/**
		 * 可重复使用的ImageView
		 */
		private ImageView mImageView;

		public LoadImageTask() {
		}

		/**
		 * 将可重复使用的ImageView传入
		 * 
		 * @param imageView
		 */
		public LoadImageTask(ImageView imageView) {
			mImageView = imageView;
		}

		@Override
		protected Bitmap doInBackground(String... params) {
			mImageUrl = params[0];
			Bitmap imageBitmap = imageLoader
					.getBitmapFromMemoryCache(mImageUrl);
			if (imageBitmap == null) {
				imageBitmap = loadImage(mImageUrl);
			}
			return imageBitmap;
		}

		@Override
		protected void onPostExecute(Bitmap bitmap) {
			if (bitmap != null) {
				double ratio = bitmap.getWidth() / (columnWidth * 1.0);
				int scaledHeight = (int) (bitmap.getHeight() / ratio);
				addImage(bitmap, columnWidth, scaledHeight);
			}
			taskCollection.remove(this);
		}

		/**
		 * 根据传入的URL,对图片进行加载。如果这张图片已经存在于SD卡中,则直接从SD卡里读取,否则就从网络上下载。
		 * 
		 * @param imageUrl
		 *            图片的URL地址
		 * @return 加载到内存的图片。
		 */
		private Bitmap loadImage(String imageUrl) {
			File imageFile = new File(getImagePath(imageUrl));
			if (!imageFile.exists()) {
				downloadImage(imageUrl);
			}
			if (imageUrl != null) {
				Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(
						imageFile.getPath(), columnWidth);
				if (bitmap != null) {
					imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
					return bitmap;
				}
			}
			return null;
		}

		/**
		 * 向ImageView中添加一张图片
		 * 
		 * @param bitmap
		 *            待添加的图片
		 * @param imageWidth
		 *            图片的宽度
		 * @param imageHeight
		 *            图片的高度
		 */
		private void addImage(Bitmap bitmap, int imageWidth, int imageHeight) {
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
					imageWidth, imageHeight);
			if (mImageView != null) {
				mImageView.setImageBitmap(bitmap);
			} else {
				ImageView imageView = new ImageView(getContext());
				imageView.setLayoutParams(params);
				imageView.setImageBitmap(bitmap);
				imageView.setScaleType(ScaleType.FIT_XY);
				imageView.setPadding(5, 5, 5, 5);
				imageView.setTag(R.string.image_url, mImageUrl);
				TextView textview=new TextView(getContext());
				
				findColumnToAdd(imageView, imageHeight).addView(imageView);
				imageViewList.add(imageView);
			}
		}

		/**
		 * 找到此时应该添加图片的一列。原则就是对三列的高度进行判断,当前高度最小的一列就是应该添加的一列。
		 * 
		 * @param imageView
		 * @param imageHeight
		 * @return 应该添加图片的一列
		 */
		private LinearLayout findColumnToAdd(ImageView imageView,
				int imageHeight) {
			if (firstColumnHeight <= secondColumnHeight) {
				if (firstColumnHeight <= thirdColumnHeight) {
					imageView.setTag(R.string.border_top, firstColumnHeight);
					firstColumnHeight += imageHeight;
					imageView.setTag(R.string.border_bottom, firstColumnHeight);
					return firstColumn;
				}
				imageView.setTag(R.string.border_top, thirdColumnHeight);
				thirdColumnHeight += imageHeight;
				imageView.setTag(R.string.border_bottom, thirdColumnHeight);
				return thirdColumn;
			} else {
				if (secondColumnHeight <= thirdColumnHeight) {
					imageView.setTag(R.string.border_top, secondColumnHeight);
					secondColumnHeight += imageHeight;
					imageView
							.setTag(R.string.border_bottom, secondColumnHeight);
					return secondColumn;
				}
				imageView.setTag(R.string.border_top, thirdColumnHeight);
				thirdColumnHeight += imageHeight;
				imageView.setTag(R.string.border_bottom, thirdColumnHeight);
				return thirdColumn;
			}
		}

		/**
		 * 将图片下载到SD卡缓存起来。
		 * 
		 * @param imageUrl
		 *            图片的URL地址。
		 */
		private void downloadImage(String imageUrl) {
			if (Environment.getExternalStorageState().equals(
					Environment.MEDIA_MOUNTED)) {
				Log.d("TAG", "monted sdcard");
			} else {
				Log.d("TAG", "has no sdcard");
			}
			HttpURLConnection con = null;
			FileOutputStream fos = null;
			BufferedOutputStream bos = null;
			BufferedInputStream bis = null;
			File imageFile = null;
			try {
				URL url = new URL(imageUrl);
				con = (HttpURLConnection) url.openConnection();
				con.setConnectTimeout(5 * 1000);
				con.setReadTimeout(15 * 1000);
				con.setDoInput(true);
				con.setDoOutput(true);
				bis = new BufferedInputStream(con.getInputStream());
				imageFile = new File(getImagePath(imageUrl));
				fos = new FileOutputStream(imageFile);
				bos = new BufferedOutputStream(fos);
				byte[] b = new byte[1024];
				int length;
				while ((length = bis.read(b)) != -1) {
					bos.write(b, 0, length);
					bos.flush();
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					if (bis != null) {
						bis.close();
					}
					if (bos != null) {
						bos.close();
					}
					if (con != null) {
						con.disconnect();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (imageFile != null) {
				Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(
						imageFile.getPath(), columnWidth);
				if (bitmap != null) {
					imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
				}
			}
		}

		/**
		 * 获取图片的本地存储路径。
		 * 
		 * @param imageUrl
		 *            图片的URL地址。
		 * @return 图片的本地存储路径。
		 */
		private String getImagePath(String imageUrl) {
			int lastSlashIndex = imageUrl.lastIndexOf("/");
			String imageName = imageUrl.substring(lastSlashIndex + 1);
			String imageDir = Environment.getExternalStorageDirectory()
					.getPath() + "/PhotoWallFalls/";
			File file = new File(imageDir);
			if (!file.exists()) {
				file.mkdirs();
			}
			String imagePath = imageDir + imageName;
			return imagePath;
		}
	}

}

 

 

 

 

 

activity_main.xml:

 

<com.example.photowallfallsdemo.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_scroll_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <LinearLayout
            android:id="@+id/first_column"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="#4BADED"
            android:layout_weight="1"
            android:orientation="vertical" >
        </LinearLayout>

        <LinearLayout
            android:id="@+id/second_column"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="#4BADED"
            android:layout_weight="1"
            android:orientation="vertical" >
        </LinearLayout>

        <LinearLayout
            android:id="@+id/third_column"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="#4BADED"
            android:layout_weight="1"
            android:orientation="vertical" >
        </LinearLayout>
    </LinearLayout>

</com.example.photowallfallsdemo.MyScrollView>

 

 

 

 

 

 

  • 大小: 83.6 KB
分享到:
评论

相关推荐

    android 瀑布流效果

    在Android中实现瀑布流效果,通常有两种主流方法:自定义ViewGroup和使用第三方库。下面将详细探讨这两种方法。 1. 自定义ViewGroup 自定义ViewGroup是最基础的方法,需要继承LinearLayout、GridView或者...

    Android 瀑布流 显示效果

    下面我们将深入探讨如何在Android中实现瀑布流显示效果。 1. **基本概念** - **瀑布流布局(Waterfall Layout)**:一种不规则的网格布局,每个元素的宽度固定,但高度可以根据内容自适应,形成类似瀑布下落的效果...

    Android瀑布流加载图片效果Demo

    2. **布局管理器**:在RecyclerView中,我们需要自定义一个布局管理器,如`StaggeredGridLayoutManager`,它可以创建不均匀的网格布局,即瀑布流效果。默认的LinearLayoutManager无法实现这种效果。 3. **图片加载...

    android 瀑布流Demo

    在Android开发中实现瀑布流效果,我们可以使用多种方法,包括自定义布局和使用第三方库。以下将详细介绍如何在Android中创建一个瀑布流Demo: 1. 自定义布局: - **GridView**: 虽然GridView默认是等宽的网格布局...

    Android应用源码之 瀑布流 Demo.zip

    这个"Android应用源码之瀑布流 Demo.zip"提供的源码应该是一个简单的Android项目,用于演示如何实现瀑布流效果。下面我们将深入探讨瀑布流布局的基本原理、实现方式以及可能涉及的关键技术点。 1. **瀑布流布局概念...

    android瀑布流容器

    瀑布流布局在Android开发中是一种常见且流行的设计模式,它主要用于展示内容丰富的列表,如商品、图片或文章等。这种布局方式模拟了真实的...通过实践和不断学习,我们可以构建出功能丰富、视觉效果出色的瀑布流应用。

    Android瀑布流布局实现

    首先,我们可以使用Google官方推出的RecyclerView配合GridLayoutManager来实现基础的瀑布流效果。RecyclerView是一个高效的列表视图,它可以动态加载数据并支持各种复杂的布局。GridLayoutManager则可以设置为网格...

    Android加载网络瀑布流二

    与ListView相比,RecyclerView提供了更灵活的LayoutManager,如GridLayoutManager用于实现瀑布流效果。开发者需要掌握RecyclerView的使用,包括创建Adapter、绑定数据、定义ItemDecoration来实现间距和分割线。 ...

    Android 瀑布流照片墙

    在本项目中,可能使用了RecyclerView并定制了一个瀑布流布局的LayoutManager,如StaggeredGridLayoutManager,它可以轻松实现不规则的网格布局,即瀑布流效果。 为了展示图片,项目中可能使用了Picasso、Glide或者...

    Android应用源码之瀑布流DEMO源码

    在Android中,通常使用`GridLayoutManager`或者自定义的布局管理器来实现瀑布流效果,因为它们能够支持多列布局和不同高度的条目。 其次,DEMO可能包含一个`Adapter`类,它负责将数据集转换为视图。在瀑布流布局中...

    android瀑布流listview

    在Android开发中,瀑布流(Waterfall Flow)布局是一种常见的展示方式,特别是在电商应用、社交应用等场景下,用于展示图片或信息丰富的列表。瀑布流布局的特点是每一列的高度不固定,根据内容自适应,使得视觉效果...

    Android 自定义瀑布流式布局

    通过上述知识点的学习和实践,开发者可以创建一个高效、灵活的Android自定义瀑布流布局。在`app3`这个项目中,我们可以预见到包含了一个自定义的ViewGroup,可能还会有相关的Adapter实现数据绑定,以及图片加载库的...

    安卓Android源码——瀑布流源码.zip

    通过深入理解这些知识点,并结合提供的"源码说明.txt",开发者可以更好地理解和学习瀑布流布局的实现,从而在自己的项目中运用。同时,"Android+瀑布流+Demo.rar"可能是实现瀑布流布局的示例代码,可以作为学习和...

    android瀑布流标签

    瀑布流布局在Android开发中是一种常见的布局方式,它模拟了网页设计中的Pinterest风格,使得屏幕上的元素能够以多列、自适应高度的方式排列,形成一种类似瀑布倾泻的效果。在这个场景下,"标签"通常指的是用于分类...

    android瀑布流效果

    在Android中实现瀑布流效果通常有以下几种方法: 1. **GridView + 自定义Adapter**:虽然GridView默认是等宽等高的网格布局,但通过自定义Adapter和测量每个子View的大小,可以实现不规则的瀑布流布局。这种方法...

    Android瀑布流demo

    1. **布局管理器**:Android系统提供了多种布局管理器,如LinearLayout、RelativeLayout、GridLayout等,但这些默认的布局管理器无法直接实现瀑布流效果。因此,我们需要自定义一个布局管理器,或者使用第三方库,如...

    Android 瀑布流源码.zip

    解压后,里面可能包含了一个完整的Android Studio工程,包括必要的XML布局文件、Java或Kotlin源代码、资源文件等,你可以通过运行这个项目来理解和学习瀑布流的实现方式。 3. `源码说明.txt`:这可能是一个简短的...

    安卓瀑布流相关-android平台仿pinterest瀑布流展现方式实现.zip

    3. `layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom)`:这个方法用于设置子View的装饰边距,比如间距和外边距,以便创建出美观的瀑布流效果。 在实现过程中,我们还需要考虑...

    Android 瀑布流开源框架

    瀑布流布局在移动应用开发中广泛用于展示图片、商品或信息列表,因为它能有效地利用屏幕空间,提供良好的视觉效果。Android 瀑布流开源框架是开发者为了简化这种布局实现而创建的工具,它们通常提供了丰富的功能和...

Global site tag (gtag.js) - Google Analytics