Совместите изображение в нижней части ImageView [duplicate]

В Win-Vector есть очень мощный новый пакет от ученых гениальных данных (люди, которые делали vtreat, seplyr и replyr), называемые cdata. Он реализует принципы «скоординированных данных», описанные в в этом документе , а также в этом сообщении в блоге . Идея заключается в том, что независимо от того, как вы организуете свои данные, должно быть возможно идентифицировать отдельные точки данных с помощью системы «координат данных». Вот отрывок из недавнего сообщения в блоге от John Mount:

Вся система основана на двух примитивах или операторах cdata :: moveValuesToRowsD () и cdata :: moveValuesToColumnsD (). Эти операторы имеют сводную, разворотную, однострочную кодировку, транспонирование, перемещение нескольких строк и столбцов и многие другие преобразования как простые частные случаи.

Легко написать много разных операций в терминах примитивов cdata. Эти операторы могут работать как в памяти, так и в больших масштабах данных (с базами данных и Apache Spark, для больших данных используются варианты cdata :: moveValuesToRowsN () и cdata :: moveValuesToColumnsN ()). Трансформации управляются таблицей управления, которая сама является диаграммой (или изображением) преобразования.

blockquote>

Сначала мы создадим контрольную таблицу (см. сообщение в блоге для получения подробной информации), а затем выполнить перемещение данных из строк в столбцы.

library(cdata)
# first build the control table
pivotControlTable <- buildPivotControlTableD(table = dat1, # reference to dataset
                        columnToTakeKeysFrom = 'numbers', # this will become column headers
                        columnToTakeValuesFrom = 'value', # this contains data
                        sep="_")                          # optional for making column names

# perform the move of data to columns
dat_wide <- moveValuesToColumnsD(tallTable =  dat1, # reference to dataset
                    keyColumns = c('name'),         # this(these) column(s) should stay untouched 
                    controlTable = pivotControlTable# control table above
                    ) 
dat_wide

#>         name  numbers_1  numbers_2  numbers_3  numbers_4
#> 1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
#> 2 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

77
задан Dori 13 June 2011 в 13:24
поделиться

11 ответов

Хорошо, у меня есть рабочее решение. Приглашение от Darko заставило меня снова взглянуть на класс ImageView (спасибо) и применил преобразование, используя Matrix (как я подозревал, но не имел успеха при первой попытке!). В моем обычном классе imageView я вызываю setScaleType(ScaleType.MATRIX) после super() в конструкторе и имею следующий метод.

    @Override
    protected boolean setFrame(int l, int t, int r, int b)
    {
        Matrix matrix = getImageMatrix(); 
        float scaleFactor = getWidth()/(float)getDrawable().getIntrinsicWidth();    
        matrix.setScale(scaleFactor, scaleFactor, 0, 0);
        setImageMatrix(matrix);
        return super.setFrame(l, t, r, b);
    }

Я поместил int в метод setFrame(), как в ImageView, вызов configureBounds() находится внутри этого метода, в котором происходит все масштабирование и материал матрицы, поэтому мне кажется логичным (скажем, если вы не согласны)

Ниже приведен метод super.setFrame () из AOSP

 @Override
    protected boolean setFrame(int l, int t, int r, int b) {
        boolean changed = super.setFrame(l, t, r, b);
        mHaveFrame = true;
        configureBounds();
        return changed;
    }

Найти полный класс src здесь

83
ответ дан Halil 22 August 2018 в 10:30
поделиться
  • 1
    Спасибо за код, @doridori! Это сработало нормально! Я просто не понимаю, почему вы повторили «setFrame». метод в вашем объяснении ... Я использовал только первый с успехом (и полностью проигнорировал второй xD) – Alesqui 11 January 2012 в 18:41
  • 2
    После этого с помощью xml-макета в течение двух часов это сработало. Хотел бы я дать вам больше ботов. – Mark Beaton 6 September 2012 в 08:45
  • 3
    Мне пришлось вызвать super () перед телом, иначе изображение не будет отображаться без перерисовки – sherpya 12 March 2014 в 00:51
  • 4
    @VitorHugoSchwaab вам нужно использовать thms like matrix.postTranslate (..) – Anton Kizema 3 September 2015 в 09:43
  • 5
    не использовать фон? использовать src? – Egos Zhang 30 January 2016 в 04:00

здесь мой код для центрирования его внизу. Btw. в коде Дори есть небольшая ошибка: поскольку super.frame() вызывается в самом конце, метод getWidth() может вернуть неправильное значение. Если вы хотите сосредоточить его наверху, просто удалите строку postTranslate, и все готово. Самое приятное, что с помощью этого кода вы можете перемещать его в любом месте. (справа, в центре => нет проблем;)

    public class CenterBottomImageView extends ImageView {

        public CenterBottomImageView(Context context) {
            super(context);
            setup();
        }

        public CenterBottomImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setup();
        }

        public CenterBottomImageView(Context context, AttributeSet attrs,
                int defStyle) {
            super(context, attrs, defStyle);
            setup();
        }

        private void setup() {
            setScaleType(ScaleType.MATRIX);
        }

        @Override
        protected boolean setFrame(int frameLeft, int frameTop, int frameRight, int frameBottom) {
            float frameWidth = frameRight - frameLeft;
            float frameHeight = frameBottom - frameTop;

            float originalImageWidth = (float)getDrawable().getIntrinsicWidth();
            float originalImageHeight = (float)getDrawable().getIntrinsicHeight();

            float usedScaleFactor = 1;

            if((frameWidth > originalImageWidth) || (frameHeight > originalImageHeight)) {
                // If frame is bigger than image
                // => Crop it, keep aspect ratio and position it at the bottom and center horizontally

                float fitHorizontallyScaleFactor = frameWidth/originalImageWidth;
                float fitVerticallyScaleFactor = frameHeight/originalImageHeight;

                usedScaleFactor = Math.max(fitHorizontallyScaleFactor, fitVerticallyScaleFactor);
            }

            float newImageWidth = originalImageWidth * usedScaleFactor;
            float newImageHeight = originalImageHeight * usedScaleFactor;

            Matrix matrix = getImageMatrix();
            matrix.setScale(usedScaleFactor, usedScaleFactor, 0, 0); // Replaces the old matrix completly
//comment matrix.postTranslate if you want crop from TOP
            matrix.postTranslate((frameWidth - newImageWidth) /2, frameHeight - newImageHeight);
            setImageMatrix(matrix);
            return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
        }

    }
38
ответ дан AZ_ 22 August 2018 в 10:30
поделиться
  • 1
    Отлично, спасибо за указание на ошибку. Я не коснулся этого кода какое-то время, так что это может быть глупое предложение, но чтобы исправить ошибку setWidth, которую вы указываете, можно было бы не просто использовать (r-l) вместо этого? – Dori 23 July 2012 в 09:52
  • 2
    Неужели линия if((frameWidth > originalImageWidth) || (frameHeight > originalImageHeight)) должна быть отменена? Другими словами, разве вы не должны проверять, больше ли изображение, чем кадр? Я предлагаю заменить его на if((originalImageWidth > frameWidth ) || (originalImageHeight > frameHeight )) – Carlos P 29 January 2013 в 20:44
  • 3
    Я выбрал ширину из родителя с помощью ((View) getParent()).getWidth(), так как ImageView имеет MATCH_PARENT – sherpya 17 March 2014 в 19:03
  • 4
    @Jay это отлично работает. Im делает вычисление матриц в методе onLayout (), который также вызывается при вращении, где setFrame этого не делает. – box 29 July 2016 в 12:00
  • 5
    Благодарим за это, отлично работает, а также благодарим за то, что вы дали возможность быстро изменить нижнюю обрезку на вершину урожая, комментируя 1 строку кода – Moonbloom 10 January 2017 в 15:35

Возможно, зайдите в исходный код для просмотра изображения на Android и посмотрите, как он рисует центральную обрезку и т. д. и, возможно, скопируйте часть этого кода в свои методы. я действительно не знаю, для лучшего решения, чем для этого. У меня есть опыт ручного изменения размера и обрезки растрового изображения (поиск преобразований растровых изображений), который уменьшает его фактический размер, но он все еще создает немного накладных расходов в этом процессе.

5
ответ дан DArkO 22 August 2018 в 10:30
поделиться

Ни одно из этих решений не работало для меня, потому что мне нужен класс, который поддерживал произвольный урожай с горизонтального или вертикального направления, и я хотел, чтобы он позволял мне динамически изменять культуру. Мне также нужна совместимость Picasso , и Пикассо лениво устанавливает образы изображений.

Моя реализация адаптирована непосредственно из ImageView.java в AOSP. Чтобы использовать его, объявите так в XML:

    <com.yourapp.PercentageCropImageView
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="matrix"/>

Из источника, если вы хотите иметь верхний обрез, вызовите:

imageView.setCropYCenterOffsetPct(0f);

Если вы хотите иметь нижняя культура, вызов:

imageView.setCropYCenterOffsetPct(1.0f);

Если вы хотите получить урожай на 1/3 пути вниз, вызовите:

imageView.setCropYCenterOffsetPct(0.33f);

Кроме того, если вы решите использовать другое метод crop, например fit_center, вы можете сделать это, и ни одна из этих пользовательских логик не будет запущена. (Другие реализации ТОЛЬКО позволяют использовать методы обрезки).

Наконец, я добавил метод, redraw (), поэтому, если вы решите изменить свой метод обрезки / scaleType динамически в коде, вы можете принудительно перерисовать представление. Например:

fullsizeImageView.setScaleType(ScaleType.FIT_CENTER);
fullsizeImageView.redraw();

Чтобы вернуться к вашей пользовательской вершине-третьей части, вызовите:

fullsizeImageView.setScaleType(ScaleType.MATRIX);
fullsizeImageView.redraw();

Вот класс:

/* 
 * Adapted from ImageView code at: 
 * http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.4_r1/android/widget/ImageView.java
 */
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class PercentageCropImageView extends ImageView{

    private Float mCropYCenterOffsetPct;
    private Float mCropXCenterOffsetPct;

    public PercentageCropImageView(Context context) {
        super(context);
    }

    public PercentageCropImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public PercentageCropImageView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
    }

    public float getCropYCenterOffsetPct() {
        return mCropYCenterOffsetPct;
    }

    public void setCropYCenterOffsetPct(float cropYCenterOffsetPct) {
        if (cropYCenterOffsetPct > 1.0) {
            throw new IllegalArgumentException("Value too large: Must be <= 1.0");
        }
        this.mCropYCenterOffsetPct = cropYCenterOffsetPct;
    }

    public float getCropXCenterOffsetPct() {
        return mCropXCenterOffsetPct;
    }

    public void setCropXCenterOffsetPct(float cropXCenterOffsetPct) {
        if (cropXCenterOffsetPct > 1.0) {
            throw new IllegalArgumentException("Value too large: Must be <= 1.0");
        }
        this.mCropXCenterOffsetPct = cropXCenterOffsetPct;
    }

    private void myConfigureBounds() {
        if (this.getScaleType() == ScaleType.MATRIX) {
            /*
             * Taken from Android's ImageView.java implementation:
             * 
             * Excerpt from their source:
    } else if (ScaleType.CENTER_CROP == mScaleType) {
       mDrawMatrix = mMatrix;

       float scale;
       float dx = 0, dy = 0;

       if (dwidth * vheight > vwidth * dheight) {
           scale = (float) vheight / (float) dheight; 
           dx = (vwidth - dwidth * scale) * 0.5f;
       } else {
           scale = (float) vwidth / (float) dwidth;
           dy = (vheight - dheight * scale) * 0.5f;
       }

       mDrawMatrix.setScale(scale, scale);
       mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
    }
             */

            Drawable d = this.getDrawable();
            if (d != null) {
                int dwidth = d.getIntrinsicWidth();
                int dheight = d.getIntrinsicHeight();

                Matrix m = new Matrix();

                int vwidth = getWidth() - this.getPaddingLeft() - this.getPaddingRight();
                int vheight = getHeight() - this.getPaddingTop() - this.getPaddingBottom();

                float scale;
                float dx = 0, dy = 0;

                if (dwidth * vheight > vwidth * dheight) {
                    float cropXCenterOffsetPct = mCropXCenterOffsetPct != null ? 
                            mCropXCenterOffsetPct.floatValue() : 0.5f;
                    scale = (float) vheight / (float) dheight;
                    dx = (vwidth - dwidth * scale) * cropXCenterOffsetPct;
                } else {
                    float cropYCenterOffsetPct = mCropYCenterOffsetPct != null ? 
                            mCropYCenterOffsetPct.floatValue() : 0f;

                    scale = (float) vwidth / (float) dwidth;
                    dy = (vheight - dheight * scale) * cropYCenterOffsetPct;
                }

                m.setScale(scale, scale);
                m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));

                this.setImageMatrix(m);
            }
        }
    }

    // These 3 methods call configureBounds in ImageView.java class, which
    // adjusts the matrix in a call to center_crop (android's built-in 
    // scaling and centering crop method). We also want to trigger
    // in the same place, but using our own matrix, which is then set
    // directly at line 588 of ImageView.java and then copied over
    // as the draw matrix at line 942 of ImageVeiw.java
    @Override
    protected boolean setFrame(int l, int t, int r, int b) {
        boolean changed = super.setFrame(l, t, r, b);
        this.myConfigureBounds();
        return changed;
    }
    @Override
    public void setImageDrawable(Drawable d) {          
        super.setImageDrawable(d);
        this.myConfigureBounds();
    }
    @Override
    public void setImageResource(int resId) {           
        super.setImageResource(resId);
        this.myConfigureBounds();
    }

    public void redraw() {
        Drawable d = this.getDrawable();

        if (d != null) {
            // Force toggle to recalculate our bounds
            this.setImageDrawable(null);
            this.setImageDrawable(d);
        }
    }
}
7
ответ дан esilver 22 August 2018 в 10:30
поделиться
  • 1
    Это работает очень хорошо. Я использую его с Glide. – Pierre-Luc Paour 20 July 2017 в 15:24
  • 2
    Я использовал так много решений, но это отлично работает для меня – Ancee 25 October 2017 в 10:18

Вам не нужно писать пользовательский вид изображения для получения функциональности TOP_CROP. Вам просто нужно изменить matrix ImageView.

  1. Установите для параметра scaleType на matrix значение ImageView:
    <ImageView
          android:id="@+id/imageView"
          android:contentDescription="Image"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:src="@drawable/image"
          android:scaleType="matrix"/>
    
  2. Установите настраиваемую матрицу для ImageView:
    final ImageView imageView = (ImageView) findViewById(R.id.imageView);
    final Matrix matrix = imageView.getImageMatrix();
    final float imageWidth = imageView.getDrawable().getIntrinsicWidth();
    final int screenWidth = getResources().getDisplayMetrics().widthPixels;
    final float scaleRatio = screenWidth / imageWidth;
    matrix.postScale(scaleRatio, scaleRatio);
    imageView.setImageMatrix(matrix);
    

. Это даст вам функциональность TOP_CROP.

22
ответ дан Henry 22 August 2018 в 10:30
поделиться
  • 1
    Это сработало для меня. Тем не менее, я должен проверить scaleRation, если он равен & lt; 1 Затем я просто изменяю scaleType на centerCrop, иначе я увижу пустое пространство по краям. – Arst 30 June 2017 в 05:54
  • 2
    Как сделать эту работу для случаев, когда есть необходимость в выровненных снизу изображениях? – Sagar 25 February 2018 в 11:53

Если вы используете Fresco (SimpleDraweeView), вы можете легко сделать это с помощью:

 PointF focusPoint = new PointF(0.5f, 0f);
 imageDraweeView.getHierarchy().setActualImageFocusPoint(focusPoint);

Этот был бы для верхнего урожая.

Дополнительная информация в Ссылка Ссылка

0
ответ дан iamsankalp89 22 August 2018 в 10:30
поделиться

Этот пример работает с изображениями, которые загружаются после создания объекта + некоторая оптимизация. Я добавил несколько комментариев в код, которые объясняют, что происходит.

Не забудьте позвонить:

imageView.setScaleType(ImageView.ScaleType.MATRIX);

или

android:scaleType="matrix"

Источник Java:

import com.appunite.imageview.OverlayImageView;

public class TopAlignedImageView extends ImageView {
    private Matrix mMatrix;
    private boolean mHasFrame;

    @SuppressWarnings("UnusedDeclaration")
    public TopAlignedImageView(Context context) {
        this(context, null, 0);
    }

    @SuppressWarnings("UnusedDeclaration")
    public TopAlignedImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @SuppressWarnings("UnusedDeclaration")
    public TopAlignedImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mHasFrame = false;
        mMatrix = new Matrix();
        // we have to use own matrix because:
        // ImageView.setImageMatrix(Matrix matrix) will not call
        // configureBounds(); invalidate(); because we will operate on ImageView object
    }

    @Override
    protected boolean setFrame(int l, int t, int r, int b)
    {
        boolean changed = super.setFrame(l, t, r, b);
        if (changed) {
            mHasFrame = true;
            // we do not want to call this method if nothing changed
            setupScaleMatrix(r-l, b-t);
        }
        return changed;
    }

    private void setupScaleMatrix(int width, int height) {
        if (!mHasFrame) {
            // we have to ensure that we already have frame
            // called and have width and height
            return;
        }
        final Drawable drawable = getDrawable();
        if (drawable == null) {
            // we have to check if drawable is null because
            // when not initialized at startup drawable we can
            // rise NullPointerException
            return;
        }
        Matrix matrix = mMatrix;
        final int intrinsicWidth = drawable.getIntrinsicWidth();
        final int intrinsicHeight = drawable.getIntrinsicHeight();

        float factorWidth = width/(float) intrinsicWidth;
        float factorHeight = height/(float) intrinsicHeight;
        float factor = Math.max(factorHeight, factorWidth);

        // there magic happen and can be adjusted to current
        // needs
        matrix.setTranslate(-intrinsicWidth/2.0f, 0);
        matrix.postScale(factor, factor, 0, 0);
        matrix.postTranslate(width/2.0f, 0);
        setImageMatrix(matrix);
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        // We have to recalculate image after chaning image
        setupScaleMatrix(getWidth(), getHeight());
    }

    @Override
    public void setImageResource(int resId) {
        super.setImageResource(resId);
        // We have to recalculate image after chaning image
        setupScaleMatrix(getWidth(), getHeight());
    }

    @Override
    public void setImageURI(Uri uri) {
        super.setImageURI(uri);
        // We have to recalculate image after chaning image
        setupScaleMatrix(getWidth(), getHeight());
    }

    // We do not have to overide setImageBitmap because it calls 
    // setImageDrawable method

}
24
ответ дан Jacek Marchwicki 22 August 2018 в 10:30
поделиться
  • 1
    Как сделать эту работу для случаев, когда есть необходимость в выровненных снизу изображениях? – Sagar 25 February 2018 в 11:50

Простейшее решение: закрепите изображение

 @Override
    public void draw(Canvas canvas) {
        if(getWidth() > 0){
            int clipHeight = 250;
            canvas.clipRect(0,clipHeight,getWidth(),getHeight());
         }
        super.draw(canvas);
    }
-1
ответ дан M. Usman Khan 22 August 2018 в 10:30
поделиться
  • 1
    Это не будет масштабировать изображение, если оно меньше, чем представление, поэтому другие решения не так просты. – OldSchool4664 7 December 2016 в 23:59

На основе Dori я использую решение, которое либо масштабирует изображение в зависимости от ширины или высоты изображения, чтобы всегда заполнять окружающий контейнер. Это позволяет масштабировать изображение до , заполняя все доступное пространство , используя верхнюю левую точку изображения, а не центр как источник (CENTER_CROP):

@Override
protected boolean setFrame(int l, int t, int r, int b)
{

    Matrix matrix = getImageMatrix(); 
    float scaleFactor, scaleFactorWidth, scaleFactorHeight;
    scaleFactorWidth = (float)width/(float)getDrawable().getIntrinsicWidth();
    scaleFactorHeight = (float)height/(float)getDrawable().getIntrinsicHeight();    

    if(scaleFactorHeight > scaleFactorWidth) {
        scaleFactor = scaleFactorHeight;
    } else {
        scaleFactor = scaleFactorWidth;
    }

    matrix.setScale(scaleFactor, scaleFactor, 0, 0);
    setImageMatrix(matrix);

    return super.setFrame(l, t, r, b);
}

Надеюсь, это поможет - работает в моем проекте.

12
ответ дан Matt 22 August 2018 в 10:30
поделиться
  • 1
    ширина не может быть разрешена переменной – Geltrude 27 May 2013 в 07:10
  • 2
    Это лучшее решение ... И добавьте: float width = r-l; высота поплавка = b - t; – Geltrude 27 May 2013 в 07:25

В решениях есть 2 проблемы:

  • Они не отображаются в редакторе макетов Android Studio (поэтому вы можете просматривать различные размеры и пропорции экрана)
  • Он масштабируется только по ширине, поэтому в зависимости от соотношения сторон устройства и изображения вы можете получить пустую полосу внизу

. Эта небольшая модификация устраняет проблему ( введите код в onDraw и проверьте масштаб и масштаб):

@Override
protected void onDraw(Canvas canvas) {

    Matrix matrix = getImageMatrix();

    float scaleFactorWidth = getWidth() / (float) getDrawable().getIntrinsicWidth();
    float scaleFactorHeight = getHeight() / (float) getDrawable().getIntrinsicHeight();

    float scaleFactor = (scaleFactorWidth > scaleFactorHeight) ? scaleFactorWidth : scaleFactorHeight;

    matrix.setScale(scaleFactor, scaleFactor, 0, 0);
    setImageMatrix(matrix);

    super.onDraw(canvas);
}
0
ответ дан OldSchool4664 22 August 2018 в 10:30
поделиться
public class ImageViewTopCrop extends ImageView {
public ImageViewTopCrop(Context context) {
    super(context);
    setScaleType(ScaleType.MATRIX);
}

public ImageViewTopCrop(Context context, AttributeSet attrs) {
    super(context, attrs);
    setScaleType(ScaleType.MATRIX);
}

public ImageViewTopCrop(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    setScaleType(ScaleType.MATRIX);
}

@Override
protected boolean setFrame(int l, int t, int r, int b) {
    computMatrix();
    return super.setFrame(l, t, r, b);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    computMatrix();
}

private void computMatrix() {
    Matrix matrix = getImageMatrix();
    float scaleFactor = getWidth() / (float) getDrawable().getIntrinsicWidth();
    matrix.setScale(scaleFactor, scaleFactor, 0, 0);
    setImageMatrix(matrix);
}

}

2
ответ дан tianxia 22 August 2018 в 10:30
поделиться
  • 1
    setFrame & amp; & amp; OnLayout – tianxia 30 December 2014 в 09:53
  • 2
    computMatrix: вы можете сделать любую матрицу здесь. – tianxia 30 December 2014 в 09:53
  • 3
    onLayout меня спасает! Благодаря! Я столкнулся с проблемой, когда он вычисляет матрицу, но не отображает изображение сразу, и добавление onLayout в код решает мою проблему. – natsumiyu 26 October 2016 в 01:44
Другие вопросы по тегам:

Похожие вопросы: