├── .project
├── MultiPhotoPicker
├── .classpath
├── .project
├── AndroidManifest.xml
├── libs
│ ├── android-support-v4.jar
│ ├── android-support-v4.jar.properties
│ └── picasso-2.2.0.jar
├── proguard-project.txt
├── project.properties
├── res
│ ├── drawable-hdpi
│ │ ├── grid_pressed.9.png
│ │ ├── grid_selected.9.png
│ │ ├── ic_action_close.png
│ │ ├── ic_action_done.png
│ │ ├── ic_launcher.png
│ │ └── picker_photo_holder.png
│ ├── drawable-ldpi
│ │ └── ic_launcher.png
│ ├── drawable-mdpi
│ │ ├── ic_action_close.png
│ │ ├── ic_action_done.png
│ │ └── ic_launcher.png
│ ├── drawable-xhdpi
│ │ ├── ic_action_close.png
│ │ ├── ic_action_done.png
│ │ ├── ic_launcher.png
│ │ └── picker_photo_holder.png
│ ├── drawable-xxhdpi
│ │ ├── ic_action_close.png
│ │ └── ic_action_done.png
│ ├── drawable
│ │ └── picker_item_selector.xml
│ ├── layout
│ │ ├── activity_demo.xml
│ │ ├── activity_main.xml
│ │ └── photo_item.xml
│ ├── menu
│ │ └── gallery.xml
│ ├── values-sw600dp
│ │ └── dimens.xml
│ ├── values-v11
│ │ ├── bools.xml
│ │ └── styles.xml
│ ├── values-v14
│ │ └── styles.xml
│ ├── values-w500dp-land
│ │ └── dimens.xml
│ ├── values-zh
│ │ └── strings.xml
│ └── values
│ │ ├── bools.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
├── sample
│ └── org
│ │ └── goodev
│ │ └── picker
│ │ ├── GridAdapter.java
│ │ └── MainActivity.java
└── src
│ └── org
│ └── goodev
│ └── picker
│ ├── GalleryAdapter.java
│ ├── GalleryPickerActivity.java
│ ├── IntentAction.java
│ ├── OptimizeGridView.java
│ └── SquareImageView.java
├── MultiPhotoPickerAppCompat
├── .classpath
├── .project
├── AndroidManifest.xml
├── libs
│ ├── android-support-v4.jar
│ ├── android-support-v4.jar.properties
│ └── picasso-2.2.0.jar
├── proguard-project.txt
├── project.properties
├── res
│ ├── drawable-hdpi
│ │ ├── grid_pressed.9.png
│ │ ├── grid_selected.9.png
│ │ ├── ic_action_close.png
│ │ ├── ic_action_done.png
│ │ ├── ic_launcher.png
│ │ └── picker_photo_holder.png
│ ├── drawable-ldpi
│ │ └── ic_launcher.png
│ ├── drawable-mdpi
│ │ ├── ic_action_close.png
│ │ ├── ic_action_done.png
│ │ └── ic_launcher.png
│ ├── drawable-xhdpi
│ │ ├── ic_action_close.png
│ │ ├── ic_action_done.png
│ │ ├── ic_launcher.png
│ │ └── picker_photo_holder.png
│ ├── drawable-xxhdpi
│ │ ├── ic_action_close.png
│ │ └── ic_action_done.png
│ ├── drawable
│ │ └── picker_item_selector.xml
│ ├── layout
│ │ ├── activity_demo.xml
│ │ ├── activity_main.xml
│ │ └── photo_item.xml
│ ├── menu
│ │ └── gallery.xml
│ ├── values-sw600dp
│ │ └── dimens.xml
│ ├── values-v11
│ │ ├── bools.xml
│ │ └── styles.xml
│ ├── values-v14
│ │ └── styles.xml
│ ├── values-w500dp-land
│ │ └── dimens.xml
│ ├── values-zh
│ │ └── strings.xml
│ └── values
│ │ ├── bools.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
├── sample
│ └── org
│ │ └── goodev
│ │ └── picker
│ │ ├── GridAdapter.java
│ │ └── MainActivity.java
└── src
│ └── org
│ └── goodev
│ └── picker
│ ├── GalleryAdapter.java
│ ├── GalleryPickerActivity.java
│ ├── IntentAction.java
│ ├── MultiSelectionUtil.java
│ ├── OptimizeGridView.java
│ └── SquareImageView.java
├── README.md
└── screencast
├── 1.png
└── 2.png
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | MultiPhotoPicker
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | MultiPhotoPicker11
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/MultiPhotoPicker/libs/android-support-v4.jar.properties:
--------------------------------------------------------------------------------
1 | src=D:/adt-bundle-windows-x86_64-20131030/sdk/extras/android/support/v4/src/java
--------------------------------------------------------------------------------
/MultiPhotoPicker/libs/picasso-2.2.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/libs/picasso-2.2.0.jar
--------------------------------------------------------------------------------
/MultiPhotoPicker/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-hdpi/grid_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-hdpi/grid_pressed.9.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-hdpi/grid_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-hdpi/grid_selected.9.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-hdpi/ic_action_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-hdpi/ic_action_close.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-hdpi/ic_action_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-hdpi/ic_action_done.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-hdpi/picker_photo_holder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-hdpi/picker_photo_holder.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-mdpi/ic_action_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-mdpi/ic_action_close.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-mdpi/ic_action_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-mdpi/ic_action_done.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-xhdpi/ic_action_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-xhdpi/ic_action_close.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-xhdpi/ic_action_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-xhdpi/ic_action_done.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-xhdpi/picker_photo_holder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-xhdpi/picker_photo_holder.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-xxhdpi/ic_action_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-xxhdpi/ic_action_close.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable-xxhdpi/ic_action_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPicker/res/drawable-xxhdpi/ic_action_done.png
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/drawable/picker_item_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/layout/activity_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
19 |
20 |
26 |
27 |
28 |
44 |
45 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
22 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/layout/photo_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/menu/gallery.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 150dp
4 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values-v11/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - true
4 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values-w500dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 140dp
4 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 选择图片
4 | 完成
5 |
6 | - "选中了 %1$d 项"
7 | - "选中了 %1$d 项"
8 | - "选中了 %1$d 项"
9 |
10 |
11 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - false
4 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #AA66cc
4 | #AA000000
5 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 100dp
4 | 2dp
5 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Select Items
5 | Done
6 |
7 | - %1$d selected
8 | - %1$d selected
9 | - %1$d selected
10 |
11 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
20 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/sample/org/goodev/picker/GridAdapter.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | import android.content.Context;
8 | import android.net.Uri;
9 | import android.os.Parcelable;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.view.ViewTreeObserver.OnGlobalLayoutListener;
14 | import android.widget.BaseAdapter;
15 | import android.widget.ImageView;
16 |
17 | import com.squareup.picasso.Picasso;
18 |
19 | public class GridAdapter extends BaseAdapter {
20 |
21 | private List mItems = new ArrayList();
22 | private Context mContext;
23 |
24 | public GridAdapter(Context context) {
25 | mContext = context;
26 | }
27 |
28 | public void setData(Parcelable[] data) {
29 | mItems.clear();
30 | mItems.addAll(Arrays.asList(data));
31 | notifyDataSetChanged();
32 | }
33 |
34 | public void setData(Parcelable data) {
35 | mItems.clear();
36 | mItems.add(data);
37 | notifyDataSetChanged();
38 | }
39 |
40 | @Override
41 | public int getCount() {
42 | return mItems.size();
43 | }
44 |
45 | @Override
46 | public Object getItem(int position) {
47 | return mItems.get(position);
48 | }
49 |
50 | @Override
51 | public long getItemId(int position) {
52 | return position;
53 | }
54 |
55 | @Override
56 | public View getView(int position, View convertView, ViewGroup parent) {
57 | if (convertView == null) {
58 | convertView = LayoutInflater.from(mContext).inflate(R.layout.photo_item, null);
59 | }
60 | final ImageView iv = (ImageView) convertView;
61 | final Context context = mContext;
62 | final Uri uri = (Uri) getItem(position);
63 | final int width = iv.getWidth();
64 | if (width <= 0) {
65 | iv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
66 |
67 | @SuppressWarnings("deprecation")
68 | @Override
69 | public void onGlobalLayout() {
70 | iv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
71 | Picasso.with(context).load(uri).placeholder(R.drawable.picker_photo_holder)
72 | .resize(iv.getWidth(), iv.getHeight()).centerCrop().into(iv);
73 | }
74 | });
75 | } else {
76 | Picasso.with(context).load(uri).placeholder(R.drawable.picker_photo_holder).resize(iv.getWidth(), iv.getHeight())
77 | .centerCrop().into(iv);
78 | }
79 | return convertView;
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/sample/org/goodev/picker/MainActivity.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.os.Parcelable;
8 | import android.view.View;
9 | import android.view.View.OnClickListener;
10 | import android.widget.GridView;
11 |
12 | public class MainActivity extends Activity implements OnClickListener {
13 | final static int CODE = 1;
14 | final static String KEY_MULTIPLE = "KEY_MULTIPLE";
15 | GridView mGridView;
16 | GridAdapter mAdapter;
17 | boolean mIsMultiple;
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_demo);
23 | GridView view = (GridView) findViewById(R.id.grid);
24 | mAdapter = new GridAdapter(this);
25 | view.setAdapter(mAdapter);
26 | mGridView = view;
27 | if (savedInstanceState != null) {
28 | mIsMultiple = savedInstanceState.getBoolean(KEY_MULTIPLE);
29 | }
30 | findViewById(R.id.single).setOnClickListener(this);
31 | findViewById(R.id.multiple).setOnClickListener(this);
32 | }
33 |
34 | @Override
35 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
36 | super.onActivityResult(requestCode, resultCode, data);
37 | if (resultCode == RESULT_OK && requestCode == CODE) {
38 | if (mIsMultiple) {
39 | Parcelable[] uris = data.getParcelableArrayExtra(IntentAction.EXTRA_DATA);
40 | mAdapter.setData(uris);
41 | } else {
42 | Uri uri = data.getParcelableExtra(IntentAction.EXTRA_DATA);
43 | mAdapter.setData(uri);
44 | }
45 | }
46 | }
47 |
48 | @Override
49 | public void onSaveInstanceState(Bundle outState) {
50 | super.onSaveInstanceState(outState);
51 | outState.putBoolean(KEY_MULTIPLE, mIsMultiple);
52 | }
53 |
54 | @Override
55 | public void onClick(View v) {
56 | Intent intent = new Intent();
57 | switch (v.getId()) {
58 | case R.id.single:
59 | mIsMultiple = false;
60 | intent.setAction(IntentAction.ACTION_PICK);
61 | break;
62 | case R.id.multiple:
63 | mIsMultiple = true;
64 |
65 | intent.setAction(IntentAction.ACTION_MULTIPLE_PICK);
66 | break;
67 |
68 | }
69 |
70 | startActivityForResult(intent, CODE);
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/src/org/goodev/picker/GalleryAdapter.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import android.content.ContentUris;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.provider.MediaStore.Images.Media;
8 | import android.support.v4.widget.CursorAdapter;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.view.ViewTreeObserver.OnGlobalLayoutListener;
13 | import android.widget.ImageView;
14 |
15 | import com.squareup.picasso.Picasso;
16 |
17 | public class GalleryAdapter extends CursorAdapter {
18 |
19 | final LayoutInflater mInflater;
20 |
21 | public GalleryAdapter(Context context) {
22 | super(context, null, false);
23 | mInflater = LayoutInflater.from(context);
24 | }
25 |
26 | @Override
27 | public View newView(Context context, Cursor cursor, ViewGroup parent) {
28 | return mInflater.inflate(R.layout.photo_item, parent, false);
29 | }
30 |
31 | @Override
32 | public long getItemId(int position) {
33 | return super.getItemId(position);
34 | }
35 |
36 | @Override
37 | public boolean hasStableIds() {
38 | return super.hasStableIds();
39 | }
40 |
41 | @Override
42 | public void bindView(View view, final Context context, Cursor cursor) {
43 | final ImageView iv = (ImageView) view;
44 | // file path
45 | final String path = cursor.getString(GalleryPickerActivity.DATA_INDEX);
46 | final long id = cursor.getLong(GalleryPickerActivity.ID_INDEX);
47 | final Uri uri = ContentUris.withAppendedId(Media.EXTERNAL_CONTENT_URI, id);
48 | final int width = iv.getWidth();
49 | if (width <= 0) {
50 | iv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
51 |
52 | @SuppressWarnings("deprecation")
53 | @Override
54 | public void onGlobalLayout() {
55 | iv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
56 | Picasso.with(context).load(uri).placeholder(R.drawable.picker_photo_holder)
57 | .resize(iv.getWidth(), iv.getHeight()).centerCrop().into(iv);
58 | }
59 | });
60 | } else {
61 | Picasso.with(context).load(uri).placeholder(R.drawable.picker_photo_holder).resize(iv.getWidth(), iv.getHeight())
62 | .centerCrop().into(iv);
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/src/org/goodev/picker/GalleryPickerActivity.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import android.content.ContentUris;
4 | import android.content.Intent;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.provider.MediaStore;
9 | import android.support.v4.app.FragmentActivity;
10 | import android.support.v4.app.LoaderManager.LoaderCallbacks;
11 | import android.support.v4.content.CursorLoader;
12 | import android.support.v4.content.Loader;
13 | import android.view.ActionMode;
14 | import android.view.Menu;
15 | import android.view.MenuInflater;
16 | import android.view.MenuItem;
17 | import android.view.View;
18 | import android.widget.AbsListView.MultiChoiceModeListener;
19 | import android.widget.AdapterView;
20 | import android.widget.AdapterView.OnItemClickListener;
21 | import android.widget.Checkable;
22 | import android.widget.GridView;
23 | import static org.goodev.picker.IntentAction.*;
24 |
25 | public class GalleryPickerActivity extends FragmentActivity implements LoaderCallbacks, OnItemClickListener, MultiChoiceModeListener {
26 | final static String[] PROJECTION = { MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID };
27 | final static String SORTORDER = MediaStore.Images.Media._ID +" DESC";
28 | final static Uri URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
29 | public static final int DATA_INDEX = 0;
30 | public static final int ID_INDEX = 1;
31 | private static final int LOADER_ID = 0;
32 | private GridView mGridView;
33 | private GalleryAdapter mAdapter;
34 | private boolean mIsMultiple;
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_main);
40 | getActionBar().setDisplayHomeAsUpEnabled(true);
41 | getActionBar().setHomeButtonEnabled(true);
42 | mIsMultiple = ACTION_MULTIPLE_PICK.equals(getIntent().getAction());
43 | final GridView view = (GridView) findViewById(R.id.grid);
44 | mAdapter = new GalleryAdapter(this);
45 | view.setAdapter(mAdapter);
46 | view.setOnItemClickListener(this);
47 | view.setMultiChoiceModeListener(this);
48 | mGridView = view;
49 | getSupportLoaderManager().initLoader(LOADER_ID, null, this);
50 | }
51 |
52 | @Override
53 | public Loader onCreateLoader(int id, Bundle args) {
54 | return new CursorLoader(this, URI, PROJECTION, null, null, SORTORDER);
55 | }
56 |
57 | @Override
58 | public void onLoadFinished(Loader loader, Cursor data) {
59 | mAdapter.swapCursor(data);
60 | }
61 |
62 | @Override
63 | public void onLoaderReset(Loader loader) {
64 | mAdapter.swapCursor(null);
65 | }
66 |
67 | @Override
68 | public boolean onOptionsItemSelected(MenuItem item) {
69 | if(item.getItemId() == android.R.id.home) {
70 | finish();
71 | return true;
72 | }
73 | return super.onOptionsItemSelected(item);
74 | }
75 |
76 | @Override
77 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
78 | if(mIsMultiple) {
79 | if(view instanceof Checkable) {
80 | Checkable c = (Checkable) view;
81 | boolean checked = !c.isChecked();
82 | mGridView.setItemChecked(position, checked);
83 | }
84 | }else {
85 | Uri uri = ContentUris.withAppendedId(URI, id);
86 | Intent data = new Intent();
87 | data.putExtra(IntentAction.EXTRA_DATA, uri);
88 | setResult(RESULT_OK, data );
89 | finish();
90 | }
91 | }
92 |
93 | @Override
94 | public boolean onCreateActionMode(ActionMode mode, Menu menu) {
95 | MenuInflater inflater = mode.getMenuInflater();
96 | inflater.inflate(R.menu.gallery, menu);
97 | int selectCount = mGridView.getCheckedItemCount();
98 | String title = getResources().getQuantityString(R.plurals.number_of_items_selected, selectCount, selectCount);
99 | mode.setTitle(title);
100 | return true;
101 | }
102 |
103 | @Override
104 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
105 | return false;
106 | }
107 |
108 | @Override
109 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
110 | if(item.getItemId() == R.id.menu_done) {
111 | System.out.println(item.getTitle() +" "+ item.getItemId());
112 | long[] ids = mGridView.getCheckedItemIds();
113 | Uri[] uris = new Uri[ids.length];
114 | for (int i = 0; i < ids.length; i++) {
115 | Uri uri = ContentUris.withAppendedId(URI, ids[i]);
116 | uris[i] = uri;
117 | }
118 |
119 | Intent data = new Intent();
120 | data.putExtra(IntentAction.EXTRA_DATA, uris);
121 | setResult(RESULT_OK, data );
122 | finish();
123 | return true;
124 | }
125 | return false;
126 | }
127 |
128 | @Override
129 | public void onDestroyActionMode(ActionMode mode) {
130 | }
131 |
132 | @Override
133 | public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
134 | int selectCount = mGridView.getCheckedItemCount();
135 | String title = getResources().getQuantityString(R.plurals.number_of_items_selected, selectCount, selectCount);
136 | mode.setTitle(title);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/src/org/goodev/picker/IntentAction.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | public class IntentAction {
4 | public static final String ACTION_PICK = "goodev.intent.action.PICK";
5 | public static final String ACTION_MULTIPLE_PICK = "goodev.intent.action.MULTIPLE_PICK";
6 | public static final String EXTRA_DATA = "goodev.intent.extra.DATA";
7 | }
8 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/src/org/goodev/picker/OptimizeGridView.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import android.annotation.SuppressLint;
8 | import android.content.Context;
9 | import android.util.AttributeSet;
10 | import android.widget.Adapter;
11 | import android.widget.GridView;
12 |
13 | public class OptimizeGridView extends GridView {
14 | /**
15 | * 要用能包含重复元素的集合
16 | *
17 | * @param
18 | */
19 | interface OptimizeGridAdapter {
20 | List getItems();
21 | /**
22 | * Should notify the listView data is changed
23 | *
24 | * @param items
25 | */
26 | void setItems(List items);
27 | T getNullItem();
28 | boolean isNullItem(T item);
29 | }
30 |
31 | public OptimizeGridView(Context context) {
32 | super(context);
33 | }
34 |
35 | public OptimizeGridView(Context context, AttributeSet attrs) {
36 | super(context, attrs);
37 | }
38 |
39 | public OptimizeGridView(Context context, AttributeSet attrs, int defStyle) {
40 | super(context, attrs, defStyle);
41 | }
42 |
43 | @SuppressLint("DrawAllocation")
44 | @SuppressWarnings({ "rawtypes", "unchecked" })
45 | @Override
46 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
47 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
48 |
49 | int numColumns = AUTO_FIT;
50 | final boolean isApi11 = getResources().getBoolean(R.bool.api11);
51 | if (isApi11) {
52 | // API level 11 引入该函数,在低于11版本中 使用反射获取列数
53 | numColumns = getNumColumns();
54 | } else {
55 | try {
56 | Field numColumnsField = GridView.class.getDeclaredField("mNumColumns");
57 | numColumnsField.setAccessible(true);
58 | numColumns = numColumnsField.getInt(this);
59 | } catch (IllegalArgumentException e1) {
60 | e1.printStackTrace();
61 | } catch (NoSuchFieldException e1) {
62 | e1.printStackTrace();
63 | } catch (IllegalAccessException e1) {
64 | e1.printStackTrace();
65 | }
66 | }
67 | if (numColumns != AUTO_FIT) {
68 | final Adapter adapter = getAdapter();
69 | if (!(adapter instanceof OptimizeGridAdapter)) {
70 | return;
71 | }
72 | final int count = adapter.getCount();
73 | final int remainder = count % numColumns;
74 | if (remainder != 0) {
75 | final int diff = numColumns - remainder;
76 | final OptimizeGridAdapter adapter2 = (OptimizeGridAdapter) adapter;
77 | final List items = new ArrayList();
78 | items.addAll(adapter2.getItems());
79 | for (int i = 0; i < diff; i++) {
80 | items.add(adapter2.getNullItem());
81 | }
82 | adapter2.setItems(items);
83 | }
84 | }
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/MultiPhotoPicker/src/org/goodev/picker/SquareImageView.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.drawable.Drawable;
6 | import android.util.AttributeSet;
7 | import android.widget.Checkable;
8 | import android.widget.ImageView;
9 |
10 | public class SquareImageView extends ImageView implements Checkable{
11 |
12 | static Drawable sCheckedDrawable;
13 | public SquareImageView(Context context) {
14 | super(context);
15 | sCheckedDrawable = context.getResources().getDrawable(R.drawable.grid_selected);
16 | }
17 |
18 | public SquareImageView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | sCheckedDrawable = context.getResources().getDrawable(R.drawable.grid_selected);
21 | }
22 |
23 | public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
24 | super(context, attrs, defStyle);
25 | sCheckedDrawable = context.getResources().getDrawable(R.drawable.grid_selected);
26 | }
27 |
28 | @SuppressWarnings("unused")
29 | @Override
30 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
31 | // For simple implementation, or internal size is always 0.
32 | // We depend on the container to specify the layout size of
33 | // our view. We can't really know what it is since we will be
34 | // adding and removing different arbitrary views and do not
35 | // want the layout to change as this happens.
36 | setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));
37 |
38 | // Children are just made to fill our space.
39 | int childWidthSize = getMeasuredWidth();
40 | int childHeightSize = getMeasuredHeight();
41 | //高度和宽度一样
42 | heightMeasureSpec = widthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
43 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
44 | }
45 |
46 | private boolean mChecked;
47 | private Drawable mDrawable;
48 | @SuppressWarnings("deprecation")
49 | @Override
50 | public void setChecked(boolean checked) {
51 | if(checked != mChecked) {
52 | mChecked = checked;
53 | mDrawable = checked ? sCheckedDrawable : null;
54 | setBackgroundDrawable(mDrawable);
55 | }
56 | }
57 |
58 |
59 | @Override
60 | protected void onDraw(Canvas canvas) {
61 | super.onDraw(canvas);
62 | if(mDrawable != null) {
63 | mDrawable.draw(canvas);
64 | }
65 | }
66 |
67 | @Override
68 | public boolean isChecked() {
69 | return mChecked;
70 | }
71 |
72 | @Override
73 | public void toggle() {
74 | mChecked = !mChecked;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | MultiPhotoPickerAppCompat
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/libs/android-support-v4.jar.properties:
--------------------------------------------------------------------------------
1 | src=D:/adt-bundle-windows-x86_64-20131030/sdk/extras/android/support/v4/src/java
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/libs/picasso-2.2.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/libs/picasso-2.2.0.jar
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 | android.library.reference.1=../android-support-v7-appcompat
16 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-hdpi/grid_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-hdpi/grid_pressed.9.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-hdpi/grid_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-hdpi/grid_selected.9.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-hdpi/ic_action_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-hdpi/ic_action_close.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-hdpi/ic_action_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-hdpi/ic_action_done.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-hdpi/picker_photo_holder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-hdpi/picker_photo_holder.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-mdpi/ic_action_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-mdpi/ic_action_close.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-mdpi/ic_action_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-mdpi/ic_action_done.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-xhdpi/ic_action_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-xhdpi/ic_action_close.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-xhdpi/ic_action_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-xhdpi/ic_action_done.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-xhdpi/picker_photo_holder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-xhdpi/picker_photo_holder.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-xxhdpi/ic_action_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-xxhdpi/ic_action_close.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable-xxhdpi/ic_action_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/MultiPhotoPickerAppCompat/res/drawable-xxhdpi/ic_action_done.png
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/drawable/picker_item_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/layout/activity_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
19 |
20 |
26 |
27 |
28 |
44 |
45 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
22 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/layout/photo_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/menu/gallery.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 150dp
4 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values-v11/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - true
4 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values-w500dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 140dp
4 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 选择图片
4 | 完成
5 |
6 | - "选中了 %1$d 项"
7 | - "选中了 %1$d 项"
8 | - "选中了 %1$d 项"
9 |
10 |
11 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - false
4 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #AA66cc
4 | #AA000000
5 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 100dp
4 | 2dp
5 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Select Items
5 | Done
6 |
7 | - %1$d selected
8 | - %1$d selected
9 | - %1$d selected
10 |
11 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
21 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/sample/org/goodev/picker/GridAdapter.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | import android.content.Context;
8 | import android.net.Uri;
9 | import android.os.Parcelable;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.view.ViewTreeObserver.OnGlobalLayoutListener;
14 | import android.widget.BaseAdapter;
15 | import android.widget.ImageView;
16 |
17 | import com.squareup.picasso.Picasso;
18 |
19 | public class GridAdapter extends BaseAdapter {
20 |
21 | private List mItems = new ArrayList();
22 | private Context mContext;
23 |
24 | public GridAdapter(Context context) {
25 | mContext = context;
26 | }
27 |
28 | public void setData(Parcelable[] data) {
29 | mItems.clear();
30 | mItems.addAll(Arrays.asList(data));
31 | notifyDataSetChanged();
32 | }
33 |
34 | public void setData(Parcelable data) {
35 | mItems.clear();
36 | mItems.add(data);
37 | notifyDataSetChanged();
38 | }
39 |
40 | @Override
41 | public int getCount() {
42 | return mItems.size();
43 | }
44 |
45 | @Override
46 | public Object getItem(int position) {
47 | return mItems.get(position);
48 | }
49 |
50 | @Override
51 | public long getItemId(int position) {
52 | return position;
53 | }
54 |
55 | @Override
56 | public View getView(int position, View convertView, ViewGroup parent) {
57 | if (convertView == null) {
58 | convertView = LayoutInflater.from(mContext).inflate(R.layout.photo_item, null);
59 | }
60 | final ImageView iv = (ImageView) convertView;
61 | final Context context = mContext;
62 | final Uri uri = (Uri) getItem(position);
63 | final int width = iv.getWidth();
64 | if (width <= 0) {
65 | iv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
66 |
67 | @SuppressWarnings("deprecation")
68 | @Override
69 | public void onGlobalLayout() {
70 | iv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
71 | Picasso.with(context).load(uri).placeholder(R.drawable.picker_photo_holder)
72 | .resize(iv.getWidth(), iv.getHeight()).centerCrop().into(iv);
73 | }
74 | });
75 | } else {
76 | Picasso.with(context).load(uri).placeholder(R.drawable.picker_photo_holder).resize(iv.getWidth(), iv.getHeight())
77 | .centerCrop().into(iv);
78 | }
79 | return convertView;
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/sample/org/goodev/picker/MainActivity.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.os.Build;
7 | import android.os.Bundle;
8 | import android.os.Parcelable;
9 | import android.support.v7.app.ActionBarActivity;
10 | import android.view.View;
11 | import android.view.View.OnClickListener;
12 | import android.widget.GridView;
13 |
14 | public class MainActivity extends ActionBarActivity implements OnClickListener {
15 | final static int CODE = 1;
16 | final static String KEY_MULTIPLE = "KEY_MULTIPLE";
17 | GridView mGridView;
18 | GridAdapter mAdapter;
19 | boolean mIsMultiple;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_demo);
25 | GridView view = (GridView) findViewById(R.id.grid);
26 | mAdapter = new GridAdapter(this);
27 | view.setAdapter(mAdapter);
28 | mGridView = view;
29 | if (savedInstanceState != null) {
30 | mIsMultiple = savedInstanceState.getBoolean(KEY_MULTIPLE);
31 | }
32 | findViewById(R.id.single).setOnClickListener(this);
33 | findViewById(R.id.multiple).setOnClickListener(this);
34 | }
35 |
36 | @Override
37 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
38 | super.onActivityResult(requestCode, resultCode, data);
39 | if (resultCode == RESULT_OK && requestCode == CODE) {
40 | if (mIsMultiple) {
41 | Parcelable[] uris = data.getParcelableArrayExtra(IntentAction.EXTRA_DATA);
42 | mAdapter.setData(uris);
43 | } else {
44 | Uri uri = data.getParcelableExtra(IntentAction.EXTRA_DATA);
45 | mAdapter.setData(uri);
46 | }
47 | }
48 | }
49 |
50 | @Override
51 | public void onSaveInstanceState(Bundle outState) {
52 | super.onSaveInstanceState(outState);
53 | outState.putBoolean(KEY_MULTIPLE, mIsMultiple);
54 | }
55 |
56 | @Override
57 | public void onClick(View v) {
58 | Intent intent = new Intent();
59 | switch (v.getId()) {
60 | case R.id.single:
61 | mIsMultiple = false;
62 | intent.setAction(IntentAction.ACTION_PICK);
63 | break;
64 | case R.id.multiple:
65 | mIsMultiple = true;
66 |
67 | intent.setAction(IntentAction.ACTION_MULTIPLE_PICK);
68 | break;
69 |
70 | }
71 |
72 | startActivityForResult(intent, CODE);
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/src/org/goodev/picker/GalleryAdapter.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import android.content.ContentUris;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.provider.MediaStore.Images.Media;
8 | import android.support.v4.util.LongSparseArray;
9 | import android.support.v4.widget.CursorAdapter;
10 | import android.util.SparseBooleanArray;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.view.ViewTreeObserver.OnGlobalLayoutListener;
15 | import android.widget.Checkable;
16 | import android.widget.ImageView;
17 |
18 | import com.squareup.picasso.Picasso;
19 |
20 | public class GalleryAdapter extends CursorAdapter {
21 |
22 | final LayoutInflater mInflater;
23 |
24 | public GalleryAdapter(Context context) {
25 | super(context, null, false);
26 | mInflater = LayoutInflater.from(context);
27 | }
28 |
29 | @Override
30 | public View newView(Context context, Cursor cursor, ViewGroup parent) {
31 | return mInflater.inflate(R.layout.photo_item, parent, false);
32 | }
33 |
34 | @Override
35 | public void bindView(View view, final Context context, Cursor cursor) {
36 | final ImageView iv = (ImageView) view;
37 | // file path
38 | final String path = cursor.getString(GalleryPickerActivity.DATA_INDEX);
39 | final long id = cursor.getLong(GalleryPickerActivity.ID_INDEX);
40 | final Uri uri = ContentUris.withAppendedId(Media.EXTERNAL_CONTENT_URI, id);
41 | final int width = iv.getWidth();
42 | if (width <= 0) {
43 | iv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
44 |
45 | @SuppressWarnings("deprecation")
46 | @Override
47 | public void onGlobalLayout() {
48 | iv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
49 | Picasso.with(context).load(uri).placeholder(R.drawable.picker_photo_holder)
50 | .resize(iv.getWidth(), iv.getHeight()).centerCrop().into(iv);
51 | }
52 | });
53 | } else {
54 | Picasso.with(context).load(uri).placeholder(R.drawable.picker_photo_holder).resize(iv.getWidth(), iv.getHeight())
55 | .centerCrop().into(iv);
56 | }
57 |
58 | if (view instanceof Checkable) {
59 | Checkable c = (Checkable) view;
60 | boolean checked = mCheckedIdStates.get(id, Boolean.FALSE);
61 | c.setChecked(checked);
62 | }
63 | }
64 |
65 | private SparseBooleanArray mCheckStates = new SparseBooleanArray();
66 | private LongSparseArray mCheckedIdStates = new LongSparseArray();
67 |
68 | public void setItemChecked(int position, boolean value) {
69 | mCheckStates.put(position, value);
70 | if (mCheckedIdStates != null) {
71 | if (value) {
72 | mCheckedIdStates.put(getItemId(position), Boolean.TRUE);
73 | } else {
74 | mCheckedIdStates.delete(getItemId(position));
75 | }
76 | }
77 | }
78 |
79 | public long[] getCheckedItemIds() {
80 |
81 | final LongSparseArray idStates = mCheckedIdStates;
82 | final int count = idStates.size();
83 | final long[] ids = new long[count];
84 |
85 | for (int i = 0; i < count; i++) {
86 | ids[i] = idStates.keyAt(i);
87 | }
88 |
89 | return ids;
90 | }
91 |
92 | public SparseBooleanArray getCheckedItemPositions() {
93 | return mCheckStates;
94 | }
95 |
96 | public boolean isItemChecked(int position) {
97 | if (mCheckStates != null) {
98 | return mCheckStates.get(position);
99 | }
100 |
101 | return false;
102 | }
103 |
104 | public void performItemClick(View view, int position, long id) {
105 | boolean newValue = !mCheckStates.get(position, false);
106 | mCheckStates.put(position, newValue);
107 | if (mCheckedIdStates != null && hasStableIds()) {
108 | if (newValue) {
109 | mCheckedIdStates.put(getItemId(position), Boolean.TRUE);
110 | } else {
111 | mCheckedIdStates.delete(getItemId(position));
112 | }
113 | }
114 |
115 | if (view instanceof Checkable) {
116 | Checkable checkable = (Checkable) view;
117 | checkable.setChecked(newValue);
118 | }
119 | }
120 |
121 | public void clearChoices() {
122 | if (mCheckStates != null) {
123 | mCheckStates.clear();
124 | }
125 | if (mCheckedIdStates != null) {
126 | mCheckedIdStates.clear();
127 | }
128 | notifyDataSetChanged();
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/src/org/goodev/picker/GalleryPickerActivity.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import static org.goodev.picker.IntentAction.ACTION_MULTIPLE_PICK;
4 |
5 | import org.goodev.picker.MultiSelectionUtil.MultiChoiceModeListener;
6 |
7 | import android.content.ContentUris;
8 | import android.content.Intent;
9 | import android.database.Cursor;
10 | import android.net.Uri;
11 | import android.os.Build;
12 | import android.os.Bundle;
13 | import android.provider.MediaStore;
14 | import android.support.v4.app.LoaderManager.LoaderCallbacks;
15 | import android.support.v4.content.CursorLoader;
16 | import android.support.v4.content.Loader;
17 | import android.support.v7.app.ActionBarActivity;
18 | import android.view.Menu;
19 | import android.view.MenuInflater;
20 | import android.view.MenuItem;
21 | import android.view.View;
22 | import android.widget.AdapterView;
23 | import android.widget.AdapterView.OnItemClickListener;
24 | import android.widget.GridView;
25 |
26 | public class GalleryPickerActivity extends ActionBarActivity implements LoaderCallbacks, OnItemClickListener,
27 | MultiChoiceModeListener {
28 | final static String[] PROJECTION = { MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID };
29 | final static String SORTORDER = MediaStore.Images.Media._ID + " DESC";
30 | final static Uri URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
31 | public static final int DATA_INDEX = 0;
32 | public static final int ID_INDEX = 1;
33 | private static final int LOADER_ID = 0;
34 | private GridView mGridView;
35 | private GalleryAdapter mAdapter;
36 | private boolean mIsMultiple;
37 | private MultiSelectionUtil.Controller mMultiSelectionController;
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_main);
43 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
44 | getSupportActionBar().setHomeButtonEnabled(true);
45 | mIsMultiple = ACTION_MULTIPLE_PICK.equals(getIntent().getAction());
46 | final GridView view = (GridView) findViewById(R.id.grid);
47 | mAdapter = new GalleryAdapter(this);
48 | view.setAdapter(mAdapter);
49 | view.setOnItemClickListener(this);
50 | mMultiSelectionController = MultiSelectionUtil.attachMultiSelectionController(view, this, this);
51 | mMultiSelectionController.tryRestoreInstanceState(savedInstanceState);
52 | mGridView = view;
53 | getSupportLoaderManager().initLoader(LOADER_ID, null, this);
54 | }
55 |
56 | @Override
57 | public Loader onCreateLoader(int id, Bundle args) {
58 | return new CursorLoader(this, URI, PROJECTION, null, null, SORTORDER);
59 | }
60 |
61 | @Override
62 | public void onLoadFinished(Loader loader, Cursor data) {
63 | mAdapter.swapCursor(data);
64 | mMultiSelectionController.tryRestoreInstanceState();
65 | }
66 |
67 | @Override
68 | public void onSaveInstanceState(Bundle outState) {
69 | super.onSaveInstanceState(outState);
70 | if (mMultiSelectionController != null) {
71 | mMultiSelectionController.saveInstanceState(outState);
72 | }
73 | }
74 |
75 | @Override
76 | public void onLoaderReset(Loader loader) {
77 | mAdapter.swapCursor(null);
78 | }
79 |
80 | @Override
81 | public boolean onOptionsItemSelected(MenuItem item) {
82 | if (item.getItemId() == android.R.id.home) {
83 | finish();
84 | return true;
85 | }
86 | return super.onOptionsItemSelected(item);
87 | }
88 |
89 | @Override
90 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
91 | if (mIsMultiple) {
92 | if (isGingerbread()) {
93 | mAdapter.performItemClick(view, position, id);
94 | }
95 | mMultiSelectionController.onItemLongClick(parent, view, position, id);
96 | // if (view instanceof Checkable) {
97 | // Checkable c = (Checkable) view;
98 | // boolean checked = !c.isChecked();
99 | // mGridView.setItemChecked(position, checked);
100 | // }
101 | } else {
102 | Uri uri = ContentUris.withAppendedId(URI, id);
103 | Intent data = new Intent();
104 | data.putExtra(IntentAction.EXTRA_DATA, uri);
105 | setResult(RESULT_OK, data);
106 | finish();
107 | }
108 | }
109 |
110 | @Override
111 | protected void onDestroy() {
112 | super.onDestroy();
113 | if (mMultiSelectionController != null) {
114 | mMultiSelectionController.finish();
115 | }
116 | mMultiSelectionController = null;
117 | }
118 |
119 | @Override
120 | public boolean onActionItemClicked(android.support.v7.view.ActionMode mode, MenuItem item) {
121 | if (item.getItemId() == R.id.menu_done) {
122 | long[] ids = isGingerbread() ? mAdapter.getCheckedItemIds() : mGridView.getCheckedItemIds();
123 | Uri[] uris = new Uri[ids.length];
124 | for (int i = 0; i < ids.length; i++) {
125 | Uri uri = ContentUris.withAppendedId(URI, ids[i]);
126 | uris[i] = uri;
127 | }
128 |
129 | Intent data = new Intent();
130 | data.putExtra(IntentAction.EXTRA_DATA, uris);
131 | setResult(RESULT_OK, data);
132 | finish();
133 | return true;
134 | }
135 | return false;
136 | }
137 |
138 | @Override
139 | public boolean onCreateActionMode(android.support.v7.view.ActionMode mode, Menu menu) {
140 | MenuInflater inflater = mode.getMenuInflater();
141 | inflater.inflate(R.menu.gallery, menu);
142 | int selectCount = getSelectCount();
143 | String title = getResources().getQuantityString(R.plurals.number_of_items_selected, selectCount, selectCount);
144 | mode.setTitle(title);
145 | return true;
146 | }
147 |
148 | @Override
149 | public void onDestroyActionMode(android.support.v7.view.ActionMode arg0) {
150 |
151 | }
152 |
153 | @Override
154 | public boolean onPrepareActionMode(android.support.v7.view.ActionMode arg0, Menu arg1) {
155 | return false;
156 | }
157 |
158 | @Override
159 | public void onItemCheckedStateChanged(android.support.v7.view.ActionMode mode, int position, long id, boolean checked) {
160 | int selectCount = getSelectCount();
161 | if (isGingerbread()) {
162 | mAdapter.setItemChecked(position, checked);
163 | }
164 | String title = getResources().getQuantityString(R.plurals.number_of_items_selected, selectCount, selectCount);
165 | mode.setTitle(title);
166 | }
167 |
168 | int getSelectCount() {
169 | if (isGingerbread()) {
170 | return mAdapter.getCheckedItemIds().length;
171 | } else {
172 | return mGridView.getCheckedItemIds().length;
173 | }
174 | }
175 |
176 | public static boolean isGingerbread() {
177 | return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/src/org/goodev/picker/IntentAction.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | public class IntentAction {
4 | public static final String ACTION_PICK = "goodev.intent.action.PICK";
5 | public static final String ACTION_MULTIPLE_PICK = "goodev.intent.action.MULTIPLE_PICK";
6 | public static final String EXTRA_DATA = "goodev.intent.extra.DATA";
7 | }
8 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/src/org/goodev/picker/MultiSelectionUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.goodev.picker;
18 |
19 | import java.util.HashSet;
20 |
21 | import android.os.Bundle;
22 | import android.os.Handler;
23 | import android.support.v7.app.ActionBarActivity;
24 | import android.support.v7.view.ActionMode;
25 | import android.util.Pair;
26 | import android.util.SparseBooleanArray;
27 | import android.view.Menu;
28 | import android.view.MenuItem;
29 | import android.view.View;
30 | import android.widget.AbsListView;
31 | import android.widget.Adapter;
32 | import android.widget.AdapterView;
33 |
34 | /**
35 | * Utilities for handling multiple selection in list views. Contains functionality similar to
36 | * {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL} but that works with {@link ActionBarActivity} and
37 | * backward-compatible action bars.
38 | */
39 | public class MultiSelectionUtil {
40 | public static Controller attachMultiSelectionController(final AbsListView listView, final ActionBarActivity activity,
41 | final MultiChoiceModeListener listener) {
42 | return Controller.attach(listView, activity, listener);
43 | }
44 |
45 | public static class Controller implements ActionMode.Callback, AdapterView.OnItemClickListener,
46 | AdapterView.OnItemLongClickListener {
47 | private Handler mHandler = new Handler();
48 | private ActionMode mActionMode;
49 | private AbsListView mListView = null;
50 | private ActionBarActivity mActivity = null;
51 | private MultiChoiceModeListener mListener = null;
52 | private HashSet mTempIdsToCheckOnRestore;
53 | private HashSet> mItemsToCheck;
54 | private AdapterView.OnItemClickListener mOldItemClickListener;
55 |
56 | private Controller() {
57 | }
58 |
59 | public static Controller attach(AbsListView listView, ActionBarActivity activity, MultiChoiceModeListener listener) {
60 | Controller controller = new Controller();
61 | controller.mListView = listView;
62 | controller.mActivity = activity;
63 | controller.mListener = listener;
64 | listView.setOnItemLongClickListener(controller);
65 | return controller;
66 | }
67 |
68 | private void readInstanceState(Bundle savedInstanceState) {
69 | mTempIdsToCheckOnRestore = null;
70 | if (savedInstanceState != null) {
71 | long[] checkedIds = savedInstanceState.getLongArray(getStateKey());
72 | if (checkedIds != null && checkedIds.length > 0) {
73 | mTempIdsToCheckOnRestore = new HashSet();
74 | for (long id : checkedIds) {
75 | mTempIdsToCheckOnRestore.add(id);
76 | }
77 | }
78 | }
79 | }
80 |
81 | public void tryRestoreInstanceState(Bundle savedInstanceState) {
82 | readInstanceState(savedInstanceState);
83 | tryRestoreInstanceState();
84 | }
85 |
86 | public void finish() {
87 | if (mActionMode != null) {
88 | mActionMode.finish();
89 | }
90 | }
91 |
92 | public void tryRestoreInstanceState() {
93 | if (mTempIdsToCheckOnRestore == null || mListView.getAdapter() == null) {
94 | return;
95 | }
96 |
97 | boolean idsFound = false;
98 | Adapter adapter = mListView.getAdapter();
99 | for (int pos = adapter.getCount() - 1; pos >= 0; pos--) {
100 | if (mTempIdsToCheckOnRestore.contains(adapter.getItemId(pos))) {
101 | idsFound = true;
102 | if (mItemsToCheck == null) {
103 | mItemsToCheck = new HashSet>();
104 | }
105 | mItemsToCheck.add(new Pair(pos, adapter.getItemId(pos)));
106 | }
107 | }
108 |
109 | if (idsFound) {
110 | // We found some IDs that were checked. Let's now restore the multi-selection
111 | // state.
112 | mTempIdsToCheckOnRestore = null; // clear out this temp field
113 | mActionMode = mActivity.startSupportActionMode(Controller.this);
114 | }
115 | }
116 |
117 | public boolean saveInstanceState(Bundle outBundle) {
118 | // TODO: support non-stable IDs by persisting positions instead of IDs
119 | if (mActionMode != null && mListView.getAdapter().hasStableIds()) {
120 | final boolean g = GalleryPickerActivity.isGingerbread();
121 | long[] checkedIds;
122 | if (g) {
123 | GalleryAdapter adapter = (GalleryAdapter) mListView.getAdapter();
124 | checkedIds = adapter.getCheckedItemIds();
125 | } else {
126 | checkedIds = mListView.getCheckedItemIds();
127 | }
128 | outBundle.putLongArray(getStateKey(), checkedIds);
129 | return true;
130 | }
131 |
132 | return false;
133 | }
134 |
135 | private String getStateKey() {
136 | return MultiSelectionUtil.class.getSimpleName() + "_" + mListView.getId();
137 | }
138 |
139 | @Override
140 | public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
141 | if (mListener.onCreateActionMode(actionMode, menu)) {
142 | mActionMode = actionMode;
143 | mOldItemClickListener = mListView.getOnItemClickListener();
144 | mListView.setOnItemClickListener(Controller.this);
145 | if (!GalleryPickerActivity.isGingerbread()) {
146 | mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
147 | }
148 | mHandler.removeCallbacks(mSetChoiceModeNoneRunnable);
149 |
150 | if (mItemsToCheck != null) {
151 | for (Pair posAndId : mItemsToCheck) {
152 | if (GalleryPickerActivity.isGingerbread()) {
153 | GalleryAdapter adapter = (GalleryAdapter) mListView.getAdapter();
154 | adapter.setItemChecked(posAndId.first, true);
155 | } else {
156 | mListView.setItemChecked(posAndId.first, true);
157 | }
158 | mListener.onItemCheckedStateChanged(mActionMode, posAndId.first, posAndId.second, true);
159 | }
160 | }
161 | return true;
162 | }
163 | return false;
164 | }
165 |
166 | @Override
167 | public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
168 | if (mListener.onPrepareActionMode(actionMode, menu)) {
169 | mActionMode = actionMode;
170 | return true;
171 | }
172 | return false;
173 | }
174 |
175 | @Override
176 | public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
177 | return mListener.onActionItemClicked(actionMode, menuItem);
178 | }
179 |
180 | @Override
181 | public void onDestroyActionMode(ActionMode actionMode) {
182 | mListener.onDestroyActionMode(actionMode);
183 | SparseBooleanArray checkedPositions;
184 | if (GalleryPickerActivity.isGingerbread()) {
185 | GalleryAdapter adapter = (GalleryAdapter) mListView.getAdapter();
186 | checkedPositions = adapter.getCheckedItemPositions();
187 | if (checkedPositions != null) {
188 | for (int i = 0; i < checkedPositions.size(); i++) {
189 | adapter.setItemChecked(checkedPositions.keyAt(i), false);
190 | }
191 | }
192 | adapter.clearChoices();
193 | } else {
194 | checkedPositions = mListView.getCheckedItemPositions();
195 | if (checkedPositions != null) {
196 | for (int i = 0; i < checkedPositions.size(); i++) {
197 | mListView.setItemChecked(checkedPositions.keyAt(i), false);
198 | }
199 | }
200 | }
201 |
202 | mListView.setOnItemClickListener(mOldItemClickListener);
203 | mActionMode = null;
204 | mHandler.post(mSetChoiceModeNoneRunnable);
205 | }
206 |
207 | private Runnable mSetChoiceModeNoneRunnable = new Runnable() {
208 | @Override
209 | public void run() {
210 | // mListView.setChoiceMode(AbsListView.CHOICE_MODE_NONE);
211 | }
212 | };
213 |
214 | @Override
215 | public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
216 | boolean checked;
217 | if (GalleryPickerActivity.isGingerbread()) {
218 | GalleryAdapter adapter = (GalleryAdapter) mListView.getAdapter();
219 | adapter.performItemClick(view, position, id);
220 | checked = adapter.isItemChecked(position);
221 | mListener.onItemCheckedStateChanged(mActionMode, position, id, checked);
222 |
223 | int numChecked = 0;
224 | SparseBooleanArray checkedItemPositions = adapter.getCheckedItemPositions();
225 | if (checkedItemPositions != null) {
226 | for (int i = 0; i < checkedItemPositions.size(); i++) {
227 | numChecked += checkedItemPositions.valueAt(i) ? 1 : 0;
228 | }
229 | }
230 |
231 | if (numChecked <= 0) {
232 | mActionMode.finish();
233 | }
234 | } else {
235 | checked = mListView.isItemChecked(position);
236 | mListener.onItemCheckedStateChanged(mActionMode, position, id, checked);
237 |
238 | int numChecked = 0;
239 | SparseBooleanArray checkedItemPositions = mListView.getCheckedItemPositions();
240 | if (checkedItemPositions != null) {
241 | for (int i = 0; i < checkedItemPositions.size(); i++) {
242 | numChecked += checkedItemPositions.valueAt(i) ? 1 : 0;
243 | }
244 | }
245 |
246 | if (numChecked <= 0) {
247 | mActionMode.finish();
248 | }
249 | }
250 | }
251 |
252 | @Override
253 | public boolean onItemLongClick(AdapterView> adapterView, View view, int position, long id) {
254 | if (mActionMode != null) {
255 | return false;
256 | }
257 |
258 | mItemsToCheck = new HashSet>();
259 | mItemsToCheck.add(new Pair(position, id));
260 | mActionMode = mActivity.startSupportActionMode(Controller.this);
261 | return true;
262 | }
263 | }
264 |
265 | /**
266 | * @see android.widget.AbsListView.MultiChoiceModeListener
267 | */
268 | public static interface MultiChoiceModeListener extends ActionMode.Callback {
269 | /**
270 | * @see android.widget.AbsListView.MultiChoiceModeListener#onItemCheckedStateChanged(android.view.ActionMode,
271 | * int, long, boolean)
272 | */
273 | public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked);
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/src/org/goodev/picker/OptimizeGridView.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import android.annotation.SuppressLint;
8 | import android.content.Context;
9 | import android.util.AttributeSet;
10 | import android.widget.Adapter;
11 | import android.widget.GridView;
12 |
13 | public class OptimizeGridView extends GridView {
14 | /**
15 | * 要用能包含重复元素的集合
16 | *
17 | * @param
18 | */
19 | interface OptimizeGridAdapter {
20 | List getItems();
21 |
22 | /**
23 | * Should notify the listView data is changed
24 | *
25 | * @param items
26 | */
27 | void setItems(List items);
28 |
29 | T getNullItem();
30 |
31 | boolean isNullItem(T item);
32 | }
33 |
34 | public OptimizeGridView(Context context) {
35 | super(context);
36 | }
37 |
38 | public OptimizeGridView(Context context, AttributeSet attrs) {
39 | super(context, attrs);
40 | }
41 |
42 | public OptimizeGridView(Context context, AttributeSet attrs, int defStyle) {
43 | super(context, attrs, defStyle);
44 | }
45 |
46 | @SuppressLint("DrawAllocation")
47 | @SuppressWarnings({ "rawtypes", "unchecked" })
48 | @Override
49 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
50 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
51 |
52 | int numColumns = AUTO_FIT;
53 | final boolean isApi11 = getResources().getBoolean(R.bool.api11);
54 | if (isApi11) {
55 | // API level 11 引入该函数,在低于11版本中 使用反射获取列数
56 | numColumns = getNumColumns();
57 | } else {
58 | try {
59 | Field numColumnsField = GridView.class.getDeclaredField("mNumColumns");
60 | numColumnsField.setAccessible(true);
61 | numColumns = numColumnsField.getInt(this);
62 | } catch (IllegalArgumentException e1) {
63 | e1.printStackTrace();
64 | } catch (NoSuchFieldException e1) {
65 | e1.printStackTrace();
66 | } catch (IllegalAccessException e1) {
67 | e1.printStackTrace();
68 | }
69 | }
70 | if (numColumns != AUTO_FIT) {
71 | final Adapter adapter = getAdapter();
72 | if (!(adapter instanceof OptimizeGridAdapter)) {
73 | return;
74 | }
75 | final int count = adapter.getCount();
76 | final int remainder = count % numColumns;
77 | if (remainder != 0) {
78 | final int diff = numColumns - remainder;
79 | final OptimizeGridAdapter adapter2 = (OptimizeGridAdapter) adapter;
80 | final List items = new ArrayList();
81 | items.addAll(adapter2.getItems());
82 | for (int i = 0; i < diff; i++) {
83 | items.add(adapter2.getNullItem());
84 | }
85 | adapter2.setItems(items);
86 | }
87 | }
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/MultiPhotoPickerAppCompat/src/org/goodev/picker/SquareImageView.java:
--------------------------------------------------------------------------------
1 | package org.goodev.picker;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.drawable.Drawable;
6 | import android.util.AttributeSet;
7 | import android.widget.Checkable;
8 | import android.widget.ImageView;
9 |
10 | public class SquareImageView extends ImageView implements Checkable {
11 |
12 | static Drawable sCheckedDrawable;
13 |
14 | public SquareImageView(Context context) {
15 | super(context);
16 | sCheckedDrawable = context.getResources().getDrawable(R.drawable.grid_selected);
17 | }
18 |
19 | public SquareImageView(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | sCheckedDrawable = context.getResources().getDrawable(R.drawable.grid_selected);
22 | }
23 |
24 | public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
25 | super(context, attrs, defStyle);
26 | sCheckedDrawable = context.getResources().getDrawable(R.drawable.grid_selected);
27 | }
28 |
29 | @SuppressWarnings("unused")
30 | @Override
31 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
32 | // For simple implementation, or internal size is always 0.
33 | // We depend on the container to specify the layout size of
34 | // our view. We can't really know what it is since we will be
35 | // adding and removing different arbitrary views and do not
36 | // want the layout to change as this happens.
37 | setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));
38 |
39 | // Children are just made to fill our space.
40 | int childWidthSize = getMeasuredWidth();
41 | int childHeightSize = getMeasuredHeight();
42 | // 高度和宽度一样
43 | heightMeasureSpec = widthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
44 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
45 | }
46 |
47 | private boolean mChecked;
48 | private Drawable mDrawable;
49 |
50 | @SuppressWarnings("deprecation")
51 | @Override
52 | public void setChecked(boolean checked) {
53 | if (checked != mChecked) {
54 | mChecked = checked;
55 | mDrawable = checked ? sCheckedDrawable : null;
56 | setBackgroundDrawable(mDrawable);
57 | }
58 | }
59 |
60 | @Override
61 | protected void onDraw(Canvas canvas) {
62 | super.onDraw(canvas);
63 | if (mDrawable != null) {
64 | mDrawable.draw(canvas);
65 | }
66 | }
67 |
68 | @Override
69 | public boolean isChecked() {
70 | return mChecked;
71 | }
72 |
73 | @Override
74 | public void toggle() {
75 | mChecked = !mChecked;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | MultiPhotoPicker
2 | ================
3 |
4 | Pick multiple photo from android gallery .
5 |
6 | **MultiPhotoPicker** : HONEYCOMB (Api 11) version.
7 |
8 | **MultiPhotoPickerAppCompat** : AppCompat (Api 7) version.
9 |
10 |
11 | 
12 | 
13 |
14 | How to use
15 | --------
16 |
17 | IntentAction.ACTION_PICK Action for pick single photo.
18 | IntentAction.ACTION_MULTIPLE_PICK for pick multiple photos.
19 |
20 | ```java
21 | Intent intent = new Intent();
22 | intent.setAction(IntentAction.ACTION_PICK);
23 | startActivityForResult(intent, CODE);
24 | ```
25 | ```java
26 | Intent intent = new Intent();
27 | intent.setAction(IntentAction.ACTION_MULTIPLE_PICK);
28 | startActivityForResult(intent, CODE);
29 | ```
30 |
31 | * you can modify **org.goodev.picker.GalleryPickerActivity.onActionItemClicked(ActionMode, MenuItem)** method to change the return data type, such as: return photo path, uri, id ...*
32 |
33 | ```java
34 | //return Selected photos ids and file path
35 | @Override
36 | public boolean onActionItemClicked(android.support.v7.view.ActionMode mode, MenuItem item) {
37 | if (item.getItemId() == R.id.menu_done) {
38 | SparseBooleanArray position = isGingerbread() ? mAdapter.getCheckedItemPositions() : mGridView.getCheckedItemPositions();
39 | final int count = position.size();
40 | final ArrayList pathList = new ArrayList();
41 |
42 | for (int i = 0; i < count; i++) {
43 | if(position.valueAt(i)) {
44 | Cursor cursor = (Cursor) mAdapter.getItem(position.keyAt(i));
45 | pathList.add(cursor.getString(DATA_INDEX));
46 | }
47 | }
48 |
49 | String[] paths = new String[pathList.size()];
50 | paths = pathList.toArray(paths);
51 | long[] ids = isGingerbread() ? mAdapter.getCheckedItemIds() : mGridView.getCheckedItemIds();
52 |
53 | Intent data = new Intent();
54 | data.putExtra(IntentAction.EXTRA_DATA, ids);
55 | data.putExtra(IntentAction.EXTRA_PATH, paths);
56 | setResult(RESULT_OK, data);
57 | finish();
58 | return true;
59 | }
60 | return false;
61 | }
62 | ```
63 |
64 | Android 系统选择多个图片 .
65 |
66 | **MultiPhotoPicker** : HONEYCOMB (Api 11, 3.0)以上版本。
67 |
68 | **MultiPhotoPickerAppCompat** : AppCompat (Api 7, 2.3) 版本,需要 AppCompat 库。
69 |
70 | 如何使用
71 | --------
72 |
73 | IntentAction.ACTION_PICK 这个 Intent Action 用来选择一个图片
74 | IntentAction.ACTION_MULTIPLE_PICK 这个 Intent Action 用来选择多个图片
75 |
--------------------------------------------------------------------------------
/screencast/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/screencast/1.png
--------------------------------------------------------------------------------
/screencast/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodev/MultiPhotoPicker/25a690984cee0d36c94d3039239d191dc40cc11e/screencast/2.png
--------------------------------------------------------------------------------