缩放时更改ImageView的大小

11

我正在使用chrisbanes的PhotoView来实现捏合缩放。当我捏合或双击时,图像会缩放,但我无法看到图像在缩放时被拉伸至全屏幕。在缩放时,它看起来像是图像在一个框内缩放,并且部分图像在缩放时消失了...如何实现图像缩放,使图像的高度随着缩放而增加?我正在使用Volley库的NetworkImageView。

NetworkImageView imageView;
 PhotoViewAttacher mAttacher;

                imageView = (NetworkImageView) mImgPagerView.findViewById(R.id.imageitem);

                mAttacher = new PhotoViewAttacher(imageView);

NetworkImageView.java(来自 Volley 库)

     import android.content.Context;
        import android.graphics.Bitmap;
        import android.text.TextUtils;
        import android.util.AttributeSet;
        import android.widget.ImageView;
        import com.android.volley.toolbox.ImageLoader.ImageContainer;

public class NetwrokImageView extends ImageView {
    /** The URL of the network image to load */
    private String mUrl;
    /**
     * Resource ID of the image to be used as a placeholder until the network image is loaded.
     */
    private int mDefaultImageId;
    /**
     * Resource ID of the image to be used if the network response fails.
     */
    private int mErrorImageId;
    /** Local copy of the ImageLoader. */
    private ImageLoader mImageLoader;
    public NetworkImageView(Context context) {
        this(context, null);
    }
    public NetworkImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public NetworkImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    /**
     * Sets URL of the image that should be loaded into this view. Note that calling this will
     * immediately either set the cached image (if available) or the default image specified by
     * {@link NetworkImageView#setDefaultImageResId(int)} on the view.
     *
     * NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} and
     * {@link NetworkImageView#setErrorImageResId(int)} should be called prior to calling
     * this function.
     *
     * @param url The URL that should be loaded into this ImageView.
     * @param imageLoader ImageLoader that will be used to make the request.
     */
    public void setImageUrl(String url, ImageLoader imageLoader) {
        mUrl = url;
        mImageLoader = imageLoader;
// The URL has potentially changed. See if we need to load it.
        loadImageIfNecessary();
    }
    /**
     * Sets the default image resource ID to be used for this view until the attempt to load it
     * completes.
     */
    public void setDefaultImageResId(int defaultImage) {
        mDefaultImageId = defaultImage;
    }
    /**
     * Sets the error image resource ID to be used for this view in the event that the image
     * requested fails to load.
     */
    public void setErrorImageResId(int errorImage) {
        mErrorImageId = errorImage;
    }
    /**
     * Loads the image for the view if it isn't already loaded.
     */
    private void loadImageIfNecessary() {
        int width = getWidth();
        int height = getHeight();
// if the view's bounds aren't known yet, hold off on loading the image.
        if (width == 0 && height == 0) {
            return;
        }
// if the URL to be loaded in this view is empty, cancel any old requests and clear the
// currently loaded image.
        if (TextUtils.isEmpty(mUrl)) {
            ImageContainer oldContainer = (ImageContainer) getTag();
            if (oldContainer != null) {
                oldContainer.cancelRequest();
                setImageBitmap(null);
            }
            return;
        }
        ImageContainer oldContainer = (ImageContainer) getTag();
// if there was an old request in this view, check if it needs to be canceled.
        if (oldContainer != null && oldContainer.getRequestUrl() != null) {
            if (oldContainer.getRequestUrl().equals(mUrl)) {
// if the request is from the same URL, return.
                return;
            } else {
// if there is a pre-existing request, cancel it if it's fetching a different URL.
                oldContainer.cancelRequest();
                setImageBitmap(null);
            }
        }
// The pre-existing content of this view didn't match the current URL. Load the new image
// from the network.
        ImageContainer newContainer = mImageLoader.get(mUrl,
                ImageLoader.getImageListener(this, mDefaultImageId, mErrorImageId));
// update the tag to be the new bitmap container.
        setTag(newContainer);
// look at the contents of the new container. if there is a bitmap, load it.
        final Bitmap bitmap = newContainer.getBitmap();
        if (bitmap != null) {
            setImageBitmap(bitmap);
        }
    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        loadImageIfNecessary();
    }
    @Override
    protected void onDetachedFromWindow() {
        ImageContainer oldContainer = (ImageContainer) getTag();
        if (oldContainer != null) {
// If the view was bound to an image request, cancel it and clear
// out the image from the view.
            oldContainer.cancelRequest();
            setImageBitmap(null);
// also clear out the tag so we can reload the image if necessary.
            setTag(null);
        }
        super.onDetachedFromWindow();
    }
    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        invalidate();
    }
}

build.gradle

compile 'com.github.chrisbanes.photoview:library:+'

xml

<com.xyz.NetworkImageView
            android:id="@+id/imageitem"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="matrix"
            android:layout_gravity="center"
            android:adjustViewBounds="true"
            android:background="@drawable/image_loading" />

ImageView位于FrameLayout内部

    <FrameLayout
        android:id="@+id/image_holder"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:background="@color/black"
        >

缩放前的图像 输入图片描述 缩放后的图像 输入图片描述


我不确定我理解你的问题。也许这个链接可以帮到你? - Phantômaxx
是的,ImageView 必须根据内容更改大小。因此,当我缩放图像时,ImageView 应该增加大小。但是,在这里,我的 ImageView 在缩放时保持固定大小,但图像确实在缩放时进行了缩放。 - Android Developer
我刚发现这个:https://dev59.com/Bm435IYBdhLWcg3wigux#5355281。如果我理解正确,它的作用是将ImageView适配其内容。默认为false。 - Phantômaxx
@DerGolem 我尝试添加 android:adjustViewBounds="true" 但是问题仍然存在..imagview的高度保持不变..只有imagview内部的内容进行了缩放 - Android Developer
你得到答案了吗? - Sushil Dubey
显示剩余28条评论
8个回答

1

如果你卡住了,最好尝试不同的方法。

以下是Jason Polites创建的一个类的链接,可以让你处理自定义ImageView上的捏合缩放:https://github.com/jasonpolites/gesture-imageview

只需将此包含到你的应用程序中,然后你就可以在XML文件中使用自定义GestureImaveView

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gesture-image="http://schemas.polites.com/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<com.polites.android.GestureImageView
    android:id="@+id/image"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:src="@drawable/image"
    gesture-image:min-scale="0.1"
    gesture-image:max-scale="10.0"
    gesture-image:strict="false"/>

这个类处理捏合缩放,也可以处理双击。
答案功劳归于Yoann :)

我认为问题是因为图像容器在图像缩放时没有重新调整大小。请查看上面完整的Java代码。 - Android Developer
我尝试了这个...我的ImageView正在扩展这个...https://android.googlesource.com/platform/frameworks/volley/+/d62a616ebca5bfa4f9ec5517208e13f2d501b69a/src/com/android/volley/toolbox/NetworkImageView.java ..我使用了https://github.com/chrisbanes/PhotoView,但仍然无法看到我的图像在缩放时被拉伸到全屏幕...在缩放时,它看起来像图像在一个框内缩放,部分图像在缩放时消失。 - Android Developer

1

我强烈建议您查看Photo View:

https://github.com/chrisbanes/PhotoView

这个库是由Chris Banes开发的,他是Android开发团队中的实际开发人员之一,所以你不会出错。这个库将会为你节省许多麻烦。

使用方法非常简单:

ImageView mImageView;
PhotoViewAttacher mAttacher;

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // Any implementation of ImageView can be used!
  mImageView = (ImageView) findViewById(R.id.iv_photo);

  // Set the Drawable displayed
  Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper);
  mImageView.setImageDrawable(bitmap);

  // Attach a PhotoViewAttacher, which takes care of all of the zooming functionality.
  mAttacher = new PhotoViewAttacher(mImageView);
}

在我的项目中需要添加单击功能。如果我使用chrisbanes的PhotoView,我可以在ImageView上添加单击功能吗?因为需要在单击时显示和隐藏面板。 - Android Developer
我的ImageView正在扩展这个..https://android.googlesource.com/platform/frameworks/volley/+/d62a616ebca5bfa4f9ec5517208e13f2d501b69a/src/com/android/volley/toolbox/NetworkImageView.java ..我使用了chrisbanes的PhotoView,但是当我缩放时,仍然看不到我的图像被拉伸到全屏..在缩放时,它看起来像图像在一个框内缩放,部分图像消失了。 - Android Developer

1
我在Github上创建了一个完全功能的项目,链接在回答的末尾。
问题要素:
1.获取触摸事件并使用其变量设置图像缩放级别和窗口。(左,上,右,下)
示例代码:每次只显示部分图像,因此设置android:scaleType="fitCenter"可以实现缩放。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/imageFrameLayout"
    android:background="@android:color/black">

    <ImageView android:id="@+id/imageView"
        android:layout_width="0px"
        android:layout_height="0px"
        android:layout_gravity="center"
        android:scaleType="fitCenter"
        android:background="@android:color/transparent" />

</FrameLayout>

触摸监听器(您可以修改此内容以添加单击事件)

OnTouchListener MyOnTouchListener = new OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent event) {
        float distx, disty;
        switch(event.getAction() & MotionEvent.ACTION_MASK){   
            case MotionEvent.ACTION_DOWN:
                //A pressed gesture has started, the motion contains the initial starting location.
                touchState = TOUCH;
                currentX = (int) event.getRawX();
                currentY = (int) event.getRawY();
            break;

            case MotionEvent.ACTION_POINTER_DOWN:
                //A non-primary pointer has gone down.
                touchState = PINCH;
                //Get the distance when the second pointer touch
                distx = event.getX(0) - event.getX(1);
                disty = event.getY(0) - event.getY(1);

                dist0 = (float) Math.sqrt(distx * distx + disty * disty);
                distLast = dist0;
            break;

            case MotionEvent.ACTION_MOVE:
                //A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
                if(touchState == PINCH){
                    // pinch started calculate scale step.
                    //Get the current distance
                    distx = event.getX(0) - event.getX(1);
                    disty = event.getY(0) - event.getY(1);
                    distCurrent = (float) Math.sqrt(distx * distx + disty * disty);
                    if (Math.abs(distCurrent-distLast) >= 35) // check sensitivity
                    {
                        stepScale = distCurrent/dist0;
                        distLast = distCurrent;
                        drawMatrix(); // draw new image
                    }
                }
                else
                {
                    // move started.
                    if (currentX==-1 && currentY==-1) 
                    {
                        // first move after touch down
                        currentX = (int) event.getRawX();
                        currentY = (int) event.getRawY();
                    }
                    else
                    {
                        // calculate move window variable.
                        int x2 = (int) event.getRawX();
                        int y2 = (int) event.getRawY();
                        int dx = (currentX - x2);
                        int dy = (currentY - y2);
                        left += dx;
                        top += dy;
                        currentX = x2;
                        currentY = y2;
                        drawMatrix(); // draw new image window
                    }
                }
            break;

            case MotionEvent.ACTION_UP:
                //A pressed gesture has finished.

                touchState = IDLE;
            break;

            case MotionEvent.ACTION_POINTER_UP:
                //A non-primary pointer has gone up.
                if (touchState == PINCH)
                {
                    // pinch ended. reset variable.
                }
                touchState = TOUCH;
            break;
        }
        return true;
    }
};

2. 由于需要缩放,我假设我们正在使用高质量的图像。

因此,在缩小时加载全尺寸的高质量图像并没有好处,因为用户无法识别细节。但是这将增加内存使用量,并且对于非常大的图像可能会崩溃。

解决方案技巧:在缩小时加载较低质量的较大窗口。 在放大时加载较高质量的较小窗口。

所提议的解决方案检查当前缩放级别和所需的图像窗口,并基于此使用BitmapRegionDecoderBitmapFactory获取具有特定质量的图像部分。

示例代码:

初始化将稍后用于请求图像部分的图像解码器:

    InputStream is = null;
    bitmapRegionDecoder = null;

    try {
        is = getAssets().open(res_id); // get image stream from assets. only the stream, no mem usage
        bitmapRegionDecoder = BitmapRegionDecoder.newInstance(is, false);

    } catch (IOException e) {
        e.printStackTrace();
    }

    bounds = new BitmapFactory.Options();
    bounds.inJustDecodeBounds = true; // only specs needed. no image yet!
    BitmapFactory.decodeStream(is, null, bounds); // get image specs.

    try {
        is.close(); // close stream no longer needed.
    } catch (IOException e) {
        e.printStackTrace();
    }

要求指定图像质量和窗口:

    Rect pRect = new Rect(left, top, left + newWidth, top + newHeight);
    BitmapFactory.Options bounds = new BitmapFactory.Options();
    int inSampleSize = 1;
    if (tempScale <= 2.75) // you can map scale with quality better than this.
        inSampleSize = 2;

    bounds.inSampleSize = inSampleSize; // set image quality. takes binary steps only. 1, 2, 4, 8 . .
    bm = bitmapRegionDecoder.decodeRegion(pRect, bounds);
    imageView.setImageBitmap(bm);

在 Github 上的项目.


1

我在照片查看图像缩放库中遇到了这个问题。我将layout_height从"wrap_content"改为"match_parent",现在它正常工作。

https://github.com/chrisbanes/PhotoView

<com.xyz.NetworkImageView
        android:id="@+id/imageitem"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="matrix"
        android:layout_gravity="center"
        android:adjustViewBounds="true"
        android:background="@drawable/image_loading" />

0
问题在于使用NetworkImageView时,它的行为与ImageView不同,我通过创建一个用于纵向和横向的xml设计来解决这个问题。 纵向

layout/item_image.xml

height="match_parent" 和 width="wrap_content",重要的是 scaleType="fitCenter"

 <com.android.volley.toolbox.NetworkImageView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:scaleType="fitCenter"
        android:background="@android:color/transparent"
        android:id="@+id/imageitem"/>

LAND

layout/land/item_image.xml

现在在布局中,高度为"wrap_content",宽度为"match_parent",始终scaleType="fitCenter"

 <com.android.volley.toolbox.NetworkImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="fitCenter"
        android:background="@android:color/transparent"
        android:id="@+id/imageitem"/>

在将资源分配给NetworkImageView控件后实例化PhotoViewAttacher对象也很重要。

    NetworkImageView imageView;
    PhotoViewAttacher mAttacher;

        imageView = (NetworkImageView) 
        mImgPagerView.findViewById(R.id.imageitem);
        //firts set the image resources, i'm using Android Volley Request
        ImageLoader imageLoader = MySocialMediaSingleton.getInstance(context).getImageLoader();
        imageView.setImageUrl(url, imageLoader);
        //next create instance of PhotoViewAttecher
        mAttacher = new PhotoViewAttacher(imageView);
        mAttacher.update();

0

如果您想要更改ImageView的大小,您需要更改其布局参数,就像我在这里演示的那样。自Honeycomb以来,您还可以更改其比例。

但是,如果您只想在图像中移动一个矩形,并使其缩放得很好(就像其他允许裁剪的应用程序一样),则可以使用类似于"cropper"的库,或者如果您不需要那里的功能,则可以使用TouchImageView这样的库。


1
我认为问题在于当图像缩放时,图像容器没有重新调整大小。 - Android Developer
当我拖动图像时,它看起来像是在矩形容器中,当我将其拖到容器外部时,图像的一部分会消失。 - Android Developer
请检查我的上面的代码。我认为问题在于当我缩放图像时,ImageContainer mImageContainer没有调整大小。 - Android Developer
代码太长了。你能否提供一个样例项目(尽可能短的代码),让我可以直接导入并运行?或者也可以发布一个视频展示问题在哪里? - android developer
请将整个项目上传到文件上传网站,但最小化以便我能够专注于问题并轻松找到它。 - android developer
显示剩余3条评论

0
因为ImageView的视图高度是"wrap_content"。将其改为"match_parent"并将图片的scaleType设置为"centerInside"以解决你的问题。

0

我使用了ImageViewTouch并遇到了同样的问题。要解决这个问题,您可以将ImageView包装在没有其他子元素的布局中。例如:

<LinearLayout
android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_marginBottom="200dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/toplayout">

    <com.android.volley.toolbox.NetworkImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:scaleType="fitCenter"
    android:background="@android:color/transparent"
    android:id="@+id/imageitem"/>
</LinearLayout>

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