准备工作
- github 找一个图片编辑器 地址,这个图就是对单个本地图片进行编辑,加载大图有问题,页面传递bitmap有问题
- 目前已有功能:编辑一张本地图片
- 想要效果:传入图片下载list,第一个页面要有图片切换效果,点击按钮对图片进行编辑,图片保存
优化
- 首页直接glide加载图片,viewpager左右切换,对bitmap压缩防止过大导致页面崩溃
- 点击进入编辑页的时候,对对应的图片进行下载,压缩,之后再编辑,跳转页面的时候传下载压缩后的路径,而不是传递bitmap
- 因为编辑的时候是对本地图编辑,会有图片下载,保证本地相册不会过多下载图片
代码
首页传入图片集合,进行展示,对图片编辑需要申请权限
package me.minetsh.imaging
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentPagerAdapter
import android.support.v4.view.PagerAdapter
import android.support.v4.view.ViewPager
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.view.ViewGroup
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ToastUtils
import com.htjy.app.common_util.ext.clickWithTrigger
import com.htjy.app.common_util.util.LuBanUtils
import com.tbruyelle.rxpermissions2.RxPermissions
import kotlinx.android.synthetic.main.image_activity_img_handle.*
import kotlinx.android.synthetic.main.image_edit_ready.*
const val EDIT_IMG_REQUESTCODE = 1000
class IMGHandleActivity : AppCompatActivity() {
private lateinit var dataList: ArrayList<String>
private var type: Int = 0
private val testUrl = arrayListOf("https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAAL-USZ-F0(0)011.png",
"https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAAM-USZ-F0(0)012.png"
, "https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAAA-USZ-F0(0)001.png",
"https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAAB-USZ-F0(0)002.png",
"https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAEC-USZ-F0(0)099.png",
"https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MACD-USZ-F0(0)052.png",
"https://animal-crossing.com/amiibo/assets/img/cards/NVL_C_MAGY_USZlow_167_R_ad.png",
"https://animal-crossing.com/amiibo/assets/img/cards/NVL_C_MAHB_USZlow_170_R_ad.png",
"https://animal-crossing.com/amiibo/assets/img/cards/NVL_C_MALZ_USZlow_264_R_ad.png")
private var test = arrayListOf(
"/storage/emulated/0/DCIM/Screenshots/Screenshot_2019-10-30-10-09-35-727_com.htjy.gardener.component_remark_teacher.png", "/storage/emulated/0/BesttimerCamera2/picture_1574761262840.jpg")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.image_activity_img_handle)
initData()
}
override fun onDestroy() {
super.onDestroy()
}
companion object {
fun launch(from: Activity, data: ArrayList<String>, type: Int) {
val intent = Intent(from, IMGHandleActivity::class.java)
intent.putExtra("type", type)
intent.putStringArrayListExtra("data", data)
from.startActivityForResult(intent, EDIT_IMG_REQUESTCODE)
}
}
private fun initData() {
dataList = intent.getStringArrayListExtra("data")
type = intent.getIntExtra("type", 0)
RxPermissions(IMGHandleActivity@ this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
.subscribe {
if (it) {
initView()
} else {
finish()
}
}
}
private var currentPage = 0
private val fragmentList = arrayListOf<ItemFragment>()
private fun initView() {
for (value in dataList) {
fragmentList.add(ItemFragment.launch(value))
}
val adapter = MyItemFragmentPagerAdapter(supportFragmentManager, fragmentList)
vp_images.adapter = adapter
vp_images.offscreenPageLimit = dataList.size
tv_count.text = "${currentPage + 1}/${dataList.size}"
vp_images.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
override fun onPageSelected(position: Int) {
currentPage = position
tv_count.text = "${currentPage + 1}/${dataList.size}"
}
})
btn_dianping.clickWithTrigger {
when (type) {
0 -> {
LuBanUtils.loadPicFromNet(dataList[currentPage], IMGHandleActivity@ this) { isSuccess, path ->
if (isSuccess) {
IMGEditActivity.launch(path, this@IMGHandleActivity)
}
}
}
1 -> {
LuBanUtils.loadPicFromLocal(dataList[currentPage], IMGHandleActivity@ this) { isSuccess, path ->
if (isSuccess) {
IMGEditActivity.launch(path, this@IMGHandleActivity)
}
}
}
}
}
btn_save.clickWithTrigger {
when (type) {
0 -> {
LuBanUtils.downloadPicFromNet(dataList[currentPage], IMGHandleActivity@ this) { isSuccess, path ->
if (isSuccess) {
sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://$path")))
ToastUtils.showShort("图片保存成功")
} else {
ToastUtils.showShort("图片保存失败")
}
}
}
}
}
}
private inner class MyItemFragmentPagerAdapter(fm: FragmentManager, var data: List<Fragment>) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return data[position]
}
override fun getCount(): Int {
return data.size
}
}
private inner class MyImgViewPagerAdapter(var context: Context, var data: List<View>) : PagerAdapter() {
override fun isViewFromObject(view: View, obj: Any): Boolean {
return view == obj
}
override fun getCount(): Int {
return data.size
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(data[position])
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
container.addView(data[position])
return data[position]
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == EDIT_IMG_REQUESTCODE && resultCode == Activity.RESULT_OK) {
data?.let {
val savePath = it.getStringExtra(IMGEditActivity.EXTRA_IMAGE_SAVE_PATH)
LogUtils.d("保存图片的绝对路径==上级页面收到>>>$savePath")
setResult(EDIT_IMG_REQUESTCODE, it)
finish()
}
}
}
}
图片编辑页的修改
package me.minetsh.imaging;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import com.blankj.utilcode.util.LogUtils;
import me.minetsh.imaging.core.IMGMode;
import me.minetsh.imaging.core.IMGText;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.UUID;
import static me.minetsh.imaging.IMGHandleActivityKt.EDIT_IMG_REQUESTCODE;
public class IMGEditActivity extends IMGEditBaseActivity {
private static final int MAX_WIDTH = 1024;
private static final int MAX_HEIGHT = 1024;
public static final String EXTRA_IMAGE_URI = "IMAGE_URI";
public static final String EXTRA_IMAGE_SAVE_PATH = "IMAGE_SAVE_PATH";
public static void launch(String url, Activity fromContext) {
Intent intent = new Intent(fromContext, IMGEditActivity.class);
intent.putExtra("bitmap", url);
intent.putExtra(EXTRA_IMAGE_SAVE_PATH, UUID.randomUUID().toString() + ".jpg");
fromContext.startActivityForResult(intent, EDIT_IMG_REQUESTCODE);
}
@Override
public void onCreated() {
}
private File currentFile;
@Override
public void onBackPressed() {
super.onBackPressed();
if (currentFile != null && currentFile.exists()) {
currentFile.delete();
}
}
@Override
public Bitmap getBitmap() {
String url = getIntent().getStringExtra("bitmap");
currentFile = new File(url);
Bitmap bitmap = BitmapFactory.decodeFile(currentFile.getPath());
return bitmap;
}
@Override
public void onText(IMGText text) {
mImgView.addStickerText(text);
}
@Override
public void onModeClick(IMGMode mode) {
IMGMode cm = mImgView.getMode();
if (cm == mode) {
mode = IMGMode.NONE;
}
mImgView.setMode(mode);
updateModeUI();
if (mode == IMGMode.CLIP) {
setOpDisplay(OP_CLIP);
}
}
@Override
public void onUndoClick() {
IMGMode mode = mImgView.getMode();
if (mode == IMGMode.DOODLE) {
mImgView.undoDoodle();
} else if (mode == IMGMode.MOSAIC) {
mImgView.undoMosaic();
}
}
@Override
public void onCancelClick() {
onBackPressed();
}
@Override
public void onDoneClick() {
File file = new File(getCacheDir(), getIntent().getStringExtra(EXTRA_IMAGE_SAVE_PATH));
String path = file.getAbsolutePath();
if (!TextUtils.isEmpty(path)) {
Bitmap bitmap = mImgView.saveBitmap();
if (bitmap != null) {
FileOutputStream fout = null;
try {
fout = new FileOutputStream(path);
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fout);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (fout != null) {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Intent intent = new Intent();
intent.putExtra(EXTRA_IMAGE_SAVE_PATH, path);
LogUtils.d("保存图片的绝对路径>>>" + path);
setResult(RESULT_OK, intent);
finish();
return;
}
}
setResult(RESULT_CANCELED);
finish();
}
@Override
public void onCancelClipClick() {
mImgView.cancelClip();
setOpDisplay(mImgView.getMode() == IMGMode.CLIP ? OP_CLIP : OP_NORMAL);
}
@Override
public void onDoneClipClick() {
mImgView.doClip();
setOpDisplay(mImgView.getMode() == IMGMode.CLIP ? OP_CLIP : OP_NORMAL);
}
@Override
public void onResetClipClick() {
mImgView.resetClip();
}
@Override
public void onRotateClipClick() {
mImgView.doRotate();
}
@Override
public void onColorChanged(int checkedColor) {
mImgView.setPenColor(checkedColor);
}
}
图片下载压缩,过多文件删除的工具类
package com.htjy.app.common_util.util
import android.content.Context
import android.os.Environment
import com.blankj.utilcode.util.LogUtils
import com.htjy.baselibrary.widget.imageloader.ImageLoaderUtil
import com.htjy.baselibrary.widget.imageloader.listener.ImageDownloadListener
import io.reactivex.Flowable
import io.reactivex.schedulers.Schedulers
import top.zibin.luban.Luban
import top.zibin.luban.OnCompressListener
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
object LuBanUtils {
private fun copy(source: File?, callBack: (pathFile: File) -> Unit) {
val pictureFolder: File = Environment.getExternalStorageDirectory()
val appDir = File(pictureFolder, "my_edit_pic")
if (!appDir.exists()) {
appDir.mkdirs()
}
val fileName = System.currentTimeMillis().toString() + ".jpg"
val destFile = File(appDir, fileName)
var fileInputStream: FileInputStream? = null
var fileOutputStream: FileOutputStream? = null
try {
fileInputStream = FileInputStream(source)
fileOutputStream = FileOutputStream(destFile)
val buffer = ByteArray(1024)
while (fileInputStream.read(buffer) > 0) {
fileOutputStream.write(buffer)
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
fileInputStream?.close()
fileOutputStream?.close()
} catch (e: IOException) {
e.printStackTrace()
}
if (source != null && source.exists()) {
source.delete()
}
callBack.invoke(destFile)
}
}
private fun deleteFile(file: File?) {
if (file != null && file.exists()) {
file.delete()
}
}
fun loadPicFromNet(uri: String, context: Context, callBack: (isSuccess: Boolean, path: String?) -> Unit) {
Flowable.just(uri).subscribeOn(Schedulers.io()).map {
ImageLoaderUtil.getInstance().downloadOnly(context, uri, object : ImageDownloadListener {
override fun onSuccess(path: String?) {
LogUtils.d("鲁班>>>Gilde下载图片成功==${uri}")
LogUtils.d("鲁班>>>Gildecopy图片")
path?.let {
val file = File(it)
if (file != null && file.exists()) {
copy(file) { targetFile ->
LogUtils.d("鲁班==copy成功===${targetFile.absolutePath}")
LogUtils.d("鲁班>>>压缩图片")
Luban.with(context)
.load(targetFile)
.setTargetDir(targetFile.parent)
.ignoreBy(10)
.setCompressListener(object : OnCompressListener {
override fun onSuccess(file: File?) {
LogUtils.d("鲁班>>>压缩成功==${uri}===${file?.absolutePath}")
deleteFile(targetFile)
callBack.invoke(true, file?.absolutePath)
}
override fun onError(e: Throwable?) {
LogUtils.d("鲁班>>>压缩失败==${uri}===${e?.message}")
callBack.invoke(false, null)
}
override fun onStart() {
LogUtils.d("鲁班>>>开始压缩==${uri}===")
}
})
.launch()
}
}
}
}
override fun onFail() {
LogUtils.d("鲁班>>>Gilde下载图片失败==${uri}")
callBack.invoke(false, null)
}
})
}.subscribe()
}
fun loadPicFromLocal(path: String, context: Context, callBack: (isSuccess: Boolean, path: String?) -> Unit) {
Flowable.just(path).subscribeOn(Schedulers.io()).map { uri ->
val file = File(uri)
LogUtils.d("鲁班>>>开始压缩==${uri}")
if (file != null && file.exists()) {
Luban.with(context)
.load(file)
.setCompressListener(object : OnCompressListener {
override fun onSuccess(file: File?) {
if (file != null && file.exists()) {
LogUtils.d("鲁班>>>压缩成功==${uri}===${file?.absolutePath}")
callBack.invoke(true, file?.absolutePath)
}
}
override fun onError(e: Throwable?) {
LogUtils.d("鲁班>>>压缩失败==${uri}===${e?.message}")
callBack.invoke(false, null)
}
override fun onStart() {
LogUtils.d("鲁班>>>开始压缩==${uri}===")
}
})
.launch()
}
}.subscribe()
}
fun downloadPicFromNet(uri: String, context: Context, callBack: (isSuccess: Boolean, path: String?) -> Unit) {
Flowable.just(uri).subscribeOn(Schedulers.io()).map {
ImageLoaderUtil.getInstance().downloadOnly(context, uri, object : ImageDownloadListener {
override fun onSuccess(path: String?) {
LogUtils.d("鲁班>>>Gilde下载图片成功==${uri}")
LogUtils.d("鲁班>>>Gildecopy图片")
path?.let {
val file = File(it)
if (file != null && file.exists()) {
copy(file) { targetFile ->
LogUtils.d("鲁班==下载成功===${targetFile.absolutePath}")
callBack.invoke(true, targetFile.absolutePath)
}
}
}
}
override fun onFail() {
LogUtils.d("鲁班>>>Gilde下载图片失败==${uri}")
callBack.invoke(false, null)
}
})
}.subscribe()
}
}
分享一个下载图片,并刷新手机相册的工具类
二进制bytearr转bitmap
fun getBitmapFromByte(temp: ByteArray): Bitmap? {
return if (temp != null) {
BitmapFactory.decodeByteArray(temp, 0, temp.size)
} else {
null
}
}
下载图片保存,刷新相册
public static void saveImageToGallery(Context context, Bitmap bmp, String picDirName) {
File appDir = new File(getFilePath(context,picDirName));
if (!appDir.exists()) {
appDir.mkdir();
}
LogUtils.d("下载啊目录>>"+appDir.getAbsolutePath());
String fileName = System.currentTimeMillis() + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
ToastUtils.showShort("保存失败");
e.printStackTrace();
} catch (IOException e) {
ToastUtils.showShort("保存失败");
e.printStackTrace();
}
try {
MediaStore.Images.Media.insertImage(context.getContentResolver(),
file.getAbsolutePath(), fileName, null);
ToastUtils.showShort("保存成功");
} catch (FileNotFoundException e) {
ToastUtils.showShort("保存失败");
e.printStackTrace();
}
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.fromFile(file)));
}
public static String getFilePath(Context context, String dir) {
String directoryPath = "";
if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
directoryPath = context.getExternalFilesDir(dir).getAbsolutePath();
} else {
directoryPath = context.getFilesDir() + File.separator + dir;
}
File file = new File(directoryPath);
if (!file.exists()) {
file.mkdirs();
}
LogUtil.i("filePath====>" + directoryPath);
return directoryPath;
}