国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

Android瀑布流照片墻實(shí)現(xiàn) 體驗(yàn)不規(guī)則排列的美感

2019-12-12 05:02:21
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

傳統(tǒng)界面的布局方式總是行列分明、坐落有序的,這種布局已是司空見(jiàn)慣,在不知不覺(jué)中大家都已經(jīng)對(duì)它產(chǎn)生了審美疲勞。這個(gè)時(shí)候瀑布流布局的出現(xiàn),就給人帶來(lái)了耳目一新的感覺(jué),這種布局雖然看上去貌似毫無(wú)規(guī)律,但是卻有一種說(shuō)不上來(lái)的美感,以至于涌現(xiàn)出了大批的網(wǎng)站和應(yīng)用紛紛使用這種新穎的布局來(lái)設(shè)計(jì)界面。

記得我在之前已經(jīng)寫過(guò)一篇關(guān)于如何在Android上實(shí)現(xiàn)照片墻功能的文章了,但那個(gè)時(shí)候是使用的GridView來(lái)進(jìn)行布局的,這種布局方式只適用于“墻”上的每張圖片大小都相同的情況,如果圖片的大小參差不齊,在GridView中顯示就會(huì)非常的難看。而使用瀑布流的布局方式就可以很好地解決這個(gè)問(wèn)題,因此今天我們也來(lái)趕一下潮流,看看如何在Android上實(shí)現(xiàn)瀑布流照片墻的功能。

首先還是講一下實(shí)現(xiàn)原理,瀑布流的布局方式雖然看起來(lái)好像排列的很隨意,其實(shí)它是有很科學(xué)的排列規(guī)則的。整個(gè)界面會(huì)根據(jù)屏幕的寬度劃分成等寬的若干列,由于手機(jī)的屏幕不是很大,這里我們就分成三列。每當(dāng)需要添加一張圖片時(shí),會(huì)將這張圖片的寬度壓縮成和列一樣寬,再按照同樣的壓縮比例對(duì)圖片的高度進(jìn)行壓縮,然后在這三列中找出當(dāng)前高度最小的一列,將圖片添加到這一列中。之后每當(dāng)需要添加一張新圖片時(shí),都去重復(fù)上面的操作,就會(huì)形成瀑布流格局的照片墻,示意圖如下所示。

聽(tīng)我這么說(shuō)完后,你可能會(huì)覺(jué)得瀑布流的布局非常簡(jiǎn)單嘛,只需要使用三個(gè)LinearLayout平分整個(gè)屏幕寬度,然后動(dòng)態(tài)地addView()進(jìn)去就好了。確實(shí)如此,如果只是為了實(shí)現(xiàn)功能的話,就是這么簡(jiǎn)單。可是別忘了,我們是在手機(jī)上進(jìn)行開(kāi)發(fā),如果不停地往LinearLayout里添加圖片,程序很快就會(huì)OOM。因此我們還需要一個(gè)合理的方案來(lái)對(duì)圖片資源進(jìn)行釋放,這里仍然是準(zhǔn)備使用LruCache算法,對(duì)這個(gè)算法不熟悉的朋友可以先參考Android高效加載大圖、多圖方案,有效避免程序OOM

下面我們就來(lái)開(kāi)始實(shí)現(xiàn)吧,新建一個(gè)Android項(xiàng)目,起名叫PhotoWallFallsDemo,并選擇4.0的API。

第一個(gè)要考慮的問(wèn)題是,我們到哪兒去收集這些大小參差不齊的圖片呢?這里我事先在百度上搜索了很多張風(fēng)景圖片,并且為了保證它們?cè)L問(wèn)的穩(wěn)定性,我將這些圖片都上傳到了我的CSDN相冊(cè)里,因此只要從這里下載圖片就可以了。新建一個(gè)Images類,將所有相冊(cè)中圖片的網(wǎng)址都配置進(jìn)去,代碼如下所示:

public class Images {   public final static String[] imageUrls = new String[] {  "http://img.my.csdn.net/uploads/201309/01/1378037235_3453.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037235_9280.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037234_3539.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037234_6318.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037194_2965.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037193_1687.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037193_1286.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037192_8379.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037178_9374.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037177_1254.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037177_6203.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037152_6352.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037151_9565.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037151_7904.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037148_7104.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037129_8825.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037128_5291.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037128_3531.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037127_1085.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037095_7515.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037094_8001.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037093_7168.jpg",  "http://img.my.csdn.net/uploads/201309/01/1378037091_4950.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949643_6410.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949642_6939.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949630_4505.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949630_4593.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949629_7309.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949629_8247.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949615_1986.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949614_8482.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949614_3743.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949614_4199.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949599_3416.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949599_5269.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949598_7858.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949598_9982.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949578_2770.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949578_8744.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949577_5210.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949577_1998.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949482_8813.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949481_6577.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949480_4490.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949455_6792.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949455_6345.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949442_4553.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949441_8987.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949441_5454.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949454_6367.jpg",  "http://img.my.csdn.net/uploads/201308/31/1377949442_4562.jpg" }; } 

然后新建一個(gè)ImageLoader類,用于方便對(duì)圖片進(jìn)行管理,代碼如下所示:

public class ImageLoader {   /**  * 圖片緩存技術(shù)的核心類,用于緩存所有下載好的圖片,在程序內(nèi)存達(dá)到設(shè)定值時(shí)會(huì)將最少最近使用的圖片移除掉。  */  private static LruCache<String, Bitmap> mMemoryCache;   /**  * ImageLoader的實(shí)例。  */  private static ImageLoader mImageLoader;   private ImageLoader() {  // 獲取應(yīng)用程序最大可用內(nèi)存  int maxMemory = (int) Runtime.getRuntime().maxMemory();  int cacheSize = maxMemory / 8;  // 設(shè)置圖片緩存大小為程序最大可用內(nèi)存的1/8  mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  @Override  protected int sizeOf(String key, Bitmap bitmap) {  return bitmap.getByteCount();  }  };  }   /**  * 獲取ImageLoader的實(shí)例。  *  * @return ImageLoader的實(shí)例。  */  public static ImageLoader getInstance() {  if (mImageLoader == null) {  mImageLoader = new ImageLoader();  }  return mImageLoader;  }   /**  * 將一張圖片存儲(chǔ)到LruCache中。  *  * @param key  * LruCache的鍵,這里傳入圖片的URL地址。  * @param bitmap  * LruCache的鍵,這里傳入從網(wǎng)絡(luò)上下載的Bitmap對(duì)象。  */  public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  if (getBitmapFromMemoryCache(key) == null) {  mMemoryCache.put(key, bitmap);  }  }   /**  * 從LruCache中獲取一張圖片,如果不存在就返回null。  *  * @param key  * LruCache的鍵,這里傳入圖片的URL地址。  * @return 對(duì)應(yīng)傳入鍵的Bitmap對(duì)象,或者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) {  // 計(jì)算出實(shí)際寬度和目標(biāo)寬度的比率  final int widthRatio = Math.round((float) width / (float) reqWidth);  inSampleSize = widthRatio;  }  return inSampleSize;  }   public static Bitmap decodeSampledBitmapFromResource(String pathName,  int reqWidth) {  // 第一次解析將inJustDecodeBounds設(shè)置為true,來(lái)獲取圖片大小  final BitmapFactory.Options options = new BitmapFactory.Options();  options.inJustDecodeBounds = true;  BitmapFactory.decodeFile(pathName, options);  // 調(diào)用上面定義的方法計(jì)算inSampleSize值  options.inSampleSize = calculateInSampleSize(options, reqWidth);  // 使用獲取到的inSampleSize值再次解析圖片  options.inJustDecodeBounds = false;  return BitmapFactory.decodeFile(pathName, options);  }  } 

這里我們將ImageLoader類設(shè)成單例,并在構(gòu)造函數(shù)中初始化了LruCache類,把它的最大緩存容量設(shè)為最大可用內(nèi)存的1/8。然后又提供了其它幾個(gè)方法可以操作LruCache,以及對(duì)圖片進(jìn)行壓縮和讀取。

接下來(lái)新建MyScrollView繼承自ScrollView,代碼如下所示:

public class MyScrollView extends ScrollView implements OnTouchListener {   /**  * 每頁(yè)要加載的圖片數(shù)量  */  public static final int PAGE_SIZE = 15;   /**  * 記錄當(dāng)前已加載到第幾頁(yè)  */  private int page;   /**  * 每一列的寬度  */  private int columnWidth;   /**  * 當(dāng)前第一列的高度  */  private int firstColumnHeight;   /**  * 當(dāng)前第二列的高度  */  private int secondColumnHeight;   /**  * 當(dāng)前第三列的高度  */  private int thirdColumnHeight;   /**  * 是否已加載過(guò)一次layout,這里onLayout中的初始化只需加載一次  */  private boolean loadOnce;   /**  * 對(duì)圖片進(jìn)行管理的工具類  */  private ImageLoader imageLoader;   /**  * 第一列的布局  */  private LinearLayout firstColumn;   /**  * 第二列的布局  */  private LinearLayout secondColumn;   /**  * 第三列的布局  */  private LinearLayout thirdColumn;   /**  * 記錄所有正在下載或等待下載的任務(wù)。  */  private static Set<LoadImageTask> taskCollection;   /**  * MyScrollView下的直接子布局。  */  private static View scrollLayout;   /**  * MyScrollView布局的高度。  */  private static int scrollViewHeight;   /**  * 記錄上垂直方向的滾動(dòng)距離。  */  private static int lastScrollY = -1;   /**  * 記錄所有界面上的圖片,用以可以隨時(shí)控制對(duì)圖片的釋放。  */  private List<ImageView> imageViewList = new ArrayList<ImageView>();   /**  * 在Handler中進(jìn)行圖片可見(jiàn)性檢查的判斷,以及加載更多圖片的操作。  */  private static Handler handler = new Handler() {   public void handleMessage(android.os.Message msg) {  MyScrollView myScrollView = (MyScrollView) msg.obj;  int scrollY = myScrollView.getScrollY();  // 如果當(dāng)前的滾動(dòng)位置和上次相同,表示已停止?jié)L動(dòng)  if (scrollY == lastScrollY) {  // 當(dāng)滾動(dòng)的最底部,并且當(dāng)前沒(méi)有正在下載的任務(wù)時(shí),開(kāi)始加載下一頁(yè)的圖片  if (scrollViewHeight + scrollY >= scrollLayout.getHeight()   && taskCollection.isEmpty()) {   myScrollView.loadMoreImages();  }  myScrollView.checkVisibility();  } else {  lastScrollY = scrollY;  Message message = new Message();  message.obj = myScrollView;  // 5毫秒后再次對(duì)滾動(dòng)位置進(jìn)行判斷  handler.sendMessageDelayed(message, 5);  }  };   };   /**  * MyScrollView的構(gòu)造函數(shù)。  *  * @param context  * @param attrs  */  public MyScrollView(Context context, AttributeSet attrs) {  super(context, attrs);  imageLoader = ImageLoader.getInstance();  taskCollection = new HashSet<LoadImageTask>();  setOnTouchListener(this);  }   /**  * 進(jìn)行一些關(guān)鍵性的初始化操作,獲取MyScrollView的高度,以及得到第一列的寬度值。并在這里開(kāi)始加載第一頁(yè)的圖片。  */  @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;  loadMoreImages();  }  }   /**  * 監(jiān)聽(tīng)用戶的觸屏事件,如果用戶手指離開(kāi)屏幕則開(kāi)始進(jìn)行滾動(dòng)檢測(cè)。  */  @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;  }   /**  * 開(kāi)始加載下一頁(yè)的圖片,每張圖片都會(huì)開(kāi)啟一個(gè)異步線程去下載。  */  public void loadMoreImages() {  if (hasSDCard()) {  int startIndex = page * PAGE_SIZE;  int endIndex = page * PAGE_SIZE + PAGE_SIZE;  if (startIndex < Images.imageUrls.length) {  Toast.makeText(getContext(), "正在加載...", Toast.LENGTH_SHORT)   .show();  if (endIndex > Images.imageUrls.length) {   endIndex = Images.imageUrls.length;  }  for (int i = startIndex; i < endIndex; i++) {   LoadImageTask task = new LoadImageTask();   taskCollection.add(task);   task.execute(Images.imageUrls[i]);  }  page++;  } else {  Toast.makeText(getContext(), "已沒(méi)有更多圖片", Toast.LENGTH_SHORT)   .show();  }  } else {  Toast.makeText(getContext(), "未發(fā)現(xiàn)SD卡", Toast.LENGTH_SHORT).show();  }  }   /**  * 遍歷imageViewList中的每張圖片,對(duì)圖片的可見(jiàn)性進(jìn)行檢查,如果圖片已經(jīng)離開(kāi)屏幕可見(jiàn)范圍,則將圖片替換成一張空?qǐng)D。  */  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);  }  }  }   /**  * 判斷手機(jī)是否有SD卡。  *  * @return 有SD卡返回true,沒(méi)有返回false。  */  private boolean hasSDCard() {  return Environment.MEDIA_MOUNTED.equals(Environment  .getExternalStorageState());  }   /**  * 異步下載圖片的任務(wù)。  *  * @author guolin  */  class LoadImageTask extends AsyncTask<String, Void, Bitmap> {   /**  * 圖片的URL地址  */  private String mImageUrl;   /**  * 可重復(fù)使用的ImageView  */  private ImageView mImageView;   public LoadImageTask() {  }   /**  * 將可重復(fù)使用的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);  }   /**  * 根據(jù)傳入的URL,對(duì)圖片進(jìn)行加載。如果這張圖片已經(jīng)存在于SD卡中,則直接從SD卡里讀取,否則就從網(wǎng)絡(luò)上下載。  *  * @param imageUrl  * 圖片的URL地址  * @return 加載到內(nèi)存的圖片。  */  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);  findColumnToAdd(imageView, imageHeight).addView(imageView);  imageViewList.add(imageView);  }  }   /**  * 找到此時(shí)應(yīng)該添加圖片的一列。原則就是對(duì)三列的高度進(jìn)行判斷,當(dāng)前高度最小的一列就是應(yīng)該添加的一列。  *  * @param imageView  * @param imageHeight  * @return 應(yīng)該添加圖片的一列  */  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卡緩存起來(lái)。  *  * @param imageUrl  * 圖片的URL地址。  */  private void downloadImage(String imageUrl) {  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);  }  }  }   /**  * 獲取圖片的本地存儲(chǔ)路徑。  *  * @param imageUrl  * 圖片的URL地址。  * @return 圖片的本地存儲(chǔ)路徑。  */  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;  }  }  } 

MyScrollView是實(shí)現(xiàn)瀑布流照片墻的核心類,這里我來(lái)重點(diǎn)給大家介紹一下。首先它是繼承自ScrollView的,這樣就允許用戶可以通過(guò)滾動(dòng)的方式來(lái)瀏覽更多的圖片。這里提供了一個(gè)loadMoreImages()方法,是專門用于加載下一頁(yè)的圖片的,因此在onLayout()方法中我們要先調(diào)用一次這個(gè)方法,以初始化第一頁(yè)的圖片。然后在onTouch方法中每當(dāng)監(jiān)聽(tīng)到手指離開(kāi)屏幕的事件,就會(huì)通過(guò)一個(gè)handler來(lái)對(duì)當(dāng)前ScrollView的滾動(dòng)狀態(tài)進(jìn)行判斷,如果發(fā)現(xiàn)已經(jīng)滾動(dòng)到了最底部,就會(huì)再次調(diào)用loadMoreImages()方法去加載下一頁(yè)的圖片。

那我們就要來(lái)看一看loadMoreImages()方法的內(nèi)部細(xì)節(jié)了。在這個(gè)方法中,使用了一個(gè)循環(huán)來(lái)加載這一頁(yè)中的每一張圖片,每次都會(huì)開(kāi)啟一個(gè)LoadImageTask,用于對(duì)圖片進(jìn)行異步加載。然后在LoadImageTask中,首先會(huì)先檢查一下這張圖片是不是已經(jīng)存在于SD卡中了,如果還沒(méi)存在,就從網(wǎng)絡(luò)上下載,然后把這張圖片存放在LruCache中。接著將這張圖按照一定的比例進(jìn)行壓縮,并找出當(dāng)前高度最小的一列,把壓縮后的圖片添加進(jìn)去就可以了。

另外,為了保證照片墻上的圖片都能夠合適地被回收,這里還加入了一個(gè)可見(jiàn)性檢查的方法,即checkVisibility()方法。這個(gè)方法的核心思想就是檢查目前照片墻上的所有圖片,判斷出哪些是可見(jiàn)的,哪些是不可見(jiàn)。然后將那些不可見(jiàn)的圖片都替換成一張空?qǐng)D,這樣就可以保證程序始終不會(huì)占用過(guò)高的內(nèi)存。當(dāng)這些圖片又重新變?yōu)榭梢?jiàn)的時(shí)候,只需要再?gòu)腖ruCache中將這些圖片重新取出即可。如果某張圖片已經(jīng)從LruCache中被移除了,就會(huì)開(kāi)啟一個(gè)LoadImageTask,將這張圖片重新加載到內(nèi)存中。

然后打開(kāi)或新建activity_main.xml,在里面設(shè)置好瀑布流的布局方式,如下所示:

<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:layout_weight="1"  android:orientation="vertical" >  </LinearLayout>   <LinearLayout  android:id="@+id/second_column"  android:layout_width="0dp"  android:layout_height="wrap_content"  android:layout_weight="1"  android:orientation="vertical" >  </LinearLayout>   <LinearLayout  android:id="@+id/third_column"  android:layout_width="0dp"  android:layout_height="wrap_content"  android:layout_weight="1"  android:orientation="vertical" >  </LinearLayout>  </LinearLayout>  </com.example.photowallfallsdemo.MyScrollView> 

可以看到,這里我們使用了剛才編寫好的MyScrollView作為根布局,然后在里面放入了一個(gè)直接子布局LinearLayout用于統(tǒng)計(jì)當(dāng)前滑動(dòng)布局的高度,然后在這個(gè)布局下又添加了三個(gè)等寬的LinearLayout分別作為第一列、第二列和第三列的布局,這樣在MyScrollView中就可以動(dòng)態(tài)地向這三個(gè)LinearLayout里添加圖片了。

最后,由于我們使用到了網(wǎng)絡(luò)和SD卡存儲(chǔ)的功能,因此還需要在AndroidManifest.xml中添加以下權(quán)限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> 

這樣我們所有的編碼工作就已經(jīng)完成了,現(xiàn)在可以嘗試運(yùn)行一下,效果如下圖所示:

瀑布流模式的照片墻果真非常美觀吧,而且由于我們有非常完善的資源釋放機(jī)制,不管你在照片墻上添加了多少圖片,程序占用內(nèi)存始終都會(huì)保持在一個(gè)合理的范圍內(nèi)。在下一篇文章中,我會(huì)帶著大家對(duì)這個(gè)程序進(jìn)行進(jìn)一步的完善,加入點(diǎn)擊查看大圖,以及多點(diǎn)觸控縮放的功能,感覺(jué)興趣的朋友請(qǐng)繼續(xù)閱讀Android多點(diǎn)觸控技術(shù)實(shí)戰(zhàn),自由地對(duì)圖片進(jìn)行縮放和移動(dòng)

源碼下載:http://xiazai.VeVB.COm/201610/yuanma/AndroidPhotoWallFalls(VeVB.COm).rar

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 乐都县| 巴塘县| 马关县| 长沙县| 迁西县| 友谊县| 开平市| 沅江市| 佳木斯市| 元朗区| 滨海县| 芜湖市| 泽库县| 桃源县| 漳平市| 怀柔区| 东至县| 黄石市| 台安县| 平舆县| 临城县| 正镶白旗| 怀远县| 光山县| 于都县| 郯城县| 新民市| 伊通| 体育| 甘洛县| 商丘市| 乐都县| 怀安县| 正定县| 昭觉县| 教育| 浦北县| 水城县| 视频| 咸阳市| 海丰县|