Android自定义View 简单实现多图片选择控件

来源:互联网 发布:数据库开发工程师要求 编辑:程序博客网 时间:2024/05/21 00:49

前言

相信很多朋友在开发中都会遇到图片上传的情况,尤其是多图上传,最
经典的莫过于微信的图片选择了。所有很多情况下会使用到多图选择。
所以就有了这篇文章,今天抽点时间写了个控件。

  • 支持自定义选择图片的样式
  • 支持设置图片选择数量
  • 支持图片预览,删除
  • 支持图片拍照

先来看看效果

这里写图片描述

这里写图片描述

实现分析

假如不定义控件,我们要实现这样一个功能,无非是写个GridView在item点击的时候去显示图片进行选择,在返回界面的时候进行GridView的数据刷新。我们把这些逻辑写在我们自定义的GridView中,就成了一个新的控件。

1、GridView的效果展示,逻辑实现。

public class ImagePickerView extends GridView{    //图片选择数量    int maxImageSize = 9;    //添加item布局    private int noImgResource;    //列选择数量    private int columnNumber = 3;    Activity context;    ImagesAdapter adapter;    List<String> imageList;//图片选择list    private static final int TYPE_SHOW_ADD = 0;    private static final int TYPE_NO_SHOW_ADD = 1;    private boolean isShowAdd = true;    int imageGridSize;    public void setNoImgResource(int noImgResource) {        this.noImgResource = noImgResource;    }    public void setColumnNumber(int columnNumber) {        if (columnNumber>5){            columnNumber = 5;        }        this.columnNumber = columnNumber;        this.setNumColumns(columnNumber);    }    public void setShowAdd(boolean showAdd) {        isShowAdd = showAdd;    }    public void setImageList(List<String> imageList) {        this.imageList = imageList;        adapter.setImageList(imageList);    }    public List<String> getImageList() {        return imageList;    }    public ImagePickerView(Context context) {        this(context,null);    }    public ImagePickerView(Context context, AttributeSet attrs) {        this(context,attrs,0);    }    /**     * 初始化ImagePickerView的一些信息     * @param context     * @param attrs     * @param defStyle     */    public ImagePickerView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        this.context = (Activity) context;        adapter = new ImagesAdapter();        this.setAdapter(adapter);        if (imageList==null){            imageList = new ArrayList<>();        }        this.setNumColumns(columnNumber);        this.setVerticalSpacing(10);        this.setHorizontalSpacing(10);        imageGridSize = (this.context.getWindowManager().getDefaultDisplay().getWidth() - Util.dp2px(context, 2) * 2) / columnNumber;    }    /**     * 提供给外部调用用来再Activity返回时获取图片信息     * @param requestCode     * @param resultCode     * @param data     */    public void onActivityResult(int requestCode, int resultCode, Intent data) {        if (data!=null&& !TextUtils.isEmpty(data.getStringExtra("photoPath"))){//拍照            imageList.add(data.getStringExtra("photoPath"));        }else if (data!=null&&data.getSerializableExtra("images")!=null){//图片选择            imageList = (List<String>) data.getSerializableExtra("images");        }else{            List<ImageItem> list = AndroidImagePicker.getInstance().getSelectedImages();            for (int i=0;i<list.size();i++){                imageList.add(list.get(i).path);            }        }        AndroidImagePicker.getInstance().setSelectLimit(maxImageSize-imageList.size());        adapter.setImageList(imageList);    }    class ImagesAdapter extends BaseAdapter {        List<String> imageList;        public ImagesAdapter() {            this.imageList = new ArrayList();        }        public void setImageList(List<String> imageList) {            this.imageList = imageList;            notifyDataSetChanged();        }        @Override        public int getCount() {            if (isShowAdd){                if (imageList == null || imageList.isEmpty()) {                    return 1;                }                if (imageList.size() >= maxImageSize) {                    return maxImageSize;                }                return imageList.size() + 1;            }            if (imageList.size() >= maxImageSize) {                return maxImageSize;            }            return imageList.size()+1;        }        @Override        public String getItem(int position) {            if (isShowAdd){                if (position==imageList.size()){                    return null;                }                return imageList.get(position-1);            }            return imageList.get(position);        }        @Override        public long getItemId(int position) {            return 0;        }        @Override        public int getViewTypeCount() {            return 2;        }        @Override        public int getItemViewType(int position) {            if (isShowAdd){                return position==imageList.size()?TYPE_SHOW_ADD:TYPE_NO_SHOW_ADD;            }else{                return TYPE_NO_SHOW_ADD;            }        }        @Override        public View getView(final int position, View convertView, ViewGroup parent) {            int itemViewType = getItemViewType(position);            if(itemViewType == TYPE_SHOW_ADD){//当前item为添加图片item                if (noImgResource!=0){//加载用户的添加item布局                    convertView = LayoutInflater.from(context).inflate(noImgResource, parent, false);                }else {//默认的添加item布局                    convertView = LayoutInflater.from(context).inflate(R.layout.grid_item_camera, parent, false);                }                convertView.setTag(null);                convertView.setOnClickListener(new View.OnClickListener() {                    @Override                    public void onClick(View v) {//点击选择图片                        Intent intent = new Intent(context, ImagesGridActivity.class);//图片选择                        Activity activity =  context;                        activity.startActivityForResult(intent,1001);                    }                });            }else{//普通item,加载图片,并对item设置点击进行预览                final ViewHolder holder;                if(convertView == null){                    convertView = LayoutInflater.from(context).inflate(R.layout.image_grid_item, null);                    holder = new ViewHolder();                    holder.ivPic = (SimpleDraweeView)convertView.findViewById(R.id.iv_thumb);                    holder.cbPanel = convertView.findViewById(R.id.thumb_check_panel);                    convertView.setTag(holder);                }else{                    holder = (ViewHolder) convertView.getTag();                }                convertView.setOnClickListener(new OnClickListener() {                    @Override                    public void onClick(View v) {//将选择的图片与当前postion传过去。                        Intent intent = new Intent(context, PreviewDelActivity.class);                        intent.putExtra("images", (Serializable) imageList);                        intent.putExtra("position",position);                        context.startActivityForResult(intent,1002);                    }                });                ImageRequestBuilder requestBuilder = ImageRequestBuilder.newBuilderWithSource(                        Uri.parse(String.format("file://%s", imageList.get(position))))                        .setResizeOptions(new ResizeOptions(imageGridSize, imageGridSize))                        .setAutoRotateEnabled(true);                PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()                        .setOldController(holder.ivPic.getController())                        .setImageRequest(requestBuilder.build())                        .build();                holder.ivPic.setController(controller);            }            return convertView;        }    }    class ViewHolder{        SimpleDraweeView ivPic;        View cbPanel;    }}

代码比较简单,都加了注释。在view的初始化方法中获取一些信息和GridView的展示信息,并且设置适配器关联。

在GridView的 item中设置了点击事件,并且提供给外部用来刷新数据的onActivityResult方法。

大家看上面的代码应该就明白了。

2、图片预览和删除

这一步很简单,获取到传过来的imageList和postion对图片进行展示,在点击删除的时候remove掉imageList对应的数据,并且刷新viewPager

直接上代码

public class PreviewDelActivity extends AppCompatActivity implements View.OnClickListener {    private static final String TAG = ImagePreviewActivity.class.getSimpleName();    TextView mTitleCount;    TextView mBtnOk;    private ImageView backBtn;    List<String> mImageList;    int mShowItemPosition = 0;    ViewPager mViewPager;    TouchImageAdapter mAdapter ;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_preview_del);        mImageList = (List<String>) getIntent().getSerializableExtra("images");        mShowItemPosition = getIntent().getIntExtra("position",0);        mBtnOk = (TextView) findViewById(R.id.btn_del);        backBtn = (ImageView) findViewById(R.id.btn_backpress);        mBtnOk.setOnClickListener(this);        backBtn.setOnClickListener(this);        mTitleCount = (TextView) findViewById(R.id.tv_title_count);        mTitleCount.setText(mShowItemPosition+1+"/" + mImageList.size());// 图片数量和当前图片信息展示        initView();        AndroidImagePicker.getInstance().clearSelectedImages();    }    private void initView() {        mViewPager = (ViewPager)findViewById(R.id.viewpager);        mAdapter = new TouchImageAdapter(getSupportFragmentManager());        mViewPager.setAdapter(mAdapter);        mViewPager.setCurrentItem(mShowItemPosition, false);//设置显示当前的图片        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {            @Override            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {            }            @Override            public void onPageSelected(int position) {                mTitleCount.setText(position+1+"/" + mImageList.size());//滑动viewPager时更新显示信息            }            @Override            public void onPageScrollStateChanged(int state) {            }        });    }    @Override    public void onClick(View v) {        int i = v.getId();        if (i == R.id.btn_del) {//删除按钮点击            mAdapter.remove(mViewPager.getCurrentItem());//            mTitleCount.setText(mViewPager.getCurrentItem()+1+"/" + mImageList.size());            if (mImageList.size()==0){                Intent intent = new Intent();                intent.putExtra("images", (Serializable) mImageList);                setResult(RESULT_OK,intent);                finish();            }        }else if (i==R.id.btn_backpress){//返回            Intent intent = new Intent();            intent.putExtra("images", (Serializable) mImageList);            setResult(RESULT_OK,intent);            finish();        }    }    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {        if (keyCode==KeyEvent.KEYCODE_BACK){            Intent intent = new Intent();            intent.putExtra("images", (Serializable) mImageList);            setResult(RESULT_OK,intent);            finish();            return true;        }        return super.onKeyDown(keyCode, event);    }    class TouchImageAdapter extends FragmentStatePagerAdapter {        public TouchImageAdapter(FragmentManager fm) {            super(fm);        }        @Override        public int getCount() {            return mImageList.size();        }        public void remove(int position){            mImageList.remove(position);            notifyDataSetChanged();        }        @Override        public int getItemPosition(Object object) {            return POSITION_NONE;        }        @Override        public Fragment getItem(int position) {            SinglePreviewFragment fragment = new SinglePreviewFragment();            Bundle bundle = new Bundle();            bundle.putSerializable(SinglePreviewFragment.KEY_URL, mImageList.get(position));            fragment.setArguments(bundle);            return fragment;        }    }    @SuppressLint("ValidFragment")    private class SinglePreviewFragment extends Fragment {        public static final String KEY_URL = "key_url";        private PhotoDraweeView photoDraweeView;        private String url;        @Override        public void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            Bundle bundle = getArguments();            url = (String) bundle.getSerializable(KEY_URL);            Log.i(TAG, "=====current show image path:" + url);            photoDraweeView = new PhotoDraweeView(getActivity());            photoDraweeView.setBackgroundColor(0xff000000);            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);            photoDraweeView.setLayoutParams(params);            photoDraweeView.setOnPhotoTapListener(new OnPhotoTapListener() {                @Override                public void onPhotoTap(View view, float x, float y) {                    getActivity().finish();                }            });            if (!url.startsWith("http://") && !url.startsWith("https://")) {                url = "file://"+url;            }            ImageRequestBuilder requestBuilder = ImageRequestBuilder.newBuilderWithSource(                    Uri.parse(url))                    .setResizeOptions(new ResizeOptions(768,1280))                    .setAutoRotateEnabled(true);            PipelineDraweeControllerBuilder controller = Fresco.newDraweeControllerBuilder();            controller.setOldController(photoDraweeView.getController());            controller.setImageRequest(requestBuilder.build());            controller.setControllerListener(new BaseControllerListener<ImageInfo>() {                @Override                public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {                    super.onFinalImageSet(id, imageInfo, animatable);                    if (imageInfo == null) {                        return;                    }                    photoDraweeView.update(imageInfo.getWidth(), imageInfo.getHeight());                }            });            photoDraweeView.setController(controller.build());        }        @Override        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {            return photoDraweeView;        }    }}

相信大家都能看懂上面代码,用ViewPager对图片进行加载,在点击回退和删除完图片的时候把ImageList传回去。
这样在onActivityResult中获取到imageList,刷新adapter,gridview就重新渲染了。

使用

1、布局中引入ImagePickerView

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    android:orientation="vertical"    tools:context="com.qiangyu.test.imagepickerviewdemo.MainActivity">    <com.redare.imagepicker.widget.ImagePickerView        android:id="@+id/imagePicker"        android:layout_width="match_parent"        android:layout_height="wrap_content">    </com.redare.imagepicker.widget.ImagePickerView>    <Button        android:id="@+id/commit_btn"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="提交"/></LinearLayout>

2、在Activity中获得ImagePickerView,并且在onActivityResult方法中调用ImagePickerView数据刷新方法
imagePicker.onActivityResult(requestCode,resultCode,data);

3、获取选择图片的路径
调用imagePicker.getImageList()即返回图片选择的List

下面是一段代码示例

public class MainActivity extends AppCompatActivity {    private ImagePickerView imagePicker;    private Button commitBtn;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Fresco.initialize(this);        imagePicker = (ImagePickerView) findViewById(R.id.imagePicker);        commitBtn = (Button) findViewById(R.id.commit_btn);        commitBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                for (int i=0;i<imagePicker.getImageList().size();i++){                    Log.d("yqy",imagePicker.getImageList().get(i));                }            }        });        imagePicker.setNoImgResource(R.layout.add_img);//自定义imagePicker  add item样式        imagePicker.setColumnNumber(5);//设置显示5列    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        imagePicker.onActivityResult(requestCode,resultCode,data);    }}

结语

看了ImagePickerView的实现,是不是发现一个自定义控件其实也很简单。在我们做自定义控件的时候,其实大部分情况只是在Android系统提供的功能上多加一点我们的需求而已。站在巨人的肩膀上,才可以看的更远嘛。

关于图片加载和图片选择本文没有提及,图片加载我参考了文章,感兴趣的朋友可以去github查看,https://github.com/easonline/AndroidImagePicker。我在自己的Demo中对源码做了修改,并统一使用了Fresco加载图片。

有需要参考源码的同学请参考:源码下载

觉得本文对你有帮助,动动手指关注我、点点赞呗!

9 0
原创粉丝点击