Android瀑布流照片墙实现

来源:互联网 发布:方正字库 mac 编辑:程序博客网 时间:2024/04/30 10:46

瀑布流的布局方式虽然看起来好像排列的很随意,其实它是有排列规则的。整个界面会根据屏幕的宽度划分成等宽的若干列,由于手机的屏幕不是很大,这里我们就分成三列。每当需要添加一张图片时,会将这张图片的宽度压缩成和列一样宽,再按照同样的压缩比例对图片的高度进行压缩,然后在这三列中找出当前高度最小的一列,将图片添加到这一列中。之后每当需要添加一张新图片时,都去重复上面的操作,就会形成瀑布流格局的照片墙,示意图如下所示。


新建一个ImageLoader类,用于方便对图片进行管理,代码如下所示:

[java] view plaincopy
  1. package com.example.photowall;  
  2.   
  3. import android.graphics.Bitmap;  
  4. import android.graphics.BitmapFactory;  
  5. import android.util.LruCache;  
  6.   
  7. /** 
  8.  * 对图片进行管理的工具类。 
  9.  *  
  10.  * @author Tony 
  11.  */  
  12. public class ImageLoader {  
  13.   
  14.     /** 
  15.      * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。 
  16.      */  
  17.     private static LruCache<String, Bitmap> mMemoryCache;  
  18.   
  19.     /** 
  20.      * ImageLoader的实例。 
  21.      */  
  22.     private static ImageLoader mImageLoader;  
  23.       
  24.     /** 
  25.      * 所有图片url 
  26.      */  
  27.     public final static String[] imageUrls = new String[] {  
  28.         "http://img.my.csdn.net/uploads/201309/01/1378037235_3453.jpg",  
  29.         "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg",  
  30.         "http://img.my.csdn.net/uploads/201309/01/1378037235_9280.jpg",  
  31.         "http://img.my.csdn.net/uploads/201309/01/1378037234_3539.jpg",  
  32.         "http://img.my.csdn.net/uploads/201309/01/1378037234_6318.jpg" };  
  33.       
  34.     private ImageLoader() {  
  35.         // 获取应用程序最大可用内存  
  36.         int maxMemory = (int) Runtime.getRuntime().maxMemory();  
  37.         int cacheSize = maxMemory / 8;  
  38.         // 设置图片缓存大小为程序最大可用内存的1/8  
  39.         mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  40.             @Override  
  41.             protected int sizeOf(String key, Bitmap bitmap) {  
  42.                 return bitmap.getByteCount();  
  43.             }  
  44.         };  
  45.     }  
  46.   
  47.     /** 
  48.      * 获取ImageLoader的实例。 
  49.      *  
  50.      * @return ImageLoader的实例。 
  51.      */  
  52.     public static ImageLoader getInstance() {  
  53.         if (mImageLoader == null) {  
  54.             mImageLoader = new ImageLoader();  
  55.         }  
  56.         return mImageLoader;  
  57.     }  
  58.   
  59.     /** 
  60.      * 将一张图片存储到LruCache中。 
  61.      *  
  62.      * @param key 
  63.      *            LruCache的键,这里传入图片的URL地址。 
  64.      * @param bitmap 
  65.      *            LruCache的键,这里传入从网络上下载的Bitmap对象。 
  66.      */  
  67.     public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
  68.         if (getBitmapFromMemoryCache(key) == null) {  
  69.             mMemoryCache.put(key, bitmap);  
  70.         }  
  71.     }  
  72.   
  73.     /** 
  74.      * 从LruCache中获取一张图片,如果不存在就返回null。 
  75.      *  
  76.      * @param key 
  77.      *            LruCache的键,这里传入图片的URL地址。 
  78.      * @return 对应传入键的Bitmap对象,或者null。 
  79.      */  
  80.     public Bitmap getBitmapFromMemoryCache(String key) {  
  81.         return mMemoryCache.get(key);  
  82.     }  
  83.   
  84.     public static int calculateInSampleSize(BitmapFactory.Options options,  
  85.             int reqWidth) {  
  86.         // 源图片的宽度  
  87.         final int width = options.outWidth;  
  88.         int inSampleSize = 1;  
  89.         if (width > reqWidth) {  
  90.             // 计算出实际宽度和目标宽度的比率  
  91.             final int widthRatio = Math.round((float) width / (float) reqWidth);  
  92.             inSampleSize = widthRatio;  
  93.         }  
  94.         return inSampleSize;  
  95.     }  
  96.   
  97.     public static Bitmap decodeSampledBitmapFromResource(String pathName,  
  98.             int reqWidth) {  
  99.         // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小  
  100.         final BitmapFactory.Options options = new BitmapFactory.Options();  
  101.         options.inJustDecodeBounds = true;  
  102.         BitmapFactory.decodeFile(pathName, options);  
  103.         // 调用上面定义的方法计算inSampleSize值  
  104.         options.inSampleSize = calculateInSampleSize(options, reqWidth);  
  105.         // 使用获取到的inSampleSize值再次解析图片  
  106.         options.inJustDecodeBounds = false;  
  107.         return BitmapFactory.decodeFile(pathName, options);  
  108.     }  
  109.   
  110. }  
这里我们将ImageLoader类设成单例,并在构造函数中初始化了LruCache类,把它的最大缓存容量设为最大可用内存的1/8。然后又提供了其它几个方法可以操作LruCache,以及对图片进行压缩和读取。
接下来新建PhotoWallScrollView继承自ScrollView,代码如下所示:

[java] view plaincopy
  1. package com.example.photowall;  
  2.   
  3. import java.io.BufferedInputStream;  
  4. import java.io.BufferedOutputStream;  
  5. import java.io.File;  
  6. import java.io.FileOutputStream;  
  7. import java.io.IOException;  
  8. import java.net.HttpURLConnection;  
  9. import java.net.URL;  
  10. import java.util.ArrayList;  
  11. import java.util.HashSet;  
  12. import java.util.List;  
  13. import java.util.Set;  
  14.   
  15. import android.content.Context;  
  16. import android.graphics.Bitmap;  
  17. import android.os.AsyncTask;  
  18. import android.os.Environment;  
  19. import android.os.Handler;  
  20. import android.os.Message;  
  21. import android.util.AttributeSet;  
  22. import android.util.Log;  
  23. import android.view.MotionEvent;  
  24. import android.view.View;  
  25. import android.view.View.OnTouchListener;  
  26. import android.widget.ImageView;  
  27. import android.widget.ImageView.ScaleType;  
  28. import android.widget.LinearLayout;  
  29. import android.widget.ScrollView;  
  30. import android.widget.Toast;  
  31.   
  32. /** 
  33.  * 自定义的ScrollView,在其中动态地对图片进行添加。 
  34.  *  
  35.  */  
  36. public class PhotoWallScrollView extends ScrollView implements OnTouchListener {  
  37.   
  38.     /** 
  39.      * 每页要加载的图片数量 
  40.      */  
  41.     public static final int PAGE_SIZE = 15;  
  42.   
  43.     /** 
  44.      * 记录当前已加载到第几页 
  45.      */  
  46.     private int page;  
  47.   
  48.     /** 
  49.      * 每一列的宽度 
  50.      */  
  51.     private int columnWidth;  
  52.   
  53.     /** 
  54.      * 当前第一列的高度 
  55.      */  
  56.     private int firstColumnHeight;  
  57.   
  58.     /** 
  59.      * 当前第二列的高度 
  60.      */  
  61.     private int secondColumnHeight;  
  62.   
  63.     /** 
  64.      * 当前第三列的高度 
  65.      */  
  66.     private int thirdColumnHeight;  
  67.   
  68.     /** 
  69.      * 是否已加载过一次layout,这里onLayout中的初始化只需加载一次 
  70.      */  
  71.     private boolean loadOnce;  
  72.   
  73.     /** 
  74.      * 对图片进行管理的工具类 
  75.      */  
  76.     private ImageLoader imageLoader;  
  77.   
  78.     /** 
  79.      * 第一列的布局 
  80.      */  
  81.     private LinearLayout firstColumn;  
  82.   
  83.     /** 
  84.      * 第二列的布局 
  85.      */  
  86.     private LinearLayout secondColumn;  
  87.   
  88.     /** 
  89.      * 第三列的布局 
  90.      */  
  91.     private LinearLayout thirdColumn;  
  92.   
  93.     /** 
  94.      * 记录所有正在下载或等待下载的任务。 
  95.      */  
  96.     private static Set<LoadImageTask> taskCollection;  
  97.   
  98.     /** 
  99.      * MyScrollView下的直接子布局。 
  100.      */  
  101.     private static View scrollLayout;  
  102.   
  103.     /** 
  104.      * MyScrollView布局的高度。 
  105.      */  
  106.     private static int scrollViewHeight;  
  107.   
  108.     /** 
  109.      * 记录上垂直方向的滚动距离。 
  110.      */  
  111.     private static int lastScrollY = -1;  
  112.   
  113.     /** 
  114.      * 记录所有界面上的图片,用以可以随时控制对图片的释放。 
  115.      */  
  116.     private List<ImageView> imageViewList = new ArrayList<ImageView>();  
  117.   
  118.     /** 
  119.      * 在Handler中进行图片可见性检查的判断,以及加载更多图片的操作。 
  120.      */  
  121.     private static Handler handler = new Handler() {  
  122.   
  123.         public void handleMessage(android.os.Message msg) {  
  124.             PhotoWallScrollView myScrollView = (PhotoWallScrollView) msg.obj;  
  125.             int scrollY = myScrollView.getScrollY();  
  126.             // 如果当前的滚动位置和上次相同,表示已停止滚动  
  127.             if (scrollY == lastScrollY) {  
  128.                 // 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片  
  129.                 if (scrollViewHeight + scrollY >= scrollLayout.getHeight()  
  130.                         && taskCollection.isEmpty()) {  
  131.                     myScrollView.loadMoreImages();  
  132.                 }  
  133.                 myScrollView.checkVisibility();  
  134.             } else {  
  135.                 lastScrollY = scrollY;  
  136.                 Message message = new Message();  
  137.                 message.obj = myScrollView;  
  138.                 // 5毫秒后再次对滚动位置进行判断  
  139.                 handler.sendMessageDelayed(message, 5);  
  140.             }  
  141.         };  
  142.   
  143.     };  
  144.   
  145.     /** 
  146.      * PhotoWallScrollView的构造函数。 
  147.      *  
  148.      * @param context 
  149.      * @param attrs 
  150.      */  
  151.     public PhotoWallScrollView(Context context, AttributeSet attrs) {  
  152.         super(context, attrs);  
  153.         imageLoader = ImageLoader.getInstance();  
  154.         taskCollection = new HashSet<LoadImageTask>();  
  155.         setOnTouchListener(this);  
  156.     }  
  157.   
  158.     /** 
  159.      * 进行一些关键性的初始化操作,获取MyScrollView的高度,以及得到第一列的宽度值。并在这里开始加载第一页的图片。 
  160.      */  
  161.     @Override  
  162.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  163.         super.onLayout(changed, l, t, r, b);  
  164.         if (changed && !loadOnce) {  
  165.             scrollViewHeight = getHeight();  
  166.             scrollLayout = getChildAt(0);  
  167.             firstColumn = (LinearLayout) findViewById(R.id.first_column);  
  168.             secondColumn = (LinearLayout) findViewById(R.id.second_column);  
  169.             thirdColumn = (LinearLayout) findViewById(R.id.third_column);  
  170.             columnWidth = firstColumn.getWidth();  
  171.             loadOnce = true;  
  172.             loadMoreImages();  
  173.         }  
  174.     }  
  175.   
  176.     /** 
  177.      * 监听用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。 
  178.      */  
  179.     @Override  
  180.     public boolean onTouch(View v, MotionEvent event) {  
  181.         if (event.getAction() == MotionEvent.ACTION_UP) {  
  182.             Message message = new Message();  
  183.             message.obj = this;  
  184.             handler.sendMessageDelayed(message, 5);  
  185.         }  
  186.         return false;  
  187.     }  
  188.   
  189.     /** 
  190.      * 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。 
  191.      */  
  192.     public void loadMoreImages() {  
  193.         if (hasSDCard()) {  
  194.             int startIndex = page * PAGE_SIZE;  
  195.             int endIndex = page * PAGE_SIZE + PAGE_SIZE;  
  196.             if (startIndex < ImageLoader.imageUrls.length) {  
  197.                 Toast.makeText(getContext(), "正在加载...", Toast.LENGTH_SHORT).show();  
  198.                 if (endIndex > ImageLoader.imageUrls.length) {  
  199.                     endIndex = ImageLoader.imageUrls.length;  
  200.                 }  
  201.                 for (int i = startIndex; i < endIndex; i++) {  
  202.                     LoadImageTask task = new LoadImageTask();  
  203.                     taskCollection.add(task);  
  204.                     task.execute(ImageLoader.imageUrls[i]);  
  205.                 }  
  206.                 page++;  
  207.             } else {  
  208.                 Toast.makeText(getContext(), "已没有更多图片", Toast.LENGTH_SHORT)  
  209.                         .show();  
  210.             }  
  211.         } else {  
  212.             Toast.makeText(getContext(), "未发现SD卡", Toast.LENGTH_SHORT).show();  
  213.         }  
  214.     }  
  215.   
  216.     /** 
  217.      * 遍历imageViewList中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。 
  218.      */  
  219.     public void checkVisibility() {  
  220.         for (int i = 0; i < imageViewList.size(); i++) {  
  221.             ImageView imageView = imageViewList.get(i);  
  222.             int borderTop = (Integer) imageView.getTag(R.string.border_top);  
  223.             int borderBottom = (Integer) imageView  
  224.                     .getTag(R.string.border_bottom);  
  225.             if (borderBottom > getScrollY()  
  226.                     && borderTop < getScrollY() + scrollViewHeight) {  
  227.                 String imageUrl = (String) imageView.getTag(R.string.image_url);  
  228.                 Bitmap bitmap = imageLoader.getBitmapFromMemoryCache(imageUrl);  
  229.                 if (bitmap != null) {  
  230.                     imageView.setImageBitmap(bitmap);  
  231.                 } else {  
  232.                     LoadImageTask task = new LoadImageTask(imageView);  
  233.                     task.execute(imageUrl);  
  234.                 }  
  235.             } else {  
  236.                 imageView.setImageResource(R.drawable.empty_photo);  
  237.             }  
  238.         }  
  239.     }  
  240.   
  241.     /** 
  242.      * 判断手机是否有SD卡。 
  243.      *  
  244.      * @return 有SD卡返回true,没有返回false。 
  245.      */  
  246.     private boolean hasSDCard() {  
  247.         return Environment.MEDIA_MOUNTED.equals(Environment  
  248.                 .getExternalStorageState());  
  249.     }  
  250.   
  251.     /** 
  252.      * 异步下载图片的任务。 
  253.      *  
  254.      * @author guolin 
  255.      */  
  256.     class LoadImageTask extends AsyncTask<String, Void, Bitmap> {  
  257.   
  258.         /** 
  259.          * 图片的URL地址 
  260.          */  
  261.         private String mImageUrl;  
  262.   
  263.         /** 
  264.          * 可重复使用的ImageView 
  265.          */  
  266.         private ImageView mImageView;  
  267.   
  268.         public LoadImageTask() {  
  269.         }  
  270.   
  271.         /** 
  272.          * 将可重复使用的ImageView传入 
  273.          *  
  274.          * @param imageView 
  275.          */  
  276.         public LoadImageTask(ImageView imageView) {  
  277.             mImageView = imageView;  
  278.         }  
  279.   
  280.         @Override  
  281.         protected Bitmap doInBackground(String... params) {  
  282.             mImageUrl = params[0];  
  283.             Bitmap imageBitmap = imageLoader.getBitmapFromMemoryCache(mImageUrl);  
  284.             if (imageBitmap == null) {  
  285.                 imageBitmap = loadImage(mImageUrl);  
  286.             }  
  287.             return imageBitmap;  
  288.         }  
  289.   
  290.         @Override  
  291.         protected void onPostExecute(Bitmap bitmap) {  
  292.             if (bitmap != null) {  
  293.                 double ratio = bitmap.getWidth() / (columnWidth * 1.0);  
  294.                 int scaledHeight = (int) (bitmap.getHeight() / ratio);  
  295.                 addImage(bitmap, columnWidth, scaledHeight);  
  296.             }  
  297.             taskCollection.remove(this);  
  298.         }  
  299.   
  300.         /** 
  301.          * 根据传入的URL,对图片进行加载。如果这张图片已经存在于SD卡中,则直接从SD卡里读取,否则就从网络上下载。 
  302.          *  
  303.          * @param imageUrl 
  304.          *            图片的URL地址 
  305.          * @return 加载到内存的图片。 
  306.          */  
  307.         private Bitmap loadImage(String imageUrl) {  
  308.             File imageFile = new File(getImagePath(imageUrl));  
  309.             if (!imageFile.exists()) {  
  310.                 downloadImage(imageUrl);  
  311.             }  
  312.             if (imageUrl != null) {  
  313.                 Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(imageFile.getPath(), columnWidth);  
  314.                 if (bitmap != null) {  
  315.                     imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);  
  316.                     return bitmap;  
  317.                 }  
  318.             }  
  319.             return null;  
  320.         }  
  321.   
  322.         /** 
  323.          * 向ImageView中添加一张图片 
  324.          *  
  325.          * @param bitmap 
  326.          *            待添加的图片 
  327.          * @param imageWidth 
  328.          *            图片的宽度 
  329.          * @param imageHeight 
  330.          *            图片的高度 
  331.          */  
  332.         private void addImage(Bitmap bitmap, int imageWidth, int imageHeight) {  
  333.             LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(  
  334.                     imageWidth, imageHeight);  
  335.             if (mImageView != null) {  
  336.                 mImageView.setImageBitmap(bitmap);  
  337.             } else {  
  338.                 ImageView imageView = new ImageView(getContext());  
  339.                 imageView.setLayoutParams(params);  
  340.                 imageView.setImageBitmap(bitmap);  
  341.                 imageView.setScaleType(ScaleType.FIT_XY);  
  342.                 imageView.setPadding(5555);  
  343.                 imageView.setTag(R.string.image_url, mImageUrl);  
  344.                 findColumnToAdd(imageView, imageHeight).addView(imageView);  
  345.                 imageViewList.add(imageView);  
  346.             }  
  347.         }  
  348.   
  349.         /** 
  350.          * 找到此时应该添加图片的一列。原则就是对三列的高度进行判断,当前高度最小的一列就是应该添加的一列。 
  351.          *  
  352.          * @param imageView 
  353.          * @param imageHeight 
  354.          * @return 应该添加图片的一列 
  355.          */  
  356.         private LinearLayout findColumnToAdd(ImageView imageView,  
  357.                 int imageHeight) {  
  358.             if (firstColumnHeight <= secondColumnHeight) {  
  359.                 if (firstColumnHeight <= thirdColumnHeight) {  
  360.                     imageView.setTag(R.string.border_top, firstColumnHeight);  
  361.                     firstColumnHeight += imageHeight;  
  362.                     imageView.setTag(R.string.border_bottom, firstColumnHeight);  
  363.                     return firstColumn;  
  364.                 }  
  365.                 imageView.setTag(R.string.border_top, thirdColumnHeight);  
  366.                 thirdColumnHeight += imageHeight;  
  367.                 imageView.setTag(R.string.border_bottom, thirdColumnHeight);  
  368.                 return thirdColumn;  
  369.             } else {  
  370.                 if (secondColumnHeight <= thirdColumnHeight) {  
  371.                     imageView.setTag(R.string.border_top, secondColumnHeight);  
  372.                     secondColumnHeight += imageHeight;  
  373.                     imageView  
  374.                             .setTag(R.string.border_bottom, secondColumnHeight);  
  375.                     return secondColumn;  
  376.                 }  
  377.                 imageView.setTag(R.string.border_top, thirdColumnHeight);  
  378.                 thirdColumnHeight += imageHeight;  
  379.                 imageView.setTag(R.string.border_bottom, thirdColumnHeight);  
  380.                 return thirdColumn;  
  381.             }  
  382.         }  
  383.   
  384.         /** 
  385.          * 将图片下载到SD卡缓存起来。 
  386.          *  
  387.          * @param imageUrl 
  388.          *            图片的URL地址。 
  389.          */  
  390.         private void downloadImage(String imageUrl) {  
  391.             if (Environment.getExternalStorageState().equals(  
  392.                     Environment.MEDIA_MOUNTED)) {  
  393.                 Log.d("TAG""monted sdcard");  
  394.             } else {  
  395.                 Log.d("TAG""has no sdcard");  
  396.             }  
  397.             HttpURLConnection con = null;  
  398.             FileOutputStream fos = null;  
  399.             BufferedOutputStream bos = null;  
  400.             BufferedInputStream bis = null;  
  401.             File imageFile = null;  
  402.             try {  
  403.                 URL url = new URL(imageUrl);  
  404.                 con = (HttpURLConnection) url.openConnection();  
  405.                 con.setConnectTimeout(5 * 1000);  
  406.                 con.setReadTimeout(15 * 1000);  
  407.                 con.setDoInput(true);  
  408.                 con.setDoOutput(true);  
  409.                 bis = new BufferedInputStream(con.getInputStream());  
  410.                 imageFile = new File(getImagePath(imageUrl));  
  411.                 fos = new FileOutputStream(imageFile);  
  412.                 bos = new BufferedOutputStream(fos);  
  413.                 byte[] b = new byte[1024];  
  414.                 int length;  
  415.                 while ((length = bis.read(b)) != -1) {  
  416.                     bos.write(b, 0, length);  
  417.                     bos.flush();  
  418.                 }  
  419.             } catch (Exception e) {  
  420.                 e.printStackTrace();  
  421.             } finally {  
  422.                 try {  
  423.                     if (bis != null) {  
  424.                         bis.close();  
  425.                     }  
  426.                     if (bos != null) {  
  427.                         bos.close();  
  428.                     }  
  429.                     if (con != null) {  
  430.                         con.disconnect();  
  431.                     }  
  432.                 } catch (IOException e) {  
  433.                     e.printStackTrace();  
  434.                 }  
  435.             }  
  436.             if (imageFile != null) {  
  437.                 Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(  
  438.                         imageFile.getPath(), columnWidth);  
  439.                 if (bitmap != null) {  
  440.                     imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);  
  441.                 }  
  442.             }  
  443.         }  
  444.   
  445.         /** 
  446.          * 获取图片的本地存储路径。 
  447.          *  
  448.          * @param imageUrl 
  449.          *            图片的URL地址。 
  450.          * @return 图片的本地存储路径。 
  451.          */  
  452.         private String getImagePath(String imageUrl) {  
  453.             int lastSlashIndex = imageUrl.lastIndexOf("/");  
  454.             String imageName = imageUrl.substring(lastSlashIndex + 1);  
  455.             String imageDir = Environment.getExternalStorageDirectory()  
  456.                     .getPath() + "/PhotoWallFalls/";  
  457.             File file = new File(imageDir);  
  458.             if (!file.exists()) {  
  459.                 file.mkdirs();  
  460.             }  
  461.             String imagePath = imageDir + imageName;  
  462.             return imagePath;  
  463.         }  
  464.     }  
  465.   
  466. }  
设置瀑布流的布局方式,如下所示:

[java] view plaincopy
  1. <com.example.photowall.PhotoWallScrollView xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:id="@+id/my_scroll_view"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <LinearLayout  
  7.         android:layout_width="match_parent"  
  8.         android:layout_height="wrap_content"  
  9.         android:orientation="horizontal" >  
  10.   
  11.         <LinearLayout  
  12.             android:id="@+id/first_column"  
  13.             android:layout_width="0dp"  
  14.             android:layout_height="wrap_content"  
  15.             android:layout_weight="1"  
  16.             android:orientation="vertical" >  
  17.         </LinearLayout>  
  18.   
  19.         <LinearLayout  
  20.             android:id="@+id/second_column"  
  21.             android:layout_width="0dp"  
  22.             android:layout_height="wrap_content"  
  23.             android:layout_weight="1"  
  24.             android:orientation="vertical" >  
  25.         </LinearLayout>  
  26.   
  27.         <LinearLayout  
  28.             android:id="@+id/third_column"  
  29.             android:layout_width="0dp"  
  30.             android:layout_height="wrap_content"  
  31.             android:layout_weight="1"  
  32.             android:orientation="vertical" >  
  33.         </LinearLayout>  
  34.     </LinearLayout>  
  35.   
  36. </com.example.photowall.PhotoWallScrollView>  
使用了刚才编写好的PhotoWallScrollView作为根布局,然后在里面放入了一个直接子布局LinearLayout用于统计当前滑动布局的高度,然后在这个布局下又添加了三个等宽的LinearLayout分别作为第一列、第二列和第三列的布局,在AndroidManifest.xml中添加以下权限:

[java] view plaincopy
  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
  2. <uses-permission android:name="android.permission.INTERNET" />  
0 0