編輯:關於Android編程
Android開發中遇到要從相冊選擇圖片時,大多數人都會選擇調用Android自帶的相冊,畢竟這樣可以節約時間,又不用自己去處理圖片的問題,不過這樣也會產生一些問題,有些系統自帶的相冊真的是丑到沒朋友,有時調用系統相冊時不時的還可能發生崩潰問題。而我的安卓架構中選擇了自定義相冊的功能,其效果是仿照QQ的圖片選擇樣式,通過dialog展現出來的,還自定義了圖片的剪裁,使用了CropImageView 實現了多種剪裁效果。
圖片選擇的直接輔助類:
/**
* 圖片選擇輔助類
* Created by tianlai on 16-4-12.
*/
public class PickImageHelper {
private static final String TAG = "PickImageHelper";
private PickType pickType;
private Type type;
private Activity activtiy;
private PicKImageDialog picKImageDialog;
private CropImageDialog cropImageDialog;
private String cachePath;
private OnImageOutputListener onImageOutputListener;
@Inject
public PickImageHelper(PicKImageDialog picKImageDialog) {
this.picKImageDialog = picKImageDialog;
}
/**
* 初始化
*
* @param activtiy
*/
public void initPickImageHelper(final Activity activtiy) {
this.activtiy = activtiy;
picKImageDialog.setCancelable(false);
cachePath = SDCardUtil.getRootPath() + activtiy.getString(R.string.cache_path);
picKImageDialog.setPickImageListener(new PicKImageDialog.PickImageListener() {
@Override
public void onPickFinish(List paths) {
LogUtil.i(TAG, "已選的圖片---" + paths.toString());
picKImageDialog.dismiss();
if (onImageOutputListener != null) {
if (pickType == PickType.HEADICON) {
onImageOutputListener.outputHeadImage(paths.get(0));
} else if (pickType == PickType.MULTIIMAGE) {
onImageOutputListener.outputPickedImages(paths);
}
}
}
@Override
public void toCropImage(String path) {
LogUtil.i(TAG, "要剪裁的圖片---" + path.toString());
picKImageDialog.dismiss();
initCropImageDialog(Uri.parse("file://" + path));
}
});
}
/**
* 初始化剪裁的對話框
*
* @param path
* @param activtiy
*/
public void onCaptureResult(final String path, Activity activtiy) {
this.activtiy = activtiy;
new AlertDialog.Builder(activtiy)
.setTitle("提示")
.setMessage("是否剪裁圖片?")
.setNegativeButton("否", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dealCropResult(path);
}
}).setPositiveButton("是", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
initCropImageDialog(Uri.parse("file://" + path));
}
}).create().show();
}
/**
* 初始化剪裁的對話框
*
* @param uri
*/
private void initCropImageDialog(Uri uri) {
cropImageDialog = new CropImageDialog(activtiy);
cropImageDialog.setCancelable(false);
cropImageDialog.setCachePath(cachePath);
cropImageDialog.setCropImageListener(new CropImageDialog.CropImageListener() {
@Override
public void onPickImage() {
LogUtil.i(TAG, "---重新選擇圖片---");
cropImageDialog.dismiss();
picKImageDialog.setMaxNum(1);
picKImageDialog.show();
}
@Override
public void onCropSucceed(String path) {
LogUtil.i(TAG, "剪裁後的圖片路徑---" + path);
cropImageDialog.dismiss();
dealCropResult(path);
}
});
cropImageDialog.show(uri, true);
}
/**
* 剪裁結果的處理
*
* @param path
*/
private void dealCropResult(String path) {
if (onImageOutputListener != null) {
if (pickType == PickType.HEADICON) {
onImageOutputListener.outputHeadImage(path);
} else if (pickType == PickType.MULTIIMAGE) {
List paths = new ArrayList();
paths.add(path);
onImageOutputListener.outputPickedImages(paths);
}
}
}
/**
* 選擇頭像
*/
public void pickHeadImage() {
this.pickType = PickType.HEADICON;
picKImageDialog.setMaxNum(1);
showPickOrCapture();
}
/**
* 選擇圖片
*/
public void pickImages(int maxNum) {
this.pickType = PickType.MULTIIMAGE;
picKImageDialog.setMaxNum(maxNum);
showPickOrCapture();
}
/**
* 從相冊選擇或者拍照
*/
private void showPickOrCapture() {
new AlertDialog.Builder(activtiy)
.setItems(R.array.pick_image, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0: //拍照
type = Type.CAPTURE;
if (onImageOutputListener != null) {
onImageOutputListener.takePhoto();
}
break;
case 1: //圖庫
type = Type.PICKIMAGE;
picKImageDialog.show();
break;
case 2: //取消
break;
}
}
}).create().show();
}
public OnImageOutputListener getOnImageOutputListener() {
return onImageOutputListener;
}
public void setOnImageOutputListener(OnImageOutputListener onImageOutputListener) {
this.onImageOutputListener = onImageOutputListener;
}
public PickType getPickType() {
return pickType;
}
public void setPickType(PickType pickType) {
this.pickType = pickType;
}
//選擇類型,單張頭像還是多張
public enum PickType {
HEADICON, MULTIIMAGE
}
//選擇類型,拍照還是圖庫
public enum Type {
CAPTURE, PICKIMAGE
}
public static interface OnImageOutputListener {
public void takePhoto();
/**
* 輸出頭像
*
* @param path
*/
public void outputHeadImage(String path);
/**
* 輸出選擇的圖片
*
* @param paths
*/
public void outputPickedImages(List paths);
}
}
圖片選擇dialog:
/**
* 圖片選擇彈出對話框
*
* Created by tianlai on 16-4-12. */ public class PicKImageDialog extends Dialog { private Context context; private LayoutInflater inflater; private ImageView topHome; private TextView topTitle; private TextView topCenter; private TextView topAction; private GridView gvPictures; private ViewPager vpPictures; private ViewSwitcher vsPictures; private TextView bottomPreview; private TextView bottomCrop; private TextView bottomSure; private ListView lvPaths; private ViewSwitcher vsLayout; private List
圖片的封裝對象:
/**
* 封裝一個目錄及其目錄下的圖片文件的路徑
* Created by tianlai on 16-4-12.
*/
public class ImageCollection {
private String rootPath;
private String rootName;
private Set pictures;
public ImageCollection() {
}
public ImageCollection(Set pictures, String rootName) {
this.pictures = pictures;
this.rootName = rootName;
}
public String getRootPath() {
return rootPath;
}
public void setRootPath(String rootPath) {
this.rootPath = rootPath;
}
public Set getPictures() {
return pictures;
}
public void setPictures(Set pictures) {
this.pictures = pictures;
}
public void addPicture(String picture){
if (pictures==null){
pictures=new HashSet<>();
}
pictures.add(picture);
}
public void rmPicture(String picture){
if (pictures==null){
return;
}
pictures.remove(picture);
}
public String getRootName() {
return rootName;
}
public void setRootName(String rootName) {
this.rootName = rootName;
}
@Override
public String toString() {
return "ImageCollection{" +
"rootPath='" + rootPath + '\'' +
", pictures=" + pictures +
'}';
}
}
圖片顯示適配器:
/**
* 圖片顯示頁面的適配器
* Created by tianlai on 16-4-12.
*/
public class PicturesAdapter extends BaseAbsListAdapter {
private Set selectedPictures;
private OnPictrueSelectListener onPictrueSelectListener;
private int maxNum;
public PicturesAdapter(Context context, LayoutInflater inflater) {
super(context, inflater);
selectedPictures=new HashSet<>();
}
@Override
public PicturesViewHolder onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater) {
return new PicturesViewHolder(inflater.inflate(R.layout.grid_item_select_image,null),this);
}
public Set getSelectedPictures() {
return selectedPictures;
}
public void setSelectedPictures(Set selectedPictures) {
this.selectedPictures = selectedPictures;
}
public void addSelectedPicture(String path){
selectedPictures.add(path);
notifySelectedItem();
}
public void removeSelectedPicture(String path){
selectedPictures.remove(path);
notifySelectedItem();
}
/**
* 有item選中或者取消選中時的事件
*/
private void notifySelectedItem() {
if (onPictrueSelectListener!=null){
int size = selectedPictures.size();
onPictrueSelectListener.selectedSingleItem(size==1);
onPictrueSelectListener.hasItemSelected(size >0,size);
}
}
public boolean isSlected(String path){
Log.i("picture",selectedPictures.toString());
return selectedPictures.contains(path);
}
public OnPictrueSelectListener getOnPictrueSelectListener() {
return onPictrueSelectListener;
}
public void setOnPictrueSelectListener(OnPictrueSelectListener onPictrueSelectListener) {
this.onPictrueSelectListener = onPictrueSelectListener;
}
public int getMaxNum() {
return maxNum;
}
public void setMaxNum(int maxNum) {
this.maxNum = maxNum;
}
public boolean isCanSelectMore(){
return selectedPictures.size()(selectedPictures).get(0).equals(path)){
return true;
}
}
return false;
}
/**
* 獲取要剪裁的圖片路徑
*
* @return
*/
public String getCorpPicturePath(){
if (selectedPictures.size()==1){
return new ArrayList<>(selectedPictures).get(0);
}
return null;
}
/**
* 清空選中的圖片
*/
public void clearSelectedPictures(){
selectedPictures.clear();
}
public static interface OnPictrueSelectListener{
public void selectedSingleItem(boolean selectedSingleItem);
public void hasItemSelected(boolean hasItemSelected, int selectNum);
}
}
圖片顯示的Viewholder:
/**
* 圖片顯示頁面的適配器的ViewHolder
* Created by tianlai on 16-4-12.
*/
public class PicturesViewHolder extends BaseViewHolder implements CompoundButton
.OnCheckedChangeListener {
SimpleDraweeView sdvPicture;
CheckBox cbSelect;
private ImageRequest imageRequest;
private DraweeController draweeController;
public PicturesViewHolder(View convertView, BaseAbsListAdapter absListAdapter) {
super(convertView, absListAdapter);
sdvPicture = (SimpleDraweeView) find(R.id.sdv_picture);
cbSelect = (CheckBox) find(R.id.cb_select);
cbSelect.setOnCheckedChangeListener(this);
}
@Override
public void loadDataToView(int position, String data) {
super.loadDataToView(position,data);
imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse("file://" + data))
.setResizeOptions(new ResizeOptions(128,128))
.setLocalThumbnailPreviewsEnabled(true)
.build();
draweeController = Fresco.newDraweeControllerBuilder()
.setImageRequest(imageRequest)
.setOldController(sdvPicture.getController())
.build();
sdvPicture.setController(draweeController);
if (((PicturesAdapter) absListAdapter).isSlected(data)) {
cbSelect.setChecked(true);
Log.i("picture","位置"+position+"--已選中");
}else {
cbSelect.setChecked(false);
Log.i("picture","位置"+position+"--未選中");
}
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
if (((PicturesAdapter) absListAdapter).isCanSelectMore()||((PicturesAdapter) absListAdapter).isSlected(data)){
((PicturesAdapter) absListAdapter).addSelectedPicture(data);
Log.i("picture","位置"+position+"--正在點擊選中");
}else {
Toast.makeText(context,"最多只能選擇"+((PicturesAdapter) absListAdapter).getMaxNum()+"張圖片",Toast
.LENGTH_SHORT).show();
buttonView.setChecked(false);
}
} else {
((PicturesAdapter) absListAdapter).removeSelectedPicture(data);
Log.i("picture","位置"+position+"--正在取消選中");
}
}
}
圖片文件夾頁面適配器:
/**
* 選擇圖片文件夾頁面的適配器
* Created by tianlai on 16-4-12.
*/
public class ParentPathsAdapter extends BaseAbsListAdapter {
public ParentPathsAdapter(Context context, LayoutInflater inflater) {
super(context, inflater);
}
@Override
public ParentPathsViewHolder onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater) {
return new ParentPathsViewHolder(inflater.inflate(R.layout.list_item_parent_paths,null),this);
}
}
圖片文件夾頁面ViewHolder:
/**
* 選擇圖片文件夾頁面的適配器的ViewHolder
* Created by tianlai on 16-4-12.
*/
public class ParentPathsViewHolder extends BaseViewHolder {
SimpleDraweeView sdvImage;
TextView tvPath;
public ParentPathsViewHolder(View convertView, BaseAbsListAdapter absListAdapter) {
super(convertView, absListAdapter);
sdvImage= (SimpleDraweeView) find(R.id.sdv_image);
tvPath= (TextView) find(R.id.tv_path);
}
@Override
public void loadDataToView(int position, ImageCollection data) {
List pictures = new ArrayList<>(data.getPictures());
if (pictures != null) {
int size = pictures.size();
if (size > 0) {
sdvImage.setImageURI(Uri.parse("file://" + pictures.get(0)));
tvPath.setText(data.getRootName() + "(" + size + ")");
}
}
}
}
選中的圖片預覽適配器:
/**
* 浏覽選中的圖片的適配器(浏覽頁面是一個ViewPager)
* Created by tianlai on 16-4-12.
*/
public class PicturesPagerAdapter extends PagerAdapter {
private List images;
private List picturePaths;
private LayoutInflater inflater;
public PicturesPagerAdapter(List picturePaths, LayoutInflater inflater) {
this.inflater = inflater;
this.picturePaths=picturePaths;
images = new ArrayList();
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup
.LayoutParams.MATCH_PARENT);
View view;
for (String path : picturePaths) {
view=inflater.inflate(R.layout.vp_item_picture,null);
((SimpleDraweeView)view.findViewById(R.id.vp_picture)).setImageURI(Uri.parse("file://"+path));
images.add(view);
}
}
@Override
public int getCount() {
return images.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = images.get(position);
container.addView(view);
return view;
}
public List getPicturePaths() {
return picturePaths;
}
public void setPicturePaths(List picturePaths) {
this.picturePaths = picturePaths;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(images.get(position));
}
}
剪裁圖片dialog:
/**
* 剪裁圖片的對話框
* Created by tianlai on 16-4-13.
*/
public class CropImageDialog extends Dialog {
private Context context;
private LayoutInflater inflater;
// Views ///////////////////////////////////////////////////////////////////////////////////////
private CropImageView mCropView;
private LinearLayout mRootLayout;
private ProgressBar progressBar;
private String cachePath;
private String cropPath;
private boolean isFromPick;
private CropImageListener cropImageListener;
public CropImageDialog(Context context) {
super(context, R.style.AppTheme_Dialog);
init(context);
}
public CropImageDialog(Context context, int theme) {
super(context, theme);
init(context);
}
private void init(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View contentView = inflater.inflate(R.layout.dialog_cropimage, null);
setContentView(contentView);
// bind Views
bindViews(contentView);
mCropView.setDebug(BuildConfig.DEBUG);
}
private void bindViews(View view) {
progressBar = (ProgressBar) view.findViewById(R.id.progress);
progressBar.getIndeterminateDrawable().setColorFilter(getContext().getResources().getColor(R.color.colorAccent),
PorterDuff.Mode.SRC_IN);
mRootLayout = (LinearLayout) view.findViewById(R.id.layout_root);
mCropView = (CropImageView) view.findViewById(R.id.cropImageView);
mCropView.setOutputMaxSize(300, 300);
view.findViewById(R.id.top_home).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
view.findViewById(R.id.top_action).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
view.findViewById(R.id.buttonDone).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showProgress();
mCropView.startCrop(createSaveUri(), mCropCallback, mSaveCallback);
}
});
view.findViewById(R.id.buttonFitImage).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.FIT_IMAGE);
}
});
view.findViewById(R.id.button1_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.SQUARE);
}
});
view.findViewById(R.id.button3_4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.RATIO_3_4);
}
});
view.findViewById(R.id.button4_3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.RATIO_4_3);
}
});
view.findViewById(R.id.button9_16).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.RATIO_9_16);
}
});
view.findViewById(R.id.button16_9).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.RATIO_16_9);
}
});
view.findViewById(R.id.buttonFree).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.FREE);
}
});
view.findViewById(R.id.buttonPickImage).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (cropImageListener != null) {
cropImageListener.onPickImage();
}
}
});
view.findViewById(R.id.buttonRotateLeft).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.rotateImage(CropImageView.RotateDegrees.ROTATE_M90D);
}
});
view.findViewById(R.id.buttonRotateRight).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.rotateImage(CropImageView.RotateDegrees.ROTATE_90D);
}
});
view.findViewById(R.id.buttonCustom).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCustomRatio(7, 5);
}
});
view.findViewById(R.id.buttonCircle).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.CIRCLE);
}
});
view.findViewById(R.id.buttonShowCircleButCropAsSquare).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCropView.setCropMode(CropImageView.CropMode.CIRCLE_SQUARE);
}
});
}
/**
* 創建保存剪裁圖片的Uri
*
* @return
*/
public Uri createSaveUri() {
try {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//檢查路徑是否存在
File dir = new File(cachePath);
if (!dir.exists()) {
dir.mkdirs();
}
//檢查文件是否存在
File file = new File(cachePath, "cropped.jpg");
if (!file.exists()) {
file.createNewFile();
}
cropPath = file.getAbsolutePath();
return Uri.fromFile(file);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public void onBackPressed() {
super.onBackPressed();
if (isFromPick) {
if (cropImageListener != null) {
cropImageListener.onPickImage();
return;
}
}
dismiss();
}
private void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}
private void dismissProgress() {
progressBar.setVisibility(View.GONE);
}
/**
* @param cachePath
* @param pickImageListener
*/
public void initCropDialog(String cachePath, CropImageListener pickImageListener) {
this.cropImageListener = pickImageListener;
this.cachePath = cachePath;
}
/**
* 開始剪裁
*
* @param pictureUri 要剪裁的圖片的Uri
*/
public void show(Uri pictureUri, boolean isFromPick) {
Log.i("CropImageDialog", pictureUri.toString());
this.isFromPick = isFromPick;
super.show();
setDialogWindowAttr();
showProgress();
mCropView.startLoad(pictureUri, mLoadCallback);
}
// /**
// * 開始剪裁
// *
// * @param pictureUri
// * @param isFromPick
// */
// public void show(Uri pictureUri, boolean isFromPick) {
// super.show();
//
// setDialogWindowAttr();
//
// Log.i("CropImageDialog", pictureUri.toString());
//
// this.isFromPick = isFromPick;
//
// showProgress();
// mCropView.setImageURI(pictureUri);
//
// }
/**
* 調整dialog的大小
*/
public void setDialogWindowAttr() {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.gravity = Gravity.CENTER;
lp.width = WindowManager.LayoutParams.MATCH_PARENT;//寬高可設置具體大小
lp.height = WindowManager.LayoutParams.MATCH_PARENT;
getWindow().setAttributes(lp);
}
public String getCachePath() {
return cachePath;
}
public void setCachePath(String cachePath) {
this.cachePath = cachePath;
}
public CropImageListener getCropImageListener() {
return cropImageListener;
}
public void setCropImageListener(CropImageListener cropImageListener) {
this.cropImageListener = cropImageListener;
}
// Callbacks ///////////////////////////////////////////////////////////////////////////////////
private final LoadCallback mLoadCallback = new LoadCallback() {
@Override
public void onSuccess() {
dismissProgress();
}
@Override
public void onError() {
dismissProgress();
}
};
private final CropCallback mCropCallback = new CropCallback() {
@Override
public void onSuccess(Bitmap cropped) {
}
@Override
public void onError() {
}
};
private final SaveCallback mSaveCallback = new SaveCallback() {
@Override
public void onSuccess(Uri outputUri) {
dismissProgress();
if (cropImageListener != null) {
cropImageListener.onCropSucceed(cropPath);
}
}
@Override
public void onError() {
dismissProgress();
}
};
public static interface CropImageListener {
/**
* 選擇圖片
*/
public void onPickImage();
/**
* 剪裁成功
*
* @param path
*/
public void onCropSucceed(String path);
}
}
下面來看看效果圖:
1.圖片顯示頁面
2.圖片文件夾顯示頁面<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="圖片文件夾顯示頁面" src="/uploadfile/Collfiles/20160518/20160518092032471.jpg" title="\" />
3.選中的圖片的預覽頁面
4.圖片剪裁頁面
好了,就到這裡吧!
更多資料:
我的github地址以及使用demo: https://github.com/naivor/naivorapp
英文詞典是手機中經常使用的應用。因此,在本文將結合Android來討論如何實現一個Android版的英文詞典。實現英文詞典的方法很多。在本文使用了SQLite數據庫來保存
先看看效果圖吧實現這樣的效果,你要知道貝塞爾曲線,何謂貝塞爾曲線?先在這裡打個問號下面就直接寫了1.activity_main.xml<RelativeLayout
其實我一直准備寫一篇關於Android事件分發機制的文章,從我的第一篇博客開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如
更多動態視圖MoreNewsView經常看朋友圈的動態,有的動態內容較多就只展示前面一段,如果用戶想看完整的再點擊展開,這樣整個頁面的動態列表比較均衡,不會出現個別動態占