大家好,这篇博客将为大家带来 如何使用ViewPager+Glide+PhotoView来实现相册图片预览效果。
前言:你若花开,清风自来
首先来看一下效果图:
设计思路:
首先使用一个GridView(网格布局)来加载所有的缩略图,然后给GridView添加单击事件监听器。当我们单击了某个item时在进入到ViewPager中去,而ViewPager中的子View就是我们上一篇博客介绍的PhotoView控件,然后将图片加载到PhotoView中去。这样我们就得到了一个可以左右滑动,又能放大缩小的相册浏览效果啦。怎么样?这么一讲起来似乎也挺简单的吧,没错,就是这么简单。但是我们还要考虑一个问题,我们把这个ViewPager要放到哪里去呢?放在Activity中去?这是最简单的办法,但是每次单击都从新打开一个Activity有些太耗资源了,要是有些顽皮的用户,每张图片都喜欢点一次岂不是要爆炸。那放在Fragment中去?这似乎是一种可行的办法,但要是其他的场景也要用到这个效果的话,好像就不够灵活 了。那么我们最终的解决办法是什么呢?那就是放到Dialog中去,这样无论用户在哪个页面,任何地方都可以很方便的使用 就好像我们经常写的Toast一样,只要你想,可以在任何地方Toast而且代价极少。
DialogFragment:
我们将要使用DialogFragment显示ViewPager。这里对于DialogFragment的描述直接搬运郭神对于DialogFragment的描述:
1、 概述
DialogFragment在android 3.0时被引入。是一种特殊的Fragment,用于在Activity的内容之上展示一个模态的对话框。典型的用于:展示警告框,输入框,确认框等等。
在DialogFragment产生之前,我们创建对话框:一般采用AlertDialog和Dialog。注:官方不推荐直接使用Dialog创建对话框。
在DialogFragment产生之前,我们创建对话框:一般采用AlertDialog和Dialog。注:官方不推荐直接使用Dialog创建对话框。
2、 好处与用法
使用DialogFragment来管理对话框,当旋转屏幕和按下后退键时可以更好的管理其声明周期,它和Fragment有着基本一致的声明周期。且DialogFragment也允许开发者把Dialog作为内嵌的组件进行重用,类似Fragment(可以在大屏幕和小屏幕显示出不同的效果)。上面会通过例子展示这些好处~使用DialogFragment至少需要实现onCreateView或者onCreateDIalog方法。onCreateView即使用定义的xml布局文件展示Dialog。onCreateDialog即利用AlertDialog或者Dialog创建出Dialog。
对于DialogFragment的具体使用可以参考这篇博客: http://blog.csdn.net/lmj623565791/article/details/37815413代码实战
技术的提升最直接的方式就是多撸代码,接下来我们直接撸代码。
实现一个网格布局展示所有的缩略图
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.zcy.hnkjxy.viewpagerdemo.MainActivity">
<GridView
android:id="@+id/gvImages"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="3" />
</FrameLayout>
给根布局添加一个GridView然后设置个ID值,宽高,和列数。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private List<String> images;
private GridView gridView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//设置图片加载的地址
images = new ArrayList<>();
images.add("http://d.hiphotos.baidu.com/image/pic/item/f31fbe096b63f624f0d3ad2e8c44ebf81a4ca315.jpg");
images.add("http://d.hiphotos.baidu.com/image/pic/item/5fdf8db1cb134954b2fe01a75f4e9258d0094a15.jpg");
images.add("http://c.hiphotos.baidu.com/image/pic/item/9825bc315c6034a87ccffc42c2134954082376c7.jpg");
images.add("http://b.hiphotos.baidu.com/image/pic/item/f3d3572c11dfa9ecf333c4d46bd0f703908fc1d0.jpg");
images.add("http://upload.365jilin.com/2016/0820/1471686023587.jpg");
images.add("http://imgs.aixifan.com/content/2016_07_11/1468254644.gif");
images.add("http://i1.hdslb.com/bfs/archive/1d6f6483d6aab106dcf011c0be3243ab197505e3.jpg");
images.add("http://img0.imgtn.bdimg.com/it/u=393555490,627047088&fm=214&gp=0.jpg");
images.add("http://i2.hdslb.com/bfs/archive/0f2afafebf13691b3bdcbf5b81cf6f50c699f8d1.jpg");
images.add("http://imgsrc.baidu.com/forum/w=580/sign=3e2d233f982397ddd679980c6983b216/214a8f025aafa40f10eed405a264034f79f01973.jpg");
images.add("http://i1.hdslb.com/bfs/archive/aded071aa1479ac7302bd57d6cbcef761419e740.jpg");
gridView = (GridView) findViewById(R.id.gvImages);
//设置适配器
gridView.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return images.size();
}
@Override
public Object getItem(int i) {
return images.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ImageView imageView = new ImageView(MainActivity.this);
Glide.with(MainActivity.this).load(images.get(i)).into(imageView);
return imageView;
}
});
}
}
代码量也不大,创建了一个存放图片地址的集合,然后通过findViewById获得GridView对象,最后为GridView这只一个适配器。效果如下:
好了,接下来才是展示真正技术。
正所谓兵马未动粮草先行,咱先写一个ViewPager的适配器,我们的数据都是通过适配器添加到ViewPager中去的。
PhotoAdapter.java
public class PhotoAdapter extends PagerAdapter {
/**
* Fragment虽然不是上下文对象但是他有个方法getContext()对象
* 我们使用Fragment是因为前面说到使用Glide加载图片时,Glide会根据传入的
* Fragment或者Activity自行关联它们的生命周期,达到优化内存的效果。
*/
private Fragment mContext;
/**
* 图片地址集合
*/
private List<String> mList;
/**
* PhotoView集合,有多少个图片就创建多少个PhotoView。
*/
private List<PhotoView> mPhoto = new ArrayList<>();
/**
* 构造方法,初始化适配器
* @param mContext
* @param mList
*/
public PhotoAdapter(Fragment mContext, List<String> mList) {
this.mContext = mContext;
this.mList = mList;
initPhoto();
}
private void initPhoto(){
List<PhotoView> photos = new ArrayList<>();
PhotoView v;
//这个LayoutParams可以理解为java代码中的布局参数,相当于XML文件中的属性。
ViewPager.LayoutParams params = new ViewPager.LayoutParams();
//设置宽度填充满父布局
params.width = ViewPager.LayoutParams.MATCH_PARENT;
//高度为自身大小
params.height = ViewPager.LayoutParams.WRAP_CONTENT;
// 一次性创建需要的PhotoView
for (int i = mPhoto.size(); i < mList.size(); i++) {
v = new PhotoView(mContext.getContext());
//将布局参数设置进PhotoView
v.setLayoutParams(params);
// 添加到集合中去
photos.add(v);
//使用Glide加载图片
Glide.with(mContext).load(mList.get(i)).into(v);
}
mPhoto.addAll(photos);
photos.clear();
}
@Override
public int getCount() {
return mList.size();
}
@Override
public void notifyDataSetChanged() {
initPhoto();
super.notifyDataSetChanged();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mPhoto.get(position));
Log.i("TAG", "instantiateItem: "+mPhoto.get(position).getScaleType());
return mPhoto.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mPhoto.get(position));
}
}
写完了适配器,接下来就是今天的主角了
ZoomPhotoView.java
public class ZoomPhotoView extends DialogFragment implements ViewPager.OnPageChangeListener {
//ViewPager的适配器
private PhotoAdapter adapter;
//图片地址集合
private List<String> mList = new ArrayList<>();
//界面中心下方的当前第N张图片的指示器
private TextView tvIndia;
//ViewPager对象
private ViewPager vpPhoto;
//显示第currentItem图片,指示器的字体颜色,背景颜色
private int currentItem, textColor, bacColor;
//指示器的字体大小
private float textSize;
/**
* 保持和AlertDialog使用方式的一致,我们也使用生成器模式和链式编程来构建对象
* 然后我们还会使用单例模式
*/
private static ZoomPhotoCreator zoomPhotoCreator;
/*
*静态代码块,当我们第一引用ZoomPhotoView时,创建一个生成器,它用来辅助我们生成ZoomPhotoView对象
*/
static {
zoomPhotoCreator = new ZoomPhotoCreator();
}
/*
*典型的单例模式
*/
static class Instance {
static ZoomPhotoView Instance = new ZoomPhotoView();
}
/*
* 构造函数私有化,典型的单例模式套路
*/
private ZoomPhotoView() {
}
// 重写DialogFragment的onCreateView方法
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//去掉标题
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
//帧布局用于存放ViewPager和下标指示器(TextView)
FrameLayout flay = new FrameLayout(getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
// 下标显示
FrameLayout.LayoutParams param = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
tvIndia = new TextView(getContext());
param.gravity = Gravity.CENTER | Gravity.BOTTOM;
param.setMargins(50, 50, 50, 50);
tvIndia.setLayoutParams(param);
//图片显示位置
vpPhoto = new ViewPager(getContext());
vpPhoto.setLayoutParams(params);
adapter = new PhotoAdapter(this, mList);
vpPhoto.setAdapter(adapter);
//设置ViewPager页面改变的监听器
vpPhoto.addOnPageChangeListener(this);
//加入flay布局
flay.addView(vpPhoto);
flay.addView(tvIndia);
return flay;
}
//设置相关属性
public static ZoomPhotoCreator size(float size) {
zoomPhotoCreator.size(size);
return zoomPhotoCreator;
}
public static ZoomPhotoCreator color(int color) {
zoomPhotoCreator.color(color);
return zoomPhotoCreator;
}
public static ZoomPhotoCreator bag(int color) {
zoomPhotoCreator.bacColor(color);
return zoomPhotoCreator;
}
public static ZoomPhotoView build() {
return zoomPhotoCreator.build();
}
@Override
public void onResume() {
super.onResume();
initView();
}
// 添加图片数据集合
public void addImages(ArrayList<String> images) {
mList.clear();
mList.addAll(images);
}
// 初始化控件
private void initView() {
//获得我们创建对象时设置的属性
currentItem = zoomPhotoCreator.currentItem;
textColor = zoomPhotoCreator.color;
bacColor = zoomPhotoCreator.bacColor;
textSize = zoomPhotoCreator.size;
//设置相关属性
tvIndia.setTextSize(textSize);
tvIndia.setTextColor(textColor);
vpPhoto.setBackgroundColor(bacColor);
tvIndia.setText(1 + currentItem + "/" + mList.size());
adapter.notifyDataSetChanged();
vpPhoto.setCurrentItem(currentItem);
}
@Override
public void onStart() {
super.onStart();
//把Dialog设置为全屏
Window window = getDialog().getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.gravity = Gravity.CENTER;
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(params);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
}
private static final String TAG = "ZoomPhotoView";
@Override
public void onPause() {
super.onPause();
Log.i(TAG, "onPause: ");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.i(TAG, "onDestroyView: ");
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
// 当页面切换时更改指示器指示的页面
@Override
public void onPageSelected(int position) {
tvIndia.setText(1 + position + "/" + mList.size());
}
@Override
public void onPageScrollStateChanged(int state) {
}
// 生成器模式辅助类
public static class ZoomPhotoCreator {
int color;
float size;
int bacColor;
int currentItem;
public ZoomPhotoCreator color(int textColor) {
this.color = textColor;
return this;
}
public ZoomPhotoCreator size(float textSize) {
this.size = textSize;
return this;
}
public ZoomPhotoCreator bacColor(int backgroundColor) {
this.bacColor = backgroundColor;
return this;
}
public ZoomPhotoCreator current(int currentItem) {
this.currentItem = currentItem;
return this;
}
public ZoomPhotoView build() {
return Instance.Instance;
}
}
}
好了写到这里,核心代码都已经写完了。接下来我们回到MainActivity中去使用ZoomPhotoView这个类。
在MainAcitivy的设置设配器后面加上如下代码即可:
//设置适配器
gridView.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return images.size();
}
@Override
public Object getItem(int i) {
return images.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ImageView imageView = new ImageView(MainActivity.this);
Glide.with(MainActivity.this).load(images.get(i)).into(imageView);
return imageView;
}
});
//给GridView添加单击事件
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
/**
* 生成器模式获得一个ZoomPhotoView对象
* 设置指示器的大小,背景颜色,和指示器颜色
* 最后通过build方法创建
* 这里我们不用担心每一次单击都会创建一个新对象造成内存浪费
* 因为我们使用的是单例模式,即第一次使用会创建新对象,后面获取的
* 其实是对象的引用并非重新创建对象
*/
ZoomPhotoView zoomPhotoView = ZoomPhotoView
.size(12)
.bacColor(Color.BLACK)
.color(Color.WHITE)
.current(i)
.build();
/**
* 添加图片数据进去
*/
zoomPhotoView.addImages((ArrayList<String>) images);
/**
* 弹出Dialog
*/
zoomPhotoView.show(getSupportFragmentManager(),"tag");
}
});
源码地址:
https://github.com/Cysir/ViewPagerDemo
有什么讲述不对的地方请您指出,有迷惑的地方也可以留言发问,我看到会第一时间回复。最后,谢谢大家的支持,谢谢!!