版权声明:本文为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:

<?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>

__picker_photo_bg.xml

修改后的代码:

<?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:

<?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>

__picker_checkbox_bg.xml

修改后的

<?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文件【主要用于添加图片目录的上下箭头图标】

原代码

<?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>

__picker_fragment_photo_picker.xml

修改后的代码

<?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,查看改动的代码:

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();
}
}

PhotoPickerFragment.java

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-v7version >= 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$* { *; }

参考资料

donglua/PhotoPicker

项目demo下载地址

https://github.com/haiyuKing/PhotoPickerNewDemo

PhotoPickerNewDemo【PhotoPicker0.9.12的个性化修改以及使用(内部glide版本号是4.1.1)】的更多相关文章

  1. PhotoPickerDemo【PhotoPicker0.9.8的个性化修改以及使用(内部glide版本号是3.7.0)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本Demo使用的是PhotoPicker 0.9.8版本,属于比较旧的版本,里面集成的glide版本号是3.7.0.本篇文章主要是留 ...

  2. Ubuntu 12/14 个性化配置

    计算机名:jianbao-pc    用户名:jianbao 修改 /opt目录的 用户名 及 用户组 : sudo chown -R jianbao:jianbao /opt Ubuntu 如何开启 ...

  3. 修改AssemblyInfo.cs自动生成版本号

    一. 版本号自动生成方法 1.把 AssemblyInfo.cs文件中的[assembly:AssemblyVersion("1.0.0.0")]改成[assembly:Assem ...

  4. nginx.conf(centos6, 1.12)主配置文件修改

    #nginx1.12 centos6.xuser admin admin;worker_processes 4; error_log /data/services/logs/nginx_error.l ...

  5. MySQL8.0.12版本密码修改策略问题

    查看密码策略(修改临时密码之后才可查看) show variables like 'validate_password%'; 8之前 validate_password_     8之后validat ...

  6. STS IDE 个性化修改

    JDK: Eclipse或MyEclipse文件系统不同步的解决方法 STS汉化: 1.解压STS中的language文件夹 以我的安装目录为例,我的STS的安装在D:盘下.将解压后的“languag ...

  7. 个性化修改Linux登录时的字符界面

    如果采用root账号登录编辑/etc/bashrc内容,那所有其他帐号登录都会提示相同的内容,如果想每个用户进行配置,那就去每个帐号的目录下去配置吧. 这里提供改一个文件所有帐号都能看到的个性显示内容 ...

  8. android studio 汉化 美化 个性化 修改 安卓工作室 2.3.3 最新版

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 先看一下效果. 建议全屏看图,或者新标签看图.

  9. Nginx修改版本信息或隐藏版本号

    一,隐藏版本号.首先说明,这个是某一方面隐藏,不是彻底隐藏.未隐藏之前查看nginx信息: 隐藏方法: 修改nginx.conf配置文件,在http { } 标签里边加入字段: server_toke ...

随机推荐

  1. VMware下对Ubuntu进行扩充磁盘大小

    今天用虚拟机的时候,发现虚拟机快满了,提示磁盘空间小,不得不扩充虚拟机空间.经过百度搜索,终于搞定了,记录如下 平台:VMware(10.0.3)+Ubuntu 14.04(32bit) 1.选择VM ...

  2. compress.go

    The Gorilla Authors. All rights reserved. // Use of this source code is governed by a BSD-style // l ...

  3. Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构(源码可下载)

    Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构(源码可下载) 说明:Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构,我采用以下三种维度来讲解 1.  代码层面. 2.  数 ...

  4. 钉钉机器人zabbix报警

    首先在钉钉群聊里添加一个自定义的机器人 并复制webhook的内容 https://oapi.dingtalk.com/robot/send?access_token=37e23308d1b84eb4 ...

  5. WPF 列表开启虚拟化的方式

    正确开启虚拟化的方式 列表如ListBox,ListView,TreeView,GridView等,开启虚拟化 ScrollViewer设置CanContentScroll=True 直接在模板中,设 ...

  6. vim永久显示行号

    先复制一份vim配置模板到个人目录下,如果目录不对的,自己找到这个文件 cp /usr/share/vim/vim74/vimrc_example.vim ~/.vimrc 注:redhat的路径为  ...

  7. HTTP 和 WebSocket的区别

    有关http和WebSocket 的区别网上有很多的质料. 个人在此仅仅是记录以下自己的学习心得,自己的理解. 1. http协议是用在应用层的协议,他是基于tcp协议的,http协议建立链接也必须要 ...

  8. React Native 之极光推送jpush-react-native 手把手配置

    这是 react native 配置极光推送使用的组件,比较常用https://github.com/jpush/jpush-react-native 先把组件地址贴出来,方便大家使用参考.如果这个大 ...

  9. 加密算法:DigestUtils与java MessageDigest

    1.使用Spring的DigestUtils public class StringUtilTest { static final String TARGET = "changeme&quo ...

  10. Emmagee--APP性能测试工具的基本使用

    一.Emmagee介绍 Emmagee是监控指定被测应用在使用过程中占用机器的CPU.内存.流量资源的性能测试小工具.该工具的优势在于如同windows系统性能监视器类似,它提供的是数据采集的功能,而 ...