【Android】ImageView实现可选择、裁剪、删除、全屏查看(一)

效果图已放在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?.setOnClickListener(onClickListener)
    }

    fun setImageResource(res : Int){
        imageView?.setImageResource(res)
    }

    fun setImageUri(uri: Uri){
        imageView?.setImageURI(uri)
    }

    fun getImage():RoundedImageView{
        return imageView as RoundedImageView
    }

    fun setRadiu(radiu : Float){
        imageView?.setCornerRadius(radiu)
    }
}

MainActivity 类


class MainActivity : AppCompatActivity() {
    var CODE = 101
    var uri:Uri ?= null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 修改状态栏颜色
        ImmersionBar.with(this).statusBarColor(R.color.colorPrimary).init();
        init()
    }

    private fun init() {
        image.setRadiu(20.0f)
        image.setImageResource(android.R.drawable.ic_menu_add)
        image.setCancelOnClick{
            uri = null
            image.setImageUri(Uri.parse(""))
            image.showCancel(false)
            image.setImageResource(android.R.drawable.ic_menu_add)
        }
        image.setImageOnClick{
            if (uri == null){
                if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
                    ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),1)
                }else{
                    var intent = Intent(Intent.ACTION_GET_CONTENT)
                    intent.type = "image/*"
                    startActivityForResult(intent,CODE)
                }
            }else{
                val with = GlideImageLoader.with(applicationContext)
               /* if (with.getCacheDir() != null){
                    with.clearCache()
                }*/
                val default = Transferee.getDefault(this)
                val bindImageView = TransferConfig.build()
                    .setImageLoader(with)
                    .bindImageView(image.getImage(), uri.toString())
                default.apply(bindImageView).show()
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode.equals(RESULT_OK)){
            when(requestCode){
                CODE ->{
                    if (data!=null){
                        val u = data.data
                        if (u != null){
                            startCrop(u!!,false,false,false)
                        }
                    }
                }
                UCrop.REQUEST_CROP->{
                    uri = UCrop.getOutput(data!!);
                    if (uri != null){
                        image.setImageUri(uri!!)
                        image.showCancel(true)
                    }
                }
            }
        }
    }

    /**
     * 图片裁剪
     */
    fun startCrop(uri:Uri, hideBottomControls : Boolean, showCropGrid :Boolean,showCropFrame : Boolean){
        val options = UCrop.Options()
        val fromFile = Uri.fromFile(File(getExternalCacheDir(), "uCrop.jpg"))
        val of = UCrop.of(uri, fromFile)
        of.withAspectRatio(1f,1f)
        options.setAllowedGestures(UCropActivity.ALL,UCropActivity.NONE,UCropActivity.ALL)
        options.setToolbarTitle("裁剪图片")
        options.setCropGridStrokeWidth(1)
        options.setMaxScaleMultiplier(3f)
        options.setHideBottomControls(hideBottomControls)
        options.setShowCropGrid(showCropGrid)
        options.setShowCropFrame(showCropFrame)
        of.withOptions(options)
        of.start(this,UCrop.REQUEST_CROP)
    }
}

xml代码就不必重复贴啦,懂的都懂,不懂也要装懂

第二篇ImageView自定义我打算写一篇仿朋友圈的图片选择,完成的时候链接会贴在这里,到时候大家还记得要观看喔~

点击下载demo

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宾有为

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值