Android自定義View實(shí)現(xiàn)多圖片選擇控件
前言
相信很多朋友在開(kāi)發(fā)中都會(huì)遇到圖片上傳的情況,尤其是多圖上傳,最經(jīng)典的莫過(guò)于微信的圖片選擇了。所有很多情況下會(huì)使用到多圖選擇,所以就有了這篇文章,今天抽點(diǎn)時(shí)間寫(xiě)了個(gè)控件。
•支持自定義選擇圖片的樣式
•支持設(shè)置圖片選擇數(shù)量
•支持圖片預(yù)覽,刪除
•支持圖片拍照
先來(lái)看看效果


實(shí)現(xiàn)分析
假如不定義控件,我們要實(shí)現(xiàn)這樣一個(gè)功能,無(wú)非是寫(xiě)個(gè)GridView在item點(diǎn)擊的時(shí)候去顯示圖片進(jìn)行選擇,在返回界面的時(shí)候進(jìn)行GridView的數(shù)據(jù)刷新。我們把這些邏輯寫(xiě)在我們自定義的GridView中,就成了一個(gè)新的控件。
1、GridView的效果展示,邏輯實(shí)現(xiàn)。
public class ImagePickerView extends GridView{
//圖片選擇數(shù)量
int maxImageSize = 9;
//添加item布局
private int noImgResource;
//列選擇數(shù)量
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;
}
/**
* 提供給外部調(diào)用用來(lái)再Activity返回時(shí)獲取圖片信息
* @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){//當(dāng)前item為添加圖片item
if (noImgResource!=0){//加載用戶(hù)的添加item布局
convertView = LayoutInflater.from(context).inflate(noImgResource, parent, false);
}else {//默認(rèn)的添加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) {//點(diǎn)擊選擇圖片
Intent intent = new Intent(context, ImagesGridActivity.class);//圖片選擇
Activity activity = context;
activity.startActivityForResult(intent,1001);
}
});
}else{//普通item,加載圖片,并對(duì)item設(shè)置點(diǎn)擊進(jìn)行預(yù)覽
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) {//將選擇的圖片與當(dāng)前postion傳過(guò)去。
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;
}
}
代碼比較簡(jiǎn)單,都加了注釋。在view的初始化方法中獲取一些信息和GridView的展示信息,并且設(shè)置適配器關(guān)聯(lián)。
在GridView的 item中設(shè)置了點(diǎn)擊事件,并且提供給外部用來(lái)刷新數(shù)據(jù)的onActivityResult方法。
大家看上面的代碼應(yīng)該就明白了。
2、圖片預(yù)覽和刪除
這一步很簡(jiǎn)單,獲取到傳過(guò)來(lái)的imageList和postion對(duì)圖片進(jìn)行展示,在點(diǎn)擊刪除的時(shí)候remove掉imageList對(duì)應(yīng)的數(shù)據(jù),并且刷新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());// 圖片數(shù)量和當(dāng)前圖片信息展示
initView();
AndroidImagePicker.getInstance().clearSelectedImages();
}
private void initView() {
mViewPager = (ViewPager)findViewById(R.id.viewpager);
mAdapter = new TouchImageAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(mShowItemPosition, false);//設(shè)置顯示當(dāng)前的圖片
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());//滑動(dòng)viewPager時(shí)更新顯示信息
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.btn_del) {//刪除按鈕點(diǎn)擊
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對(duì)圖片進(jìn)行加載,在點(diǎn)擊回退和刪除完圖片的時(shí)候把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方法中調(diào)用ImagePickerView數(shù)據(jù)刷新方法
imagePicker.onActivityResult(requestCode,resultCode,data);
3、獲取選擇圖片的路徑
調(diào)用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);//設(shè)置顯示5列
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
imagePicker.onActivityResult(requestCode,resultCode,data);
}
}
結(jié)語(yǔ)
看了ImagePickerView的實(shí)現(xiàn),是不是發(fā)現(xiàn)一個(gè)自定義控件其實(shí)也很簡(jiǎn)單。在我們做自定義控件的時(shí)候,其實(shí)大部分情況只是在Android系統(tǒng)提供的功能上多加一點(diǎn)我們的需求而已。站在巨人的肩膀上,才可以看的更遠(yuǎn)嘛。
關(guān)于圖片加載和圖片選擇本文沒(méi)有提及,圖片加載我參考了文章,感興趣的朋友可以去github查看,https://github.com/easonline/AndroidImagePicker。我在自己的Demo中對(duì)源碼做了修改,并統(tǒng)一使用了Fresco加載圖片。
有需要參考源碼的同學(xué)請(qǐng)參考:源碼下載
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android實(shí)現(xiàn)朋友圈多圖顯示功能
- Android 自定義 HorizontalScrollView 打造多圖片OOM 的橫向滑動(dòng)效果(實(shí)例代碼)
- Android OkHttp 結(jié)合php 多圖片上傳實(shí)例
- 微信多圖上傳解決android多圖上傳失敗問(wèn)題
- Android 加載大圖及多圖避免程序出現(xiàn)OOM(OutOfMemory)異常
- Android Retrofit實(shí)現(xiàn)多圖片/文件、圖文上傳功能
- Android仿微信微博多圖展示效果
- Android 加載大圖、多圖和LruCache緩存詳細(xì)介紹
- Android高效加載大圖、多圖解決方案 有效避免程序OOM
- Android仿微信發(fā)表說(shuō)說(shuō)實(shí)現(xiàn)拍照、多圖上傳功能
- android實(shí)現(xiàn)多圖文分享朋友圈功能
相關(guān)文章
Android Dialog 對(duì)話(huà)框詳解及示例代碼
本文主要介紹Android Dialog,這里詳細(xì)介紹Android Dialog的基本使用方法,并提供了示例代碼和實(shí)現(xiàn)效果圖,有需要的小伙伴可以參考下2016-08-08
Android自定義實(shí)現(xiàn)BaseAdapter的優(yōu)化布局
這篇文章主要為大家詳細(xì)介紹了Android自定義實(shí)現(xiàn)BaseAdapter的優(yōu)化布局,感興趣的小伙伴們可以參考一下2016-08-08
Android開(kāi)發(fā)之利用Activity實(shí)現(xiàn)Dialog對(duì)話(huà)框
這篇文章主要給大家介紹了Android開(kāi)發(fā)之如何利用Activity實(shí)現(xiàn)Dialog對(duì)話(huà)框效果,文中給出了詳細(xì)的示例代碼,相信對(duì)大家的理解及學(xué)習(xí)具有一定的參考借鑒價(jià)值,有需要的朋友們下面來(lái)一起看看吧。2016-12-12
Android實(shí)現(xiàn)3D層疊式卡片圖片展示
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)3D層疊式卡片圖片展示,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10
Android開(kāi)發(fā)中CheckBox的簡(jiǎn)單用法示例
這篇文章主要介紹了Android開(kāi)發(fā)中CheckBox的簡(jiǎn)單用法,結(jié)合實(shí)例形式分析了Android中CheckBox控件的基本功能設(shè)置與頁(yè)面布局技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
Android 創(chuàng)建與解析XML(五)——詳解Dom4j方式
本篇文章主要介紹了Android創(chuàng)建與解析XML(二)——詳解Dom4j方式,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2016-11-11
Android仿QQ空間動(dòng)態(tài)界面分享功能
這篇文章主要介紹了Android仿QQ空間動(dòng)態(tài)界面分享功能,本文圖文并茂給大家介紹的非常詳細(xì),需要的朋友可以參考下2017-04-04

