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. 定制界面
- 隐藏ActionBar
- 增加操作提示——移动和缩放
- 增加操作按钮——取消、选取
- 使用沉浸式全屏模式
得益于uCrop清晰的代码结构,很容易完成以上功能,实现目标功能。
<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>
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。也就是说为了裁剪功能,安装包体积增加了将近一半大小。
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文件,安装包大小进一步减小。
~独在异乡为异客~每逢佳节倍思亲~