欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-06 09:31:37
前言 相信很多朋友在开发中都会遇到图片上传的情况,尤其是多图上传,最经典的莫过于微信的图片选择了。所有很多情况下会使用到多图选择,所以就有了这篇文章,今天抽点时间写了个控...

前言

相信很多朋友在开发中都会遇到图片上传的情况,尤其是多图上传,最经典的莫过于微信的图片选择了。所有很多情况下会使用到多图选择,所以就有了这篇文章,今天抽点时间写了个控件。
 •支持自定义选择图片的样式
 •支持设置图片选择数量
 •支持图片预览,删除
 •支持图片拍照 

先来看看效果

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

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

实现分析

假如不定义控件,我们要实现这样一个功能,无非是写个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加载图片。

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。