在Android中裁剪图像

39
我想裁剪图片,我找到了一些非常有用的方法,但是似乎缺少使未选择区域变暗的功能,所以我想知道是否有人知道如何实现?或者能否引导我走向正确的方向?我找到的在线教程显示它会使选定区域变暗,但是当我使用它时,它不起作用。请帮帮我,非常感谢,对我的英语表达不好表示抱歉。
链接到我使用的教程: 裁剪图像教程1 裁剪图像教程2 我希望它能像这样:

I want it be something like this

editButton.setOnClickListener(new Button.OnClickListener(){

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            Intent goEdit;
            goEdit = new Intent(PreviewActivity.this, CropImage.class);
            goEdit.putExtra("image-path", path);
            goEdit.putExtra("scale", true);
            goEdit.putExtra("fileName", nameFromPath);
            //finish();
            checkEdit = true;
            startActivityForResult(goEdit,0);

        }
});

编辑 我使用这个按钮监听器来调用CropImage文件,通过调用CropImage活动类。这是一个自定义意图而不是Android内部的裁剪功能,但我认为它是其副本,因此支持所有版本,但当我调用它时,所选区域没有变亮,我不知道问题在哪里,有人可以指导我吗?谢谢 这是我正在使用的库drioid4you crop image


1
请问您能否发布您尝试过的代码?并且,描述一下当您尝试运行它时会发生什么?您是否收到任何错误信息? - Jodes
我的意思是我没有收到任何错误消息,我只是不知道如何使所选区域变亮并使未选区域变暗以显示差异。 - I Yeu C
请查看这个问题,我在那里提供了一个替代库的建议。 - hcpl
请正确声明如何使用手动裁剪视图。 - Ramani Hitesh
请查看这个 Stack Overflow 的答案: https://stackoverflow.com/questions/38367876/android-cropped-image-quality-issue/50528138#50528138 - Hitesh Kanjani
5个回答

53

你能使用默认的Android裁剪功能吗?

这是我的代码

private void performCrop(Uri picUri) {
    try {
        Intent cropIntent = new Intent("com.android.camera.action.CROP");
        // indicate image type and Uri
        cropIntent.setDataAndType(picUri, "image/*");
        // set crop properties here
        cropIntent.putExtra("crop", true);
        // indicate aspect of desired crop
        cropIntent.putExtra("aspectX", 1);
        cropIntent.putExtra("aspectY", 1);
        // indicate output X and Y
        cropIntent.putExtra("outputX", 128);
        cropIntent.putExtra("outputY", 128);
        // retrieve data on return
        cropIntent.putExtra("return-data", true);
        // start the activity - we handle returning in onActivityResult
        startActivityForResult(cropIntent, PIC_CROP);
    }
    // respond to users whose devices do not support the crop action
    catch (ActivityNotFoundException anfe) {
        // display an error message
        String errorMessage = "Whoops - your device doesn't support the crop action!";
        Toast toast = Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT);
        toast.show();
    }
}

声明:
final int PIC_CROP = 1;

我可以帮您进行翻译。以下是翻译的结果:

在顶部。

在onActivityResult方法中,编写以下代码:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == PIC_CROP) {
        if (data != null) {
            // get the returned data
            Bundle extras = data.getExtras();
            // get the cropped bitmap
            Bitmap selectedBitmap = extras.getParcelable("data");

            imgView.setImageBitmap(selectedBitmap);
        }
    }
}

这对我来说很容易实现,而且还显示了暗区域。


1
@domji84,你在清单文件中设置了<uses-feature android:name="android.hardware.camera" > </uses-feature>和<uses-permission android:name="android.permission.CAMERA" />吗? - Akbari Dipali
@AkbariDipali 我查看了代码并按照答案中提到的步骤进行了操作...我尝试使用以下代码缩放位图,但这会降低图像质量...http://pastebin.com/Y0R52gAp - AndroidDev
是的,缩放位图会降低图像质量。但在我的情况下,我得到的是128X128的图像。 - Akbari Dipali
3
在我的情况下,data.getExtras()返回null,那么我该如何获取Bitmap - SilentKnight
29
安卓操作系统没有"CROP"意图(Intent)。 - CommonsWare
显示剩余25条评论

10

这个库:Android-Image-Cropper非常强大,可以用于裁剪图像。目前在GitHub上有3,731颗星。

你只需使用几行代码即可对图像进行裁剪。

1 - 将依赖添加到build.gradle(Module: app)中。

compile 'com.theartofdev.edmodo:android-image-cropper:2.7.+'

2 - 将权限添加到 AndroidManifest.xml 中

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

3 - 将CropImageActivity添加到AndroidManifest.xml中

<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
 android:theme="@style/Base.Theme.AppCompat"/>

4-根据您的要求,从下面的情况之一开始活动:

// start picker to get image for cropping and then use the image in cropping activity
CropImage.activity()
.setGuidelines(CropImageView.Guidelines.ON)
.start(this);

// start cropping activity for pre-acquired image saved on the device
CropImage.activity(imageUri)
.start(this);

// for fragment (DO NOT use `getActivity()`)
CropImage.activity()
.start(getContext(), this);

5 - 在onActivityResult中获取结果

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
    CropImage.ActivityResult result = CropImage.getActivityResult(data);
    if (resultCode == RESULT_OK) {
      Uri resultUri = result.getUri();
    } else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
      Exception error = result.getError();
    }
  }
}

您可以进行多个自定义设置,例如设置纵横比或将形状设置为矩形、椭圆等等。


我遇到了一个问题,即在裁剪后我的URI以'file:///'开头,但我想要'content://',在将路径转换为字符串时它会崩溃。 - Kishan
如何在裁剪后保存图像 - Vinit Poojary
使用命令:mImageProfile.setImageBitmap(result.getBitmap()); 返回一张空白图片,我的意思是这张图片不可见。但是使用命令:mImageProfile.setImageURI(result.getUri()); 正常工作。 - Aliton Oliveira
您还可以从CustomAdapter开始,使用以下命令:CropImage.activity().setGuidelines(CropImageView.Guidelines.ON).start((AppCompatActivity) context); - Aliton Oliveira
我正在我的Realme 3 Pro手机上使用这个库,它运行的是Android 10系统,但是它没有显示出相册选项,只有相机选项。然而,在Android 9系统上的Nexus 5x和Realme U1上,可以看到相册选项。有什么建议吗? - Akshay Rajput
1
这个库现在已经不再维护了。请使用这个 -> 链接 - Asif Ullah

3

19
你确实是创造了那个库。这没有错,但至少不要试图误导人们认为你是找到了它,或者在发布时加上免责声明。 - darkrider1287
2
这个库没有关于“Fragments”的正确使用说明。此外,作者对问题的回复不是很好。 - Chayan C
这是一个很好的库,谢谢你的分享。它具有我需要的精确行为规范,这真的很幸运,因为另一个库(Android-Image-Cropper)没有。 - EpicPandaForce

2
希望您一切顺利。您可以使用我的代码剪裁图像。您只需要创建一个类并将该类用于您的XMLJava类中。 剪裁图像。您可以将所选图像剪裁成圆形和正方形,并有许多选项可供选择。 希望这对您有用。因为这完全可以由您自己管理,您可以根据自己的需要进行更改。 祝您工作愉快 :)

你的代码片段中没有包含 R.styleable.,所以很难使其正常工作,特别是对于字符串属性。 - Xenolion

0

我最近为自己的项目完成了这个。请根据您的需求进行自定义。您还可以使用4个边缘和4个角来调整大小!虽然我的英语不好,但我尽力在代码中添加注释。请阅读它们,它们可能会有所帮助。

...

enter image description here

XML

     <your_package_name.CV4
            android:id="@+id/cv4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />

        <Button
            android:id="@+id/cropBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Crop"/>

        <ImageView
            android:id="@+id/croppedImage"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

在活动或片段中编写代码

cropBtn.setOnClickListener {
            val x = cv4.test()
            croppedImage.setImageBitmap(x)
        }

自定义视图代码

package your.package.name  

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.voice.translator.app.speak.to.world.camerax.R
import kotlin.math.max
import kotlin.math.min


class CV4 @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

//    onMeasure will be called before onSizeChanged and onDraw
//    please see the View lifecycle for more details
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        screenWidth = MeasureSpec.getSize(widthMeasureSpec)
        imageWidth = screenWidth
        imageHeight = ((screenWidth.toFloat() / src.width) * src.height).toInt()
        setMeasuredDimension(screenWidth, imageHeight)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        val desiredWidthInPx = imageWidth
        val derivedHeightInPx = (desiredWidthInPx / aspectRatio).toInt()

        output = Bitmap.createScaledBitmap(src, desiredWidthInPx, derivedHeightInPx, true)
        rectF2 = RectF(0f, 0f, imageWidth.toFloat(), derivedHeightInPx.toFloat())
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        parent.requestDisallowInterceptTouchEvent(true)
        canvas?.apply {

            // background image under overlay
            drawBitmap(output, 0f, 0f, null)

            // dark overlay
            drawRect(rectF2, rectPaint2)

            // clip rect same as movable rect. This will hide everything outside
            clipRect(rectF)

            // visible clear image covered by clip rect
            drawBitmap(output, 0f, 0f, null)

            // movable rect
            drawRoundRect(rectF, 10f, 10f, rectPaint)
        }
    }


    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {

        if (event != null) {
            motionX = event.x
            motionY = event.y
            when (event.action) {
                MotionEvent.ACTION_MOVE -> moveMove()
                MotionEvent.ACTION_DOWN -> moveDown()
                MotionEvent.ACTION_UP -> moveUp()
            }
        }

        return true

    }

    private fun moveMove() {

        //moving the whole rect
        if (c5) {

            if (pr < screenWidth && motionX > plx) {
                pr = min(r + (motionX - plx), screenWidth.toFloat())
                pl = min(l + (motionX - plx), screenWidth.toFloat() - (r - l))
            }
            if (pl > 0 && motionX < plx) {
                pr = max(r - (plx - motionX), 0f + (r - l))
                pl = max(l - (plx - motionX), 0f)
            }
            if (pb < imageHeight && motionY > pty) {
                pb = min(b + (motionY - pty), imageHeight.toFloat())
                pt = min(t + (motionY - pty), imageHeight.toFloat() - (b - t))
            }
            if (pt > 0 && motionY < pty) {
                pb = max(b - (pty - motionY), 0f + (b - t))
                pt = max(t - (pty - motionY), 0f)
            }
            rectF.set(pl + 5, pt + 5, pr - 5, pb - 5)
            invalidate()
        }

        // moving while holding corners
        if (c6) {
            if (motionX > 0 && motionX < (pr - 100)) pl = motionX
            if (motionY > 0 && motionY < (pb - 100)) pt = motionY
        }
        if (c7) {
            if (motionY > 0 && motionY < (pb - 100)) pt = motionY
            if (motionX > (pl + 100) && motionX < screenWidth) pr = motionX
        }
        if (c8) {
            if (motionX > (pl + 100) && motionX < screenWidth) pr = motionX
            if (motionY > (pt + 100) && motionY < imageHeight) pb = motionY
        }
        if (c9) {
            if (motionX > 0 && motionX < (pr - 100)) pl = motionX
            if (motionY > (pt + 100) && motionY < imageHeight) pb = motionY
        }

        // For moving the edge
        if (c1) if (motionX > 0 && motionX < (pr - 100)) pl = motionX
        if (c2) if (motionY > 0 && motionY < (pb - 100)) pt = motionY
        if (c3) if (motionX > (pl + 100) && motionX < screenWidth) pr = motionX
        if (c4) if (motionY > (pt + 100) && motionY < imageHeight) pb = motionY


        rectF.set(pl + 5, pt + 5, pr - 5, pb - 5)
        invalidate()

    }

    private fun moveDown() {

        if (motionX > (pl + rng) && motionX < (pr - rng) && motionY > (pt + rng) && motionY < (pb - rng)) {

            c5 = true
            l = pl
            t = pt
            r = pr
            b = pb

            if (motionY >= 0 && motionY <= imageHeight) pty = motionY
            if (motionX >= 0 && motionX <= screenWidth) plx = motionX

            invalidate()
            return
        }

        if (motionX in pl - rng..pl + rng && motionY in pt - rng..pt + rng) {
            c6 = true
            invalidate()
            return
        }
        if (motionY in pt - rng..pt + rng && motionX in pr - rng..pr + rng) {
            c7 = true
            invalidate()
            return
        }
        if (motionX in pr - rng..pr + rng && motionY in pb - rng..pb + rng) {
            c8 = true
            invalidate()
            return
        }
        if (motionY in pb - rng..pb + rng && motionX in pl - rng..pl + rng) {
            c9 = true
            invalidate()
            return
        }




        if (motionX > (pl - rng) && motionX < (pl + rng) && motionY > pt && motionY < pb) {
            c1 = true
            invalidate()
            return
        }
        if (motionY > (pt - rng) && motionY < (pt + rng) && motionX > pl && motionX < pr) {
            c2 = true
            invalidate()
            return
        }
        if (motionX > (pr - rng) && motionX < (pr + rng) && motionY > pt && motionY < pb) {
            c3 = true
            invalidate()
            return
        }
        if (motionY > (pb - rng) && motionY < (pb + rng) && motionX > pl && motionX < pr) {
            c4 = true
            invalidate()
            return
        }


        invalidate()

    }

    private fun moveUp() {
        c1 = false
        c2 = false
        c3 = false
        c4 = false
        c5 = false

        c6 = false
        c7 = false
        c8 = false
        c9 = false

        invalidate()
    }


    // pass bitmap image
    private val src: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.mountain)

    private var screenWidth = 0
    private var imageWidth = 0
    private var imageHeight = 0
    private val aspectRatio: Float = src.width / src.height.toFloat()

    //get the bitmap from test():Bitmap? function
    private lateinit var output: Bitmap

    fun test(): Bitmap? {

        val cropWidth: Float = (pr - pl)
        val cropHeight: Float = (pb - pt)

        invalidate()
        //returning the bitmap
        return Bitmap.createBitmap(
            output,
            pl.toInt(),
            pt.toInt(),
            cropWidth.toInt(),
            cropHeight.toInt()
        )
    }


    private var motionX = 0f
    private var motionY = 0f
    private var rng = 40f // Touch range for the 4 side and 4 corners of the rect

    // p for point and l=left t=top r=right b=bottom
    private var pl = 100f
    private var plx = 100f //hold motionX value from moveDown() function

    private var pt = 100f
    private var pty = 100f //hold motionY value from moveDown() function

    private var pr = 300f
    private var pb = 400f

    //hold left,top,right,bottom value from moveDown() function
    private var l = 0f
    private var t = 0f
    private var r = 0f
    private var b = 0f

    // check user touch Down on rect edges, corners or inside

    //edges point
    private var c1 = false
    private var c2 = false
    private var c3 = false
    private var c4 = false

    private var c5 = false  // for inside selection to move the whole rect

    //corners point
    private var c6 = false
    private var c7 = false
    private var c8 = false
    private var c9 = false


    // resizable rect
    private var rectF = RectF(pl + 5, pt + 5, pr - 5, pb - 5)
    private val rectPaint = Paint().apply {
        style = Paint.Style.STROKE
        strokeWidth = 10f
        color = Color.YELLOW

    }

    // dark overlay rect
    private val foregroundArcColor =
        context.resources?.getColor(R.color.custom3, null) ?: Color.GRAY
    private var rectF2 = RectF(0f, 0f, screenWidth.toFloat(), imageHeight.toFloat())
    private val rectPaint2 = Paint().apply {
        style = Paint.Style.FILL
        color = foregroundArcColor
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接