diff --git a/README.md b/README.md index 05050ce..d129753 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ Image selector for Android device. Support single choice and multi-choice. ------------------- +###Run Demo + +>./gradlew installDebug + ###Quick Start * Step 0 Add module `multi-image-selector` as your dependence. @@ -15,6 +19,11 @@ Add module `multi-image-selector` as your dependence. * Step 1 Declare permission `android.permission.READ_EXTERNAL_STORAGE` in your `AndroidManifest.xml` . Declare `MultiImageSelectorActivity` in your `AndroidManifest.xml` . +```xml + +``` * Step 2 Call image selector activity in your code, eg. @@ -101,6 +110,12 @@ class CustomerActivity extends Activity implements MultiImageSelectorFragment.Ca 1. Fixed. When set `EXTRA_SHOW_CAMERA` to `true`, the first grid item onclick event were messed. 2. Add. Support initial selected image list. +* 2015-4-16 + 1. Fixed. Crack when rotate device. (Issue by [@Leminity](https://github.com/Leminity)) + 2. Fixed. PopupListView position error. (Issue by [@Slock](https://github.com/Slock)) + 3. Change. Demo application shortcut. + 4. Change. Readme file. + ------------------- ###Thanks diff --git a/README_zh.md b/README_zh.md index d82d4fe..b4900fa 100644 --- a/README_zh.md +++ b/README_zh.md @@ -8,6 +8,10 @@ ------------------- +###运行DEMO + +>./gradlew installDebug + ###快速开始 * 第0步 把模块 `multi-image-selector` 作为你的项目依赖添加到工程中. @@ -15,6 +19,11 @@ * 第1步 在你的 `AndroidManifest.xml` 文件中添加权限 `android.permission.READ_EXTERNAL_STORAGE`. 别忘了同时在 `AndroidManifest.xml` 中声明 `MultiImageSelectorActivity` 这个Activity. +```xml + +``` * 第2步 代码中调用,例如: @@ -101,6 +110,12 @@ class CustomerActivity extends Activity implements MultiImageSelectorFragment.Ca 1. 修复. 当设置 `EXTRA_SHOW_CAMERA` 为 `true` 时, 点击第一个Item会混乱的问题. 2. 新增. 支持初始化图片选择设定。 +* 2015-4-16 + 1. 修复. 旋转设备时,程序会崩溃. (Issue by [@Leminity](https://github.com/Leminity)) + 2. 修复. 文件夹PopupListView位置错误. (Issue by [@Slock](https://github.com/Slock)) + 3. 更改. 演示程序截图. + 4. 更改. Readme 文件. + ------------------- ###感谢 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml new file mode 100644 index 0000000..2399710 --- /dev/null +++ b/app/src/main/res/values-zh/strings.xml @@ -0,0 +1,6 @@ + + MultiImageSelector + + Hello world! + Settings + diff --git a/multi-image-selector/multi-image-selector.iml b/multi-image-selector/multi-image-selector.iml index d9aa2fa..658af94 100644 --- a/multi-image-selector/multi-image-selector.iml +++ b/multi-image-selector/multi-image-selector.iml @@ -1,5 +1,5 @@ - + @@ -12,8 +12,9 @@ - - + \ No newline at end of file diff --git a/multi-image-selector/src/main/java/me/nereo/multi_image_selector/MultiImageSelectorFragment.java b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/MultiImageSelectorFragment.java index b99f6ca..01876c6 100644 --- a/multi-image-selector/src/main/java/me/nereo/multi_image_selector/MultiImageSelectorFragment.java +++ b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/MultiImageSelectorFragment.java @@ -3,6 +3,7 @@ package me.nereo.multi_image_selector; import android.annotation.TargetApi; import android.app.Activity; import android.content.Intent; +import android.content.res.Configuration; import android.database.Cursor; import android.net.Uri; import android.os.Build; @@ -14,6 +15,7 @@ import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v7.widget.ListPopupWindow; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -44,6 +46,8 @@ import me.nereo.multi_image_selector.utils.TimeUtils; */ public class MultiImageSelectorFragment extends Fragment { + private static final String TAG = "MultiImageSelector"; + /** 最大图片选择次数,int类型 */ public static final String EXTRA_SELECT_COUNT = "max_select_count"; /** 图片选择模式,int类型 */ @@ -62,6 +66,7 @@ public class MultiImageSelectorFragment extends Fragment { // 请求加载系统照相机 private static final int REQUEST_CAMERA = 100; + // 结果数据 private ArrayList resultList = new ArrayList<>(); // 文件夹数据 @@ -143,7 +148,7 @@ public class MultiImageSelectorFragment extends Fragment { mCategoryText = (TextView) view.findViewById(R.id.category_btn); // 初始化,加载所有图片 - mCategoryText.setText("所有图片"); + mCategoryText.setText(R.string.folder_all); mCategoryText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -161,7 +166,7 @@ public class MultiImageSelectorFragment extends Fragment { mPreviewBtn = (Button) view.findViewById(R.id.preview); // 初始化,按钮状态初始化 if(resultList == null || resultList.size()<=0){ - mPreviewBtn.setText("预览"); + mPreviewBtn.setText(R.string.preview); mPreviewBtn.setEnabled(false); } mPreviewBtn.setOnClickListener(new View.OnClickListener() { @@ -206,10 +211,20 @@ public class MultiImageSelectorFragment extends Fragment { mGridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public void onGlobalLayout() { - final int numCount = mGridView.getNumColumns(); + + final int width = mGridView.getWidth(); + final int height = mGridView.getHeight(); + + final int desireSize = getResources().getDimensionPixelOffset(R.dimen.image_size); + final int numCount = width / desireSize; final int columnSpace = getResources().getDimensionPixelOffset(R.dimen.space_size); - int columnWidth = (mGridView.getWidth() - columnSpace*(numCount-1)) / numCount; + int columnWidth = (width - columnSpace*(numCount-1)) / numCount; mImageAdapter.setItemSize(columnWidth); + + if(mFolderPopupWindow == null){ + createPopupFolderList(width, height); + } + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){ mGridView.getViewTreeObserver().removeOnGlobalLayoutListener(this); }else{ @@ -238,30 +253,26 @@ public class MultiImageSelectorFragment extends Fragment { }); mFolderAdapter = new FolderAdapter(getActivity()); - createPopupFolderList(); } /** * 创建弹出的ListView */ - private void createPopupFolderList() { + private void createPopupFolderList(int width, int height) { mFolderPopupWindow = new ListPopupWindow(getActivity()); -// mFolderPopupWindow.getListView().setDividerHeight(1); -// mFolderPopupWindow.getListView().setDivider(new ColorDrawable(Color.parseColor("#CCCCCC"))); mFolderPopupWindow.setAdapter(mFolderAdapter); - int sWidthPix = getResources().getDisplayMetrics().widthPixels; - mFolderPopupWindow.setContentWidth(sWidthPix); - mFolderPopupWindow.setHeight(sWidthPix); + mFolderPopupWindow.setContentWidth(width); + mFolderPopupWindow.setHeight(height * 5/8); mFolderPopupWindow.setAnchorView(mPopupAnchorView); mFolderPopupWindow.setModal(true); mFolderPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { - if(i == 0){ + if (i == 0) { getActivity().getSupportLoaderManager().restartLoader(LOADER_ALL, null, mLoaderCallback); - mCategoryText.setText("所有照片"); + mCategoryText.setText(R.string.folder_all); mImageAdapter.setShowCamera(true); - }else { + } else { Folder folder = (Folder) adapterView.getAdapter().getItem(i); if (null != folder) { Bundle args = new Bundle(); @@ -303,6 +314,49 @@ public class MultiImageSelectorFragment extends Fragment { } } + @Override + public void onConfigurationChanged(Configuration newConfig) { + Log.d(TAG, "on change"); + + final int orientation = newConfig.orientation; + + if(mFolderPopupWindow != null){ + if(mFolderPopupWindow.isShowing()){ + mFolderPopupWindow.dismiss(); + } + } + + mGridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + public void onGlobalLayout() { + + final int height = mGridView.getHeight(); + + final int desireSize = getResources().getDimensionPixelOffset(R.dimen.image_size); + Log.d(TAG, "Desire Size = "+desireSize); + final int numCount = mGridView.getWidth() / desireSize; + Log.d(TAG, "Grid Size = "+mGridView.getWidth()); + Log.d(TAG, "num count = "+numCount); + final int columnSpace = getResources().getDimensionPixelOffset(R.dimen.space_size); + int columnWidth = (mGridView.getWidth() - columnSpace*(numCount-1)) / numCount; + mImageAdapter.setItemSize(columnWidth); + + if(mFolderPopupWindow != null){ + mFolderPopupWindow.setHeight(height * 5/8); + } + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){ + mGridView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + }else{ + mGridView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + } + }); + + super.onConfigurationChanged(newConfig); + + } + /** * 选择相机 */ @@ -314,7 +368,7 @@ public class MultiImageSelectorFragment extends Fragment { cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile)); startActivityForResult(cameraIntent, REQUEST_CAMERA); }else{ - Toast.makeText(getActivity(), "没有系统相机", Toast.LENGTH_SHORT).show(); + Toast.makeText(getActivity(), R.string.msg_no_camera, Toast.LENGTH_SHORT).show(); } } @@ -330,10 +384,10 @@ public class MultiImageSelectorFragment extends Fragment { resultList.remove(image.path); if(resultList.size() != 0) { mPreviewBtn.setEnabled(true); - mPreviewBtn.setText("预览(" + resultList.size() + ")"); + mPreviewBtn.setText(getResources().getString(R.string.preview) + "(" + resultList.size() + ")"); }else{ mPreviewBtn.setEnabled(false); - mPreviewBtn.setText("预览"); + mPreviewBtn.setText(R.string.preview); } if (mCallback != null) { mCallback.onImageUnselected(image.path); @@ -341,13 +395,13 @@ public class MultiImageSelectorFragment extends Fragment { } else { // 判断选择数量问题 if(mDesireImageCount == resultList.size()){ - Toast.makeText(getActivity(), "已经达到最高选择数量了…", Toast.LENGTH_SHORT).show(); + Toast.makeText(getActivity(), R.string.msg_amount_limit, Toast.LENGTH_SHORT).show(); return; } resultList.add(image.path); mPreviewBtn.setEnabled(true); - mPreviewBtn.setText("预览(" + resultList.size() + ")"); + mPreviewBtn.setText(getResources().getString(R.string.preview) + "(" + resultList.size() + ")"); if (mCallback != null) { mCallback.onImageSelected(image.path); } @@ -432,8 +486,6 @@ public class MultiImageSelectorFragment extends Fragment { mFolderAdapter.setData(mResultFolder); hasFolderGened = true; - } else { - System.out.println("no image found"); } } } diff --git a/multi-image-selector/src/main/java/me/nereo/multi_image_selector/adapter/ImageGridAdapter.java b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/adapter/ImageGridAdapter.java index c47e2b1..2e042f5 100644 --- a/multi-image-selector/src/main/java/me/nereo/multi_image_selector/adapter/ImageGridAdapter.java +++ b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/adapter/ImageGridAdapter.java @@ -174,32 +174,11 @@ public class ImageGridAdapter extends BaseAdapter { @Override public View getView(int i, View view, ViewGroup viewGroup) { -/* ViewHolde holde = null; - - if(showCamera){ - int type = getItemViewType(i); - if(view == null){ - - view = mInflater.inflate(R.layout.list_item_camera, viewGroup, false); - }else{ - - } - }else{ - if(view == null){ - view = mInflater.inflate(R.layout.list_item_image, viewGroup, false); - holde = new ViewHolde(view); - }else{ - holde = (ViewHolde) view.getTag(); - } - } - // 绑定数据 - if(holde != null) { - holde.bindData(getItem(i)); - }*/ int type = getItemViewType(i); if(type == TYPE_CAMERA){ view = mInflater.inflate(R.layout.list_item_camera, viewGroup, false); + view.setTag(null); }else if(type == TYPE_NORMAL){ ViewHolde holde; if(view == null){ @@ -216,10 +195,13 @@ public class ImageGridAdapter extends BaseAdapter { holde.bindData(getItem(i)); } } + + /** Fixed View Size */ GridView.LayoutParams lp = (GridView.LayoutParams) view.getLayoutParams(); if(lp.height != mItemSize){ view.setLayoutParams(mItemLayoutParams); } + return view; } @@ -249,14 +231,17 @@ public class ImageGridAdapter extends BaseAdapter { indicator.setVisibility(View.GONE); } File imageFile = new File(data.path); - // 显示图片 - Picasso.with(mContext) - .load(imageFile) - .placeholder(R.drawable.default_error) - //.error(R.drawable.default_error) - .resize(mItemSize, mItemSize) - .centerCrop() - .into(image); + + if(mItemSize > 0) { + // 显示图片 + Picasso.with(mContext) + .load(imageFile) + .placeholder(R.drawable.default_error) + //.error(R.drawable.default_error) + .resize(mItemSize, mItemSize) + .centerCrop() + .into(image); + } } } diff --git a/multi-image-selector/src/main/java/me/nereo/multi_image_selector/utils/TimeUtils.java b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/utils/TimeUtils.java index d1fd5ef..95740eb 100644 --- a/multi-image-selector/src/main/java/me/nereo/multi_image_selector/utils/TimeUtils.java +++ b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/utils/TimeUtils.java @@ -20,7 +20,7 @@ public class TimeUtils { } public static String formatPhotoDate(long time){ - return timeFormat(time, "yyyy年MM月dd日"); + return timeFormat(time, "yyyy-MM-dd"); } public static String formatPhotoDate(String path){ diff --git a/multi-image-selector/src/main/java/me/nereo/multi_image_selector/view/GestureImageView.java b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/view/GestureImageView.java new file mode 100644 index 0000000..55a5297 --- /dev/null +++ b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/view/GestureImageView.java @@ -0,0 +1,128 @@ +package me.nereo.multi_image_selector.view; + +import android.content.Context; +import android.graphics.Matrix; +import android.support.v4.view.GestureDetectorCompat; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ScaleGestureDetectorCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.DragEvent; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.ViewConfiguration; +import android.widget.ImageView; +import android.widget.Toast; + +/** + * 支持手势的ImageView + * Created by Nereo on 2015/4/10. + */ +public class GestureImageView extends ImageView{ + + private static final String TAG = "GestureImageView"; + + public GestureImageView(Context context) { + super(context); + init(context); + } + + public GestureImageView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public GestureImageView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + private ScaleGestureDetector mScaleGesture; + private Matrix mImageMatrix; + private GestureDetectorCompat mGestureDetector; + + // 最大缩放比例 + private static final float MAX_SCALE_FACTOR = 3.0f; + // 最小缩放比例 + private static final float MIN_SCALE_FACTOR = 0.3f; + // 系统常量,系统认为手指是否移动的最小距离 + private int mTouchSlop; + + private float mCurrentFactor = 1.0f; + + private float mFirstPointerX, mFirstPointerY; + private float mSecondPointerX, mSecondPointerY; + + private int mCenterX, mCenterY; + + /** + * 初始化 + * @param context + */ + private void init(final Context context){ + + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + + setScaleType(ScaleType.MATRIX); + + mImageMatrix = new Matrix(); + + mScaleGesture = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener(){ + @Override + public boolean onScale(ScaleGestureDetector detector) { + float factor = detector.getScaleFactor(); + mImageMatrix.postScale(factor, factor, mCenterX, mCenterY); + setImageMatrix(mImageMatrix); + return true; + } + }); + + mGestureDetector = new GestureDetectorCompat(context, new GestureDetector.SimpleOnGestureListener(){ + @Override + public boolean onDoubleTap(MotionEvent e) { + mImageMatrix.postScale(1.f, 1.f, mCenterX, mCenterY); + setImageMatrix(mImageMatrix); + return true; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + //mImageMatrix.setTranslate(0, 0); + //setImageMatrix(mImageMatrix); + return true; + } + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + }); + + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if( w != oldw || h != oldh){ + int cx = (w - getDrawable().getIntrinsicWidth()) / 2 ; + int cy = (h - getDrawable().getIntrinsicHeight()) / 2; + mImageMatrix.setTranslate(cx, cy); + setImageMatrix(mImageMatrix); + + mCenterX = w / 2; + mCenterY = h / 2; + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + + boolean retValue = mScaleGesture.onTouchEvent(event); + + retValue = mGestureDetector.onTouchEvent(event) || retValue; + + return retValue || super.onTouchEvent(event); + } + +} diff --git a/multi-image-selector/src/main/java/me/nereo/multi_image_selector/view/SuperViewPager.java b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/view/SuperViewPager.java new file mode 100644 index 0000000..e26dbd3 --- /dev/null +++ b/multi-image-selector/src/main/java/me/nereo/multi_image_selector/view/SuperViewPager.java @@ -0,0 +1,21 @@ +package me.nereo.multi_image_selector.view; + +import android.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; + +/** + * 自定义ViewPager + * Created by Nereo on 2015/4/10. + */ +public class SuperViewPager extends ViewPager { + + public SuperViewPager(Context context) { + super(context); + } + + public SuperViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + } + +} diff --git a/multi-image-selector/src/main/res/layout/fragment_multi_image.xml b/multi-image-selector/src/main/res/layout/fragment_multi_image.xml index 147e639..20cf2a8 100644 --- a/multi-image-selector/src/main/res/layout/fragment_multi_image.xml +++ b/multi-image-selector/src/main/res/layout/fragment_multi_image.xml @@ -1,6 +1,7 @@ @@ -10,9 +11,10 @@ android:layout_height="match_parent" android:horizontalSpacing="@dimen/space_size" android:verticalSpacing="@dimen/space_size" - android:paddingBottom="48dp" + android:paddingBottom="?android:attr/actionBarSize" android:clipToPadding="false" - android:numColumns="3"/> + android:numColumns="auto_fit" + android:columnWidth="@dimen/image_size"/> + android:layout_height="?android:attr/actionBarSize"> + + 96dp + 2dp + 72dp + \ No newline at end of file diff --git a/multi-image-selector/src/main/res/values-sw480dp/dimens.xml b/multi-image-selector/src/main/res/values-sw480dp/dimens.xml new file mode 100644 index 0000000..fe0c496 --- /dev/null +++ b/multi-image-selector/src/main/res/values-sw480dp/dimens.xml @@ -0,0 +1,6 @@ + + + 100dp + 2dp + 72dp + \ No newline at end of file diff --git a/multi-image-selector/src/main/res/values-sw720dp/dimens.xml b/multi-image-selector/src/main/res/values-sw720dp/dimens.xml new file mode 100644 index 0000000..8a71ee0 --- /dev/null +++ b/multi-image-selector/src/main/res/values-sw720dp/dimens.xml @@ -0,0 +1,6 @@ + + + 120dp + 3dp + 82dp + \ No newline at end of file diff --git a/multi-image-selector/src/main/res/values-zh/strings.xml b/multi-image-selector/src/main/res/values-zh/strings.xml new file mode 100644 index 0000000..d439ac9 --- /dev/null +++ b/multi-image-selector/src/main/res/values-zh/strings.xml @@ -0,0 +1,7 @@ + + 多图选择器 + 所有照片 + 预览 + 没有系统相机 + 已经达到最高选择数量 + diff --git a/multi-image-selector/src/main/res/values/dimens.xml b/multi-image-selector/src/main/res/values/dimens.xml index df1e644..b298eb7 100644 --- a/multi-image-selector/src/main/res/values/dimens.xml +++ b/multi-image-selector/src/main/res/values/dimens.xml @@ -1,5 +1,6 @@ + 120dp 2dp 72dp \ No newline at end of file diff --git a/multi-image-selector/src/main/res/values/strings.xml b/multi-image-selector/src/main/res/values/strings.xml index 92af08e..d6732fd 100644 --- a/multi-image-selector/src/main/res/values/strings.xml +++ b/multi-image-selector/src/main/res/values/strings.xml @@ -1,3 +1,7 @@ multi-image-selector + All Images + Preview + No system camera found + Select images amount is limit