I am using chrisbanes PhotoView to implement pinch zoom..Image zooms on pinching and double tapping but i can't see that my image streched to full screen on zooming..on zooming it looks that image zooms inside a box and part of image disappears on zooming...How can i implement image zoom so that height of Image increases on zooming?I am using NetworkImageView ( of Volley library).
NetworkImageView imageView;
PhotoViewAttacher mAttacher;
imageView = (NetworkImageView) mImgPagerView.findViewById(R.id.imageitem);
mAttacher = new PhotoViewAttacher(imageView);
NetworkImageView.java ( of Volley library)
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 is inside 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"
>
Image before zooming
Image after zooming
Better try a different approach if youre stuck
You can find below a link to a class created by Jason Polites that will allow you to handle pinch zooms on custom ImageViews: https://github.com/jasonpolites/gesture-imageview.
Just include this package into your application and then you will be able to use a custom GestureImaveView in your XML files:
<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"/>
This class handles pinch zooms, but also double taps.
Answer credit goes to Yoann :)
I will highly recommend having a look at Photo View:
https://github.com/chrisbanes/PhotoView
It was developed by Chris Banes who is one of the actual developers that worked on the Android development team, so you can't go wrong here. This library will save you A LOT of headaches.
Usage is as simple as:
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);
}
I created a fully functional project on github. link in the end of the answer.
Problem elements:
1. Getting touch events and use it's variables to set image zoom level and window. (left, top, right, bottom).
Sample Code: only part of image will be showing at a time. therefore setting android:scaleType="fitCenter" will achieve the zooming.
<?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>
Touch Listener (you can modify this to add your click event)
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. Since zooming required. I assume that we are using high quality images.
Therefore, loading the full sized quality image when zoomed out won't benefit, since the user won't recognise the small details. But it will increase memory usage and could crash for very large images.
Solution Trick: On zooming out load larger window with lower quality.
On zoom in load smaller window with higher quality.
The proposed solution checks the current zoom level and image window required and based on that gets only part of the image with specific quality using BitmapRegionDecoder and BitmapFactory.
Sample Code:
Initialise image decoder that is going to be used later to ask for a part of the image:
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();
}
Ask for image with quality and window:
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);
project on github.
I had faced this issue in photo view image zoom library. i was changed the layout_height is "wrap_content" to "match_parent. It's working fine for me.
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" />
If you wish to change the ImageView's size, you need to change its layoutParams, as I've demonstrated here. You can also change its scale as of Honeycomb.
However, if you wish to just move a rectangle within the image, which also zooms nicely (like other apps that allow to crop), you can use a library like "cropper" or if you don't need the features there you can use a library like TouchImageView
It is because the ImageView has a view height of "wrap_content". Change it to "match_parent" and set the scaleType of the image to be "centerInside" to fix your problem.
The problem is that when using NetworkImageView does not behave the same as an ImageView, I solve it by creating an xml design for portrait and landscape
PORTRAIT
layout/item_image.xml
height="match_parent" and width="wrap_content", important 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
now in the land layout the height="wrap_content" and width="match_parent", always 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"/>
It's also important to instantiate the PhotoViewAttacher object after assigning the resource to the NetworkImageView control
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();
I use imageviewtouch and have same problem. To solve it you can wrap imageview inside layout without any other children. Example :
<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>
Related
I need to show some Images inside the Frame (Multiple Image Frames), I have left and top coordinates correspondence to each Image.
For example, I have Image coordinates 100 from left and 100 from top, So what i need to do is just to show the image inside the ImageView which starts from (100,100).
Can anyone tell me how could i do that without Bitmap, because i need to show the list of Frames with Multiple Images inside RecyclerView.
I have used TouchImageView.class for Zoom and Move feature for creating the Frame.
1. Below screen shot while i am selecting layout, as you can clearly seen that both the images are quite close to each other. I mean that i am selecting half portion of each image.
2. Next is the Home screen at which i need to replot that image, I am having the left and top coordinate of both the images.But How can i plot that Image inside ImageView by some custom x and y.
Code snippet for populating image inside ImageView from left and top -
img.setPadding((int) x, (int) y, 0, 0);
But still not working.
Please suggest me something, i have done google search but didn't got the appropriate solution for my requirement.
Suggestions really appreciated.
Many Thanks in advance.
Kind Regards.
Update: I have added a sample app at the end of this post to illustrate this concept.
Second update: Added some code to to sample app to accommodate xy scaling
I have changed the sample app to include code that will fit the image, whether smaller or larger than the ImageView, to the view size while respecting the aspect ratio. This may not be exactly what you are looking for, but it should get you into the ballpark. This is sample code, so there are assuredly other things to accommodate in an actual application. For instance, xScale and yScale could go negative in extreme cases. I am not sure what happens if they do, but a crash is probably the result, so a bounds check is in order.
If I understand what you are trying to do, you want a fixed size ImageView to show different parts of a
larger image by setting custom (x,y) coordinates of the image to sit at the (0,0) coordinate of the ImageView.
This is basically a cropping of the image across the top and left edges.
There are probably many different ways to do this and which one works best for you will depend on your exact needs,
but here is one way:
Use the following, or some variation, in your code:
float xOffset = -100;
float yOffset = -100;
ImageView imageView = (ImageView) findViewById(R.id.shiftedView);
Matrix matrix = new Matrix();
matrix.setTranslate(xOffset, yOffset);
imageView.setImageMatrix(matrix);
Set up your ImageView something like the following. The key here is android:scaleType="matrix"
<ImageView
android:id="#+id/shiftedView"
android:layout_width="200dp"
android:layout_height="match_parent"
android:background="#FF00CCCC"
android:scaleType="matrix"
android:src="#drawable/image" />
I hope this helps you.
Sample App
Here is a quick mock-up of this concept. This app is composed of the main activity and one layout. You will have to supply an image that is the source for the ImageView to see how this works.
The seven buttons will shift the image left, right, up and down on a click within the layout of the ImageView. The "Fit X" and "Fit Y" buttons will scale the image to the corresponding dimension available within the ImageView. The "Reset" button will set the image back to the original default setting of the ImageView.
MainActivity.java
package com.example.imageshift;
import android.graphics.Matrix;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
ImageView imageView;
float xOffset = 0f;
float yOffset = 0f;
float xScale = 1.0f;
float yScale = 1.0f;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.shiftedView);
findViewById(R.id.shiftUp).setOnClickListener(this);
findViewById(R.id.shiftDown).setOnClickListener(this);
findViewById(R.id.shiftLeft).setOnClickListener(this);
findViewById(R.id.shiftRight).setOnClickListener(this);
findViewById(R.id.shiftReset).setOnClickListener(this);
findViewById(R.id.fitX).setOnClickListener(this);
findViewById(R.id.fitY).setOnClickListener(this);
}
public void doMatrixTransformations() {
Matrix matrix = new Matrix();
matrix.setTranslate(xOffset, yOffset);
matrix.postScale(xScale, yScale);
imageView.setImageMatrix(matrix);
}
#Override
public void onClick(View view) {
final int shiftAmount = 50;
switch (view.getId()) {
case R.id.shiftUp:
yOffset -= shiftAmount;
break;
case R.id.shiftDown:
yOffset += shiftAmount;
break;
case R.id.shiftLeft:
xOffset -= shiftAmount;
break;
case R.id.shiftRight:
xOffset += shiftAmount;
break;
case R.id.fitX:
xScale = (float) imageView.getWidth() / (
(float) imageView.getDrawable().getIntrinsicWidth() + xOffset);
yScale = xScale;
break;
case R.id.fitY:
yScale = (float) imageView.getHeight() /
((float) imageView.getDrawable().getIntrinsicHeight() + yOffset);
xScale = yScale;
break;
case R.id.shiftReset:
xOffset = 0;
yOffset = 0;
xScale = 1.0f;
yScale = 1.0f;
break;
}
doMatrixTransformations();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.imageshift.MainActivity">
<ImageView
android:id="#+id/shiftedView"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:padding="2dp"
android:scaleType="matrix"
android:src="#drawable/image" />
<Button
android:id="#+id/shiftUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Up" />
<Button
android:id="#+id/shiftDown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/shiftUp"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Down" />
<Button
android:id="#+id/shiftLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/shiftDown"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Left" />
<Button
android:id="#+id/shiftRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/shiftLeft"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Right" />
<Button
android:id="#+id/fitX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/shiftRight"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Fit X" />
<Button
android:id="#+id/fitY"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/fitX"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Fit Y" />
<Button
android:id="#+id/shiftReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/fitY"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Reset" />
</RelativeLayout>
It sounds like you want to take two images and create a new image based on some combination of the two images.
There are lots of different ways to go about this. The most straightforward ways involve using Bitmaps. There's two main approaches I can see that are straightforward to accomplish this.
Position your image views how you like within some common parent and create a bitmap of that parent to use as your resulting image.
Create a bitmap yourself by drawing the images of your imageviews based on your own calculations that should mimic the layout structure you have.
Since you already seem to be showing the two images on the screen in the way you want the resulting image to look like, the first is going to be the most straightforward and won't require you to redo the calculations that your layouts and imageviews are already performing.
What we're going to do is take advantage of the fact that every view will draw itself onto a canvas and at the end of the day is just translating itself into an image itself. To get a bitmap of your parent layout you can simply create a bitmap of the same size and then tell your parent layout to draw itself onto a canvas of the bitmap, like so:
private Bitmap getCombinedBitmap(View parentView) {
Bitmap result = Bitmap.createBitmap(parentView.getWidth(), parentView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
parentView.draw(canvas);
return result;
}
If however you would like to not be bound by just whatever the parent layout is drawing to the screen, you can create the bitmap you want by drawing your images onto a similar canvas. Here I have implemented the first layout from your question:
private Bitmap getCombinedBitmap(Bitmap a, Bitmap b) {
int aWidth = a.getWidth() / 2;
int bWidth = b.getWidth() / 2;
int resultHeight = a.getHeight() < b.getHeight()? a.getHeight() : b.getHeight();
Bitmap result = new Bitmap(aWidth + bWidth, resultHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect aRect = new Rect(0, 0, aWidth, resultHeight);
Rect aResultRect = new Rect(0, 0, aWidth, resultHeight);
canvas.drawBitmap(a, aRect, aResultRect);
Rect bRect = new Rect(0, 0, bWidth, resultHeight);
Rect bResultRect = new Rect(bWidth, 0, bWidth, resultHeight);
canvas.drawBitmap(b, bRect, bResultRect);
return result;
}
In this example I opted for clearer code rather than more optimised code. You can optimise this by not creating as many Rects, etc and generally simplifying your calculations. But you can use this approach to recreate any of your layouts onto a single bitmap, you would just need to change the calculations.
What we are doing is first creating a bitmap that has a width of half the first image plus half the second image, and the height of the smallest of the two images. We then take a section of the first image that is the left most half width wise and as high as the result height wise and we draw that onto our result. We then do the same with the second image except this time the section is the right most half width wise.
Hope this helps.
I have to admit that I'm not 100% sure I understand what you want to do. If I understand correctly, what you need is to put an ImageView inside another element (I show an example with FrameLayout but you could use LinearLayout or RelativeLayout too). Then you set the padding on the outer element.
<!-- we put the ImageView inside a Frame so that we can change -->
<!-- the padding size to create borders -->
<FrameLayout
android:layout_width="140dp"
android:layout_height="140dp"
android:id="#+id/image_frame"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/image_view"
/>
</FrameLayout>
Then in the code: (use frame instead of img)
FrameLayout frame = (FrameLayout)itemLayout.findViewById(R.id.image_frame);
frame.setPadding((int) x, (int) y, 0, 0);
If I understand what you want to do, this will do it.
You can do it easily by merging BitMaps.
I have created a test project which loads 2 image bitmaps with Glide and then merges them in one BitMap using canvas and then set it on any ImageView.
You can also save the image in a file on SD Card to avoid heavy calculation each time. It looks like this (Gif - https://github.com/hiteshsahu/Android-Merge-Bitmaps-With-Glide/blob/master/Art/demo.gif ):-
I have used a single Image view as the background of my Frame layout and I have used seek bar to change the relative position of the movie poster Raees. You can change start X and Y coordinates of either of the bitmaps using those seekbars.
How I did it:-
This is the code snippet of how I did it. It is self-explanatory
Glide.with(context)
.load(R.drawable.raees)
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
// Add Bitmap in Array and load next bitmap
arts[0] = resource;
Glide.with(context)
.load(R.drawable.raees_edited)
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource1, GlideAnimation<? super Bitmap> glideAnimation) {
// Both Bitmap loaded do the Merging
arts[1] = resource1;
//Set seekbars Max
satrtX1SeekBar.setMax(arts[0].getWidth());
satrtX2SeekBar.setMax(arts[1].getWidth());
satrtY1SeekBar.setMax(arts[0].getHeight());
satrtY2SeekBar.setMax(arts[1].getHeight());
Bitmap bmOverlay = Bitmap.createBitmap(arts[0].getWidth(), arts[0].getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmOverlay);
if (shouldDrawLeftHalf) {
// Rect(left, top, right, bottom)
canvas.drawBitmap(arts[0],
new Rect(0, 0, arts[0].getWidth() / 2, arts[0].getHeight()),
new Rect(0, 0, arts[0].getWidth() / 2, arts[0].getHeight()), null);
} else {
canvas.drawBitmap(arts[0], startX1, startY1, null);
}
if (shouldDrawRightHalf) {
// Rect(left, top, right, bottom)
canvas.drawBitmap(arts[1],
new Rect(arts[1].getWidth() / 2, 0, arts[0].getWidth(), arts[1].getHeight()),
new Rect(arts[1].getWidth() / 2, 0, arts[1].getWidth(), arts[1].getHeight()), null);
} else {
canvas.drawBitmap(arts[1], startX2, startY2, null);
}
background.setImageBitmap(bmOverlay);
// saveToDisk("Test", bmOverlay);
}
});
}
});
You can download entire project from My GitHub
https://github.com/hiteshsahu/Android-Merge-Bitmaps-With-Glide
Eventually i have found the solution of my question -
Thanks for all the help provided.
So, basically i have view height, view width, top coordinate, left coordinate, Original Image Height and Original Image Width.
I need to calculate the aspect factor regarding View and Image.
Here is the formula for that -
float newAspectFactor = 0;
if (viewHeight > viewWidth) {
//Aspect factor for Height
newAspectFactor = viewHeight / bean.getPostMedia().get(i).getImg_height();
} else {
//Aspect factor for Width
newAspectFactor = viewWidth / bean.getPostMedia().get(i).getImg_width();
}
float imgNewHeight = newAspectFactor * imageHeight;
float imgNewWidth = newAspectFactor * imageWidth;
Logger.logsError(TAG, " Image New Height : " + imgNewHeight);
Logger.logsError(TAG, " Image New Width : " + imgNewWidth);
if (imgNewHeight < viewHeight) {
newAspectFactor = viewHeight / bean.getPostMedia().get(i).getImg_height();
} else if (imgNewWidth < viewWidth) {
newAspectFactor = viewWidth / bean.getPostMedia().get(i).getImg_width();
}
Top and Left coordinates.
float x = 42.0;
float y = 0.0;
Magic starts from here.
Matrix m = img.getImageMatrix();
RectF drawableRect = new RectF(x,
y,
((x * newAspectFactor) + viewWidth) / newAspectFactor,
imageHeight);
RectF viewRect = new RectF(0,
0,
viewWidth,
viewHeight);
m.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.FILL);
img.setImageMatrix(m);
img.setScaleType(ImageView.ScaleType.MATRIX);
imageLoaderNew.displayImage("IMAGE URL", img, optionsPostImg);
I need an ImageView that can be touched zoom in/out and pan. Each time when I touch the ImageView, I need to know the touch position of the source image. For example the image resolution is 1280*720, even the image in the ImageView is zoomed in, I still know exactly the touch position of the image(not touch position of the ImageView)
Thanks.
Why don't you skip all that and use an existing library?
Try this: PhotoView works perfectly for me on API 23, with just a few lines of code you will be able to zoom in/out and pan
compile 'com.github.chrisbanes:PhotoView:1.3.0'
repositories {
...
maven { url "https://jitpack.io" }
}
Example
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.
// (not needed unless you are going to change the drawable later)
mAttacher = new PhotoViewAttacher(mImageView);
}
// If you later call mImageView.setImageDrawable/setImageBitmap/setImageResource/etc then you just need to call
mAttacher.update();
Finally, I used this open source code
https://github.com/sephiroth74/ImageViewZoom
I modified the code, but there is still a lot of bugs in this open source project, I have fixed several of them.
The following function was added to class ImageViewTouch.GestureListener which is used to transfer touchpoint(in screen) to point in the real image(no matter the image is zoom in/out).
private PointF calculatePositionInSourceImage(PointF touchPointF) {
//point relative to imageRect
PointF touchPointRelativeToImageRect = new PointF();
RectF imageRect = getBitmapRect();
touchPointRelativeToImageRect.set(touchPointF.x - imageRect.left,
touchPointF.y - imageRect.top);
//real image resolution
int imageWidth = getDrawable().getIntrinsicWidth();
int imageHeight = getDrawable().getIntrinsicHeight();
//touch point in image
PointF touchPointRelativeToImage = new PointF();
touchPointRelativeToImage.set(touchPointRelativeToImageRect.x/imageRect.width() * imageWidth,
touchPointRelativeToImageRect.y/imageRect.height() * imageHeight);
if(touchPointRelativeToImage.x < 0 || touchPointRelativeToImage.y < 0 )
touchPointRelativeToImage.set(0,0);
return touchPointRelativeToImage;
}
I have created an app that actually uses flood fill algorithm to fill colors in bitmaps. I have created some bitmaps (200x200) but I don't know the exact size of bitmap that I should create, I want bitmaps to cover full screen and when I scale bitmaps, they become blur and flood fill doesn't work on them. I saw an app that used GridView to show images and click on image started new activity with image covering full screen. How can I achieve this. Attached is CustomImage that I've use to show bitmap. Any help will be appreciated.
EDITED: I know that GridView doesn't scale up image to full screen, it use another image. That app behaves same for different screen size, those image fill screen of any size without effecting the quality.
public class CustomImage extends View {
public CustomImage(Context context) {
super(context);
} // end constructor
public CustomImage(Context context, int resource_id) {
super(context);
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPoint = new Point();
mProgressIndicator = (ProgressIndicator) context;
mBitmap = BitmapFactory.decodeResource(getResources(), resource_id)
.copy(Bitmap.Config.ARGB_8888, true);
} // end constructor
#Override
protected void onDraw(Canvas canvas) {
// draw image
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
} // end onDraw
#Override
public boolean onTouchEvent(MotionEvent event) {
int bWidth = mBitmap.getWidth();
int bHeight = mBitmap.getHeight();
mPoint.x = (int) event.getX();
mPoint.y = (int) event.getY();
mPoint.x = (mPoint.x > bWidth) ? (bWidth - 5) : mPoint.x;
mPoint.y = (mPoint.y > bHeight) ? (bHeight - 5) : mPoint.y;
switch (event.getAction()) {
// called when screen clicked i.e New touch started
case MotionEvent.ACTION_DOWN:
mProgressIndicator.updateProgress(0);
new Filler(mBitmap, mPoint, mPaint.getColor(), mBitmap.getPixel(
mPoint.x, mPoint.y), this).execute();
invalidate();
} // end Case
return true;
} // end onTouchEvent
public void setColor(int color) {
mPaint.setColor(color);
} // end setColor
} // end Class
The GridView you have seen might be using two versions of the same image. One as a big image and another as a scaled down thumbnail image for the grid.
I want to display animated GIF images in my aplication.
As I found out the hard way Android doesn't support animated GIF natively.
However it can display animations using AnimationDrawable:
Develop > Guides > Images & Graphics > Drawables Overview
The example uses animation saved as frames in application resources but what I need is to display animated gif directly.
My plan is to break animated GIF to frames and add each frame as drawable to AnimationDrawable.
Does anyone know how to extract frames from animated GIF and convert each of them into Drawable?
Android actually can decode and display animated GIFs, using android.graphics.Movie class.
This is not too much documented, but is in SDK Reference. Moreover, it is used in Samples in ApiDemos in BitmapDecode example with some animated flag.
UPDATE:
Use glide:
dependencies {
implementation 'com.github.bumptech.glide:glide:4.9.0'
}
usage:
Glide.with(context).load(GIF_URI).into(new DrawableImageViewTarget(IMAGE_VIEW));
see docs
also put (main/assets/htmls/name.gif) [with this html adjust to the size]
<html style="margin: 0;">
<body style="margin: 0;">
<img src="name.gif" style="width: 100%; height: 100%" />
</body>
</html>
declare in your Xml for example like this (main/res/layout/name.xml): [you define the size, for example]
<WebView
android:layout_width="70dp"
android:layout_height="70dp"
android:id="#+id/webView"
android:layout_gravity="center_horizontal" />
in your Activity put the next code inside of onCreate
web = (WebView) findViewById(R.id.webView);
web.setBackgroundColor(Color.TRANSPARENT); //for gif without background
web.loadUrl("file:///android_asset/htmls/name.html");
if you want load dynamically you have to load the webview with data:
// or "[path]/name.gif" (e.g: file:///android_asset/name.gif for resources in asset folder), and in loadDataWithBaseURL(), you don't need to set base URL, on the other hand, it's similar to loadData() method.
String gifName = "name.gif";
String yourData = "<html style=\"margin: 0;\">\n" +
" <body style=\"margin: 0;\">\n" +
" <img src=" + gifName + " style=\"width: 100%; height: 100%\" />\n" +
" </body>\n" +
" </html>";
// Important to add this attribute to webView to get resource from outside.
webView.getSettings().setAllowFileAccess(true);
// Notice: should use loadDataWithBaseURL. BaseUrl could be the base url such as the path to asset folder, or SDCard or any other path, where your images or the other media resides related to your html
webView.loadDataWithBaseURL("file:///android_asset/", yourData, "text/html", "utf-8", null);
// Or if you want to load image from SD card or where else, here is the idea.
String base = Environment.getExternalStorageDirectory().getAbsolutePath().toString();
webView.loadDataWithBaseURL(base + '/', yourData, "text/html", "utf-8", null);
suggestion: is better load gif with static images for more information check https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
That's it, I hope you help.
Currently we can use Glide https://github.com/bumptech/glide
I solved the problem by splitting gif animations into frames before saving it to phone, so I would not have to deal with it in Android.
Then I download every frame onto phone, create Drawable from it and then create AnimationDrawable - very similar to example from my question
i found a very easy way, with a nice and simple working example here
display animated widget
Before getting it working there are some chages to do do in the code
IN THE FOLLOWING
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceStated);
setContentView(new MYGIFView());
}
}
just replace
setContentView(new MYGIFView());
in
setContentView(new MYGIFView(this));
AND IN
public GIFView(Context context) {
super(context);
Provide your own gif animation file
is = context.getResources().openRawResource(R.drawable.earth);
movie = Movie.decodeStream(is);
}
REPLACE THE FIRST LINE IN
public MYGIFView(Context context) {
according to the name of the class...
after done this little changes it should work as for me...
hope this help
Glide 4.6
1. To Load gif
GlideApp.with(context)
.load(R.raw.gif) // or url
.into(imageview);
2. To get the file object
GlideApp.with(context)
.asGif()
.load(R.raw.gif) //or url
.into(new SimpleTarget<GifDrawable>() {
#Override
public void onResourceReady(#NonNull GifDrawable resource, #Nullable Transition<? super GifDrawable> transition) {
resource.start();
//resource.setLoopCount(1);
imageView.setImageDrawable(resource);
}
});
Ways to show animated GIF on Android:
Movie class. As mentioned above, it's fairly buggy.
WebView. It's very simple to use and usually works. But sometimes it starts to misbehave, and it's always on some obscure devices you don't have. Plus, you can’t use multiple instances in any kind of list views, because it does things to your memory. Still, you might consider it as a primary approach.
Custom code to decode gifs into bitmaps and show them as Drawable or ImageView. I'll mention two libraries:
https://github.com/koral--/android-gif-drawable - decoder is implemented in C, so it's very efficient.
https://code.google.com/p/giffiledecoder - decoder is implemented in Java, so it's easier to work with. Still reasonably efficient, even with large files.
You'll also find many libraries based on GifDecoder class. That's also a Java-based decoder, but it works by loading the entire file into memory, so it's only applicable to small files.
I had a really hard time to have animated gif working in Android. I only had following two working:
WebView
Ion
WebView works OK and really easy, but the problem is it makes the view loads slower and the app would be unresponsive for a second or so. I did not like that. So I have tried different approaches (DID NOT WORK):
ImageViewEx is deprecated!
picasso did not load animated gif
android-gif-drawable looks great, but it caused some wired NDK issues in my project. It caused my local NDK library stop working, and I was not able to fix it
I had some back and forth with Ion; Finally, I have it working, and it is really fast :-)
Ion.with(imgView)
.error(R.drawable.default_image)
.animateGif(AnimateGifMode.ANIMATE)
.load("file:///android_asset/animated.gif");
Glide
Image Loader Library for Android, recommended by Google.
Glide is quite similar to Picasso but this is much faster than Picasso.
Glide consumes less memory than Picasso.
What that Glide has but Picasso doesn't
An ability to load GIF Animation to a simple ImageView might be the most interesting feature of Glide. And yes, you can't do that with Picasso.
Some important links-
https://github.com/bumptech/glide
http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
Use ImageViewEx, a library that makes using a gif as easy as using an ImageView.
Try this, bellow code display gif file in progressbar
loading_activity.xml(in Layout folder)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff" >
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:indeterminate="true"
android:indeterminateDrawable="#drawable/custom_loading"
android:visibility="gone" />
</RelativeLayout>
custom_loading.xml(in drawable folder)
here i put black_gif.gif(in drawable folder), you can put your own gif here
<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/black_gif"
android:pivotX="50%"
android:pivotY="50%" />
LoadingActivity.java(in res folder)
public class LoadingActivity extends Activity {
ProgressBar bar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
bar = (ProgressBar) findViewById(R.id.progressBar);
bar.setVisibility(View.VISIBLE);
}
}
Nobody has mentioned the Ion or Glide library. they work very well.
It's easier to handle compared to a WebView.
I have had success with the solution proposed within this article, a class called GifMovieView, which renders a View which can then be displayed or added to a specific ViewGroup. Check out the other methods presented in parts 2 and 3 of the specified article.
The only drawback to this method is that the antialiasing on the movie is not that good (must be a side-effect of using the "shady" Android Movie Class). You are then better off setting the background to a solid color within your animated GIF.
Some thoughts on the BitmapDecode example... Basically it uses the ancient, but rather featureless Movie class from android.graphics.
On recent API versions you need to turn off hardware acceleration, as described here. It was segfaulting for me otherwise.
<activity
android:hardwareAccelerated="false"
android:name="foo.GifActivity"
android:label="The state of computer animation 2014">
</activity>
Here is the BitmapDecode example shortened with only the GIF part. You have to make your own Widget (View) and draw it by yourself. Not quite as powerful as an ImageView.
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.*;
import android.view.View;
public class GifActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GifView(this));
}
static class GifView extends View {
Movie movie;
GifView(Context context) {
super(context);
movie = Movie.decodeStream(
context.getResources().openRawResource(
R.drawable.some_gif));
}
#Override
protected void onDraw(Canvas canvas) {
if (movie != null) {
movie.setTime(
(int) SystemClock.uptimeMillis() % movie.duration());
movie.draw(canvas, 0, 0);
invalidate();
}
}
}
}
2 other methods, one with ImageView another with WebView can be found in this fine tutorial. The ImageView method uses the Apache licensed android-gifview from Google Code.
#PointerNull gave good solution, but it is not perfect. It doesn't work on some devices with big files and show buggy Gif animation with delta frames on pre ICS version.
I found solution without this bugs. It is library with native decoding to drawable: koral's android-gif-drawable.
For only android API (Android Pie)28 and + use AnimatedImageDrawable as
// ImageView from layout
val ima : ImageView = findViewById(R.id.img_gif)
// create AnimatedDrawable
val decodedAnimation = ImageDecoder.decodeDrawable(
// create ImageDecoder.Source object
ImageDecoder.createSource(resources, R.drawable.tenor))
// set the drawble as image source of ImageView
ima.setImageDrawable(decodedAnimation)
// play the animation
(decodedAnimation as? AnimatedImageDrawable)?.start()
XML code, add a ImageView
<ImageView
android:id="#+id/img_gif"
android:background="#drawable/ic_launcher_background" <!--Default background-->
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="200dp"
android:layout_height="200dp" />
AnimatedImageDrawable is a child of Drawable and created by ImageDecoder.decodeDrawable
ImageDecoder.decodeDrawable which further required the instance of ImageDecoder.Source created by ImageDecoder.createSource.
ImageDecoder.createSource can only take source as a name, ByteBuffer, File, resourceId, URI, ContentResolver to create source object and uses it to create AnimatedImageDrawable as Drawable (polymorphic call)
static ImageDecoder.Source createSource(AssetManager assets, String fileName)
static ImageDecoder.Source createSource(ByteBuffer buffer)
static ImageDecoder.Source createSource(File file)
static ImageDecoder.Source createSource(Resources res, int resId)
static ImageDecoder.Source createSource(ContentResolver cr, Uri uri)
Note: You can also create Bitmap using ImageDecoder#decodeBitmap.
Output:
AnimatedDrawable also supports resizing, frame and color manipulation
Put it into a WebView, it has to be able to display it correctly, since the default browser supports gif files. (Froyo+, if i am not mistaken)
There are two options to load animated gifs into our Android apps
1)Using Glide to load the gif into an ImageView.
String urlGif = "https://cdn.dribbble.com/users/263558/screenshots/1337078/dvsd.gif";
//add Glide implementation into the build.gradle file.
ImageView imageView = (ImageView)findViewById(R.id.imageView);
Uri uri = Uri.parse(urlGif);
Glide.with(getApplicationContext()).load(uri).into(imageView);
2) Using an html to load the gif into a WebView
Create the html with the address to the .gif file:
<html style="margin: 0;">
<body style="margin: 0;">
<img src="https://..../myimage.gif" style="width: 100%; height: 100%" />
</body>
</html>
store this file into the assets directory:
The load this html into the WebView of your application:
WebView webView = (WebView)findViewById(R.id.webView);
webView = (WebView) findViewById(R.id.webView);
webView.loadUrl("file:///android_asset/html/webpage_gif.html");
Heres is a complete example of this two options.
I think the better library to handle gif files is this one: by koral
Used it and i'm successful and this library is dedicated to GIF'S; but where as the picasso and glide are general purpose image framework; so i think the developers of this library have entirely concentrated on gif files
Use fresco. Here's how to do it:
http://frescolib.org/docs/animations.html
Here's the repo with the sample:
https://github.com/facebook/fresco/tree/master/samples/animation
Beware fresco does not support wrap content!
Just wanted to add that the Movie class is now deprecated.
This class was deprecated in API level P.
It is recommended to use this
AnimatedImageDrawable
Drawable for drawing animated images (like GIF).
Similar to what #Leonti said, but with a little more depth:
What I did to solve the same problem was open up GIMP, hide all layers except for one, export it as its own image, and then hide that layer and unhide the next one, etc., until I had individual resource files for each one. Then I could use them as frames in the AnimationDrawable XML file.
Something I did for showing gifs in apps. I extended ImageView so people can use its attributes freely. It can show gifs from url or from the assets directory.
The library also makes it easy for extending classes to inherit from it and extend it to support different methods to initialize the gif.
https://github.com/Gavras/GIFView
There's a little guide on the github page.
It was also published on Android Arsenal:
https://android-arsenal.com/details/1/4947
Use example:
From XML:
<com.whygraphics.gifview.gif.GIFView xmlns:gif_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_activity_gif_vie"
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="center"
gif_view:gif_src="url:http://pop.h-cdn.co/assets/16/33/480x264/gallery-1471381857-gif-season-2.gif" />
In the activity:
GIFView mGifView = (GIFView) findViewById(R.id.main_activity_gif_vie);
mGifView.setOnSettingGifListener(new GIFView.OnSettingGifListener() {
#Override
public void onSuccess(GIFView view, Exception e) {
Toast.makeText(MainActivity.this, "onSuccess()", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(GIFView view, Exception e) {
}
});
Setting the gif programmatically:
mGifView.setGifResource("asset:gif1");
Easiest way - Can be consider the below code
We can take advantage of Imageview setImageResource , refer below code for the same.
The below code can be used to show the image like gif incase if you have the multiple split image of gif. Just split the gif into individual png from a online tool and put image in the drawable like the below order
image_1.png, image_2.png, etc.
Have the handler to change the image dynamically.
int imagePosition = 1;
Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
updateImage();
}
};
public void updateImage() {
appInstance.runOnUiThread(new Runnable() {
#Override
public void run() {
int resId = getResources().getIdentifier("image_" + imagePosition, "drawable", appInstance.getPackageName());
gifImageViewDummy.setImageResource(resId);
imagePosition++;
//Consider you have 30 image for the anim
if (imagePosition == 30) {
//this make animation play only once
handler.removeCallbacks(runnable);
} else {
//You can define your own time based on the animation
handler.postDelayed(runnable, 50);
}
//to make animation to continue use below code and remove above if else
// if (imagePosition == 30)
//imagePosition = 1;
// handler.postDelayed(runnable, 50);
//
}
});
}
The easy way to display animated GIF directly from URL to your app layout is to use WebView class.
Step 1:
In your layout XML
<WebView
android:id="#+id/webView"
android:layout_width="50dp"
android:layout_height="50dp"
/>
Step 2: In your Activity
WebView wb;
wb = (WebView) findViewById(R.id.webView);
wb.loadUrl("https://.......);
Step 3: In your Manifest.XML make Internet permission
<uses-permission android:name="android.permission.INTERNET" />
Step 4: In case you want to make your GIF background transparent and make GIF fit to your Layout
wb.setBackgroundColor(Color.TRANSPARENT);
wb.getSettings().setLoadWithOverviewMode(true);
wb.getSettings().setUseWideViewPort(true);
If you want to use Glide for loading gif:
Glide.with(this)
.asGif()
.load(R.raw.onboarding_layers) //Your gif resource
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.NONE))
.listener(new RequestListener<GifDrawable>() {
#Override
public boolean onLoadFailed(#Nullable #org.jetbrains.annotations.Nullable GlideException e, Object model, Target<GifDrawable> target, boolean isFirstResource) {
return false;
}
#Override
public boolean onResourceReady(GifDrawable resource, Object model, Target<GifDrawable> target, DataSource dataSource, boolean isFirstResource) {
resource.setLoopCount(1);
return false;
}
})
.into((ImageView) view.findViewById(R.id.layer_icons));
To save resources there is glide library for.
Have no idea why to use anything else, especialy webview to show image only.
Glide is perfect and easy library that prepares animated drawable from gif and put it directly to imageview.
The logic of gifdrawable handle animation itself.
Gif have lzw ziped raw rgb data of an animation inside.
There is no reason for complicated usage of webview and manage more files to show just a gif file in app.
First of all the Android browser should support Animated GIFs. If it doesn't then it's a bug! Have a look at the issue trackers.
If you're displaying these animated GIFs outside of a browser it might be a different story. To do what you're asking would require external library that supports the decoding of Animated GIFs.
The first port of call would be to look at Java2D or JAI (Java Advanced Imaging) API, although I would be very surprised if Android Dalvik would support those libraries in your App.
public class Test extends GraphicsActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private Bitmap mBitmap;
private Bitmap mBitmap2;
private Bitmap mBitmap3;
private Bitmap mBitmap4;
private Drawable mDrawable;
private Movie mMovie;
private long mMovieStart;
// Set to false to use decodeByteArray
private static final boolean DECODE_STREAM = true;
private static byte[] streamToBytes(InputStream is) {
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int len;
try {
while ((len = is.read(buffer)) >= 0) {
os.write(buffer, 0, len);
}
} catch (java.io.IOException e) {
}
return os.toByteArray();
}
public SampleView(Context context) {
super(context);
setFocusable(true);
java.io.InputStream is;
is = context.getResources().openRawResource(R.drawable.icon);
BitmapFactory.Options opts = new BitmapFactory.Options();
Bitmap bm;
opts.inJustDecodeBounds = true;
bm = BitmapFactory.decodeStream(is, null, opts);
// now opts.outWidth and opts.outHeight are the dimension of the
// bitmap, even though bm is null
opts.inJustDecodeBounds = false; // this will request the bm
opts.inSampleSize = 4; // scaled down by 4
bm = BitmapFactory.decodeStream(is, null, opts);
mBitmap = bm;
// decode an image with transparency
is = context.getResources().openRawResource(R.drawable.icon);
mBitmap2 = BitmapFactory.decodeStream(is);
// create a deep copy of it using getPixels() into different configs
int w = mBitmap2.getWidth();
int h = mBitmap2.getHeight();
int[] pixels = new int[w * h];
mBitmap2.getPixels(pixels, 0, w, 0, 0, w, h);
mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h,
Bitmap.Config.ARGB_8888);
mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h,
Bitmap.Config.ARGB_4444);
mDrawable = context.getResources().getDrawable(R.drawable.icon);
mDrawable.setBounds(150, 20, 300, 100);
is = context.getResources().openRawResource(R.drawable.animated_gif);
if (DECODE_STREAM) {
mMovie = Movie.decodeStream(is);
} else {
byte[] array = streamToBytes(is);
mMovie = Movie.decodeByteArray(array, 0, array.length);
}
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFCCCCCC);
Paint p = new Paint();
p.setAntiAlias(true);
canvas.drawBitmap(mBitmap, 10, 10, null);
canvas.drawBitmap(mBitmap2, 10, 170, null);
canvas.drawBitmap(mBitmap3, 110, 170, null);
canvas.drawBitmap(mBitmap4, 210, 170, null);
mDrawable.draw(canvas);
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) { // first time
mMovieStart = now;
}
if (mMovie != null) {
int dur = mMovie.duration();
if (dur == 0) {
dur = 1000;
}
int relTime = (int) ((now - mMovieStart) % dur);
mMovie.setTime(relTime);
mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight()
- mMovie.height());
invalidate();
}
}
}
}
class GraphicsActivity extends Activity {
// set to true to test Picture
private static final boolean TEST_PICTURE = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void setContentView(View view) {
if (TEST_PICTURE) {
ViewGroup vg = new PictureLayout(this);
vg.addView(view);
view = vg;
}
super.setContentView(view);
}
}
class PictureLayout extends ViewGroup {
private final Picture mPicture = new Picture();
public PictureLayout(Context context) {
super(context);
}
public PictureLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void addView(View child) {
if (getChildCount() > 1) {
throw new IllegalStateException(
"PictureLayout can host only one direct child");
}
super.addView(child);
}
#Override
public void addView(View child, int index) {
if (getChildCount() > 1) {
throw new IllegalStateException(
"PictureLayout can host only one direct child");
}
super.addView(child, index);
}
#Override
public void addView(View child, LayoutParams params) {
if (getChildCount() > 1) {
throw new IllegalStateException(
"PictureLayout can host only one direct child");
}
super.addView(child, params);
}
#Override
public void addView(View child, int index, LayoutParams params) {
if (getChildCount() > 1) {
throw new IllegalStateException(
"PictureLayout can host only one direct child");
}
super.addView(child, index, params);
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
maxWidth += getPaddingLeft() + getPaddingRight();
maxHeight += getPaddingTop() + getPaddingBottom();
Drawable drawable = getBackground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
resolveSize(maxHeight, heightMeasureSpec));
}
private void drawPict(Canvas canvas, int x, int y, int w, int h, float sx,
float sy) {
canvas.save();
canvas.translate(x, y);
canvas.clipRect(0, 0, w, h);
canvas.scale(0.5f, 0.5f);
canvas.scale(sx, sy, w, h);
canvas.drawPicture(mPicture);
canvas.restore();
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(mPicture.beginRecording(getWidth(), getHeight()));
mPicture.endRecording();
int x = getWidth() / 2;
int y = getHeight() / 2;
if (false) {
canvas.drawPicture(mPicture);
} else {
drawPict(canvas, 0, 0, x, y, 1, 1);
drawPict(canvas, x, 0, x, y, -1, 1);
drawPict(canvas, 0, y, x, y, 1, -1);
drawPict(canvas, x, y, x, y, -1, -1);
}
}
#Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
location[0] = getLeft();
location[1] = getTop();
dirty.set(0, 0, getWidth(), getHeight());
return getParent();
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = super.getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final int childLeft = getPaddingLeft();
final int childTop = getPaddingTop();
child.layout(childLeft, childTop,
childLeft + child.getMeasuredWidth(),
childTop + child.getMeasuredHeight());
}
}
}
}
Background
Facebook app has a nice transition animation between a small image on a post, and an enlarged mode of it that the user can also zoom to it.
As I see it, the animation not only enlarges and moves the imageView according to its previous location and size, but also reveals content instead of stretching the content of the imageView.
This can be seen using the next sketch i've made:
The question
How did they do it? did they really have 2 views animating to reveal the content?
How did they make it so fluid as if it's a single view?
the only tutorial i've seen (link here) of an image that is enlarged to full screen doesn't show well when the thumbnail is set to be center-crop.
Not only that, but it works even on low API of Android.
does anybody know of a library that has a similar ability?
EDIT: I've found a way and posted an answer, but it's based on changing the layoutParams , and i think it's not efficient and recommended.
I've tried using the normal animations and other animation tricks, but for now that's the only thing that worked for me.
If anyone know what to do in order to make it work in a better way, please write it down.
Ok, i've found a possible way to do it.
i've made the layoutParams as variables that keep changing using the ObjectAnimator of the nineOldAndroids library. i think it's not the best way to achieve it since it causes a lot of onDraw and onLayout, but if the container has only a few views and doesn't change its size, maybe it's ok.
the assumption is that the imageView that i animate will take the exact needed size in the end, and that (currently) both the thumbnail and the animated imageView have the same container (but it should be easy to change it.
as i've tested, it is also possible to add zoom features by extending the TouchImageView class . you just set the scale type in the beginning to center-crop, and when the animation ends you set it back to matrix, and if you want, you can set the layoutParams to fill the entire container (and set the margin to 0,0).
i also wonder how come the AnimatorSet didn't work for me, so i will show here something that works, hoping someone could tell me what i should do.
here's the code:
MainActivity.java
public class MainActivity extends Activity {
private static final int IMAGE_RES_ID = R.drawable.test_image_res_id;
private static final int ANIM_DURATION = 5000;
private final Handler mHandler = new Handler();
private ImageView mThumbnailImageView;
private CustomImageView mFullImageView;
private Point mFitSizeBitmap;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFullImageView = (CustomImageView) findViewById(R.id.fullImageView);
mThumbnailImageView = (ImageView) findViewById(R.id.thumbnailImageView);
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
prepareAndStartAnimation();
}
}, 2000);
}
private void prepareAndStartAnimation() {
final int thumbX = mThumbnailImageView.getLeft(), thumbY = mThumbnailImageView.getTop();
final int thumbWidth = mThumbnailImageView.getWidth(), thumbHeight = mThumbnailImageView.getHeight();
final View container = (View) mFullImageView.getParent();
final int containerWidth = container.getWidth(), containerHeight = container.getHeight();
final Options bitmapOptions = getBitmapOptions(getResources(), IMAGE_RES_ID);
mFitSizeBitmap = getFitSize(bitmapOptions.outWidth, bitmapOptions.outHeight, containerWidth, containerHeight);
mThumbnailImageView.setVisibility(View.GONE);
mFullImageView.setVisibility(View.VISIBLE);
mFullImageView.setContentWidth(thumbWidth);
mFullImageView.setContentHeight(thumbHeight);
mFullImageView.setContentX(thumbX);
mFullImageView.setContentY(thumbY);
runEnterAnimation(containerWidth, containerHeight);
}
private Point getFitSize(final int width, final int height, final int containerWidth, final int containerHeight) {
int resultHeight, resultWidth;
resultHeight = height * containerWidth / width;
if (resultHeight <= containerHeight) {
resultWidth = containerWidth;
} else {
resultWidth = width * containerHeight / height;
resultHeight = containerHeight;
}
return new Point(resultWidth, resultHeight);
}
public void runEnterAnimation(final int containerWidth, final int containerHeight) {
final ObjectAnimator widthAnim = ObjectAnimator.ofInt(mFullImageView, "contentWidth", mFitSizeBitmap.x)
.setDuration(ANIM_DURATION);
final ObjectAnimator heightAnim = ObjectAnimator.ofInt(mFullImageView, "contentHeight", mFitSizeBitmap.y)
.setDuration(ANIM_DURATION);
final ObjectAnimator xAnim = ObjectAnimator.ofInt(mFullImageView, "contentX",
(containerWidth - mFitSizeBitmap.x) / 2).setDuration(ANIM_DURATION);
final ObjectAnimator yAnim = ObjectAnimator.ofInt(mFullImageView, "contentY",
(containerHeight - mFitSizeBitmap.y) / 2).setDuration(ANIM_DURATION);
widthAnim.start();
heightAnim.start();
xAnim.start();
yAnim.start();
// TODO check why using AnimatorSet doesn't work here:
// final com.nineoldandroids.animation.AnimatorSet set = new AnimatorSet();
// set.playTogether(widthAnim, heightAnim, xAnim, yAnim);
}
public static BitmapFactory.Options getBitmapOptions(final Resources res, final int resId) {
final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, bitmapOptions);
return bitmapOptions;
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.facebookstylepictureanimationtest.CustomImageView
android:id="#+id/fullImageView"
android:layout_width="0px"
android:layout_height="0px"
android:background="#33ff0000"
android:scaleType="centerCrop"
android:src="#drawable/test_image_res_id"
android:visibility="invisible" />
<ImageView
android:id="#+id/thumbnailImageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:scaleType="centerCrop"
android:src="#drawable/test_image_res_id" />
</RelativeLayout>
CustomImageView.java
public class CustomImageView extends ImageView {
public CustomImageView(final Context context) {
super(context);
}
public CustomImageView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public CustomImageView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
public void setContentHeight(final int contentHeight) {
final LayoutParams layoutParams = getLayoutParams();
layoutParams.height = contentHeight;
setLayoutParams(layoutParams);
}
public void setContentWidth(final int contentWidth) {
final LayoutParams layoutParams = getLayoutParams();
layoutParams.width = contentWidth;
setLayoutParams(layoutParams);
}
public int getContentHeight() {
return getLayoutParams().height;
}
public int getContentWidth() {
return getLayoutParams().width;
}
public int getContentX() {
return ((MarginLayoutParams) getLayoutParams()).leftMargin;
}
public void setContentX(final int contentX) {
final MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = contentX;
setLayoutParams(layoutParams);
}
public int getContentY() {
return ((MarginLayoutParams) getLayoutParams()).topMargin;
}
public void setContentY(final int contentY) {
final MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
layoutParams.topMargin = contentY;
setLayoutParams(layoutParams);
}
}
Another solution, if you just want to make an animation of an image from small to large, you can try ActivityOptions.makeThumbnailScaleUpAnimation or makeScaleUpAnimationand see if they suit you.
http://developer.android.com/reference/android/app/ActivityOptions.html#makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int)
You can achieve this through Transition Api, and this the resut gif:
essential code below:
private void zoomIn() {
ViewGroup.LayoutParams layoutParams = mImage.getLayoutParams();
int width = layoutParams.width;
int height = layoutParams.height;
layoutParams.width = (int) (width * 2);
layoutParams.height = height * 2;
mImage.setLayoutParams(layoutParams);
mImage.setScaleType(ImageView.ScaleType.FIT_CENTER);
TransitionSet transitionSet = new TransitionSet();
Transition bound = new ChangeBounds();
transitionSet.addTransition(bound);
Transition changeImageTransform = new ChangeImageTransform();
transitionSet.addTransition(changeImageTransform);
transitionSet.setDuration(1000);
TransitionManager.beginDelayedTransition(mRootView, transitionSet);
}
View demo on github
sdk version >= 21
I found a way to get a similar affect in a quick prototype. It might not be suitable for production use (I'm still investigating), but it is quick and easy.
Use a fade transition on your activity/fragment transition (which starts with the ImageView in exactly the same position). The fragment version:
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
...etc
The activity version:
Intent intent = new Intent(context, MyDetailActivity.class);
startActivity(intent);
getActivity().overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
This gives a smooth transition without a flicker.
Adjust the layouts dynamically in the onStart() of the new fragment (you need to save member fields to the appropriate parts of your UI in onCreateView, and add some flags to ensure this code only gets called once).
#Override
public void onStart() {
super.onStart();
// Remove the padding on any layouts that the image view is inside
mMainLayout.setPadding(0, 0, 0, 0);
// Get the screen size using a utility method, e.g.
// http://stackoverflow.com/a/12082061/112705
// then work out your desired height, e.g. using the image aspect ratio.
int desiredHeight = (int) (screenWidth * imgAspectRatio);
// Resize the image to fill the whole screen width, removing
// any layout margins that it might have (you may need to remove
// padding too)
LinearLayout.LayoutParams layoutParams =
new LinearLayout.LayoutParams(screenWidth, desiredHeight);
layoutParams.setMargins(0, 0, 0, 0);
mImageView.setLayoutParams(layoutParams);
}
I think the easiest way is to animate the height of the ImageView (a regular imageview, not necessary a custom view) while keeping the scaleType to centerCrop until full height, which you can know in advance if you set the image height to wrap_content in your layout and then use a ViewTreeObserver to know when the layout has ended, so you can get the ImageView height and then set the new "collapsed" height. I have not tested it but this is how I would do it.
You can also have a look at this post, they do something similar http://nerds.airbnb.com/host-experience-android/
I'm not sure why everyone is talking about the framework. Using other peoples code can be great at times; but it sounds like what you are after is precise control over the look. By getting access to the graphics context you can have that. The task is pretty simple in any environment that has a graphics context. In android you can get it by overriding the onDraw method and using the Canvas Object. It has everything you need to draw an image at many different scales, positions and clippings. You can even use a matrix if your familiar with that type of thing.
Steps
Make sure you have exact control of positioning, scale, and clip. This means disabling any layouts or auto-alignment that might be setup inside your objects container.
Figure you out what your parameter t will be for linear interpolation and how you will want it to relate to time. How fast or slow, and will there be any easing. t should be dependent on time.
After the thumbnails are cached, load the full scale image in the background. But don't show it yet.
When the animation trigger fires, show the large image and drive your animation with your t parameter using interpolation between the initial properties' states to the final properties' states. Do this for all three properties, position, scale and clip. So for all properties do the following:
Sinterpolated = Sinitial * (t-1) + Sfinal * t;
// where
// t is between 0.0 and 1.0
// and S is the states value
// for every part of scale, position, and clip
//
// Sinitial is what you are going from
// Sfinal is what you are going to
//
// t should change from 0.0->1.0 in
// over time anywhere from 12/sec or 60/sec.
If all your properties are driven by the same parameter the animation will be smooth. As an added bonus, here is a tip for timing. As long as you can keep your t parameter between 0 and 1, easing in or out can be hacked with one line of code:
// After your t is all setup
t = t * t; // for easing in
// or
t = Math.sqrt(t); // for easing out
I made a sample code in Github.
The key of this code is using canvas.clipRect().
But, it only works when the CroppedImageview is match_parent.
To explain simply,
I leave scale and translation animation to ViewPropertyAnimator.
Then, I can focus on cropping the image.
Like above picture, calculate the clipping region, and change the clip region to final view size.
AnimationController
class ZoomAnimationController(private val view: CroppedImageView, startRect: Rect, private val viewRect: Rect, imageSize: Size) {
companion object {
const val DURATION = 300L
}
private val startViewRect: RectF
private val scale: Float
private val startClipRect: RectF
private val animatingRect: Rect
private var cropAnimation: ValueAnimator? = null
init {
val startImageRect = getProportionalRect(startRect, imageSize, ImageView.ScaleType.CENTER_CROP)
startViewRect = getProportionalRect(startImageRect, viewRect.getSize(), ImageView.ScaleType.CENTER_CROP)
scale = startViewRect.width() / viewRect.width()
val finalImageRect = getProportionalRect(viewRect, imageSize, ImageView.ScaleType.FIT_CENTER)
startClipRect = getProportionalRect(finalImageRect, startRect.getSize() / scale, ImageView.ScaleType.FIT_CENTER)
animatingRect = Rect()
startClipRect.round(animatingRect)
}
fun init() {
view.x = startViewRect.left
view.y = startViewRect.top
view.pivotX = 0f
view.pivotY = 0f
view.scaleX = scale
view.scaleY = scale
view.setClipRegion(animatingRect)
}
fun startAnimation() {
cropAnimation = createCropAnimator().apply {
start()
}
view.animate()
.x(0f)
.y(0f)
.scaleX(1f)
.scaleY(1f)
.setDuration(DURATION)
.start()
}
private fun createCropAnimator(): ValueAnimator {
return ValueAnimator.ofFloat(0f, 1f).apply {
duration = DURATION
addUpdateListener {
val weight = animatedValue as Float
animatingRect.set(
(startClipRect.left * (1 - weight) + viewRect.left * weight).toInt(),
(startClipRect.top * (1 - weight) + viewRect.top * weight).toInt(),
(startClipRect.right * (1 - weight) + viewRect.right * weight).toInt(),
(startClipRect.bottom * (1 - weight) + viewRect.bottom * weight).toInt()
)
Log.d("SSO", "animatingRect=$animatingRect")
view.setClipRegion(animatingRect)
}
}
}
private fun getProportionalRect(viewRect: Rect, imageSize: Size, scaleType: ImageView.ScaleType): RectF {
return getProportionalRect(RectF(viewRect), imageSize, scaleType)
}
private fun getProportionalRect(viewRect: RectF, imageSize: Size, scaleType: ImageView.ScaleType): RectF {
val viewRatio = viewRect.height() / viewRect.width()
if ((scaleType == ImageView.ScaleType.FIT_CENTER && viewRatio > imageSize.ratio)
|| (scaleType == ImageView.ScaleType.CENTER_CROP && viewRatio <= imageSize.ratio)) {
val width = viewRect.width()
val height = width * imageSize.ratio
val paddingY = (height - viewRect.height()) / 2f
return RectF(viewRect.left, viewRect.top - paddingY, viewRect.right, viewRect.bottom + paddingY)
} else if ((scaleType == ImageView.ScaleType.FIT_CENTER && viewRatio <= imageSize.ratio)
|| (scaleType == ImageView.ScaleType.CENTER_CROP && viewRatio > imageSize.ratio)){
val height = viewRect.height()
val width = height / imageSize.ratio
val paddingX = (width - viewRect.width()) / 2f
return RectF(viewRect.left - paddingX, viewRect.top, viewRect.right + paddingX, viewRect.bottom)
}
return RectF()
}
CroppedImageView
override fun onDraw(canvas: Canvas?) {
if (clipRect.width() > 0 && clipRect.height() > 0) {
canvas?.clipRect(clipRect)
}
super.onDraw(canvas)
}
fun setClipRegion(rect: Rect) {
clipRect.set(rect)
invalidate()
}
it only works when the CroppedImageview is match_parent,
because
The paths from start to end is included in CroppedImageView. If not, animation is not shown. So, Making it's size match_parent is easy to think.
I didn't implement the code for special case...