ViewFlipper inside ViewPager - OutOfMemory - android

I have 20 images in my app. All the images are in 640 * 360 resolution with not more than 60KB each.
I make use of Viewpager to slide the images. And use ViewFlipper inside ViewPager to flip the images.. When the user clicks on it, I show the corresponding text for the image.
The issue is that I get OutOfMemory exception when I swipe back and forth for 5 times. I read various Stackoverflow threads here!, here!, here! and Handling Large Bitmaps Efficiently! but not able to fix the issue,
Here is the code,
In Main_Fragment.java,
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
--- some code ---
--- some more code ---
View v = inflater.inflate(R.layout.main_fragment, container,false);
viewAnimator = ((ViewAnimator) v.findViewById(R.id.cardFlipper));
TextView caption_text = (TextView) v.findViewById(R.id.caption_text);
caption_text.setText(caption.toUpperCase());
ImageView main_img = (ImageView) v.findViewById(R.id.main_img);
int mainimg_resID = getResources().getIdentifier(mainimg,"drawable",context.getPackageName());
Bitmap icon = decodeSampledBitmapFromResource(getResources(),mainimg_resID,640,360);
main_img.setImageBitmap(icon);
viewAnimator.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
AnimationFactory.flipTransition((ViewAnimator) v,FlipDirection.LEFT_RIGHT);
}
});
return v;
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Can you please tell me what I'm doing wrong?? This is driving me nuts for the past few days :(

yes, I'm seen something very wrong that you are doing:
Bitmap icon = decodeSampledBitmapFromResource(getResources(),mainimg_resID,640,360);
you got it all wrong: the reqHeight and reqWidth parameters should be the image view width and height, and not the original image dimension!! that's all what calculateInSampleSize() is all about..
so, you should do the following:
Bitmap icon = decodeSampledBitmapFromResource(getResources(),mainimg_resID,main_img.getWidth(), main_img.getHeight());
by decoding bitmap in scale calculated based on the dimensions needed only for display - the result bitmap allocation would be minimal.
and by the way - the fact that the size of the resource you are using is only 60KB does not change anything. in fact - the image memory size (Kilobites/Megas) does not have any impact on the allocated bitmap. it's the resolution only that makes the difference!
EDIT
because the imageView dimentions are still zero at the onCreateView() method - you should prform the decoding only after it got it dimentions:
--- some code ---
--- some more code ---
View v = inflater.inflate(R.layout.main_fragment, container,false);
viewAnimator = ((ViewAnimator) v.findViewById(R.id.cardFlipper));
TextView caption_text = (TextView) v.findViewById(R.id.caption_text);
caption_text.setText(caption.toUpperCase());
final ImageView main_img = (ImageView) v.findViewById(R.id.main_img);
ViewTreeObserver vto = main_img.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
final ViewTreeObserver obs = main_img.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
obs.removeOnGlobalLayoutListener(this);
} else {
obs.removeGlobalOnLayoutListener(this);
}
// do here the image decoding + setting the image to the image view
Bitmap icon = decodeSampledBitmapFromResource(getResources(),mainimg_resID,main_img.getWidth(), main_img.getHeight());
// setImage....
}
});
viewAnimator.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
AnimationFactory.flipTransition((ViewAnimator) v,FlipDirection.LEFT_RIGHT);
}
});
return v;
EDIT 2
you should never use wrap_content for ImageView. that's because it forces the view to be in the size of the original image (potentially very large) instead of the opposite. when calculating required scale - the whole point is to compare between the ImageView dimentions to the original image(resource/file/stream) dimentions. that's why it does not make any sense use wrap_content.

Related

Why is Bitmap loading still so slow even with Google's implementation?

I use these two functions, slightly modified from Google's code in the Android docs to use filepaths:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromFilePath(String pathName, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(pathName, options);
}
With the idea being to use a scaled-down version of the Bitmap to be mapped onto an ImageView rather than the full-thing, which is wasteful.
mImageView.setImageBitmap(decodeSampledBitmapFromFilePath(pathToFile, 100, 100));
I implemented a thing where you press a button and it rotates to the next image, but there's still a significant lag (it takes a moment for the ImageView to populate) on my phone compared to my emulator. And then occasionally my phone app will crash and I can't replicate it on my emulator.
Is there a problem with this code I've posted above? Is there a problem with the way I am using the code?
Example:
public void reloadPic() {
new Thread(new Runnable() {
#Override
public void run() {
final Bitmap bm = decodeSampledBitmapFromFilePath(filepath, mImageViewWidth, mImageViewHeight);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
mImageView.setImageBitmap(bm);
}
});
}
}).start();
}
Your code is jumping between threads several times. First you launch a thread. Then you wait for it to be scheduled You decode on that thread. Then you post a command to the UI thread and wait for it to be scheduled. THen you post a draw command to the ui thread (that's part of what setImageBitmap does). Then you have to process any other commands that came in first. Then you actually draw the screen. There's really only 3 ways to speed this up:
1) Get rid of the thread. You shouldn't decode lots of images on the UI thread, but decoding 1 isn't too bad.
2)Store the images in the right size to begin with. This may mean creating thumbnails of the images ahead of time. Then you don't need to scale.
3)Preload your images. If there's only one button and you know what image it will load, load it before you need it, so when the button is pressed you have it ready. Wastes a bit of memory, but only 1 image worth. (This isn't a viable solution if you have a lot of possible next images).

How to get the MvxGridView to be efficient and performant?

Using Xamarin and MvvmCross, I'm writing an Android application that is loading the images from an album into an MvxGridView with a custom binding:
<MvxGridView
android:id="#+id/grid_Photos"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:numColumns="3"
android:verticalSpacing="4dp"
android:horizontalSpacing="4dp"
android:stretchMode="columnWidth"
android:fastScrollEnabled="true"
local:MvxBind="ItemsSource AllPhotos"
local:MvxItemTemplate="#layout/item_photo_thumbnail" />
Which uses item_photo_thumbnail.axml:
<ImageView
local:MvxBind="PicturePath PhotoPath"
style="#style/ImageView_Thumbnail" />
Here is the binding class:
public class PicturePathBinding : MvxTargetBinding
{
private readonly ImageView _imageView;
public PicturePathBinding(ImageView imageView)
: base(imageView)
{
_imageView = imageView;
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
public override Type TargetType
{
get { return typeof(string); }
}
public override void SetValue(object value)
{
if (value == null)
{
return;
}
string path = value as string;
if (!string.IsNullOrEmpty(path))
{
Java.IO.File imgFile = new Java.IO.File(path);
if (imgFile.Exists())
{
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = true;
BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);
// Calculate inSampleSize
options.InSampleSize = CalculateInSampleSize(options, 100, 100);
// Decode bitmap with inSampleSize set
options.InJustDecodeBounds = false;
Bitmap myBitmap = BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);
_imageView.SetImageBitmap(myBitmap);
}
}
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
var target = Target as ImageView;
if (target != null)
{
target.Dispose();
target = null;
}
}
base.Dispose(isDisposing);
}
private int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Raw height and width of image
int height = options.OutHeight;
int width = options.OutWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
{
int halfHeight = height / 2;
int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight &&
(halfWidth / inSampleSize) > reqWidth)
{
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
The problem I'm having is that it is very sluggish and slow. I would love for the each image to load asynchronously. I don't know how to do that. In .net (XAML), their GridView control does everything automatically (with virtualization), but I'm realizing that in Android, it might have to be manually handled?
Can someone help me with this?
I am currently working with remote images, instead of local, so I have been using the Download Cache plugin that gives you the MvxImageView class. That in itself may give you some benefit.
So far my experience with Android is that everything runs if foreground by default, for the most part. Right now, with all of the calculation code inside of the binding class, that is almost certainly going to be run in the foreground.
What I would do to make this run faster is:
Use something like an ObservableCollection for your ItemsSource.
Kick off another thread in the start (or Start) of your View Model to add your items to the ObservableCollection. You can accomplish this easily with Task.Run()
Try to process as much as possible in that background thread with each item before adding it to the ObservableCollection
When updating ObservableCollection from the background thread, the actual update itself has to be done on the UI thread. This is easily done if you are using MvxViewModel as your base for your view model.
this.InvokeOnMainThread(() => myObservableCollection.Add(myItem) );
Following that pattern should actually help you Windows based clients as well.

Setting View visibility to Invisible and getting the dimensions

I have an ImageView with its visibility set as GONE. I am trying to set a Bitmap Resource onto it and make it appear.
I realize the dimensions of the ImageView (which I need to subsample my bitmap appropriately) is zero when it's visibility is still GONE, so I set this line of code just before my BitmapAsyncTask runs.
ImageView postImageView= (ImageView) getActivity().findViewById(R.id.post_image);
// Set it as visible to take up necessary space for bitmap computation
postImageView.setVisibility(View.INVISIBLE);
The dimensions still return zero and upon further testing, the ImageView takes some time before it's visibility is set to INVISIBLE again. My fix for now is a while loop inside the AsyncTask to wait until the dimensions are available, but I would like to find out whether there is a better way to do this?
My current code for the AsyncTask:
#Override
protected Bitmap doInBackground(Void... voids) {
Log.i(TAG,"BitmapWorkerTask initialized.");
while(mImageView.getWidth() ==0){
// Wait for the ImageView to be ready
Log.i(TAG,"asd");
}
int reqWidth = mImageView.getWidth()==0?1920:mImageView.getWidth();
int reqHeight = mImageView.getHeight()==0?1080:mImageView.getHeight();
Log.i(TAG, "Dimensions (required): "+reqWidth+" X "+ reqHeight);
//Decode and scale image
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, options);
Log.i(TAG, "Dimensions (source): "+options.outWidth+" X "+ options.outHeight);
options.inSampleSize = calculateInSampleSize(options,reqWidth, reqHeight);
options.inJustDecodeBounds = false;
Bitmap imageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath,options);
Log.i(TAG,"Dimensions (Bitmap): "+ imageBitmap.getWidth()+" X "+ imageBitmap.getHeight());
return imageBitmap;
}
Try adding a layout listener, to wait for the layout to be measured:
final ImageView postImageView = (ImageView) getActivity().findViewById(R.id.post_image);
postImageView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View view, int i, int i2, int i3, int i4, int i5, int i6, int i7, int i8) {
postImageView.removeOnLayoutChangeListener(this);
Log.e(TAG, "W:" + postImageView.getWidth() + " H:"+postImageView.getHeight());
}
});
postImageView.setVisibility(View.INVISIBLE);

Android - Trying to display bitmaps efficiently for wallpaper app which are currently 0.5MB in size each

Updated
I'm trying to display bitmaps efficiently for a wallpaper app which holds 50 wallpapers.
The images I'm using are each 400kb # 1080x1920
I've researched and read I should load a scaled down version of each wallpaper into memory when the app loads instead of loading each wallpaper when it is pressed.
I've read this http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#read-bitmap
How would I implement this using my current code? I've got here from this tutorial on YouTube http://www.youtube.com/watch?v=SxGYGYBFrOM
Here is the updated code, ive included the whole of my MainActivity class
package com.example.ultimateabstractwallpaperhd;
import java.io.IOException;
public class MainActivity extends Activity implements OnClickListener {
// Two classes you mentioned I put here.
ImageView display;
int toPhone;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// check if next two lines are necessary
requestWindowFeature(Window.FEATURE_NO_TITLE); // gets rid of app title
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
if (android.os.Build.VERSION.SDK_INT>=11) {
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
setContentView(R.layout.wallpaper);
toPhone = R.drawable.one;
display = (ImageView) findViewById(R.id.IVdisplay);
ImageView one = (ImageView) findViewById(R.id.IVimage1);
ImageView two = (ImageView) findViewById(R.id.IVimage2);
ImageView three = (ImageView) findViewById(R.id.IVimage3);
ImageView four = (ImageView) findViewById(R.id.IVimage4);
ImageView five = (ImageView) findViewById(R.id.IVimage5);
Button setWall = (Button) findViewById(R.id.BsetWallpaper);
one.setOnClickListener(this);
two.setOnClickListener(this);
three.setOnClickListener(this);
four.setOnClickListener(this);
five.setOnClickListener(this);
setWall.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()){
case R.id.IVimage1:
display.setImageResource(R.drawable.one);
toPhone = R.drawable.one;
break;
case R.id.IVimage2:
display.setImageResource(R.drawable.two);
toPhone = R.drawable.two;
break;
case R.id.IVimage3:
display.setImageResource(R.drawable.three);
toPhone = R.drawable.three;
break;
case R.id.IVimage4:
display.setImageResource(R.drawable.four);
toPhone = R.drawable.four;
break;
case R.id.IVimage5:
display.setImageResource(R.drawable.five);
toPhone = R.drawable.five;
break;
case R.id.BsetWallpaper:
InputStream yeaaaa = getResources().openRawResource(toPhone);
Bitmap whatever = decodeSampledBitmapFromResource(getResources(), R.id.IVimage1, 1080, 1500)
try {
getApplicationContext().setWallpaper(whatever);
}catch(IOException e){
e.printStackTrace();
}
break;
}
}
}
EDIT:
Ok, my resources are in the drawable-hpdi folder.
I've still got a few errors, which eclipse has flagged. I've put them in bold.
1)
public static Bitmap decodeSampledBitmapFromResource(**Resources** res, int resId,
int reqWidth, int reqHeight) {
Eclipse explanation: Says resource cannot be resolved into a type
2)
int resId = getResources().**getIdentifier**(R.drawable.one, "drawable", getPackageName());
Bitmap whatever = **decodeSampledBitmapFromResource**(getResources(), R.id.IVimage1, 1080, 1500);
Eclipse explanation:
Method getIdentifier not applicable for arguments list (Sorry if I've simple referenced the drawable incorrectly)
Method decodeSampledBitmapFromResource from the type MainActivity refers to the missing type resources.
Thanks so much for your help so far, it's fantastic to get such quick responses.
EDIT
Name of resource/s: Multiple Resources, named one, two, three, four, five, all with jpg extensions.
Location of resource/s:
res/drawable-hdpi/one.jpg
res/drawable-hdpi/two.jpg
..
res/drawable-hdpi/two.jpg
alternate answer:
InputStream yeaaaa = getResources().openRawResource(toPhone)
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2; \\1 would be the full resolution, 2 is half the size, 4 a quarter etc..
Bitmap whatever = BitmapFactory.decodeStream(inputStream, null, options);
So if you look at the documention you mentioned, you want to create these 2 methods in your class:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
then instead of :
Bitmap whatever = BitmapFactory.decodeStream(yeaaaa);
use
Bitmap whatever = decodeSampledBitmapFromResource(getResources(), resourceID, width, height)
don't put the resource in raw put it in your drawables folder so you can use the resource ID
you can get the resourceID like so:
getResources().getIdentifier([what ever your named your drawable] , "drawable", getPackageName());
EDIT
per your ajusted code a few things to take note of..
it looks like you are passing the ImageView not the actual resource of a Bitmap..
this line:
Bitmap whatever = decodeSampledBitmapFromResource(getResources(), R.id.IVimage1, 1080, 1500)
should be:
int resId = getResources().getIdentifier([what ever your named your drawable] , "drawable", getPackageName());
Bitmap whatever = decodeSampledBitmapFromResource(getResources(), resId, 1080, 1500)

Android imageview get pixel color from scaled image

My home automation app has a feature where people can upload images to their phone with floorplans and dashboards that they can use to control their home automation software. I have them upload two images: one visible image with the graphics that they want displayed, and a second color map with solid colors corresponding to the objects they want to target areas from the visible image. Both images have to be the same size, pixel-wise. When they tap the screen, I want to be able to get the color from the colormap overlay, and then I proceed to do what ever action has been associated with that color. The problem is, the scaling of the image is screwing me up. The images that they use can be larger than the device screen, so I scale them so they will fit within the display. I don't really need pinch to zoom capability right now, but I might implement it later. For now, I just want the image to be displayed at the largest size so it fits on the screen. So, my question is, how could I modify this code so that I can get the correct touchpoint color from the scaled image. The image scaling itself seems to be working fine. It is scaled and displays correctly. I just can't get the right touchpoint.
final Bitmap bm = decodeSampledBitmapFromFile(visible_image, size, size);
if (bm!=null) {
imageview.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageview.setImageBitmap(bm);
}
final Bitmap bm2 = decodeSampledBitmapFromFile(image_overlay, size, size);
if (bm2!=null) {
overlayimageview.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
overlayimageview.setImageBitmap(bm2);
imageview.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent mev) {
DecodeActionDownEvent(v, mev, bm2);
return false;
}
});
}
private void DecodeActionDownEvent(View v, MotionEvent ev, Bitmap bm2)
{
xCoord = Integer.valueOf((int)ev.getRawX());
yCoord = Integer.valueOf((int)ev.getRawY());
try {
// Here's where the trouble is.
// returns the value of the unscaled pixel at the scaled touch point?
colorTouched = bm2.getPixel(xCoord, yCoord);
} catch (IllegalArgumentException e) {
colorTouched = Color.WHITE; // nothing happens when touching white
}
}
private static Bitmap decodeSampledBitmapFromFile(String fileName,
int reqWidth, int reqHeight) {
// code from
// http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(fileName, options);
}
private static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// code from
// http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
I figured it out. I replaced
xCoord = Integer.valueOf((int)ev.getRawX());
yCoord = Integer.valueOf((int)ev.getRawY());
with
Matrix inverse = new Matrix();
v.getImageMatrix().invert(inverse);
float[] touchPoint = new float[] {ev.getX(), ev.getY()};
inverse.mapPoints(touchPoint);
xCoord = Integer.valueOf((int)touchPoint[0]);
yCoord = Integer.valueOf((int)touchPoint[1]);
After 7 years I have to provide another answer, because after upgrading to LG G8s from LG G6 the code from accepted answer stopped returning the correct color.
This solution works on all the phones I found at home, but who knows... maybe even this one is not universal.
(Using Xamarin C# but the principle is easy to understand)
First we have to get the actual ImageView size as float to get floating point division
private float imageSizeX;
private float imageSizeY;
private void OnCreate(...) {
...
FindViewById<ImageView>(Resource.Id.colpick_Image).LayoutChange += OnImageLayout;
FindViewById<ImageView>(Resource.Id.colpick_Image).Touch += Image_Touch;
...
}
//Event called every time the layout of our ImageView is updated
private void OnImageLayout(object sender, View.LayoutChangeEventArgs e) {
imageSizeX = Image.Width;
imageSizeY = Image.Height;
Image.LayoutChange -= OnImageLayout; //Just need it once then unsubscribe
}
//Called every time user touches/is touching the ImageView
private void Image_Touch(object sender, View.TouchEventArgs e) {
MotionEvent m = e.Event;
ImageView img = sender as ImageView;
int x = Convert.ToInt32(m.GetX(0));
int y = Convert.ToInt32(m.GetY(0));
Bitmap bmp = (img.Drawable as BitmapDrawable).Bitmap;
// The scale value (how many times is the image bigger)
// I only use it with images where Width == Height, so I get
// scaleX == scaleY
float scaleX = bmp.Width / imageSizeX;
float scaleY = bmp.Height / imageSizeY;
x = (int)(x * scaleX);
y = (int)(y * scaleY);
// Check for invalid values/outside of image bounds etc...
// Get the correct Color ;)
int color = bmp.GetPixel(x, y);
}

Categories

Resources