Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android使用ViewPager實現畫廊效果的加強版照片牆

Android使用ViewPager實現畫廊效果的加強版照片牆

編輯:Android開發實例

記得關於照片牆的文章我已經寫過好幾篇了,有最基本的照片牆,有瀑布流模式的照片牆,後來又在瀑布流的基礎之上加入了查看大圖和多點觸控縮放的功能。總體來說,照片牆這個Demo在這幾篇文章的改進中已經變得較為完善了,本想關於這個功能的系列到此為止,但有朋友跟我反應,覺得在查看大圖的時候最好能通過左右滑動來浏覽前後的圖片。恩,確實,好像比較高端的一些應用都有這樣的效果,那麼本篇文章中我們來繼續對照片牆這個Demo進行改進,讓它變得更加高端大氣上檔次!

整理了一下思路,感覺自己去實現一套通過左右滑動來切換圖片的功能非常不劃算,需要編寫不少的代碼。這裡為了要讓實現簡單化,我們准備使用Android提供的ViewPager來完成這個功能。

ViewPager的基本用法我就不在本文中介紹了,如果還不了解的朋友可以看http://www.fengfly.com/plus/view-215108-1.html 。

另外,本篇文章的代碼是完全在之前文章的基礎上進行開發的,所以如果你還沒有看過我前面所寫的關於照片牆的文章,建議先去閱讀一下http://www.fengfly.com/plus/view-214550-1.html和 http://www.fengfly.com/plus/view-214551-1.html這兩篇文章。

下面就讓我們開始動手吧,打開PhotoWallFallsDemo這個項目,首先修改image_details.xml這個布局文件中的代碼,如下所示:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:layout_width="match_parent" 
  4.     android:layout_height="match_parent" > 
  5.  
  6.     <android.support.v4.view.ViewPager 
  7.         android:id="@+id/view_pager" 
  8.         android:layout_width="match_parent" 
  9.         android:layout_height="match_parent" > 
  10.     </android.support.v4.view.ViewPager> 
  11.  
  12.     <TextView 
  13.         android:id="@+id/page_text" 
  14.         android:layout_width="wrap_content" 
  15.         android:layout_height="wrap_content" 
  16.         android:layout_alignParentBottom="true" 
  17.         android:layout_centerHorizontal="true" 
  18.         android:layout_marginBottom="10dp" 
  19.         android:textColor="#fff" 
  20.         android:textSize="18sp" /> 
  21.  
  22. </RelativeLayout> 

這裡我們在布局文件中放置了兩個控件,ViewPager和TextView,其中ViewPager自然是用來管理所有的圖片的了,而TextView則是用於顯示當前圖片的頁數以及總頁數。

 

然後新建一個zoom_image_layout.xml,在這裡放入ZoomImageView控件,如下所示:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <com.example.photowallfallsdemo.ZoomImageView xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:id="@+id/zoom_image_view" 
  4.     android:layout_width="match_parent" 
  5.     android:layout_height="match_parent" 
  6.     android:background="#000000" > 
  7.  
  8. </com.example.photowallfallsdemo.ZoomImageView> 

 

接下來修改ImageDetailsActivity中的代碼,在這裡去實現ViewPager的具體功能,代碼如下所示:

  1. public class ImageDetailsActivity extends Activity implements OnPageChangeListener {  
  2.  
  3.     /**  
  4.      * 用於管理圖片的滑動  
  5.      */ 
  6.     private ViewPager viewPager;  
  7.  
  8.     /**  
  9.      * 顯示當前圖片的頁數  
  10.      */ 
  11.     private TextView pageText;  
  12.  
  13.     @Override 
  14.     protected void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  17.         setContentView(R.layout.image_details);  
  18.         int imagePosition = getIntent().getIntExtra("image_position", 0);  
  19.         pageText = (TextView) findViewById(R.id.page_text);  
  20.         viewPager = (ViewPager) findViewById(R.id.view_pager);  
  21.         ViewPagerAdapter adapter = new ViewPagerAdapter();  
  22.         viewPager.setAdapter(adapter);  
  23.         viewPager.setCurrentItem(imagePosition);  
  24.         viewPager.setOnPageChangeListener(this);  
  25.         // 設定當前的頁數和總頁數  
  26.         pageText.setText((imagePosition + 1) + "/" + Images.imageUrls.length);  
  27.     }  
  28.  
  29.     /**  
  30.      * ViewPager的適配器  
  31.      *   
  32.      * @author guolin  
  33.      */ 
  34.     class ViewPagerAdapter extends PagerAdapter {  
  35.  
  36.         @Override 
  37.         public Object instantiateItem(ViewGroup container, int position) {  
  38.             String imagePath = getImagePath(Images.imageUrls[position]);  
  39.             Bitmap bitmap = BitmapFactory.decodeFile(imagePath);  
  40.             if (bitmap == null) {  
  41.                 bitmap = BitmapFactory.decodeResource(getResources(),  
  42.                         R.drawable.empty_photo);  
  43.             }  
  44.             View view = LayoutInflater.from(ImageDetailsActivity.this).inflate(  
  45.                     R.layout.zoom_image_layout, null);  
  46.             ZoomImageView zoomImageView = (ZoomImageView) view  
  47.                     .findViewById(R.id.zoom_image_view);  
  48.             zoomImageView.setImageBitmap(bitmap);  
  49.             container.addView(view);  
  50.             return view;  
  51.         }  
  52.  
  53.         @Override 
  54.         public int getCount() {  
  55.             return Images.imageUrls.length;  
  56.         }  
  57.  
  58.         @Override 
  59.         public boolean isViewFromObject(View arg0, Object arg1) {  
  60.             return arg0 == arg1;  
  61.         }  
  62.  
  63.         @Override 
  64.         public void destroyItem(ViewGroup container, int position, Object object) {  
  65.             View view = (View) object;  
  66.             container.removeView(view);  
  67.         }  
  68.  
  69.     }  
  70.  
  71.     /**  
  72.      * 獲取圖片的本地存儲路徑。  
  73.      *   
  74.      * @param imageUrl  
  75.      *            圖片的URL地址。  
  76.      * @return 圖片的本地存儲路徑。  
  77.      */ 
  78.     private String getImagePath(String imageUrl) {  
  79.         int lastSlashIndex = imageUrl.lastIndexOf("/");  
  80.         String imageName = imageUrl.substring(lastSlashIndex + 1);  
  81.         String imageDir = Environment.getExternalStorageDirectory().getPath()  
  82.                 + "/PhotoWallFalls/";  
  83.         File file = new File(imageDir);  
  84.         if (!file.exists()) {  
  85.             file.mkdirs();  
  86.         }  
  87.         String imagePath = imageDir + imageName;  
  88.         return imagePath;  
  89.     }  
  90.  
  91.     @Override 
  92.     public void onPageScrollStateChanged(int arg0) {  
  93.  
  94.     }  
  95.  
  96.     @Override 
  97.     public void onPageScrolled(int arg0, float arg1, int arg2) {  
  98.  
  99.     }  
  100.  
  101.     @Override 
  102.     public void onPageSelected(int currentPage) {  
  103.         // 每當頁數發生改變時重新設定一遍當前的頁數和總頁數  
  104.         pageText.setText((currentPage + 1) + "/" + Images.imageUrls.length);  
  105.     }  
  106.  
  107. }  

這個類也是實現滑動切換圖片功能最主要的一個類了,由於ViewPager的用法並不復雜,所以這個類的代碼也不多,下面我們來仔細地分析一下。

 

首先在onCreate()方法中要去加載我們剛剛修改的image_details.xml布局,然後要從Intent中取出當前要展示的那張圖片的位置。接下來通過findViewById()方法獲取到ViewPager和TextView控件的實例,並創建了一個ViewPagerAdapter對象作為ViewPager的適配器,之後去調用setCurrentItem()方法來設置當前顯示的是哪一張圖片。

那麼這個ViewPagerAdapter又是什麼呢?可以看到,它是一個繼承了PagerAdapter的適配器,是專門用於在ViewPager中使用的。一般情況下我們都需要至少去重寫PagerAdapter中的instantiateItem()、getCount()、isViewFromObject()和destroyItem()這四個方法。在instantiateItem()方法中,我們根據圖片的位置獲取到了圖片對應的存儲路徑,然後調用BitmapFactory的解析方法將這張圖片解析成一個Bitmap對象,接著實例化zoom_image_layout.xml這個布局,並獲取其中的ZoomImageView控件,然後把Bitmap對象設定進去。在getCount()方法,只是簡單地返回了一共有多少張圖片。isViewFromObject()方法比較簡單,就是判斷兩個參數是否相等就好。而destroyItem()方法中,則是要把應該銷毀的View對象回收掉,以防止圖片過多導致OOM出現。

另外,這裡的ViewPager還注冊了OnPageChangeListener接口,每當ViewPager的頁數發現改變時,onPageSelected()方法就會調用。我們在這裡讓TextView顯示當前圖片的頁數以及總頁數即可。

目前的ImageDetailsActivity已經具備了翻頁浏覽圖片的功能了,如果你心急的話,可以現在就運行試一試。不過一但你運行之後,就會發現,我們還有一些細節工作還沒完成。比如說在onCreate()方法中會從Intent中取出要顯示的那張圖片的位置,而很明顯目前是取不到了。於是,我們還需要修改MyScrollView中的代碼,在這裡將點擊的那張圖片的位置傳遞過來。由於這個類中的代碼非常多,我只列出需要修改的那些部分,如下所示:

  1. public class MyScrollView extends ScrollView implements OnTouchListener {  
  2.  
  3.     ......  
  4.  
  5.     /**  
  6.      * 開始加載下一頁的圖片,每張圖片都會開啟一個異步線程去下載。  
  7.      */ 
  8.     public void loadMoreImages() {  
  9.         if (hasSDCard()) {  
  10.             int startIndex = page * PAGE_SIZE;  
  11.             int endIndex = page * PAGE_SIZE + PAGE_SIZE;  
  12.             if (startIndex < Images.imageUrls.length) {  
  13.                 Toast.makeText(getContext(), "正在加載...", Toast.LENGTH_SHORT).show();  
  14.                 if (endIndex > Images.imageUrls.length) {  
  15.                     endIndex = Images.imageUrls.length;  
  16.                 }  
  17.                 for (int i = startIndex; i < endIndex; i++) {  
  18.                     LoadImageTask task = new LoadImageTask();  
  19.                     taskCollection.add(task);  
  20.                     task.execute(i);  
  21.                 }  
  22.                 page++;  
  23.             } else {  
  24.                 Toast.makeText(getContext(), "已沒有更多圖片", Toast.LENGTH_SHORT).show();  
  25.             }  
  26.         } else {  
  27.             Toast.makeText(getContext(), "未發現SD卡", Toast.LENGTH_SHORT).show();  
  28.         }  
  29.     }  
  30.  
  31.     /**  
  32.      * 遍歷imageViewList中的每張圖片,對圖片的可見性進行檢查,如果圖片已經離開屏幕可見范圍,則將圖片替換成一張空圖。  
  33.      */ 
  34.     public void checkVisibility() {  
  35.         for (int i = 0; i < imageViewList.size(); i++) {  
  36.             ImageView imageView = imageViewList.get(i);  
  37.             int borderTop = (Integer) imageView.getTag(R.string.border_top);  
  38.             int borderBottom = (Integer) imageView.getTag(R.string.border_bottom);  
  39.             if (borderBottom > getScrollY() && borderTop < getScrollY() + scrollViewHeight) {  
  40.                 String imageUrl = (String) imageView.getTag(R.string.image_url);  
  41.                 Bitmap bitmap = imageLoader.getBitmapFromMemoryCache(imageUrl);  
  42.                 if (bitmap != null) {  
  43.                     imageView.setImageBitmap(bitmap);  
  44.                 } else {  
  45.                     LoadImageTask task = new LoadImageTask(imageView);  
  46.                     task.execute(i);  
  47.                 }  
  48.             } else {  
  49.                 imageView.setImageResource(R.drawable.empty_photo);  
  50.             }  
  51.         }  
  52.     }  
  53.  
  54.     ......  
  55.  
  56.     /**  
  57.      * 異步下載圖片的任務。  
  58.      *   
  59.      * @author guolin  
  60.      */ 
  61.     class LoadImageTask extends AsyncTask<Integer, Void, Bitmap> {  
  62.  
  63.         /**  
  64.          * 記錄每個圖片對應的位置  
  65.          */ 
  66.         private int mItemPosition;  
  67.  
  68.         ......  
  69.  
  70.         @Override 
  71.         protected Bitmap doInBackground(Integer... params) {  
  72.             mItemPosition = params[0];  
  73.             mImageUrl = Images.imageUrls[mItemPosition];  
  74.             Bitmap imageBitmap = imageLoader.getBitmapFromMemoryCache(mImageUrl);  
  75.             if (imageBitmap == null) {  
  76.                 imageBitmap = loadImage(mImageUrl);  
  77.             }  
  78.             return imageBitmap;  
  79.         }  
  80.  
  81.         ......  
  82.  
  83.         /**  
  84.          * 向ImageView中添加一張圖片  
  85.          *   
  86.          * @param bitmap  
  87.          *            待添加的圖片  
  88.          * @param imageWidth  
  89.          *            圖片的寬度  
  90.          * @param imageHeight  
  91.          *            圖片的高度  
  92.          */ 
  93.         private void addImage(Bitmap bitmap, int imageWidth, int imageHeight) {  
  94.             LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(imageWidth,  
  95.                     imageHeight);  
  96.             if (mImageView != null) {  
  97.                 mImageView.setImageBitmap(bitmap);  
  98.             } else {  
  99.                 ImageView imageView = new ImageView(getContext());  
  100.                 imageView.setLayoutParams(params);  
  101.                 imageView.setImageBitmap(bitmap);  
  102.                 imageView.setScaleType(ScaleType.FIT_XY);  
  103.                 imageView.setPadding(5, 5, 5, 5);  
  104.                 imageView.setTag(R.string.image_url, mImageUrl);  
  105.                 imageView.setOnClickListener(new OnClickListener() {  
  106.                     @Override 
  107.                     public void onClick(View v) {  
  108.                         Intent intent = new Intent(getContext(), ImageDetailsActivity.class);  
  109.                         intent.putExtra("image_position", mItemPosition);  
  110.                         getContext().startActivity(intent);  
  111.                     }  
  112.                 });  
  113.                 findColumnToAdd(imageView, imageHeight).addView(imageView);  
  114.                 imageViewList.add(imageView);  
  115.             }  
  116.         }  
  117.           
  118.         ......  
  119.           
  120.     }  
  121.  
  122. }  

可以看到,這裡我們將LoadImageTask 的泛型進行了修改,doInBackground不再接收一個字符串數組,而是接收一個整型數組,這裡傳入的參數也就代表著每張圖片的位置。這樣的話,每個調用LoadImageTask 的地方也都需要進行相應的修改,在loadMoreImages()和checkVisibility()方法中,都將傳入的參數改成了圖片的位置。最後在addImage()方法中,使用Intent將點擊的那張圖片對應的位置傳遞給了ImageDetailsActivity。

 

目前看上去一切都完美了吧!但其實還有一點工作我們還沒完成。由於ViewPager的事件和ZoomImageView的事件是存在沖突的,所以加入了ViewPager後,ZoomImageView本身的單個手指拖動圖片的功能會受很大的影響。所以我們還需要在ZoomImageView的onTouchEvent()中進行判斷,如果當前的圖片是沒有縮放的,則允許通過滑動來切換圖片,如果當前的圖片已經放大了,則要屏蔽掉ViewPager的事件,這樣ZoomImageView本身的事件就不會受影響。代碼如下所示:

  1. public class ZoomImageView extends View {  
  2.  
  3.     ......  
  4.  
  5.     @Override 
  6.     public boolean onTouchEvent(MotionEvent event) {  
  7.         if (initRatio == totalRatio) {  
  8.             getParent().requestDisallowInterceptTouchEvent(false);  
  9.         } else {  
  10.             getParent().requestDisallowInterceptTouchEvent(true);  
  11.         }  
  12.         ......  
  13.         return true;  
  14.     }  
  15.  
  16.     ......  
  17.  
  18. }  

這裡使用getParent()獲取到的就是ViewPager對象,然後調用requestDisallowInterceptTouchEvent()方法來啟動和禁用ViewPager的功能。

 

好了,這樣的話,所有的代碼就已經完成了,可以運行一下看看完整的效果了。點擊任意一張圖片可以查看大圖,然後通過左右滑動可以浏覽前後的圖片,並且仍然能夠通過多點觸控對圖片進行縮放,效果如下圖所示:

                                                       
 

除了滑動切換圖片之外,在屏幕的底部還能顯示當前圖片的頁數以及總頁數,功能已經是相當完善了。目前這個照片牆Demo的效果已經不亞於市場上一些常見的圖片浏覽程序了吧。

源碼下載,請點擊這裡
 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved