《安卓ViewPager系列自定义效果,助你打造炫酷轮播图——ViewPager+Glide+PhotView 图片相册加载效果》

大家好,这篇博客将为大家带来 如何使用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创建对话框。

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



    
给根布局添加一个GridView然后设置个ID值,宽高,和列数。

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private List 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 mList;
    /**
     * PhotoView集合,有多少个图片就创建多少个PhotoView。
     */
    private List mPhoto = new ArrayList<>();

    /**
     * 构造方法,初始化适配器
     * @param mContext
     * @param mList
     */
    public PhotoAdapter(Fragment mContext, List mList) {
        this.mContext = mContext;
        this.mList = mList;
        initPhoto();
    }
    private void initPhoto(){
        List 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 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 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) images);
                /**
                 * 弹出Dialog
                 */
                zoomPhotoView.show(getSupportFragmentManager(),"tag");
            }
        });

源码地址:

https://github.com/Cysir/ViewPagerDemo

小菜还是小菜啊,国庆大佬们都出去happy了,小菜还得在宿舍苦逼的撸代码。
有什么讲述不对的地方请您指出,有迷惑的地方也可以留言发问,我看到会第一时间回复。最后,谢谢大家的支持,谢谢!!


你可能感兴趣的:(Android自定义View)