AndroidImageView实现可选择裁剪删除全屏查看

Posted 宾有为

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AndroidImageView实现可选择裁剪删除全屏查看相关的知识,希望对你有一定的参考价值。

效果图已放在Java版本的文末,功能有选择、裁剪、删除与全屏查看。最近正在学习kotlin,因此干脆做了两个代码版本的demo,后期如果有时间,会在该功能的基础上再加上一些功能,比如朋友圈选择图片的效果

用到的框架:

	// 图片裁剪 UCrop
	implementation 'com.github.yalantis:ucrop:2.2.5'
	// 全屏查看图片 Transferee
    implementation 'com.github.Hitomis.transferee:Transferee:v1.1.0'
    // 图片加载器
    implementation 'com.github.Hitomis.transferee:PicassoImageLoader:1.6.1'
    implementation 'com.squareup.picasso:picasso:2.5.2'
    // 图片加载器
    implementation "com.github.bumptech.glide:glide:4.11.0"
    // 工具库
    implementation "com.blankj:utilcodex:1.26.0"
    // 如果想制作点击多个图片放大可用到这个
	// implementation "com.zhy:base-rvadapter:3.0.3"
    // 沉侵式标题栏
    implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
    // 图片圆角框架
    implementation 'com.makeramen:roundedimageview:2.3.0'

Java版本

1.自定义View

既然是自定义的布局,那第一步就要先把我们的模块先画出来。而我想要的效果,是展示一张图片的同时右上角有一个可以删除图片的按钮(如果想长按图片是删除的话,可以把相关的布局代码省略掉,到时候弄一个长按事件监听(ImageView.setOnLongClickListener())即可)在这里插入图片描述
删除按钮在图片的右上角,我们可以使用 ConstraintLayout 布局使其位于指定位置,至于默认图片用的是android自带的图片,真不是懒得找

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="100dp"
    android:layout_height="100dp">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:src="@android:drawable/ic_menu_add"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <ImageView
        android:id="@+id/cancel"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:src="@android:drawable/ic_menu_close_clear_cancel"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

新建一个类用以绑定自定义的布局,在这个项目中,不需要为自定义的ImageView设置新的属性,便不再定义declare-styleable,xml布局的第一个Layout是ConstraintLayout,也要把继承的对象修改为ConstraintLayout

/**
 * Created by BinJianxin on 2021/5/03
 */
public class CustomImageView  extends ConstraintLayout {
    private ImageView imageView;
    private ImageView cancel;

    public CustomImageView(Context context) {
        this(context, null);
    }

    public CustomImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View inflate = LayoutInflater.from(context).inflate(/**自定义的Layout布局**/, this);
        cancel = inflate.findViewById(R.id.cancel);
        imageView = inflate.findViewById(R.id.imageView);
    }

    public void setCancelOnClick(OnClickListener click){
        cancel.setOnClickListener(click);
    }

    public void setImageOnClick(OnClickListener click){
        imageView.setOnClickListener(click);
    }

    public void setImageURI(Uri uri){
        imageView.setImageURI(uri);
    }

    public void showCancel(boolean b){
        if (b){
            cancel.setVisibility(View.VISIBLE);
        }else{
            cancel.setVisibility(View.INVISIBLE);
        }
    }

    public void setImageResource(int res){
        imageView.setImageResource(res);
    }
}

到这里,自定义的样式已经差不多了,接下来就是使用

在需要用到的地方将其加入

<自定义CustomImageView类的路径.CustomImageView
        android:id="@+id/image"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@android:drawable/ic_menu_close_clear_cancel"/>

在对应的Activity绑定其id,设置点击事件

image.setCancelOnClick(new View.OnClickListener() {
    @Override
      public void onClick(View view) {
            image.showCancel(false);
            image.setImageResource(android.R.drawable.ic_menu_add);
        }
    });
        
int CODE = 100;
image.setImageOnClick(new View.OnClickListener() {
     @Override
        public void onClick(View view) {
           // 申请权限
           if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
            }else {
            	// 跳转到选择图片界面
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("image/*");
                startActivityForResult(intent,CODE);
            }
        }
    });

读写权限被Google列为危险权限,动态申请权限分别需要在Java代码与需要在 AndroidManifest.xml 文件分别进行声明

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

当一张图片被选中后,可以通过 onActivityResult 方法得到这个选中照片的相关信息

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK){
        switch (requestCode){
            case CODE:
                uri = data.getData();
                if (uri != null){
                    image.setImageURI(uri);
                }
                break;
        }
    }
}

这样一来,图片就能正常显示出来了,点击右上角小叉叉便可以将其恢复成最初滴样子啦

在这里插入图片描述

看了上面效果图,oh my god,这图片四四方方,上下又不对称,原本一个正方形的展示框让他以一个长方形的形状出现的我的面前。俗话说:丑的一批。这时候我们就可以在该布局上进行点缀,让它稍微好看点,不好看的东西客户可不会为此买单喔~

2.裁剪

图片裁剪用的是GitHub有着 Start 10.7k的UCrop框架,详情请看:GitHub

使用裁剪框架需要将执行裁剪操作的Activity加入到 AndroidManifest.xml 的 application 标签内

<activity
     android:name="com.yalantis.ucrop.UCropActivity"
     android:screenOrientation="portrait"
     android:theme="@style/Theme.AppCompat.Light.NoActionBar" />

加入为大家准备好的裁剪方法:

/**
 * @param uri                所选图片的路径
 * @param requestCode        UCrop裁剪后在onActivityResult执行的代码
 * @param hideBottomControls 隐藏UCrop下边控制栏
 * @param showCropGrid       设置是否显示裁剪网格
 * @param showCropFrame      设置是否显示裁剪边框
 * @用途 图片裁剪
 */
public void startCrop(Uri uri, int requestCode, boolean hideBottomControls, boolean showCropGrid, boolean showCropFrame) {
    UCrop.Options options = new UCrop.Options();
    //裁剪后图片保存在文件夹中
    Uri destinationUri = Uri.fromFile(new File(MainActivity.this.getExternalCacheDir(), "uCrop.jpg"));
    UCrop uCrop = UCrop.of(uri, destinationUri);//第一个参数是裁剪前的uri,第二个参数是裁剪后的uri
    uCrop.withAspectRatio(1, 1);//设置裁剪框的宽高比例
    //下面参数分别是缩放,旋转,裁剪框的比例
    options.setAllowedGestures(UCropActivity.ALL, UCropActivity.NONE, UCropActivity.ALL);
    options.setToolbarTitle("裁剪图片");//设置标题栏文字
    options.setCropGridStrokeWidth(1);//设置裁剪网格线的宽度(我这网格设置不显示,所以没效果)
    options.setCropFrameStrokeWidth(1);//设置裁剪框的宽度
    options.setMaxScaleMultiplier(3);//设置最大缩放比例
    options.setHideBottomControls(hideBottomControls);//隐藏下边控制栏
    options.setShowCropGrid(showCropGrid);  //设置是否显示裁剪网格
    options.setShowCropFrame(showCropFrame); //设置是否显示裁剪边框(true为方形边框)
//        options.setToolbarWidgetColor(Color.parseColor("#000000"));//标题字的颜色以及按钮颜色
//        options.setDimmedLayerColor(Color.parseColor("#000000"));//设置裁剪外颜色
//        options.setToolbarColor(Color.parseColor("#ffffff")); // 设置标题栏颜色
//        options.setStatusBarColor(Color.parseColor("#ffffff"));//设置状态栏颜色
//        options.setCropGridColor(Color.parseColor("#ffffff"));//设置裁剪网格的颜色
//        options.setCropFrameColor(Color.parseColor("#ffffff"));//设置裁剪框的颜色
    uCrop.withOptions(options);
    uCrop.start(MainActivity.this, requestCode);
}

在得到图片的时候进行调用

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK){
        switch (requestCode){
            case CODE:
                Uri uri1 = data.getData();
                if (uri1 != null){
                    // 选择图片后对图片进行裁剪
                    startCrop(uri1,UCrop.REQUEST_CROP,false,false,true);
                }
                break;
            // 裁剪图片后的操作
            case UCrop.REQUEST_CROP:
                uri = UCrop.getOutput(data);
                image.setImageURI(uri);
                image.showCancel(true);
                break;
        }
    }
}

这个时候就可以得到以下效果
在这里插入图片描述

3.图片全屏

图片点击放大、查看使用的是GitHub Start 达2.6K的 transferee 框架,详情请看:GitHub

当我们点击图片时,对图片进行放大且全屏,就要根据一个 Uri 来进行判断,如果是null,那我们不执行查看图片,而执行选择图片,反之,点击即查看图片

image.setImageOnClick(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (uri == null){
            if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
            }else {
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("image/*");
                startActivityForResult(intent,CODE);
            }
        }else {
            transferee = Transferee.getDefault(MainActivity.this);
            TransferConfig recyclerTransConfig = TransferConfig.build()
                    .setImageLoader(GlideImageLoader.with(getApplicationContext()))
                    .bindImageView(image.getImageView(),uri.toString());
            transferee.apply(recyclerTransConfig).show();
        }
    }
});

使用 ImageLoader 需要注意的一点就是在读取第一张图片的时候会有缓存,如果不清理,放大后的照片除了第一张和你预想的一样,基本不会相同,解决办法就是设置 ImageLoader 不缓存或者在执行 ImageLoader 相关操作前先清除一遍缓存

GlideImageLoader.with(getApplicationContext()).clearCache();

在这里插入图片描述
自此,点击图片放大功能完成

这有一个问题还是得说一下哈

点击放大后主Activity顶部状态栏就变样了,操作的时候标题栏会陷入标签栏或标签栏颜色被改动,
针对类似问题,只需要两步
(1)在Activity的onCreate方法加上

ImmersionBar.with(this).statusBarColor(R.color.colorPrimary).init();

(2)在Activity的xml第一层layout标签内加上

android:fitsSystemWindows="true"

显示是显示出来了,但是看着咋还那么丑呢???

在这里插入图片描述
图片展示的不好看,不能说是图片不好看所导致,即使图片是世界级摄影师所拍摄,展示的不好,可能也真就不好看了。

那么,如何弄得好看点呢?

我们可以对展示图片的ImageView进行一点修饰,比如说加点半径?就像下面这样

原图:
在这里插入图片描述
加了半径后:
在这里插入图片描述
哈哈,毫无违和感

好了,接下来又到用人家框架的时刻了

对图片的操作我们可以使用 roundedimageview 框架,

自定义的View,我们也只需要将对应的 ImageView 改为 RoundedImageView

 private com.makeramen.roundedimageview.RoundedImageView imageView;
<com.makeramen.roundedimageview.RoundedImageView
        android:id="@+id/imageView"
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:src="@android:drawable/ic_menu_add"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

再加一个方法用以设置半径

public void setRadiu(int radiu){
  imageView.setCornerRadius(radiu);
}

调用之后就变成这样啦
在这里插入图片描述
以及,这个样子

在这里插入图片描述
到此便完成了所有工作

Kotlin版

上面的Java版本已经把我能说的话几乎说完了,kotlin版本我就不在多说

CustomImageView 类

class CustomImageView : ConstraintLayout {
    var cancel: ImageView? = null;
    var imageView: RoundedImageView? = null

//    constructor(context: Context) : this(context,attribSet)

    constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet,0)

    constructor(context: Context, attributeSet: AttributeSet, defValue: Int) : super(
        context,
        attributeSet,
        defValue
    ) {
        val inflate =
            LayoutInflater.from(context).inflate(R.layout.activity_custom_image_view, this)
        cancel = inflate.findViewById<View>(R.id.cancel) as ImageView?
        imageView = inflate.findViewById<View>(R.id.imageView) as RoundedImageView?
        val obtainStyledAttributes =
            context.obtainStyledAttributes(attributeSet, R.styleable.CustomImageView)
        val indexCount = obtainStyledAttributes.indexCount
        for (index in 0..indexCount) {
            val index1 = obtainStyledAttributes.getIndex(index)
            when (index1) {
                R.styleable.CustomImageView_showCancel -> {
                    val boolean = obtainStyledAttributes.getBoolean(
                        R.styleable.CustomImageView_showCancel,
                        false
                    )
                    if (boolean) {
                        cancel!!.visibility = View.VISIBLE
                    }else{
                        cancel!!.visibility = View.INVISIBLE
                    }
                }
            }
        }
    }

    fun showCancel(boolean: Boolean){
        if (boolean){
            cancel?.visibility = View.VISIBLE
        }else{
            cancel?.visibility = View.INVISIBLE
        }
    }

    fun setImageOnClick(onClickListener: OnClickListener){
        imageView?.setOnClickListener(onClickListener)
    }

    fun setCancelOnClick(onClickListener: OnClickListener){
        cancel?以上是关于AndroidImageView实现可选择裁剪删除全屏查看的主要内容,如果未能解决你的问题,请参考以下文章

Android ImageView 将较小的图像缩放到具有灵活高度的宽度,而不会裁剪或扭曲

如何像imageview一样裁剪位图中心? [复制]

网页保存PDF 保留文字 删除页面 裁剪页面 删除不需要的内容

React 实现图片裁剪/上传/预览/下载/删除(主要是图片裁剪)

如何使用Swift选择图像的一部分,裁剪并保存?

在 iOS/swift 中上传图像时如何删除裁剪选项