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>
相关推荐
在Android中实现瀑布流效果,通常有两种主流方法:自定义ViewGroup和使用第三方库。下面将详细探讨这两种方法。 1. 自定义ViewGroup 自定义ViewGroup是最基础的方法,需要继承LinearLayout、GridView或者...
下面我们将深入探讨如何在Android中实现瀑布流显示效果。 1. **基本概念** - **瀑布流布局(Waterfall Layout)**:一种不规则的网格布局,每个元素的宽度固定,但高度可以根据内容自适应,形成类似瀑布下落的效果...
2. **布局管理器**:在RecyclerView中,我们需要自定义一个布局管理器,如`StaggeredGridLayoutManager`,它可以创建不均匀的网格布局,即瀑布流效果。默认的LinearLayoutManager无法实现这种效果。 3. **图片加载...
在Android开发中实现瀑布流效果,我们可以使用多种方法,包括自定义布局和使用第三方库。以下将详细介绍如何在Android中创建一个瀑布流Demo: 1. 自定义布局: - **GridView**: 虽然GridView默认是等宽的网格布局...
这个"Android应用源码之瀑布流 Demo.zip"提供的源码应该是一个简单的Android项目,用于演示如何实现瀑布流效果。下面我们将深入探讨瀑布流布局的基本原理、实现方式以及可能涉及的关键技术点。 1. **瀑布流布局概念...
瀑布流布局在Android开发中是一种常见且流行的设计模式,它主要用于展示内容丰富的列表,如商品、图片或文章等。这种布局方式模拟了真实的...通过实践和不断学习,我们可以构建出功能丰富、视觉效果出色的瀑布流应用。
首先,我们可以使用Google官方推出的RecyclerView配合GridLayoutManager来实现基础的瀑布流效果。RecyclerView是一个高效的列表视图,它可以动态加载数据并支持各种复杂的布局。GridLayoutManager则可以设置为网格...
与ListView相比,RecyclerView提供了更灵活的LayoutManager,如GridLayoutManager用于实现瀑布流效果。开发者需要掌握RecyclerView的使用,包括创建Adapter、绑定数据、定义ItemDecoration来实现间距和分割线。 ...
在本项目中,可能使用了RecyclerView并定制了一个瀑布流布局的LayoutManager,如StaggeredGridLayoutManager,它可以轻松实现不规则的网格布局,即瀑布流效果。 为了展示图片,项目中可能使用了Picasso、Glide或者...
在Android中,通常使用`GridLayoutManager`或者自定义的布局管理器来实现瀑布流效果,因为它们能够支持多列布局和不同高度的条目。 其次,DEMO可能包含一个`Adapter`类,它负责将数据集转换为视图。在瀑布流布局中...
在Android开发中,瀑布流(Waterfall Flow)布局是一种常见的展示方式,特别是在电商应用、社交应用等场景下,用于展示图片或信息丰富的列表。瀑布流布局的特点是每一列的高度不固定,根据内容自适应,使得视觉效果...
通过上述知识点的学习和实践,开发者可以创建一个高效、灵活的Android自定义瀑布流布局。在`app3`这个项目中,我们可以预见到包含了一个自定义的ViewGroup,可能还会有相关的Adapter实现数据绑定,以及图片加载库的...
通过深入理解这些知识点,并结合提供的"源码说明.txt",开发者可以更好地理解和学习瀑布流布局的实现,从而在自己的项目中运用。同时,"Android+瀑布流+Demo.rar"可能是实现瀑布流布局的示例代码,可以作为学习和...
瀑布流布局在Android开发中是一种常见的布局方式,它模拟了网页设计中的Pinterest风格,使得屏幕上的元素能够以多列、自适应高度的方式排列,形成一种类似瀑布倾泻的效果。在这个场景下,"标签"通常指的是用于分类...
在Android中实现瀑布流效果通常有以下几种方法: 1. **GridView + 自定义Adapter**:虽然GridView默认是等宽等高的网格布局,但通过自定义Adapter和测量每个子View的大小,可以实现不规则的瀑布流布局。这种方法...
1. **布局管理器**:Android系统提供了多种布局管理器,如LinearLayout、RelativeLayout、GridLayout等,但这些默认的布局管理器无法直接实现瀑布流效果。因此,我们需要自定义一个布局管理器,或者使用第三方库,如...
解压后,里面可能包含了一个完整的Android Studio工程,包括必要的XML布局文件、Java或Kotlin源代码、资源文件等,你可以通过运行这个项目来理解和学习瀑布流的实现方式。 3. `源码说明.txt`:这可能是一个简短的...
3. `layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom)`:这个方法用于设置子View的装饰边距,比如间距和外边距,以便创建出美观的瀑布流效果。 在实现过程中,我们还需要考虑...
瀑布流布局在移动应用开发中广泛用于展示图片、商品或信息列表,因为它能有效地利用屏幕空间,提供良好的视觉效果。Android 瀑布流开源框架是开发者为了简化这种布局实现而创建的工具,它们通常提供了丰富的功能和...