转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/12646775
记得关于照片墙的文章我已经写过好几篇了,有最基本的照片墙,有瀑布流模式的照片墙,后来又在瀑布流的基础之上加入了查看大图和多点触控缩放的功能。总体来说,照片墙这个Demo在这几篇文章的改进中已经变得较为完善了,本想关于这个功能的系列到此为止,但有朋友跟我反应,觉得在查看大图的时候最好能通过左右滑动来浏览前后的图片。恩,确实,好像比较高端的一些应用都有这样的效果,那么本篇文章中我们来继续对照片墙这个Demo进行改进,让它变得更加高端大气上档次!
整理了一下思路,感觉自己去实现一套通过左右滑动来切换图片的功能非常不划算,需要编写不少的代码。这里为了要让实现简单化,我们准备使用Android提供的ViewPager来完成这个功能。
ViewPager的基本用法我就不在本文中介绍了,如果还不了解的朋友可以到王鹏兄那里先学习一下 http://blog.csdn.net/wangjinyu501/article/details/8169924 。
另外,本篇文章的代码是完全在之前文章的基础上进行开发的,所以如果你还没有看过我前面所写的关于照片墙的文章,建议先去阅读一下 Android瀑布流照片墙实现,体验不规则排列的美感 和 Android多点触控技术实战,自由地对图片进行缩放和移动 这两篇文章。
下面就让我们开始动手吧,打开PhotoWallFallsDemo这个项目,首先修改image_details.xml这个布局文件中的代码,如下所示:
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <android.support.v4.view.ViewPager
- android:id="@+id/view_pager"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- </android.support.v4.view.ViewPager>
- <TextView
- android:id="@+id/page_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="10dp"
- android:textColor="#fff"
- android:textSize="18sp" />
- </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
<TextView
android:id="@+id/page_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:textColor="#fff"
android:textSize="18sp" />
</RelativeLayout>
这里我们在布局文件中放置了两个控件,ViewPager和TextView,其中ViewPager自然是用来管理所有的图片的了,而TextView则是用于显示当前图片的页数以及总页数。
然后新建一个zoom_image_layout.xml,在这里放入ZoomImageView控件,如下所示:
- <?xml version="1.0" encoding="utf-8"?>
- <com.example.photowallfallsdemo.ZoomImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/zoom_image_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#000000" >
- </com.example.photowallfallsdemo.ZoomImageView>
<?xml version="1.0" encoding="utf-8"?>
<com.example.photowallfallsdemo.ZoomImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/zoom_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000" >
</com.example.photowallfallsdemo.ZoomImageView>
接下来修改ImageDetailsActivity中的代码,在这里去实现ViewPager的具体功能,代码如下所示:
- public class ImageDetailsActivity extends Activity implements OnPageChangeListener {
- /**
- * 用于管理图片的滑动
- */
- private ViewPager viewPager;
- /**
- * 显示当前图片的页数
- */
- private TextView pageText;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.image_details);
- int imagePosition = getIntent().getIntExtra("image_position", 0);
- pageText = (TextView) findViewById(R.id.page_text);
- viewPager = (ViewPager) findViewById(R.id.view_pager);
- ViewPagerAdapter adapter = new ViewPagerAdapter();
- viewPager.setAdapter(adapter);
- viewPager.setCurrentItem(imagePosition);
- viewPager.setOnPageChangeListener(this);
- // 设定当前的页数和总页数
- pageText.setText((imagePosition + 1) + "/" + Images.imageUrls.length);
- }
- /**
- * ViewPager的适配器
- *
- * @author guolin
- */
- class ViewPagerAdapter extends PagerAdapter {
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- String imagePath = getImagePath(Images.imageUrls[position]);
- Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
- if (bitmap == null) {
- bitmap = BitmapFactory.decodeResource(getResources(),
- R.drawable.empty_photo);
- }
- View view = LayoutInflater.from(ImageDetailsActivity.this).inflate(
- R.layout.zoom_image_layout, null);
- ZoomImageView zoomImageView = (ZoomImageView) view
- .findViewById(R.id.zoom_image_view);
- zoomImageView.setImageBitmap(bitmap);
- container.addView(view);
- return view;
- }
- @Override
- public int getCount() {
- return Images.imageUrls.length;
- }
- @Override
- public boolean isViewFromObject(View arg0, Object arg1) {
- return arg0 == arg1;
- }
- @Override
- public void destroyItem(ViewGroup container, int position, Object object) {
- View view = (View) object;
- container.removeView(view);
- }
- }
- /**
- * 获取图片的本地存储路径。
- *
- * @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;
- }
- @Override
- public void onPageScrollStateChanged(int arg0) {
- }
- @Override
- public void onPageScrolled(int arg0, float arg1, int arg2) {
- }
- @Override
- public void onPageSelected(int currentPage) {
- // 每当页数发生改变时重新设定一遍当前的页数和总页数
- pageText.setText((currentPage + 1) + "/" + Images.imageUrls.length);
- }
- }
public class ImageDetailsActivity extends Activity implements OnPageChangeListener {
/**
* 用于管理图片的滑动
*/
private ViewPager viewPager;
/**
* 显示当前图片的页数
*/
private TextView pageText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.image_details);
int imagePosition = getIntent().getIntExtra("image_position", 0);
pageText = (TextView) findViewById(R.id.page_text);
viewPager = (ViewPager) findViewById(R.id.view_pager);
ViewPagerAdapter adapter = new ViewPagerAdapter();
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(imagePosition);
viewPager.setOnPageChangeListener(this);
// 设定当前的页数和总页数
pageText.setText((imagePosition + 1) + "/" + Images.imageUrls.length);
}
/**
* ViewPager的适配器
*
* @author guolin
*/
class ViewPagerAdapter extends PagerAdapter {
@Override
public Object instantiateItem(ViewGroup container, int position) {
String imagePath = getImagePath(Images.imageUrls[position]);
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
if (bitmap == null) {
bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.empty_photo);
}
View view = LayoutInflater.from(ImageDetailsActivity.this).inflate(
R.layout.zoom_image_layout, null);
ZoomImageView zoomImageView = (ZoomImageView) view
.findViewById(R.id.zoom_image_view);
zoomImageView.setImageBitmap(bitmap);
container.addView(view);
return view;
}
@Override
public int getCount() {
return Images.imageUrls.length;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
}
}
/**
* 获取图片的本地存储路径。
*
* @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;
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageSelected(int currentPage) {
// 每当页数发生改变时重新设定一遍当前的页数和总页数
pageText.setText((currentPage + 1) + "/" + Images.imageUrls.length);
}
}
这个类也是实现滑动切换图片功能最主要的一个类了,由于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中的代码,在这里将点击的那张图片的位置传递过来。由于这个类中的代码非常多,我只列出需要修改的那些部分,如下所示:
- public class MyScrollView extends ScrollView implements OnTouchListener {
- ......
- /**
- * 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。
- */
- 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(i);
- }
- page++;
- } else {
- Toast.makeText(getContext(), "已没有更多图片", Toast.LENGTH_SHORT).show();
- }
- } else {
- Toast.makeText(getContext(), "未发现SD卡", Toast.LENGTH_SHORT).show();
- }
- }
- /**
- * 遍历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(i);
- }
- } else {
- imageView.setImageResource(R.drawable.empty_photo);
- }
- }
- }
- ......
- /**
- * 异步下载图片的任务。
- *
- * @author guolin
- */
- class LoadImageTask extends AsyncTask<Integer, Void, Bitmap> {
- /**
- * 记录每个图片对应的位置
- */
- private int mItemPosition;
- ......
- @Override
- protected Bitmap doInBackground(Integer... params) {
- mItemPosition = params[0];
- mImageUrl = Images.imageUrls[mItemPosition];
- Bitmap imageBitmap = imageLoader.getBitmapFromMemoryCache(mImageUrl);
- if (imageBitmap == null) {
- imageBitmap = loadImage(mImageUrl);
- }
- return imageBitmap;
- }
- ......
- /**
- * 向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);
- imageView.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(getContext(), ImageDetailsActivity.class);
- intent.putExtra("image_position", mItemPosition);
- getContext().startActivity(intent);
- }
- });
- findColumnToAdd(imageView, imageHeight).addView(imageView);
- imageViewList.add(imageView);
- }
- }
- ......
- }
- }
public class MyScrollView extends ScrollView implements OnTouchListener {
......
/**
* 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。
*/
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(i);
}
page++;
} else {
Toast.makeText(getContext(), "已没有更多图片", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getContext(), "未发现SD卡", Toast.LENGTH_SHORT).show();
}
}
/**
* 遍历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(i);
}
} else {
imageView.setImageResource(R.drawable.empty_photo);
}
}
}
......
/**
* 异步下载图片的任务。
*
* @author guolin
*/
class LoadImageTask extends AsyncTask<Integer, Void, Bitmap> {
/**
* 记录每个图片对应的位置
*/
private int mItemPosition;
......
@Override
protected Bitmap doInBackground(Integer... params) {
mItemPosition = params[0];
mImageUrl = Images.imageUrls[mItemPosition];
Bitmap imageBitmap = imageLoader.getBitmapFromMemoryCache(mImageUrl);
if (imageBitmap == null) {
imageBitmap = loadImage(mImageUrl);
}
return imageBitmap;
}
......
/**
* 向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);
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), ImageDetailsActivity.class);
intent.putExtra("image_position", mItemPosition);
getContext().startActivity(intent);
}
});
findColumnToAdd(imageView, imageHeight).addView(imageView);
imageViewList.add(imageView);
}
}
......
}
}
可以看到,这里我们将LoadImageTask 的泛型进行了修改,doInBackground不再接收一个字符串数组,而是接收一个整型数组,这里传入的参数也就代表着每张图片的位置。这样的话,每个调用LoadImageTask 的地方也都需要进行相应的修改,在loadMoreImages()和checkVisibility()方法中,都将传入的参数改成了图片的位置。最后在addImage()方法中,使用Intent将点击的那张图片对应的位置传递给了ImageDetailsActivity。
目前看上去一切都完美了吧!但其实还有一点工作我们还没完成。由于ViewPager的事件和ZoomImageView的事件是存在冲突的,所以加入了ViewPager后,ZoomImageView本身的单个手指拖动图片的功能会受很大的影响。所以我们还需要在ZoomImageView的onTouchEvent()中进行判断,如果当前的图片是没有缩放的,则允许通过滑动来切换图片,如果当前的图片已经放大了,则要屏蔽掉ViewPager的事件,这样ZoomImageView本身的事件就不会受影响。代码如下所示:
- public class ZoomImageView extends View {
- ......
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (initRatio == totalRatio) {
- getParent().requestDisallowInterceptTouchEvent(false);
- } else {
- getParent().requestDisallowInterceptTouchEvent(true);
- }
- ......
- return true;
- }
- ......
- }
public class ZoomImageView extends View {
......
@Override
public boolean onTouchEvent(MotionEvent event) {
if (initRatio == totalRatio) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
getParent().requestDisallowInterceptTouchEvent(true);
}
......
return true;
}
......
}
这里使用getParent()获取到的就是ViewPager对象,然后调用requestDisallowInterceptTouchEvent()方法来启动和禁用ViewPager的功能。
好了,这样的话,所有的代码就已经完成了,可以运行一下看看完整的效果了。点击任意一张图片可以查看大图,然后通过左右滑动可以浏览前后的图片,并且仍然能够通过多点触控对图片进行缩放,效果如下图所示:
除了滑动切换图片之外,在屏幕的底部还能显示当前图片的页数以及总页数,功能已经是相当完善了。目前这个照片墙Demo的效果已经不亚于市场上一些常见的图片浏览程序了吧。
好了,今天的讲解到此结束,有疑问的朋友请在下面留言。