最近开发项目遇到一个功能,类似微信发朋友圈功能,查找资料并没有类似的效果,于是自己参照微信相册动手撸了一个,三天的开发时间比较仓促,有可能会有BUG,希望各撸友们批评指正(QQ:330093887或者QQ邮箱)。在开发此功能的过程中踩了不少坑,也得到不少的经验,特此在这里写博客记录一下。
先上效果图:
1.特点
· 使用“调整图像”将您的相片调整至尽善尽美
· 使用“选择性调整”对相片的特定对象或区域加以美化
·提供各种有趣且极具新意的滤镜,例如“怀旧”、“戏剧”、“复古”、“杂质”以及“移轴”
· 提供许多优质相框,可为美化相片起到画龙点睛的效果
2.功能
(1) 基本调整功能
·选择性调整-图片编辑库使用c++代码,在数秒钟内对相片中的特定区域做出精准的选择和增强。
·调整图像-使用“环境”来制造特别适合色彩和纹理的深度和自然饱和度。调整“白平衡”、“饱和度”和“对比度”等等。
·拉直旋转-使用简单的手势控制旋转90°及/或拉直相片。
·裁切-使用标准的纵横比或自由裁切,轻松裁切图像以去除相片上分散注意力的部分。
(2) 创造性增强
·黑白-此滤镜的灵感源于暗室,可为相片创造经典的黑白外观。
·复古胶片-使任何相片看起来像50年代、60年代或70年代的古老彩色胶片照。
·戏剧-透过为您的相片量身定制的效果来增添风格,从细微的纹理到异想天开的艺术效果都信手拈来。
·杂质-使您的相片呈现完全独特的时尚昏暗外观。
·移轴镜摄影-建立窄聚焦带,用以模拟微缩场景中常见的景深外观。
·中心焦点-透过模糊和调节周围背景的亮度来突出相片的拍摄主体。
·有机相框-为相片增加风格化边框,以达到画龙点睛的完美效果。
3.图片编辑库
{"滤镜","图像变形","剪切","涂鸦","边框","添加文字","添加水印","马赛克","增强","旋转"}等功能底层使用c++完成,达到快速处理图片不卡顿。
4.注意事项:
1,权限问题2,对图片操作过程中处理Bitmap需要谨慎3,数据保存以及异步操作4,更新UI……
5.代码片段:
(1)启动相册
public void selectPhoto() {
Intent photoIntent = new Intent(this, ImageGridActivity.class);
photoIntent.putExtra(ImagePicker.MAX_PHOTO_NUMBER, 4);
startActivityForResult(photoIntent, CommonUtils.REQUEST_CODE_ALBUN);
}
(2)得到图片路径
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CommonUtils.REQUEST_CODE_ALBUN) {
if (data != null) {
try {
final ArrayList<ImageItem> images = (ArrayList<ImageItem>) data.getSerializableExtra(ImagePicker.EXTRA_RESULT_ITEMS);
int size = images.size();
for (int i = 0; i < size; i++) {
mThumbIds.add(images.get(i).path);
}
imageAdapter.notifyDataSetChanged();
} catch (Exception e) {
Toast.makeText(this, "请您打开读取存储文件权限", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
}
(3)调用各个功能CompileBitmapActivity
package injection.sw.com.mycocularlater.imagepicker.ui;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import java.io.File;
import cn.jarlen.photoedit.activity.AddTextActivity;
import cn.jarlen.photoedit.activity.AddWatermarkActivity;
import cn.jarlen.photoedit.activity.DrawBaseActivity;
import cn.jarlen.photoedit.activity.EnhanceActivity;
import cn.jarlen.photoedit.activity.ImageFilterActivity;
import cn.jarlen.photoedit.activity.ImagePasteActivity;
import cn.jarlen.photoedit.activity.MosaicActivity;
import cn.jarlen.photoedit.activity.PhotoFrameActivity;
import cn.jarlen.photoedit.activity.RevolveActivity;
import cn.jarlen.photoedit.activity.WarpActivity;
import injection.sw.com.mycocularlater.CommonUtils;
import injection.sw.com.mycocularlater.R;
import injection.sw.com.mycocularlater.imagepicker.Utils;
import injection.sw.com.mycocularlater.imagepicker.adapter.CompileBitmapAdapter;
import static injection.sw.com.mycocularlater.imagepicker.ui.ImagePreviewActivity.DATA_OF_IMAGE_PATH;
/**
* Created by zhouqiong on 2017/6/6.
*/
public class CompileBitmapActivity extends TranslucentActivity implements View.OnClickListener {
private RecyclerView recyclerView;
private CompileBitmapAdapter adapter;
private Class<?> intentClass;
private int intentType = 0;
private String cameraPath = null;
private ImageView pictureShow, backImageView;
private TextView saveTextView;
private String[] str;
private FrameLayout bannerFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_compile_bitmap);
initView();
initData();
setOnClickListener();
}
private void initView() {
setArgument();
recyclerView = (RecyclerView) findViewById(R.id.bottom_gallery);
pictureShow = (ImageView) findViewById(R.id.pictureShow);
backImageView = (ImageView) findViewById(R.id.back_btn);
saveTextView = (TextView) findViewById(R.id.save_btn);
bannerFragment = (FrameLayout) findViewById(R.id.banner);
Glide.with(this).load(cameraPath).into(pictureShow);
}
private void initData() {
//设置布局管理器
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
str = new String[]{"滤镜", "图像变形", "剪切", "涂鸦", "边框", "添加文字", "添加水印", "马赛克", "增强", "旋转"};
recyclerView.setLayoutManager(linearLayoutManager);
//设置适配器
adapter = new CompileBitmapAdapter(this, str);
recyclerView.setAdapter(adapter);
}
private void setOnClickListener() {
backImageView.setOnClickListener(this);
saveTextView.setOnClickListener(this);
pictureShow.setOnClickListener(this);
adapter.setOnItemClickListener(new CompileBitmapAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
switch (position) {
case 0:
intentClass = ImageFilterActivity.class;
intentType = CommonUtils.PHOTO_FILTER_WITH_DATA;
break;
case 1:
intentClass = WarpActivity.class;
intentType = CommonUtils.PHOTO_WARP_WITH_DATA;
break;
case 2:
intentClass = ImagePasteActivity.class;
intentType = CommonUtils.PHOTO_CROP_WITH_DATA;
break;
case 3:
intentClass = DrawBaseActivity.class;
intentType = CommonUtils.PHOTO_DRAW_WITH_DATA;
break;
case 4:
intentClass = PhotoFrameActivity.class;
intentType = CommonUtils.PHOTO_FRAME_WITH_DATA;
break;
case 5:
intentClass = AddTextActivity.class;
intentType = CommonUtils.PHOTO_ADD_TEXT_DATA;
break;
case 6:
intentClass = AddWatermarkActivity.class;
intentType = CommonUtils.PHOTO_ADD_WATERMARK_DATA;
break;
case 7:
intentClass = MosaicActivity.class;
intentType = CommonUtils.PHOTO_MOSAIC_WITH_DATA;
break;
case 8:
intentClass = EnhanceActivity.class;
intentType = CommonUtils.PHOTO_ENHANCE_WITH_DATA;
break;
case 9:
intentClass = RevolveActivity.class;
intentType = CommonUtils.PHOTO_REVOLVE_WITH_DATA;
break;
default:
intentClass = null;
intentType = 0;
break;
}
if (cameraPath == null) {
Toast.makeText(CompileBitmapActivity.this, "请选择图片",
Toast.LENGTH_SHORT).show();
return;
}
if (intentClass == null) {
Toast.makeText(CompileBitmapActivity.this, "请图片操作类型",
Toast.LENGTH_SHORT).show();
return;
}
// 将图片路径photoPath传到所要调试的模块
Intent photoFrameIntent = new Intent(CompileBitmapActivity.this, intentClass);
photoFrameIntent.putExtra("camera_path", cameraPath);
CompileBitmapActivity.this.startActivityForResult(photoFrameIntent, intentType);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case CommonUtils.PHOTO_FRAME_WITH_DATA:
case CommonUtils.PHOTO_MOSAIC_WITH_DATA:
case CommonUtils.PHOTO_DRAW_WITH_DATA:
case CommonUtils.PHOTO_CROP_WITH_DATA:
case CommonUtils.PHOTO_FILTER_WITH_DATA:
case CommonUtils.PHOTO_ENHANCE_WITH_DATA:
case CommonUtils.PHOTO_REVOLVE_WITH_DATA:
case CommonUtils.PHOTO_WARP_WITH_DATA:
case CommonUtils.PHOTO_ADD_WATERMARK_DATA:
case CommonUtils.PHOTO_ADD_TEXT_DATA:
cameraPath = data.getStringExtra("camera_path");
Glide.with(this)
.load(Uri.fromFile(new File(cameraPath)))
.centerCrop()
.placeholder(R.mipmap.default_image)
.into(pictureShow);
break;
}
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.back_btn) {
finish();
} else if (id == R.id.save_btn) {
File file = Utils.saveBitmap(this, BitmapFactory.decodeFile(cameraPath), "" + SystemClock.currentThreadTimeMillis() + ".jpg");
Intent mIntent = new Intent();
mIntent.putExtra(DATA_OF_IMAGE_PATH, file.getAbsolutePath());
this.setResult(RESULT_OK, mIntent);
finish();
} else if (id == R.id.pictureShow) {
//点击图片,其他view隐藏
onImageSingleTap();
}
}
public void onImageSingleTap() {
if (bannerFragment.getVisibility() == View.VISIBLE) {
showToolBar(AnimationUtils.loadAnimation(this, R.anim.top_out), AnimationUtils.loadAnimation(this, R.anim.fade_out), View.GONE);
} else {
showToolBar(AnimationUtils.loadAnimation(this, R.anim.top_in), AnimationUtils.loadAnimation(this, R.anim.fade_in), View.VISIBLE);
}
}
private void showToolBar(Animation animation, Animation animation2, int visible) {
bannerFragment.setAnimation(animation);
bannerFragment.setVisibility(visible);
bannerFragment.setAnimation(animation);
recyclerView.setAnimation(animation2);
bannerFragment.setVisibility(visible);
recyclerView.setVisibility(visible);
}
private void setArgument() {
Bundle bundle = getIntent().getExtras();
cameraPath = bundle.getString("bitmap");
}
}
(4)判断某个点是否在多边形区域内
package cn.jarlen.photoedit.operate;
import android.graphics.PointF;
import android.util.Log;
import java.util.List;
/**
* 判断某个点是否在多边形区域内
* Created by zhouqiong on 2017/6/5.
*/
public class Lasso {
private float[] mPolyX, mPolyY;
private int mPolySize;
/**
* 构造方法
*
* @param
*/
public Lasso(List<PointF> pointFs) {
this.mPolySize = pointFs.size();
this.mPolyX = new float[this.mPolySize];
this.mPolyY = new float[this.mPolySize];
for (int i = 0; i < this.mPolySize; i++) {
this.mPolyX[i] = pointFs.get(i).x;
this.mPolyY[i] = pointFs.get(i).y;
}
Log.d("lasso", "lasso size:" + mPolySize);
}
/**
* 判断多边形是否包含点
*
* @param x X坐标
* @param y Y坐标
* @return true
*/
public boolean contains(float x, float y) {
boolean result = false;
for (int i = 0, j = mPolySize - 1; i < mPolySize; j = i++) {
if ((mPolyY[i] < y && mPolyY[j] >= y)
|| (mPolyY[j] < y && mPolyY[i] >= y)) {
if (mPolyX[i] + (y - mPolyY[i]) / (mPolyY[j] - mPolyY[i])
* (mPolyX[j] - mPolyX[i]) < x) {
result = !result;
}
}
}
return result;
}
}
(5)马赛克效果代码片段
public static Bitmap getMosaic(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int radius = 10;
Bitmap mosaicBitmap = Bitmap.createBitmap(width, height,
Config.ARGB_8888);
Canvas canvas = new Canvas(mosaicBitmap);
int horCount = (int) Math.ceil(width / (float) radius);
int verCount = (int) Math.ceil(height / (float) radius);
Paint paint = new Paint();
paint.setAntiAlias(true);
for (int horIndex = 0; horIndex < horCount; ++horIndex) {
for (int verIndex = 0; verIndex < verCount; ++verIndex) {
int l = radius * horIndex;
int t = radius * verIndex;
int r = l + radius;
if (r > width) {
r = width;
}
int b = t + radius;
if (b > height) {
b = height;
}
int color = bitmap.getPixel(l, t);
Rect rect = new Rect(l, t, r, b);
paint.setColor(color);
canvas.drawRect(rect, paint);
}
}
canvas.save();
return mosaicBitmap;
}
特别感谢
特别感谢大神@jarlen,使用了@jarlen底层C++对图片处理,大大减少了代码量,类似与美图秀秀。但是具体得不到大神的项目地址和联系方式@jarlen的名字也只是在朋友给我的demo中看到的,这里表达我对大神的崇高敬意。
源码
源码下载地址github