一、簡介
評(píng)價(jià)曬單頁面, 一般商城類的APP都是必不可少的
今天就來簡單的實(shí)現(xiàn)一下, 只有兩個(gè)Activity, 曬單、圖片顯示Activity
二、使用
- 開發(fā)工具:
Android Studio
- 使用的第三方框架:
Fresco 圖片加載緩存
AndroidAutoLayout 屏幕適配
MultiImageSelector 仿照微信的選擇圖片(照片)
PhotoDraweeview 圖片縮放(配合
Fresco使用)PhotoView 圖片縮放(不使用
Fresco時(shí)使用)
配置如下

布局沒有太大難度, 大家照著依葫蘆畫瓢就寫出來了, 就不做介紹了
由于代碼過多就不全部展示了, 只講解重要的邏輯處理
廢話不多說, 先看效果圖
效果圖可能加載有點(diǎn)慢 耐心等待

1.MainActivity
點(diǎn)擊相機(jī)后執(zhí)行MultiImageSelector.create().count(MAX_PIC - imageUrls.size()).start(this, REQUEST_CODE_PICTURE);
調(diào)出選擇圖片界面, 選擇完成會(huì)進(jìn)入onActivityResult方法
我們就在該方法中處理選中的曬單圖集合
回調(diào)的onActivityResult后核心的處理在handleCommentPicList方法里
public static final String KEY_IMAGE_LIST = "imageList";
public static final String KEY_CURRENT_INDEX = "currentIndex";
private final int REQUEST_CODE_PICTURE = 1;
private final int RESULT_CODE_LARGE_IMAGE = 1;
//曬單圖片最多選擇四張
private final int MAX_PIC = 4;
@Override
protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CODE_PICTURE) {
// 獲取返回的圖片列表
List<String> path = data.getStringArrayListExtra(MultiImageSelectorActivity.EXTRA_RESULT);
imageUrls.addAll(path);
handleCommentPicList(imageUrls, false);
}
} else if (resultCode == RESULT_CODE_LARGE_IMAGE) {
//曬單大圖頁返回, 重新設(shè)置曬單圖片
handleCommentPicList(imageUrls = data.getStringArrayListExtra(KEY_IMAGE_LIST), true);
}
}
handleCommentPicList方法作用是
把返回的幾張壓縮后保存在一個(gè)零時(shí)文件夾, 并在用戶退出時(shí)清空該文件夾
傳到服務(wù)器時(shí)也是上傳壓縮后的圖
現(xiàn)在的手機(jī)拍出的圖基本都是5MB左右, 不可能就把這么大的圖片完全顯示上去
ps: 我最開始沒這樣壓縮時(shí), 把4張5M左右的圖不做處理直接顯示, 直接卡著動(dòng)不了, app占用內(nèi)存秒升到300M+把壓縮后的圖顯示到控件上
sdv_pic.setImageURI(Uri.parse("file://" + path));設(shè)置
onClickListener跳轉(zhuǎn)到圖片詳情Activity
/**
* 處理選擇的評(píng)價(jià)圖片
*
* @param paths 圖片的路徑集合
* @param isFromBack 是否來自LargeImageActivity返回
*/
private void handleCommentPicList(final List<String> paths, boolean isFromBack) {
LinearLayout rootview = new LinearLayout(context);
View commentView;
SimpleDraweeView sdv_pic;
for (int i = 0, len = paths.size(); i < len; i++) {
commentView = getLayoutInflater().inflate(R.layout.order_comment_pic_item, null);
sdv_pic = (SimpleDraweeView) commentView.findViewById(R.id.sdv_pic);
if (isFromBack) {
//來自LargeImageActivity
sdv_pic.setImageURI(Uri.parse("file://" + paths.get(i)));
} else {
//來自圖片選擇器
String path = FileUtils.getCachePath(context);//獲取app緩存路徑來存放臨時(shí)圖片
BitmapUtils.compressImage(paths.get(i), path, 95);
sdv_pic.setImageURI(Uri.parse("file://" + path));
imageUrls.set(i, path);
}
final int finalI = i;
sdv_pic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//點(diǎn)擊HorizontalScrollView里的曬單圖進(jìn)入圖片詳情頁
Intent intent = new Intent(context, CommentLargeImageActivity.class);
intent.putExtra(KEY_CURRENT_INDEX, finalI);
intent.putStringArrayListExtra(KEY_IMAGE_LIST, (ArrayList<String>) paths);
startActivityForResult(intent, REQUEST_CODE_PICTURE);
}
});
AutoUtils.auto(commentView);
rootview.addView(commentView);
}
hsv_comment_imgs.removeAllViews();
hsv_comment_imgs.addView(rootview);
}
用戶退出時(shí)需要清除臨時(shí)壓縮圖片文件
@Override
public void onBackPressed() {
super.onBackPressed();
//清除臨時(shí)壓縮圖片文件
CleanCacheManager.cleanExternalCache(this);
}
public class CleanCacheManager {
/**
* 清除外部cache下的內(nèi)容(/mnt/sdcard/android/data/com.xxx.xxx/cache)
*
* @param context
*/
public static void cleanExternalCache(Context context) {
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
deleteFilesByDirectory(context.getExternalCacheDir());
}
}
/**
* 刪除方法 這里只會(huì)刪除某個(gè)文件夾下的文件,如果傳入的directory是個(gè)文件,將不做處理
*
* @param directory
*/
private static void deleteFilesByDirectory(File directory) {
if (directory != null && directory.exists() && directory.isDirectory()) {
for (File item : directory.listFiles()) {
item.delete();
}
}
}
}
2.CommentLargeImageActivity
獲取點(diǎn)擊圖片的索引, 作用顯示于當(dāng)前索引圖片
獲取傳來的圖片集合, 并設(shè)置到ViewPager的適配器
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
currentIndex = bundle.getInt(MainActivity.KEY_CURRENT_INDEX);
imgUrls = bundle.getStringArrayList(MainActivity.KEY_IMAGE_LIST);
vp_large_image.setAdapter(adapter = new LargeImageAdapter(this, imgUrls));//設(shè)置曬單圖顯示
vp_large_image.setOffscreenPageLimit(imgUrls.size());//預(yù)加載的數(shù)量為圖片集合的長度
vp_large_image.setCurrentItem(currentIndex);
tv_current_index.setText(++currentIndex + " / " + imgUrls.size());
currentIndex--;
}
點(diǎn)擊圖片詳情頁右上角的刪除時(shí). 會(huì)刪除當(dāng)前的圖片
case R.id.ll_remove:
//刪除當(dāng)前曬單圖
if (imgUrls.size() == 1) {
//刪除最后一張時(shí)直接回到曬單評(píng)論頁
imgUrls.clear();
onBackPressed();//圖片刪除完后退出當(dāng)前頁面
} else {
//刪除指定索引的圖片
removeImage(currentIndex);
}
break;
點(diǎn)擊左上角的返回或者物理鍵盤的返回
就把處理后的圖片集合返回到MainActivity
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putStringArrayListExtra(MainActivity.KEY_IMAGE_LIST, (ArrayList<String>) adapter.getData());
//此處返回到MainActivity的onActivityResult回調(diào)方法的resultCode == RESULT_CODE_LARGE_IMAGE判斷
setResult(RESULT_CODE_LARGE_IMAGE, intent);
super.onBackPressed();
}
/**
* 刪除指定索引的曬單圖
* @param index
*/
private void removeImage(int index) {
imgUrls.remove(index);
setImageTitle(index);
vp_large_image.removeAllViews();//刪除viewpager所有的子View
vp_large_image.setAdapter(adapter = new LargeImageAdapter(this, imgUrls));//重新設(shè)置適配器數(shù)據(jù)顯示
vp_large_image.setCurrentItem(index == imgUrls.size() - 1 ? ++index : --index);//顯示指定位置
adapter.notifyDataSetChanged();
}
刪除圖片后, 標(biāo)題需要的操作
/**
* 設(shè)置標(biāo)題顯示
* <p>比如3 / 4
* @param index
*/
private void setImageTitle(int index) {
//刪除圖片路徑后
if (index == 0 || imgUrls.size() == 1) {
// 索引 == 0 || 圖片集合只剩一張圖
// 就把索引值固定為1
index = 1;
} else if (index == imgUrls.size() - 1) {
// 當(dāng)前索引 == 圖片集合的最后一張
// 就不做任何處理
} else {
//否則就把索引+1便于顯示
index += 1;
}
tv_current_index.setText(index + " / " + imgUrls.size());
}
3.LargeImageAdapter
適配器的代碼由于使用到圖片縮放框架, 所以大部分代碼都是固定寫法
public class LargeImageAdapter extends PagerAdapter {
private Context context;
private List<String> imgUrls;
public LargeImageAdapter(Context context, List<String> imgUrls) {
this.context = context;
this.imgUrls = imgUrls;
}
public List<String> getData() {
return this.imgUrls;
}
public void setData(List<String> imgUrls) {
this.imgUrls = imgUrls;
notifyDataSetChanged();
}
@Override
public int getCount() {
return imgUrls.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
ViewPager viewPager = (ViewPager) container;
View view = (View) object;
viewPager.removeView(view);
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
@Override
public Object instantiateItem(ViewGroup viewGroup, int position) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final PhotoDraweeView photoDraweeView = (PhotoDraweeView) inflater.inflate(R.layout.include_large_image, null).findViewById(R.id.sdv_larget_image);
PipelineDraweeControllerBuilder controller = Fresco.newDraweeControllerBuilder();
controller.setUri(Uri.parse("file://" + imgUrls.get(position)));//設(shè)置圖片顯示
controller.setOldController(photoDraweeView.getController());
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.setOnViewTapListener(new OnViewTapListener() {
@Override
public void onViewTap(View view, float x, float y) {
//單擊退出
((BaseActivity) context).finish();
}
});
photoDraweeView.setController(controller.build());
try {
viewGroup.addView(photoDraweeView, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
} catch (Exception e) {
e.printStackTrace();
}
return photoDraweeView;
}
}