一.這個(gè)功能相信每個(gè)小伙伴都會(huì)遇到,話不多說(shuō)聚唐,直接上效果圖(花屏是錄屏軟件的原因)與步驟辫继,以供小伙伴們參考。
1.依賴:
//圖片選擇器
compile 'com.jph.takephoto:takephoto_library:4.0.3'
//仿iOS的AlertViewController
compile 'com.bigkoo:alertview:1.0.3'
//最新版本Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
//design中包含的RecycleView
compile 'com.android.support:design:25.3.1'
//ButterKnife
compile 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
//photoView 圖片放大控件
compile 'com.github.chrisbanes:PhotoView:1.3.0'
2.xml文件布局:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleview_check"
android:layout_width="match_parent"
android:layout_height="wrap_content"
></android.support.v7.widget.RecyclerView>
3.當(dāng)前Activity中初始化代碼:
RecycleView recyCheck =(Recycleview)findViewById(R.id.recycleview_check);
//設(shè)置展示框中單行展示的圖片個(gè)數(shù)
recyCheck.setLayoutManager(new GridLayoutManager(this, 5));
//初始化自定義Adapter禽车,onAddPicListener是添加圖片的點(diǎn)擊監(jiān)聽(tīng)器,onPicClickListener是添加圖片成功以后刊殉,點(diǎn)擊放大的監(jiān)聽(tīng)器殉摔。
photoAdapter = new PhotoAdapter(this, onAddPicListener, onPicClickListener);
//設(shè)置多選時(shí)最多選擇的圖片張數(shù)
photoAdapter.setSelectMax(5);
recyCheck.setAdapter(photoAdapter);
其中,PhotoAdapter代碼如下:
public class PhotoAdapter extends
RecyclerView.Adapter<PhotoAdapter.ViewHolder> {
private int selectMax = 3;
public final int TYPE_CAMERA = 1;
public final int TYPE_PICTURE = 2;
private Context mContext;
private LayoutInflater mInflater;
private List<TImage> list = new ArrayList<>();
//點(diǎn)擊添加圖片跳轉(zhuǎn)
private onAddPicListener mOnAddPicListener;
public interface onAddPicListener {
void onAddPicClick(int type, int position);
}
//點(diǎn)擊圖片放大
private onPicClickListener mOnPicClickListener;
public interface onPicClickListener {
void onPicClick(View view, int position);
}
public PhotoAdapter(Context context, onAddPicListener mOnAddPicListener, onPicClickListener mOnPicClickListener) {
mInflater = LayoutInflater.from(context);
this.mContext = context;
this.mOnAddPicListener = mOnAddPicListener;
this.mOnPicClickListener = mOnPicClickListener;
}
public void setSelectMax(int selectMax) {
this.selectMax = selectMax;
}
public void setList(List<TImage> list) {
this.list = list;
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView mPhoto_image;
ImageView mPhoto_del;
public ViewHolder(View view) {
super(view);
mPhoto_image = (ImageView) view.findViewById(R.id.photo_image);
mPhoto_del = (ImageView) view.findViewById(R.id.photo_del);
}
}
@Override
public int getItemCount() {
if (list.size() < selectMax) {
return list.size() + 1;
} else {
return list.size();
}
}
@Override
public int getItemViewType(int position) {
if (isShowAddItem(position)) {
return TYPE_CAMERA;
} else {
return TYPE_PICTURE;
}
}
/**
* 創(chuàng)建ViewHolder
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = mInflater.inflate(R.layout.activity_photo_item,
viewGroup, false);
final ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
private boolean isShowAddItem(int position) {
int size = list.size() == 0 ? 0 : list.size();
return position == size;
}
/**
* 設(shè)置值
*/
@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
//少于3張记焊,顯示繼續(xù)添加的圖標(biāo)
Log.d("...", "onBindViewHolder: " + position);
if (getItemViewType(position) == TYPE_CAMERA) {
//設(shè)置添加按鈕圖片的樣式
viewHolder.mPhoto_image.setImageResource(R.mipmap.icon_addpic);
viewHolder.mPhoto_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnAddPicListener.onAddPicClick(0, viewHolder.getAdapterPosition());
}
});
viewHolder.mPhoto_del.setVisibility(View.INVISIBLE);
} else {
viewHolder.mPhoto_del.setVisibility(View.VISIBLE);
viewHolder.mPhoto_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mOnPicClickListener.onPicClick(view, viewHolder.getAdapterPosition());
}
});
viewHolder.mPhoto_del.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mOnAddPicListener.onAddPicClick(1, viewHolder.getAdapterPosition());
}
});
Glide.with(mContext)
.load(list.get(position).getCompressPath())
.crossFade()
.into(viewHolder.mPhoto_image);
}
}
}
PhotoAdapter中所依賴的xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true">
<ImageView
android:id="@+id/photo_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@color/image_color" />
<ImageView
android:id="@+id/photo_del"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|top"
android:scaleType="center"
android:src="@mipmap/icon_delete" />
</FrameLayout>
添加圖片的點(diǎn)擊監(jiān)聽(tīng)器onAddPicListener 代碼及所調(diào)用方法如下:
private List<TImage> selectMedia = new ArrayList<>();
private List<TImage> updateMedia = new ArrayList<>();
ArrayList<String> imageUrls = new ArrayList<>();
private List<String> internetUrls = new ArrayList<>();
private PhotoAdapter.onAddPicListener onAddPicListener = new PhotoAdapter.onAddPicListener() {
@Override
public void onAddPicClick(int type, int position) {
switch (type) {
case 0: //點(diǎn)擊圖片
new AlertView("上傳圖片", null, "取消", null,
new String[]{"拍照", "從相冊(cè)中選擇"},
MainActivity.this, AlertView.Style.ActionSheet, new OnItemClickListener() {
@Override
public void onItemClick(Object o, int position) {
TakePhoto takePhoto = getTakePhoto();
//獲取TakePhoto圖片路徑
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
d(TAG, "SD卡可用");
File file = new File(Environment.getExternalStorageDirectory(), "/photo/" + System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
Uri imageUri = Uri.fromFile(file);
d(TAG, "文件創(chuàng)建成功并獲取URL == " + imageUri);
//設(shè)置takephoto的照片使用壓縮
configCompress(takePhoto);
//設(shè)置Takephoto 使用TakePhoto自帶的相冊(cè) 照片旋轉(zhuǎn)角度糾正
configTakePhotoOption(takePhoto);
switch (position) {
case 0: //拍照
takePhoto.onPickFromCapture(imageUri);
break;
case 1: //相冊(cè)選擇
//如果已展示圖片的個(gè)數(shù)為0逸月,就設(shè)置多選時(shí)最多選中3張
if (selectMedia.size() == 0) {
takePhoto.onPickMultiple(3);
//如果已展示圖片的個(gè)數(shù)為1,就設(shè)置多選時(shí)最多選中2張
} else if (selectMedia.size() == 1) {
takePhoto.onPickMultiple(2);
//如果已展示圖片的個(gè)數(shù)為2遍膜,就設(shè)置多選時(shí)最多選中1張
} else if (selectMedia.size() == 2) {
takePhoto.onPickMultiple(1);
}
break;
}
} else {
Log.d(TAG, "SD卡bu可用");
}
}
}).show();
break;
case 1: //點(diǎn)擊刪除按鈕
selectMedia.remove(position);
photoAdapter.notifyItemRemoved(position);
internetUrls.remove(position);
break;
}
}
};
//設(shè)置Takephoto 使用TakePhoto自帶的相冊(cè) 照片旋轉(zhuǎn)角度糾正
private void configTakePhotoOption(TakePhoto takePhoto) {
TakePhotoOptions.Builder builder = new TakePhotoOptions.Builder();
builder.setWithOwnGallery(true);
builder.setCorrectImage(true);
takePhoto.setTakePhotoOptions(builder.create());
}
//設(shè)置takephoto的照片使用壓縮
private void configCompress(TakePhoto takePhoto) {
CompressConfig config;
config = new CompressConfig.Builder()
.setMaxSize(102400)
.setMaxPixel(800)
.enableReserveRaw(true)
.create();
takePhoto.onEnableCompress(config, false);
}
/**
* 獲取TakePhoto實(shí)例
* 沒(méi)有繼承TakePhotoActivity 所寫
*/
public TakePhoto getTakePhoto() {
if (takePhoto == null) {
takePhoto = (TakePhoto) TakePhotoInvocationHandler.of(this).bind(new TakePhotoImpl(this, this));
}
return takePhoto;
}
getTakePhoto中會(huì)報(bào)錯(cuò)碗硬,此時(shí)瓤湘,我們讓當(dāng)前Activity實(shí)現(xiàn)InvokeListener與TakePhoto.TakeResultListener接口,重寫invoke與方法恩尾,重寫以下方法代碼如下:
@Override
public void takeCancel() {
Log.d(TAG, "takeCancel");
}
@Override
public void takeFail(TResult result, String msg) {
ArrayList<TImage> images = result.getImages();
showImg(result.getImages());
Log.d(TAG, "takeFail" + images.get(0).toString());
}
@Override
public void takeSuccess(TResult result) {
showImg(result.getImages());
}
//沒(méi)有繼承TakePhotoActivity 所寫
@Override
protected void onSaveInstanceState(Bundle outState) {
getTakePhoto().onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
//沒(méi)有繼承TakePhotoActivity 所寫
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionManager.TPermissionType type = PermissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionManager.handlePermissionsResult(this, type, invokeParam, this);
}
//沒(méi)有繼承TakePhotoActivity 所寫
@Override
public PermissionManager.TPermissionType invoke(InvokeParam invokeParam) {
PermissionManager.TPermissionType type = PermissionManager.checkPermission(TContextWrap.of(this), invokeParam.getMethod());
if (PermissionManager.TPermissionType.WAIT.equals(type)) {
this.invokeParam = invokeParam;
}
return type;
}
//圖片成功后返回執(zhí)行的方法
private void showImg(ArrayList<TImage> images) {
Log.d(TAG, images.toString());
//目的是防止上傳重復(fù)的圖片
updateMedia.clear();
for (int i = 0; i < images.size(); i++) {
if (images.get(i).getCompressPath() != null) {
selectMedia.add(images.get(i));
updateMedia.add(images.get(i));
}
}
if (selectMedia != null) {
photoAdapter.setList(selectMedia);
photoAdapter.notifyDataSetChanged();
}
//需上傳圖片弛说,請(qǐng)使用用updateMedia數(shù)組。
}
回過(guò)頭來(lái)看翰意,既然照相肯定有回調(diào)木人,我們應(yīng)當(dāng)在當(dāng)前Activity中重寫回調(diào)方法,并交給takePhoto來(lái)操作
//沒(méi)有繼承TakePhotoActivity 所寫
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//將拍照返回的結(jié)果交給takePhoto處理
getTakePhoto().onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
}
圖片點(diǎn)擊放大監(jiān)聽(tīng)器onPicClickListener代碼如下:
//圖片點(diǎn)擊事件
private PhotoAdapter.onPicClickListener onPicClickListener = new PhotoAdapter.onPicClickListener() {
@Override
public void onPicClick(View view, int position) {
imageUrls.clear();
for (int i = 0; i < selectMedia.size(); i++) {
String compressPath = selectMedia.get(i).getCompressPath();
imageUrls.add(compressPath);
}
imageBrower(position, imageUrls);
}
};
private void imageBrower(int position, ArrayList<String> imageUrls) {
Intent intent = new Intent(this, ImagePagerActivity.class);
intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_URLS, imageUrls);
intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_INDEX, position);
startActivity(intent);
}
我們可以看到冀偶,圖片點(diǎn)擊以后跳轉(zhuǎn)到了另一個(gè)Activity醒第,這個(gè)Activity就是我們專門用來(lái)放大圖片的Activity了,使用的方法很簡(jiǎn)單进鸠,傳入需要展示的圖片數(shù)組和你點(diǎn)擊的圖片索引稠曼,就會(huì)自動(dòng)選中對(duì)應(yīng)的圖片進(jìn)行展示并具備左右滑動(dòng)切換圖片的效果,代碼如下(可當(dāng)作工具類使用堤如,應(yīng)用于項(xiàng)目各個(gè)需要展示圖片數(shù)組的位置蒲列,調(diào)用imageBrower方法即可):
public class ImagePagerActivity extends FragmentActivity {
private static final String STATE_POSITION = "STATE_POSITION";
public static final String EXTRA_IMAGE_INDEX = "image_index";
public static final String EXTRA_IMAGE_URLS = "image_urls";
@BindView(R.id.indicator)
TextView indicator;
@BindView(R.id.pager)
HackyViewPager pager;
private int pagerPosition;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_detail_pager);
ButterKnife.bind(this);
MyUtils.setSystemColor(this,R.color.bg_shouye);
pagerPosition = getIntent().getIntExtra(EXTRA_IMAGE_INDEX, 0);
ArrayList<String> urls = getIntent().getStringArrayListExtra(EXTRA_IMAGE_URLS);
ImagePagerAdapter mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), urls);
pager.setAdapter(mAdapter);
CharSequence text = getString(R.string.viewpager_indicator, 1, pager.getAdapter().getCount());
indicator.setText(text);
// 更新下標(biāo)
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageSelected(int arg0) {
CharSequence text = getString(R.string.viewpager_indicator, arg0 + 1, pager.getAdapter().getCount());
indicator.setText(text);
}
});
if (savedInstanceState != null) {
pagerPosition = savedInstanceState.getInt(STATE_POSITION);
}
pager.setCurrentItem(pagerPosition);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(STATE_POSITION, pager.getCurrentItem());
}
private class ImagePagerAdapter extends FragmentStatePagerAdapter {
public ArrayList<String> fileList;
public ImagePagerAdapter(FragmentManager fm, ArrayList<String> fileList) {
super(fm);
this.fileList = fileList;
}
@Override
public int getCount() {
return fileList == null ? 0 : fileList.size();
}
@Override
public Fragment getItem(int position) {
String url = fileList.get(position);
return ImageDetailFragment.newInstance(url);
}
}
}
ImagePagerActivity 對(duì)應(yīng)的xml文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/bg_shouye"
>
<com.mihomes.checkphotodemo.HackyViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black" />
<TextView
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@android:color/transparent"
android:gravity="center"
android:text="@string/viewpager_indicator"
android:textColor="@android:color/white"
android:textSize="18sp" />
</LinearLayout>
HackyViewPager是一個(gè)重寫了ViewPager的自定義類,解決了PhotoView和ViewPager的滑動(dòng)沖突搀罢,代碼如下:
public class HackyViewPager extends ViewPager {
private static final String TAG = "HackyViewPager";
public HackyViewPager(Context context) {
super(context);
}
public HackyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) { // 不理會(huì)
Log.e(TAG, "hacky viewpager error1");
return false;
} catch (ArrayIndexOutOfBoundsException e) {
// 不理會(huì)
Log.e(TAG, "hacky viewpager error2");
return false;
}
}
}
同時(shí),ImagePagerActivity中所引用的ImageDetailFragment代碼如下:
public class ImageDetailFragment extends Fragment {
private static final String TAG = "ImageDetailFragment";
private String mImageUrl;
private PhotoView mImageView;
private ProgressBar progressBar;
private PhotoViewAttacher mAttacher;
private Dialog dialog;
private Button save;
private Button saveCancel;
public static ImageDetailFragment newInstance(String imageUrl) {
final ImageDetailFragment f = new ImageDetailFragment();
final Bundle args = new Bundle();
args.putString("url", imageUrl);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mImageUrl = getArguments() != null ? getArguments().getString("url") : null;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
mImageView = (PhotoView) v.findViewById(R.id.maxphoto_image);
mAttacher = new PhotoViewAttacher(mImageView);
mAttacher.setOnPhotoTapListener(new PhotoViewAttacher.OnPhotoTapListener() {
@Override
public void onPhotoTap(View arg0, float arg1, float arg2) {
getActivity().finish();
}
@Override
public void onOutsidePhotoTap() {
}
});
progressBar = (ProgressBar) v.findViewById(R.id.maxphoto_loading);
Glide.with(getContext()).load(mImageUrl).into(mImageView);
mAttacher.update();
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
}
ImageDetailFragment所引用的xml文件代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_shouye">
<uk.co.senab.photoview.PhotoView
android:id="@+id/maxphoto_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<ProgressBar
android:id="@+id/maxphoto_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</LinearLayout>
最后侥猩,莫要忘了在清單文件(AndroidManifest.xml)中聲明我們所引用的ImagePagerActivity:
<activity android:name=".ImagePagerActivity"></activity>
該Demo中所用的色值屬性如下:
<color name="image_color">#f6f6f6</color>
<color name="bg_shouye">#333333</color>
文字屬性如下:
<string name="viewpager_indicator">指示器</string>
二.值得一提的是榔至,takePhoto多選框修改樣式的方法如下
自定義相冊(cè)
如果TakePhoto自帶相冊(cè)的UI不符合你應(yīng)用的主題的話,你可以對(duì)它進(jìn)行自定義欺劳。方法如下:
自定義Toolbar
在“res/layout”目錄中創(chuàng)建一個(gè)名為“toolbar.xml”的布局文件唧取,內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/CustomToolbarTheme"
android:background="#ffa352">
</android.support.v7.widget.Toolbar>
在“toolbar.xml”文件中你可以指定TakePhoto自帶相冊(cè)的主題以及Toolbar的背景色。
自定義狀態(tài)欄
在“res/values”目錄中創(chuàng)建一個(gè)名為“colors.xml”的資源文件纺荧,內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="multiple_image_select_primaryDark">#212121</color>
</resources>
通過(guò)上述方式便可以自定義狀態(tài)欄的顏色懒构。
自定義提示文字
在“res/values”目錄的“string.xml”文件沖添加如下代碼:
<resources>
<string name="album_view">選擇圖片</string>
<string name="image_view">單擊選擇</string>
<string name="add">確定</string>
<string name="selected">已選</string>
<string name="limit_exceeded">最多能選 %d 張</string>
</resources>
重寫上述代碼糊秆,便可以自定義TakePhoto自帶相冊(cè)的提示文字。
自定義標(biāo)題欄顏色
在styles.xml里面重寫以下style即可:
<style name="MultipleImageSelectTheme"
parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/multiple_image_select_primary</item>//標(biāo)題欄顏色
<item name="colorPrimaryDark">@color/multiple_image_select_primaryDark</item> //狀態(tài)欄顏色
<item name="colorAccent">@color/multiple_image_select_accent</item>
<item name="actionModeStyle">@style/CustomActionModeStyle</item>
<item name="windowActionModeOverlay">true</item>
<item name="actionMenuTextColor">@android:color/white</item> //標(biāo)題欄文字顏色
</style>
<style name="CustomActionModeStyle" parent="Base.Widget.AppCompat.ActionMode">
<item name="background">@color/rdb_bg</item> //選中標(biāo)題欄背景顏色
</style>
三.本項(xiàng)目GitHub地址:https://github.com/MiHomes/checkPhotoDemo
四.隨手分享淡诗,喜歡的朋友可以關(guān)注微信公眾號(hào)MiHomes,后續(xù)會(huì)有更多更好的博客推送給您伊履。
另:歡迎指出不足韩容,會(huì)進(jìn)行更正
末尾:移動(dòng)互聯(lián)&人力資源交流群,可加微信zy666128入群交流唐瀑。