Android使用uCrop实现图片裁剪功能

Android使用uCrop实现图片裁剪功能

一、目标

二、下载地址

神马笔记最新版本下载:【神马笔记 版本1.5.0——笔名功能.apk

三、功能设计

笔名中包含2个图片信息——头像和图片签名。

头像比例为1:1,显示为圆形图片。

图片签名比例为2.164:1,显示为长条形矩形图片。

用户可以选择任意的图片,然后通过裁剪图片,转化为目标图片尺寸比例。

四、准备工作

图片裁剪实现方式有2种

  • 调用第三方应用
  • 自己动手写一个

1. 使用com.android.camera.action.CROP调用第三方应用

可以通过设置Intent的Action为com.android.camera.action.CROP来调用第三方应用。

具体的调用方式参考《Android中com.android.camera.action.CROP(图片裁剪)所有属性》。

2. 实现自定义的裁剪功能

虽然是自己动手写一个,当然不是指从零开始实现。我们在GitHub选择一个开源的项目,然后在这基础上进行修改以提高开发效率。

推荐使用uCrop

GitHub项目地址:https://github.com/Yalantis/uCrop

开发者Yalantis的官方介绍:https://yalantis.com/blog/introducing-ucrop-our-own-image-cropping-library-for-android/

五、组合起来

uCrop功能强大并且提供了丰富的配置接口。

通常情况下直接调用uCrop提供的接口即可实现需要的功能。

因为目标设计中要求不显示ActionBar,uCrop并没有提供这样的设置接口,因此采用源代码的形式来使用uCrop。

1. 迁移到androidx

从GitHub clone下来的项目使用的是support包,而主项目工程使用的是androidx包。

要做的第一个便是将uCrop迁移到androidx。

2. 不使用native

从GitHub clone下来的源码,使用的是native的方式。经测试native方式存在2个问题。

  • 增加了安装包体积(0.5M~1.5M,根据打包的so文件而定)
  • 遇到gif裁剪不成功的情况。

对上图尽心裁剪时,发现裁剪失败,原因未明。

3. 定制界面

  1. 隐藏ActionBar
  2. 增加操作提示——移动和缩放
  3. 增加操作按钮——取消、选取
  4. 使用沉浸式全屏模式

得益于uCrop清晰的代码结构,很容易完成以上功能,实现目标功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ucrop_photobox"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/ucrop_color_toolbar"
android:minHeight="?attr/actionBarSize"
android:visibility="gone">

<TextView
android:id="@+id/toolbar_title"
style="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/ucrop_label_edit_photo"
android:textColor="@color/ucrop_color_toolbar_widget"/>

</androidx.appcompat.widget.Toolbar>

<FrameLayout
android:id="@+id/ucrop_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/wrapper_controls"
android:layout_below="@+id/toolbar"
android:background="@color/ucrop_color_crop_background">

<ImageView
android:id="@+id/image_view_logo"
android:layout_width="@dimen/ucrop_default_crop_logo_size"
android:layout_height="@dimen/ucrop_default_crop_logo_size"
android:layout_gravity="center"
app:srcCompat="@drawable/ucrop_vector_ic_crop"
tools:background="@drawable/ucrop_vector_ic_crop"
tools:ignore="ContentDescription,MissingPrefix"/>

<com.yalantis.ucrop.view.UCropView
android:id="@+id/ucrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0"/>

<TextView
android:textAppearance="@style/TextAppearance.AppCompat.Title.Inverse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="?actionBarSize"
android:paddingTop="?listPreferredItemPaddingLeft"
android:text="@string/ucrop_title"/>


<TextView
android:id="@+id/ucrop_btn_cancel"
android:textAppearance="@style/TextAppearance.AppCompat.Title.Inverse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left"
android:padding="?listPreferredItemPaddingLeft"
android:layout_marginBottom="?listPreferredItemPaddingRight"
android:text="@string/ucrop_cancel"
/>


<TextView
android:id="@+id/ucrop_btn_choose"
android:textAppearance="@style/TextAppearance.AppCompat.Title.Inverse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:padding="?listPreferredItemPaddingLeft"
android:layout_marginBottom="?listPreferredItemPaddingRight"
android:text="@string/ucrop_choose"

/>
</FrameLayout>

</RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void setupImmersive() {
View view = getWindow().getDecorView();

{
int flags = View.SYSTEM_UI_FLAG_VISIBLE;

flags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
flags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
flags |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE;

flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;

// flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;

flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;

view.setSystemUiVisibility(flags);

}

{
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(Color.TRANSPARENT);
}
}

void setupButtons() {
findViewById(R.id.ucrop_btn_choose).setOnClickListener(this::onChooseClick);
findViewById(R.id.ucrop_btn_cancel).setOnClickListener(this::onCancelClick);
}

void onCancelClick(View view) {
onBackPressed();
}

void onChooseClick(View view) {
cropAndSaveImage();
}

六、Finally

一些小波折。

导出最终apk文件时,安装包体积从3.6M增加到了5.3M,增加了1.7M。也就是说为了裁剪功能,安装包体积增加了将近一半大小。

1
2
3
4
5
6
7
8
9
10
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
ndk {
abiFilters "armeabi-v7a"
}
}
}

修改gradle配置文件,打包时只包含armeabi-v7a的so文件以减少安装包大小。修改后的安装包大小为4.0M,增加了0.4M,在可以接受范围内。

之后,在手机上测试时,发现有张gif图片无法裁剪。修改代码不使用native方式,裁剪成功。

因为不需要额外的so文件,安装包大小进一步减小。

~独在异乡为异客~每逢佳节倍思亲~