版权声明:本文为HaiyuKing原创文章,转载请注明出处!
前言
本Demo使用的是PhotoPicker 0.9.12版本,里面集成的glide版本号是4.1.1。这里就不进行特殊的个性化处理了(比如新增NewImagePagerDialogFragment.java用于对话框样式预览图片、新增NewPhotoPickerFragment.java用于单独作为自定义DialogFragment的其中一个Fragment和其他Fragment共存),而是记录下使用步骤(当然了,github上讲解的很清楚了,大家可以直接参考《donglua/PhotoPicker》)。
不过,本文对PhotoPicker进行了简单的修改,对UI进行了部分修改。【所以本Demo最后使用的是方式2——通过引入PhotoPicker这个module的方式】
效果图
代码分析
引入PhotoPicker有两种方式【二选一】
1、通过Gradle方式,在APP的build.gradle中引用【见导入步骤】
2、下载整个压缩包,然后导入PhotoPicker的module,然后修改图片资源
2.1、下载压缩包
2.2、导入PhotoPicker的Module
提示下面的错误:
2.3、解决Plugin with id 'com.novoda.bintray-release' not found.问题
在项目的build.gradle文件中添加以下代码
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
//解决PhotoPicker的Plugin with id 'com.novoda.bintray-release' not found.
classpath 'com.novoda:bintray-release:0.5.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
2.4、修改图片资源(比如__picker_ic_photo_black_48dp.png、__picker_checkbox_marked.png、__picker_checkbox_n.png)
注意:需要修改所有drawable-xxxx目录下的图片
原始的图片资源:
修改后的图片资源:
2.5、添加图片资源【__picker_transparent_bg.png、down_arrow.png、up_arrow.png】
添加到drawable-xxhdpi目录下
2.6、修改drawable目录下的__picker_photo_bg.xml和__picker_checkbox_bg.xml【主要用于替换图片列表项的底部背景图】
原代码1:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true"> <shape> <stroke android:color="@color/__picker_item_photo_border_selected" android:width="1dip"/> </shape> </item> <item> <shape> <stroke android:color="@color/__picker_item_photo_border_n" android:width="1dip"/> </shape> </item> </selector>
修改后的代码:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true"> <shape android:shape="rectangle"> <stroke android:width="2dip" android:color="@color/__picker_item_photo_border_selected"/> <solid android:color="@color/__picker_selected_bg"/> <solid></solid> </shape> </item> <item android:drawable="@drawable/__picker_transparent_bg"> <shape android:shape="rectangle"> <stroke android:width="2dip" android:color="@color/__picker_item_photo_border_n"/> <!--<solid android:color="#00000000"/>--> </shape> </item> </selector>
原代码2:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true"> <layer-list> <item> <shape> <corners android:radius="2dip"/> <padding android:top="-2dip" android:left="-2dip" android:bottom="-2dip" android:right="-2dip"/> <stroke android:width="1dip" android:color="@android:color/white"/> </shape> </item> <item android:drawable="@drawable/__picker_checkbox_marked"/> </layer-list> </item> <item> <layer-list> <item> <shape> <corners android:radius="2dip"/> <padding android:top="-2dip" android:left="-2dip" android:bottom="-2dip" android:right="-2dip"/> <stroke android:width="1dip" android:color="@android:color/white"/> </shape> </item> <item android:drawable="@drawable/__picker_checkbox_n"/> </layer-list> </item> </selector>
修改后的
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true"> <layer-list> <!--<item> <shape> <corners android:radius="10dip"/> <padding android:top="-2dip" android:left="-2dip" android:bottom="-2dip" android:right="-2dip"/> <stroke android:width="1dip" android:color="@android:color/white"/> <solid android:color="@android:color/white"></solid> </shape> </item>--> <item android:drawable="@drawable/__picker_checkbox_marked"/> </layer-list> </item> <item> <layer-list> <!--<item> <shape> <corners android:radius="10dip"/> <padding android:top="-2dip" android:left="-2dip" android:bottom="-2dip" android:right="-2dip"/> <stroke android:width="1dip" android:color="@android:color/white"/> <solid android:color="@android:color/white"></solid> </shape> </item>--> <item android:drawable="@drawable/__picker_checkbox_n"/> </layer-list> </item> </selector>
2.7、修改__picker_fragment_photo_picker.xml文件【主要用于添加图片目录的上下箭头图标】
原代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v7.widget.RecyclerView android:id="@+id/rv_photos" android:layout_width="match_parent" android:gravity="center" android:layout_weight="1" android:layout_height="0dip" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" > <Button android:id="@+id/button" android:text="@string/__picker_all_image" android:layout_width="wrap_content" android:gravity="center" android:layout_height="wrap_content" style="@style/Widget.AppCompat.ActionButton" /> </LinearLayout> </LinearLayout>
修改后的代码
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v7.widget.RecyclerView android:id="@+id/rv_photos" android:layout_width="match_parent" android:gravity="center" android:layout_weight="1" android:layout_height="0dip" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#e9e9e9"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" > <!--<Button android:id="@+id/button" android:text="@string/__picker_all_image" android:layout_width="wrap_content" android:gravity="center" android:layout_height="wrap_content" style="@style/Widget.AppCompat.ActionButton" />--> <TextView android:id="@+id/button" android:layout_width="0.0dp" android:layout_weight="1" android:layout_height="match_parent" android:text="@string/__picker_all_image" android:textColor="@color/__picker_text_120" android:textSize="16sp" android:drawableRight="@drawable/up_arrow" android:drawablePadding="8dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:gravity="center_vertical" style="@style/Widget.AppCompat.ActionButton" /> <!-- 现在用不到,只是占位用 --> <ImageView android:layout_width="0.0dp" android:layout_weight="1" android:layout_height="match_parent" android:src="@drawable/__picker_camera" android:layout_gravity="center_vertical" android:paddingLeft="15dp" android:paddingRight="15dp" android:visibility="invisible"/> </LinearLayout> </LinearLayout>
2.8、修改PhotoPickerFragment.java文件【主要是实现上下箭头的更换功能】
搜索why,查看改动的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package me.iwf.photopicker.fragment; import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.content.ContextCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.ListPopupWindow; import android.support.v7.widget.OrientationHelper; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.PopupWindow; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestManager; import java.io.IOException; import java.util.ArrayList; import java.util.List; import me.iwf.photopicker.PhotoPickerActivity; import me.iwf.photopicker.R; import me.iwf.photopicker.adapter.PhotoGridAdapter; import me.iwf.photopicker.adapter.PopupDirectoryListAdapter; import me.iwf.photopicker.entity.Photo; import me.iwf.photopicker.entity.PhotoDirectory; import me.iwf.photopicker.event.OnPhotoClickListener; import me.iwf.photopicker.utils.AndroidLifecycleUtils; import me.iwf.photopicker.utils.ImageCaptureManager; import me.iwf.photopicker.utils.MediaStoreHelper; import me.iwf.photopicker.utils.PermissionsConstant; import me.iwf.photopicker.utils.PermissionsUtils; import static android.app.Activity.RESULT_OK; import static me.iwf.photopicker.PhotoPicker.DEFAULT_COLUMN_NUMBER; import static me.iwf.photopicker.PhotoPicker.EXTRA_PREVIEW_ENABLED; import static me.iwf.photopicker.PhotoPicker.EXTRA_SHOW_GIF; import static me.iwf.photopicker.utils.MediaStoreHelper.INDEX_ALL_PHOTOS; /** * Created by donglua on 15/5/31. */ public class PhotoPickerFragment extends Fragment { private ImageCaptureManager captureManager; private PhotoGridAdapter photoGridAdapter; private PopupDirectoryListAdapter listAdapter; //所有photos的路径 private List<PhotoDirectory> directories; //传入的已选照片 private ArrayList<String> originalPhotos; private int SCROLL_THRESHOLD = 30; int column; //目录弹出框的一次最多显示的目录数目 public static int COUNT_MAX = 4; private final static String EXTRA_CAMERA = "camera"; private final static String EXTRA_COLUMN = "column"; private final static String EXTRA_COUNT = "count"; private final static String EXTRA_GIF = "gif"; private final static String EXTRA_ORIGIN = "origin"; private ListPopupWindow listPopupWindow; private RequestManager mGlideRequestManager; public static PhotoPickerFragment newInstance(boolean showCamera, boolean showGif, boolean previewEnable, int column, int maxCount, ArrayList<String> originalPhotos) { Bundle args = new Bundle(); args.putBoolean(EXTRA_CAMERA, showCamera); args.putBoolean(EXTRA_GIF, showGif); args.putBoolean(EXTRA_PREVIEW_ENABLED, previewEnable); args.putInt(EXTRA_COLUMN, column); args.putInt(EXTRA_COUNT, maxCount); args.putStringArrayList(EXTRA_ORIGIN, originalPhotos); PhotoPickerFragment fragment = new PhotoPickerFragment(); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); mGlideRequestManager = Glide.with(this); directories = new ArrayList<>(); originalPhotos = getArguments().getStringArrayList(EXTRA_ORIGIN); column = getArguments().getInt(EXTRA_COLUMN, DEFAULT_COLUMN_NUMBER); boolean showCamera = getArguments().getBoolean(EXTRA_CAMERA, true); boolean previewEnable = getArguments().getBoolean(EXTRA_PREVIEW_ENABLED, true); photoGridAdapter = new PhotoGridAdapter(getActivity(), mGlideRequestManager, directories, originalPhotos, column); photoGridAdapter.setShowCamera(showCamera); photoGridAdapter.setPreviewEnable(previewEnable); listAdapter = new PopupDirectoryListAdapter(mGlideRequestManager, directories); Bundle mediaStoreArgs = new Bundle(); boolean showGif = getArguments().getBoolean(EXTRA_GIF); mediaStoreArgs.putBoolean(EXTRA_SHOW_GIF, showGif); MediaStoreHelper.getPhotoDirs(getActivity(), mediaStoreArgs, new MediaStoreHelper.PhotosResultCallback() { @Override public void onResultCallback(List<PhotoDirectory> dirs) { directories.clear(); directories.addAll(dirs); photoGridAdapter.notifyDataSetChanged(); listAdapter.notifyDataSetChanged(); adjustHeight(); } }); captureManager = new ImageCaptureManager(getActivity()); } @Override public void onResume() { super.onResume(); if(getActivity() instanceof PhotoPickerActivity){ PhotoPickerActivity photoPickerActivity = (PhotoPickerActivity) getActivity(); photoPickerActivity.updateTitleDoneItem(); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View rootView = inflater.inflate(R.layout.__picker_fragment_photo_picker, container, false); RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.rv_photos); StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(column, OrientationHelper.VERTICAL); layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(photoGridAdapter); recyclerView.setItemAnimator(new DefaultItemAnimator()); //final Button btSwitchDirectory = (Button) rootView.findViewById(R.id.button); final TextView btSwitchDirectory = (TextView) rootView.findViewById(R.id.button);//why listPopupWindow = new ListPopupWindow(getActivity()); listPopupWindow.setWidth(ListPopupWindow.MATCH_PARENT); listPopupWindow.setAnchorView(btSwitchDirectory); listPopupWindow.setAdapter(listAdapter); listPopupWindow.setModal(true); listPopupWindow.setDropDownGravity(Gravity.BOTTOM); listPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { listPopupWindow.dismiss(); PhotoDirectory directory = directories.get(position); btSwitchDirectory.setText(directory.getName()); photoGridAdapter.setCurrentDirectoryIndex(position); photoGridAdapter.notifyDataSetChanged(); } }); //添加popwindow隐藏的监听--why listPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { //监听popwindow隐藏的时候的操作 Drawable drawableUp = ContextCompat.getDrawable(getActivity(),R.drawable.up_arrow); drawableUp.setBounds(0, 0, drawableUp.getMinimumWidth(), drawableUp.getMinimumHeight()); btSwitchDirectory.setCompoundDrawables(null,null,drawableUp,null); } }); photoGridAdapter.setOnPhotoClickListener(new OnPhotoClickListener() { @Override public void onClick(View v, int position, boolean showCamera) { final int index = showCamera ? position - 1 : position; List<String> photos = photoGridAdapter.getCurrentPhotoPaths(); ImagePagerFragment imagePagerFragment = ImagePagerFragment.newInstance(photos, index); ((PhotoPickerActivity) getActivity()).addImagePagerFragment(imagePagerFragment); } }); photoGridAdapter.setOnCameraClickListener(new OnClickListener() { @Override public void onClick(View view) { if (!PermissionsUtils.checkCameraPermission(PhotoPickerFragment.this)) return; if (!PermissionsUtils.checkWriteStoragePermission(PhotoPickerFragment.this)) return; openCamera(); } }); btSwitchDirectory.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (listPopupWindow.isShowing()) { listPopupWindow.dismiss(); } else if (!getActivity().isFinishing()) { adjustHeight(); //why Drawable drawableDown = ContextCompat.getDrawable(getActivity(),R.drawable.down_arrow); drawableDown.setBounds(0, 0, drawableDown.getMinimumWidth(), drawableDown.getMinimumHeight()); btSwitchDirectory.setCompoundDrawables(null,null,drawableDown,null); listPopupWindow.show(); } } }); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // Log.d(">>> Picker >>>", "dy = " + dy); if (Math.abs(dy) > SCROLL_THRESHOLD) { mGlideRequestManager.pauseRequests(); } else { resumeRequestsIfNotDestroyed(); } } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { resumeRequestsIfNotDestroyed(); } } }); return rootView; } private void openCamera() { try { Intent intent = captureManager.dispatchTakePictureIntent(); startActivityForResult(intent, ImageCaptureManager.REQUEST_TAKE_PHOTO); } catch (IOException e) { e.printStackTrace(); } catch (ActivityNotFoundException e) { Log.e("PhotoPickerFragment", "No Activity Found to handle Intent", e); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == ImageCaptureManager.REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) { if (captureManager == null) { FragmentActivity activity = getActivity(); captureManager = new ImageCaptureManager(activity); } captureManager.galleryAddPic(); if (directories.size() > 0) { String path = captureManager.getCurrentPhotoPath(); PhotoDirectory directory = directories.get(INDEX_ALL_PHOTOS); directory.getPhotos().add(INDEX_ALL_PHOTOS, new Photo(path.hashCode(), path)); directory.setCoverPath(path); photoGridAdapter.notifyDataSetChanged(); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { switch (requestCode) { case PermissionsConstant.REQUEST_CAMERA: case PermissionsConstant.REQUEST_EXTERNAL_WRITE: if (PermissionsUtils.checkWriteStoragePermission(this) && PermissionsUtils.checkCameraPermission(this)) { openCamera(); } break; } } } public PhotoGridAdapter getPhotoGridAdapter() { return photoGridAdapter; } @Override public void onSaveInstanceState(Bundle outState) { captureManager.onSaveInstanceState(outState); super.onSaveInstanceState(outState); } @Override public void onViewStateRestored(Bundle savedInstanceState) { captureManager.onRestoreInstanceState(savedInstanceState); super.onViewStateRestored(savedInstanceState); } public ArrayList<String> getSelectedPhotoPaths() { return photoGridAdapter.getSelectedPhotoPaths(); } public void adjustHeight() { if (listAdapter == null) return; int count = listAdapter.getCount(); count = count < COUNT_MAX ? count : COUNT_MAX; if (listPopupWindow != null) { listPopupWindow.setHeight(count * getResources().getDimensionPixelOffset(R.dimen.__picker_item_directory_height)); } } @Override public void onDestroy() { super.onDestroy(); if (directories == null) { return; } for (PhotoDirectory directory : directories) { directory.getPhotoPaths().clear(); directory.getPhotos().clear(); directory.setPhotos(null); } directories.clear(); directories = null; } private void resumeRequestsIfNotDestroyed() { if (!AndroidLifecycleUtils.canLoadImage(this)) { return; } mGlideRequestManager.resumeRequests(); } }
2.9、修改__picker_item_photo.xml,设置图片列表项的内边距值
<?xml version="1.0" encoding="utf-8"?> <me.iwf.photopicker.widget.SquareItemLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="3dp" > <ImageView android:padding="1dip" android:layout_gravity="center" android:id="@+id/iv_photo" android:scaleType="centerCrop" android:layout_width="match_parent" android:background="@drawable/__picker_photo_bg" android:layout_height="match_parent" android:adjustViewBounds="true" /> <ImageView android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:clickable="true" android:paddingTop="10dip" android:paddingRight="10dip" android:paddingLeft="20dip" android:paddingBottom="20dip" android:id="@+id/v_selected" android:src="@drawable/__picker_checkbox_bg" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </me.iwf.photopicker.widget.SquareItemLayout>
2.10、修改图片列表项选中后的颜色值(colors.xml文件中修改)
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="__picker_pager_bg">#CA000000</color> <color name="__picker_selected_bg">#44000000</color> <color name="__picker_black_40">#282828</color> <color name="__picker_common_primary">#f3f3f3</color> <color name="__picker_text_40">#282828</color> <color name="__picker_text_80">#505050</color> <color name="__picker_text_120">#787878</color> <!--<color name="__picker_item_photo_border_selected">#ff99cc00</color>--> <!--why--> <color name="__picker_item_photo_border_selected">#1A78EC</color> <color name="__picker_item_photo_border_n">#33ffffff</color> </resources>
至此,PhotoPicker基本上修改完了,后续可以根据需求继续修改。下面就是运用到APP中的步骤【导入步骤跟方式1几乎一样,不同的就是app的build.gradle导入的photopicker不一样】。
2.11、在APP的build.gradle文件添加以下代码
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.why.project.photopickernewdemo"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//PhotoPicker
//compile 'me.iwf.photopicker:PhotoPicker:0.9.12@aar'
//方式2:使用自己导入并且修改后的module
implementation project(':PhotoPicker')
//compile 'com.android.support:appcompat-v7:27.1.1'//需要注释,因为新建项目都会自动引用这个appcompat-v7
compile 'com.android.support:recyclerview-v7:27.1.1'
compile 'com.android.support:design:27.1.1'
compile 'com.github.bumptech.glide:glide:4.1.1'
}
2.12、在APP的AndroidManifest.xml中添加以下代码
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.why.project.photopickernewdemo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- PhotoPicker --> <activity android:name="me.iwf.photopicker.PhotoPickerActivity" android:theme="@style/customTheme" /> <activity android:name="me.iwf.photopicker.PhotoPagerActivity" android:theme="@style/customTheme"/> </application> </manifest>
2.13、在styles.xml文件中添加以下代码
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> <!-- PhotoPicker --> <style name="customTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- 解开注释的话,右侧的完成文本始终是设置的颜色,无法实现禁用状态下是灰色的功能 --> <!--<item name="actionBarTheme">@style/actionBarTheme</item>--> <!--背景颜色值--> <item name="colorPrimary">#ffffff</item> <!--导航栏高度值--> <item name="actionBarSize">52dp</item> <!-- 状态栏着色 --> <item name="colorPrimaryDark">#378dfc</item> </style> <style name="actionBarTheme" parent="ThemeOverlay.AppCompat.Dark.ActionBar"> <!--标题颜色值--> <item name="android:textColorPrimary">#434343</item> <!--右侧完成文本的大小值--> <item name="android:textSize">16sp</item> <!--右侧完成文本的颜色值--> <item name="android:actionMenuTextColor">#1A78EC</item> </style> </resources>
具体使用参考《三、使用方法》
使用步骤
一、项目组织结构图
注意事项:
1、 导入类文件后需要change包名以及重新import R文件路径
2、 Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖
二、导入步骤【方式1的导入步骤】
(1)在app的build.gradle文件中导入PhotoPicker【修改recyclerview、design的版本号和appcompat-v7统一】
注意:appcompat-v7
version >= 23.0.0
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.why.project.photopickernewdemo"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//PhotoPicker
compile 'me.iwf.photopicker:PhotoPicker:0.9.12@aar'
//compile 'com.android.support:appcompat-v7:27.1.1'//需要注释,因为新建项目都会自动引用这个appcompat-v7
compile 'com.android.support:recyclerview-v7:27.1.1'
compile 'com.android.support:design:27.1.1'
compile 'com.github.bumptech.glide:glide:4.1.1'
}
(2)在APP的styles.xml文件中自定义样式(颜色、高度值等)
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> <!-- PhotoPicker --> <style name="customTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- 解开注释的话,右侧的完成文本始终是设置的颜色,无法实现禁用状态下是灰色的功能 --> <!--<item name="actionBarTheme">@style/actionBarTheme</item>--> <!--背景颜色值--> <item name="colorPrimary">#ffffff</item> <!--导航栏高度值--> <item name="actionBarSize">52dp</item> <!-- 状态栏着色 --> <item name="colorPrimaryDark">#378dfc</item> </style> <style name="actionBarTheme" parent="ThemeOverlay.AppCompat.Dark.ActionBar"> <!--标题颜色值--> <item name="android:textColorPrimary">#434343</item> <!--右侧完成文本的大小值--> <item name="android:textSize">16sp</item> <!--右侧完成文本的颜色值--> <item name="android:actionMenuTextColor">#1A78EC</item> </style> </resources>
(3)在APP的AndroidManifest.xml中添加以下代码【注意:使用上面自定义的样式】
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.why.project.photopickernewdemo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- PhotoPicker --> <activity android:name="me.iwf.photopicker.PhotoPickerActivity" android:theme="@style/customTheme" /> <activity android:name="me.iwf.photopicker.PhotoPagerActivity" android:theme="@style/customTheme"/> </application> </manifest>
三、使用方法
(1)在项目中实现Recyclerview基本数据展现【比较简单,省略】【或者参考《Android快速开发常用知识点系列目录》下的RecyclerView篇章相关文章】
注意:PictureAdapter.java中使用的Glide是4.1.1版本,所以写法跟之前的Glide3.7.0版本是不一样的。
package com.why.project.photopickernewdemo.adapter; import android.content.Context; import android.net.Uri; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.Priority; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.RequestOptions; import com.why.project.photopickernewdemo.R; import com.why.project.photopickernewdemo.bean.PictureBean; import java.io.File; import java.util.ArrayList; import java.util.List; import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade; /** * Created by HaiyuKing * Used 照片网格适配器 */ public class PictureAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ private static final String TAG = PictureAdapter.class.getSimpleName(); /**上下文*/ private Context myContext; /**自定义列表项标题集合*/ private ArrayList<PictureBean> listitemList; final static int TYPE_ADD = 1; final static int TYPE_PHOTO = 2; public final static int MAX = 15;//总数目,这里根据实际情况设置,设置100基本上表明无限制了 /* * 构造函数 */ public PictureAdapter(Context context, ArrayList<PictureBean> itemlist) { myContext = context; listitemList = itemlist; } /** * 获取总的条目数 */ @Override public int getItemCount() { Log.w(TAG,"{getItemCount}listitemList.size()="+listitemList.size()); int count = listitemList.size(); if (count > MAX) { count = MAX; } count = count + 1; return count; } @Override public int getItemViewType(int position) { Log.w(TAG,"{getItemViewType}position="+position); Log.w(TAG,"{getItemViewType}listitemList.size()="+listitemList.size()); return (position == listitemList.size()) ? TYPE_ADD : TYPE_PHOTO; } /** * 创建ViewHolder */ @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if(viewType == TYPE_ADD) { View viewfoot = LayoutInflater.from(myContext).inflate(R.layout.pic_grid_foot_item, parent, false); ItemFootViewHolder itemfootViewHolder = new ItemFootViewHolder(viewfoot); return itemfootViewHolder; } else if(viewType == TYPE_PHOTO) { View view = LayoutInflater.from(myContext).inflate(R.layout.pic_grid_item, parent, false); ItemViewHolder itemViewHolder = new ItemViewHolder(view); return itemViewHolder; } return null; } /** * 声明列表项ViewHolder*/ static class ItemViewHolder extends RecyclerView.ViewHolder { public ItemViewHolder(View view) { super(view); griditemLayout = (LinearLayout) view.findViewById(R.id.griditemLayout); griditemimgLayout = (RelativeLayout) view.findViewById(R.id.griditemimgLayout); grid_img = (ImageView) view.findViewById(R.id.grid_img); grid_img_state = (TextView) view.findViewById(R.id.grid_img_state); } LinearLayout griditemLayout; RelativeLayout griditemimgLayout; ImageView grid_img; TextView grid_img_state; } /** * 声明最后一个ViewHolder*/ static class ItemFootViewHolder extends RecyclerView.ViewHolder { public ItemFootViewHolder(View view) { super(view); gridfootitemLayout = (RelativeLayout) view.findViewById(R.id.gridfootitemLayout); } RelativeLayout gridfootitemLayout; } /** * 将数据绑定至ViewHolder */ @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int index) { if(viewHolder instanceof ItemViewHolder){ PictureBean listItemModel = listitemList.get(index); final ItemViewHolder itemViewHold = ((ItemViewHolder)viewHolder); Uri uri = Uri.fromFile(new File(listItemModel.getPicPath())); RequestOptions options = new RequestOptions() //设置等待时的图片 .placeholder(R.drawable.img_loading) //设置加载失败后的图片显示 .error(R.drawable.img_error) //缓存策略,跳过内存缓存【此处应该设置为false,否则列表刷新时会闪一下】 .skipMemoryCache(false) //缓存策略,硬盘缓存-仅仅缓存最终的图像,即降低分辨率后的(或者是转换后的) .diskCacheStrategy(DiskCacheStrategy.ALL) //设置图片加载的优先级 .priority(Priority.HIGH); Glide.with(myContext) .load(uri) .apply(options) //默认淡入淡出动画 .transition(withCrossFade()) .into(itemViewHold.grid_img); itemViewHold.grid_img_state.setText("(" + (index+1) + "/" + listitemList.size() + ")"); //如果设置了回调,则设置点击事件 if (mOnItemClickLitener != null) { itemViewHold.grid_img.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int position = itemViewHold.getLayoutPosition();//在增加数据或者减少数据时候,position和index就不一样了 mOnItemClickLitener.onItemClick(view,position); } }); } }else if(viewHolder instanceof ItemFootViewHolder){ final ItemFootViewHolder itemFootViewHold = ((ItemFootViewHolder)viewHolder); //如果设置了回调,则设置点击事件 if (mOnItemClickLitener != null) { itemFootViewHold.gridfootitemLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mOnItemClickLitener.onItemAddClick(); } }); } } } /** * 添加Item--用于动画的展现*/ public void addItem(int position,PictureBean itemModel) { listitemList.add(position,itemModel); notifyItemInserted(position); } /** * 删除Item--用于动画的展现*/ public void removeItem(int position) { listitemList.remove(position); notifyItemRemoved(position); } /*=====================添加OnItemClickListener回调================================*/ public interface OnItemClickLitener { /**图片的点击事件*/ void onItemClick(View view, int position); /**添加的点击事件*/ void onItemAddClick(); } private OnItemClickLitener mOnItemClickLitener; public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener) { this.mOnItemClickLitener = mOnItemClickLitener; } //返回当前图片集合的所有路径集合【用于预览】 public List<String> getAllPhotoPaths() { List<String> allPhotoPaths = new ArrayList<String>(listitemList.size()); for (PictureBean pictureBean: listitemList) { allPhotoPaths.add(pictureBean.getPicPath()); } return allPhotoPaths; } }
(2)常规使用
package com.why.project.photopickernewdemo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.widget.Toast; import com.why.project.photopickernewdemo.adapter.PictureAdapter; import com.why.project.photopickernewdemo.bean.PictureBean; import com.why.project.photopickernewdemo.utils.Globals; import java.util.ArrayList; import me.iwf.photopicker.PhotoPicker; import me.iwf.photopicker.PhotoPreview; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private RecyclerView mRecyclerView; private ArrayList<PictureBean> mPictureBeansList; private PictureAdapter mPictureAdapter; private ArrayList<String> selPhotosPath = null;//选中的图片路径集合 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initDatas(); initEvents(); } private void initViews() { mRecyclerView = findViewById(R.id.picture_grid); } private void initDatas() { selPhotosPath = new ArrayList<String>(); //=============图片九宫格========================= mPictureAdapter = null; mPictureBeansList = new ArrayList<PictureBean>(); //设置布局管理器 GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3); mRecyclerView.setLayoutManager(gridLayoutManager); if(mPictureAdapter == null){ //设置适配器 mPictureAdapter = new PictureAdapter(this, mPictureBeansList); mRecyclerView.setAdapter(mPictureAdapter); //添加分割线 //设置添加删除动画 //调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局 mRecyclerView.setSelected(true); }else{ mPictureAdapter.notifyDataSetChanged(); } } private void initEvents() { //图片九宫格点击事件 mPictureAdapter.setOnItemClickLitener(new PictureAdapter.OnItemClickLitener() { @Override public void onItemClick(View v, int position) { //打开图片预览界面 ArrayList<String> photos = (ArrayList<String>) mPictureAdapter.getAllPhotoPaths(); PhotoPreview.builder() .setPhotos(photos) .setCurrentItem(position) .setShowDeleteButton(false) .start(MainActivity.this); } @Override public void onItemAddClick() { PhotoPicker.builder() .setPhotoCount(mPictureAdapter.MAX) .setGridColumnCount(3) //.setSelected(selPhotosPath) .start(MainActivity.this, Globals.CHOOSE_PIC_REQUEST_CODE); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.w(TAG, "{onActivityResult}resultCode="+resultCode); Log.w(TAG, "{onActivityResult}requestCode="+requestCode); if (resultCode == Activity.RESULT_OK) { //选择照片 if(requestCode == Globals.CHOOSE_PIC_REQUEST_CODE){ if (data != null) { selPhotosPath = data.getStringArrayListExtra(PhotoPicker.KEY_SELECTED_PHOTOS); } if (selPhotosPath != null) { //下面的代码主要用于这样一个场景,就是注释了.setSelected(selPhotosPath)之后,还想要保证选择的图片不重复 /*for(String path : selPhotosPath){ Log.w(TAG,"path="+path);///storage/emulated/0/tempHxzk/IMG_1498034535796.jpg boolean existThisPic = false; for(int i=0;i<mPictureBeansList.size();i++){ if(path.equals(mPictureBeansList.get(i).getPicPath())){ //如果新选择的图片集合中存在之前选中的图片,那么跳过去 existThisPic = true; break; } } if(! existThisPic){ PictureBean pictureBean = new PictureBean(); pictureBean.setPicPath(path); pictureBean.setPicName(getFileName(path)); //去掉总数目的限制,这里通过增大MAX的数字来实现 if (mPictureBeansList.size() < mPictureAdapter.MAX) { mPictureBeansList.add(pictureBean); } else { Toast.makeText(MainActivity.this, "最多可以选择" + mPictureAdapter.MAX + "张图片", Toast.LENGTH_SHORT).show(); break; } } }*/ //是常规操作,和上面的代码不可共存 for (String path : selPhotosPath) { PictureBean pictureBean = new PictureBean(); pictureBean.setPicPath(path); pictureBean.setPicName(Globals.getFileName(path)); //去掉总数目的限制,这里通过增大MAX的数字来实现 if (mPictureBeansList.size() < mPictureAdapter.MAX) { mPictureBeansList.add(pictureBean); } else { Toast.makeText(MainActivity.this, "最多可以选择" + mPictureAdapter.MAX + "张图片", Toast.LENGTH_SHORT).show(); break; } } mPictureAdapter.notifyDataSetChanged(); } } } } }
混淆配置
# PhotoPicker混淆 # Glide -keep public class * implements com.bumptech.glide.module.GlideModule -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { **[] $VALUES; public *; } # support-v7-appcompat -keep public class android.support.v7.widget.** { *; } -keep public class android.support.v7.internal.widget.** { *; } -keep public class android.support.v7.internal.view.menu.** { *; } -keep public class * extends android.support.v4.view.ActionProvider { public <init>(android.content.Context); } # support-design -dontwarn android.support.design.** -keep class android.support.design.** { *; } -keep interface android.support.design.** { *; } -keep public class android.support.design.R$* { *; }