美文网首页第三方扩展Yzs的Android进阶之路
仿微信朋友圈9图上传选择器

仿微信朋友圈9图上传选择器

作者: 小小一瓶水 | 来源:发表于2017-06-26 21:16 被阅读71次
    Android百科全书二维码.jpg

    本人最新公众号<Android百科全书>,汇集了各个公众号的优秀文章,进行分类整理,让大家能够更方便的查阅,希望大家多多支持,来个关注奥。

    这个开源项目,之前就想写,一直没有时间整理,这次整理出来,方便以后使用,封装成了库,支持定制,废话不多说,先上图

    这里写图片描述

    这是微信的朋友圈发布选择器,一般大家都是用recyclerview或者gridview写一个出来,然后里面再做其他处理,当时我就想,我能不能把它封装成一个控件,然后以后就再也不用写了,得出的结论是能,于是开始封装,封装完成呢,效果图就是下面这个

    这里写图片描述

    动图太大上传不了了,那就放出github地址来

    单纯的上传图片展示控件ImageShowPicker

    这里的readme有动态图,大家可以看下。

    废话不多说,大家先来看看怎么使用这个控件

     ImageShowPickerView  pickerView = (ImageShowPickerView)findViewById(R.id.it_picker_view);
            final List<ImageBean> list = getItem(position);
            pickerView.setImageLoaderInterface(new Loader());
            pickerView.setNewData(list);
            //展示有动画和无动画
            
             //设置监听
            pickerView.setPickerListener(new ImageShowPickerListener() {
                @Override
                public void addOnClickListener(int remainNum) {
                    Toast.makeText(context, "remainNum" + remainNum, Toast.LENGTH_SHORT).show();
                    //在listview或recyclerview才会使用这个list.add(),其他情况都不用
                    list.add(new ImageBean("http://pic78.huitu.com/res/20160604/1029007_20160604114552332126_1.jpg"));
                    pickerView.addData(new ImageBean("http://pic78.huitu.com/res/20160604/1029007_20160604114552332126_1.jpg"));
                }
    
                @Override
                public void picOnClickListener(List<ImageShowPickerBean> list, int position, int remainNum) {
                    Toast.makeText(context, list.size() + "========" + position + "remainNum" + remainNum, Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void delOnClickListener(int position, int remainNum) {
                    list.remove(position);
                    Toast.makeText(context, "delOnClickListenerremainNum" + remainNum, Toast.LENGTH_SHORT).show();
                }
            });
            //所有设置完毕后调用该方法
            pickerView.show();
    
    
            //获取所有数据
            pickerView.getDataList();
    

    声明控件后做一些设置就使用这个控件了,是不是比之前的自己写recyclerview简单多了,再也不用一次次重写了,
    这里就不做过多说明,更详细的使用方法,请大家移步github,欢迎star呀

    下面,来为大家做一下介绍,看看我们的这个picker到底是怎么封装的,我是通过这几部来封装的

    • 1、分析需求,分析定制目标
    • 2、选择实现方法
    • 3、编写代码
    • 4、完善方法
      以上4步就是我这个项目的经历过程,下面一一介绍,并说明原理

    1、分析需求,分析定制目标

    我们要写的这个选择器,首先他要轻便,就是不抢其他人的工作,不去给开发者带来其他的局限性,so我们就不能
    直接指定某个图片加载框架,者就是一个需求;其次,我们的选择器应该有多种定制选择,比如微信的选择器就没
    有删除小按钮,但是其他的app里有,我们就要定制这一个属性,让用户来选择是否显示,还有recyclerview自
    带的增加删除动画,有的app就想要,有的就不想要,so我们也要提供方法让使用者来定制。
    

    2、选择实现方法

    实现方法的选择是个问题,关于图片加载的实现方法,我参考了[banner]这个开源项目的接口处理,十分感谢该
    项目给我的思路,他的地址如下https://github.com/youth5201314/banner,解决了这个问题,还有一个
    问题困扰我,就是如何获取数据,开始我想用反射来进行操作,但是反射的性能不好,同时使用者混淆代码时,又
    增添了很多麻烦,因为数据类的参数的不确定性,所以抛弃了这种处理方式,最后选择继承重写方法的这个思路来
    实现。
    

    3、编写代码

    解决了上面两个问题,下面就可以开始写代码了,一点点来说
    

    (1).为view 设置属性

    这里一定要注意,自定义控件设置属性后一定要调用typedArray.recycle();这个方法,否则后果你可以试试,爽到飞起

      TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ImageShowPickerView);
            mPicSize = typedArray.getDimensionPixelSize(R.styleable.ImageShowPickerView_pic_size, SizeUtils.getSizeUtils().dp2px(getContext(), PIC_SIZE));
            isShowDel = typedArray.getBoolean(R.styleable.ImageShowPickerView_is_show_del, true);
            isShowAnim = typedArray.getBoolean(R.styleable.ImageShowPickerView_is_show_anim, false);
            mAddLabel = typedArray.getResourceId(R.styleable.ImageShowPickerView_add_label, R.mipmap.image_show_piceker_add);
            mDelLabel = typedArray.getResourceId(R.styleable.ImageShowPickerView_del_label, R.mipmap.image_show_piceker_del);
            oneLineShowNum = typedArray.getInt(R.styleable.ImageShowPickerView_one_line_show_num, ONE_LINE_SHOW_NUM);
            maxNum = typedArray.getInt(R.styleable.ImageShowPickerView_max_num, MAX_NUM);
            typedArray.recycle();
    

    (2).添加数据时刷新,这里就包括动画的处理了

    这只是其中一个方法,写到这里就是为了让大家了解一下,提供个思路

     /**
         * 添加新数据
         *
         * @param bean
         * @param <T>
         */
        public <T extends ImageShowPickerBean> void addData(T bean) {
            if (bean == null) {
                return;
            }
            this.list.add(bean);
            if (isShowAnim) {
                if (adapter != null) {
                    adapter.notifyItemChanged(list.size() - 1);
                    adapter.notifyItemChanged(list.size());
                }
            } else {
                adapter.notifyDataSetChanged();
            }
    
        }
    

    (3).重头戏来了,也就是加载图片接口

    我们定义这个接口,用来实现加载图片的自定义化,无论你用什么框架,就都可以兼容了,第一眼看,有的人可能没明白怎么处理,其实就是继承接口后重写这个方法,我们在view里只不过把这个方法当成已经写好的方法,调用了一下他的方法名,这样解释是不是就更清晰些,动手试试就懂了

    /**
     * Author 姚智胜
     * Version V1.0版本
     * Description: 加载图片接口
     * Date: 2017/4/6
     */
    public interface ImageLoaderInterface<T extends View> extends Serializable {
    
        void displayImage(Context context, String path, T imageView);
    
        void displayImage(Context context, @DrawableRes Integer resId, T imageView);
    
        T createImageView(Context context);
    }
    
    

    (4).RecyclerView的holder处理

    把这个放到这里,主要是为了给大家看下onclick事件应该写在哪里,如果写到onBindViewHolder这个方法里,那就是无限的重复创建了,没有理解Holder的作用,我们要在最开始就对他进行创建,然后去复用他

      //自定义的ViewHolder,持有每个Item的的所有界面元素
        public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
            public View iv_pic;
            public ImageView iv_del;
            private ImageShowPickerPicListener picOnClickListener;
    
    
            public ViewHolder(View view, ImageLoaderInterface imageLoaderInterface, ImageShowPickerPicListener picOnClickListener) {
                super(view);
                this.picOnClickListener = picOnClickListener;
                iv_pic = imageLoaderInterface.createImageView(view.getContext());
                FrameLayout.LayoutParams pic_params = new FrameLayout.LayoutParams(iconHeight,
                        iconHeight);
                pic_params.setMargins(10, 10, 10, 10);
                iv_pic.setLayoutParams(pic_params);
                iv_del = new ImageView(view.getContext());
                FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
                layoutParams.gravity = Gravity.TOP | Gravity.END;
                iv_del.setPadding(5, 5, 5, 5);
                iv_del.setLayoutParams(layoutParams);
                iv_pic.setId(R.id.iv_image_show_picker_pic);
                iv_del.setId(R.id.iv_image_show_picker_del);
                iv_pic.setOnClickListener(this);
                iv_del.setOnClickListener(this);
            }
    
            @Override
            public void onClick(View v) {
                int i = v.getId();
                if (i == R.id.iv_image_show_picker_pic) {
                    picOnClickListener.onPicClickListener(getLayoutPosition());
                } else if (i == R.id.iv_image_show_picker_del) {
                    picOnClickListener.onDelClickListener(getLayoutPosition());
                }
            }
        }
    

    (5).意外发现recyclerview的bug

    在代码里,我们在adapter内部使用了notifyItemChanged,程序却异常崩溃了,查阅过资料后,发现这个google的bug,所以重写了这个布局管理器,处理这个问题,算是意外收获

    /**
     * Author 姚智胜
     * Version V1.0版本
     * Description: 处理recyclerview在adapter内调用notifyItemChanged崩溃的解决方法
     * Date: 2017/4/15
     */
    
    public class MyGridLayoutManager extends GridLayoutManager {
        public MyGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
        public MyGridLayoutManager(Context context, int spanCount) {
            super(context, spanCount);
        }
    
        public MyGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
            super(context, spanCount, orientation, reverseLayout);
        }
    
        @Override
        public boolean supportsPredictiveItemAnimations() {
            return false;
        }
    
        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            //override this method and implement code as below
            try {
                super.onLayoutChildren(recycler, state);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    

    (6).数据类的父类

    原理很简单,只是思路问题,我们继承这个父类后,只需重写两个方法,把我们数据类的url,为指定方法赋值就可以了,一个小思路,有可能不是最好的,希望有更好意见的来提下意见

    /**
     * Author 姚智胜
     * Version V1.0版本
     * Description: 显示数据类的父类,必须继承于该类
     * Date: 2017/4/10
     */
    
    public abstract class ImageShowPickerBean {
    
        public String getImageShowPickerUrl() {
            return setImageShowPickerUrl();
        }
    
        public int getImageShowPickerDelRes() {
            return setImageShowPickerDelRes();
        }
    
        /**
         * 为URL赋值,必须重写方法
         *
         * @return
         */
        public abstract String setImageShowPickerUrl();
    
        /**
         * 为删除label赋值,必须重写方法
         *
         * @return
         */
        public abstract int setImageShowPickerDelRes();
    
    
    }
    

    4、完善方法

    这里要做的其实就是小修小补,比如我们的封装的view ,在最后的时候我又增加了几个自定义属性,让他的可定制性更强,也是在这个步骤里对我们的封装进行最后一次的检查和完善。

    以上就是我在写ImageShowPickerView的时候走的几个步骤,会有比我这个更加好的模式,分享出来,就是为了共同进步,和给自己写完一个项目的总结,继续努力,向资深程序员进发!!!

    相关文章

      网友评论

      • 等风来_Android:大神,我要是想在这里添加相机📷功能,该怎么做呢?求大神指点
        小小一瓶水:这个就需要第三方或者自己写了,我这个就是一个显示图片的view,不是一整套框架,但是可以兼容其他第三方框架,你自己在点击事件里处理就可以了

      本文标题:仿微信朋友圈9图上传选择器

      本文链接:https://www.haomeiwen.com/subject/vqajcxtx.html