Now I've got this fragment which i want to use setContentView with but so far i cant find how. You can see my case in the code below, im not trying to inflate a layout, im trying to use it with the view called SampleView. So how can I do that?
public class largeImageScroller extends SherlockFragment {
// Physical display width and height.
private static int displayWidth = 0;
private static int displayHeight = 0;
/** Called when the activity is first created. */
public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle saved) {
getActivity();
// displayWidth and displayHeight will change depending on screen
// orientation. To get these dynamically, we should hook onSizeChanged().
// This simple example uses only landscape mode, so it's ok to get them
// once on startup and use those values throughout.
Display display = ((WindowManager)
getActivity().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
// SampleView constructor must be constructed last as it needs the
// displayWidth and displayHeight we just got.
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static Bitmap bmLargeImage; //bitmap large enough to be scrolled
private static Rect displayRect = null; //rect we display to
private Rect scrollRect = null; //rect we scroll over our bitmap with
private int scrollRectX = 0; //current left location of scroll rect
private int scrollRectY = 0; //current top location of scroll rect
private float scrollByX = 0; //x amount to scroll by
private float scrollByY = 0; //y amount to scroll by
private float startX = 0; //track x from one ACTION_MOVE to the next
private float startY = 0; //track y from one ACTION_MOVE to the next
public SampleView(Context context) {
super(context);
// Destination rect for our main canvas draw. It never changes.
displayRect = new Rect(0, 0, displayWidth, displayHeight);
// Scroll rect: this will be used to 'scroll around' over the
// bitmap in memory. Initialize as above.
scrollRect = new Rect(0, 0, displayWidth, displayHeight);
// Load a large bitmap into an offscreen area of memory.
bmLargeImage = BitmapFactory.decodeResource(getResources(),
R.drawable.ground_floor_b);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Remember our initial down event location.
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float x = event.getRawX();
float y = event.getRawY();
// Calculate move update. This will happen many times
// during the course of a single movement gesture.
scrollByX = x - startX; //move update x increment
scrollByY = y - startY; //move update y increment
startX = x; //reset initial values to latest
startY = y;
invalidate(); //force a redraw
break;
}
return true; //done with this event so consume it
}
#Override
protected void onDraw(Canvas canvas) {
// Our move updates are calculated in ACTION_MOVE in the opposite direction
// from how we want to move the scroll rect. Think of this as dragging to
// the left being the same as sliding the scroll rect to the right.
int newScrollRectX = scrollRectX - (int)scrollByX;
int newScrollRectY = scrollRectY - (int)scrollByY;
// Don't scroll off the left or right edges of the bitmap.
if (newScrollRectX < 0)
newScrollRectX = 0;
else if (newScrollRectX > (bmLargeImage.getWidth() - displayWidth))
newScrollRectX = (bmLargeImage.getWidth() - displayWidth);
// Don't scroll off the top or bottom edges of the bitmap.
if (newScrollRectY < 0)
newScrollRectY = 0;
else if (newScrollRectY > (bmLargeImage.getHeight() - displayHeight))
newScrollRectY = (bmLargeImage.getHeight() - displayHeight);
// We have our updated scroll rect coordinates, set them and draw.
scrollRect.set(newScrollRectX, newScrollRectY,
newScrollRectX + displayWidth, newScrollRectY + displayHeight);
Paint paint = new Paint();
canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, paint);
// Reset current scroll coordinates to reflect the latest updates,
// so we can repeat this update process.
scrollRectX = newScrollRectX;
scrollRectY = newScrollRectY;
}
}
}
You dont call setContentView in fragments, in fact you need to return a View from onCreateView.
Try replacing:
setContentView(new SampleView(this));
With this:
return new SampleView(this);
Return the view instance you want to use:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.ads_tab, container, false);
}
Also it's not safe to call getActivity() from onCreateView().
Make sure you call it in or after onActivityCreated(), as at this point your Fragment is fully associated with the Activity. Check Fragment's lifecycle.
Fragments
As explained already, you need to return the view in case of fragments.
But still if you want to use it just like setContentView(), you can do so in following way.
1.Put this code snippet wherever you had to put setContentView()
View v = inflater.inflate(R.layout.activity_home, container, false);
2.Now if you want to access something from xml file, you can do so by using
chart = v.findViewById(R.id.chart);
3. And in the end of OnCreateView() you will have to put
return v;
Full example :
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_home, container, false);
chart = v.findViewById(R.id.chart);
return v;
}
In activities we need to set the view using setContentView(R.layout.main)
Where as in fragments we need to override onCreateView() to set the desired view.
Related
I have a gridview that I need to implement drag and drop feature. I've been trying to find a possible solution or existing library that would fit my problem but still no luck.
Here's a before and after illustration of the drag and drop that I need to implement:
The red tiles are items wherein they cannot be dragged nor dropped on. the blue ones are draggable and can be dropped on any tile on any row, just not on the red tiles. The white tiles are just placeholders which are placed to have the red tiles on the first column.
Now, when tile A is dragged on the 3rd row, as you can see, they go side by side, not swapped, even if put on top of tile C. The number of white tiles is depending on the number of blue tiles per row, an arraylist is assigned on each row, so it'll just follow. My real problem is that all examples on gridview drag and drop is that tiles swaps or the whole grid follows the flow of items.
My plan to implement this:
When long pressed on a tile to be dragged, it will show a tile that looks like that tile, only larger and lesser opacity.
When dropped on a certain position, will compute for the row.
Adjust arraylists and notifydatasetchanged.
Here's breaking down the problem to slightly smaller problems:
How can I make a larger tile of the long pressed tile?
Is it possible to get the position where the enlarged tile is dropped?
1.How can I make a larger tile of the long pressed tile? yes,you can get the tile's view and create a new bitmap,the add the bitmap to Windows.Like this : it's a class extends GridView.
public boolean setOnItemLongClickListener(final MotionEvent ev)
{
this.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener()
{
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3)
{
// onInterceptTouchEvent(ev);
// TODO Auto-generated method stub
L.l("============on Long Click=========");
L.l("============X:" + ev.getX() + " Y:" + ev.getY());
int x = (int) ev.getX();
int y = (int) ev.getY();
dragPosition = dropPosition = pointToPosition(x, y);
System.out.println(dragPosition);
if (dragPosition == AdapterView.INVALID_POSITION)
{
}
ViewGroup itemView = (ViewGroup) getChildAt(dragPosition
- getFirstVisiblePosition());
dragPointX = x - itemView.getLeft();
dragPointY = y - itemView.getTop();
dragOffsetX = (int) (ev.getRawX() - x);
dragOffsetY = (int) (ev.getRawY() - y);
itemHeight=itemView.getHeight();
L.l("========================y:" + y + " getRawY:"
+ ev.getRawY());
itemView.destroyDrawingCache();
itemView.setDrawingCacheEnabled(true);
Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());
startDrag(bm, x, y);
return false;
};
});
return super.onInterceptTouchEvent(ev);
}
private void startDrag(Bitmap bm, int x, int y)
{
windowParams = new WindowManager.LayoutParams();
windowParams.gravity = Gravity.TOP | Gravity.LEFT;// 这个必须加
windowParams.x = x - dragPointX + dragOffsetX;
windowParams.y = y - dragPointY + dragOffsetY;
windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
windowParams.format = PixelFormat.TRANSLUCENT;
windowParams.windowAnimations = 0;
ImageView iv = new ImageView(getContext());
iv.setImageBitmap(bm);
windowManager = (WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE);// "window"
windowManager.addView(iv, windowParams);
dragImageView = iv;
}
#Override
public boolean onTouchEvent(MotionEvent ev)
{
if (dragImageView != null
&& dragPosition != AdapterView.INVALID_POSITION)
{
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction())
{
case MotionEvent.ACTION_MOVE:
onDrag(x, y);
break;
case MotionEvent.ACTION_UP:
stopDrag();
onDrop(x, y);
break;
}
}
return super.onTouchEvent(ev);
}
private void onDrag(int x, int y)
{
if (dragImageView != null)
{
windowParams.alpha = 0.6f;
windowParams.x = x - dragPointX + dragOffsetX;
windowParams.y = y - dragPointY + dragOffsetY;
// L.l("=================windowParams.y=====000========"+windowParams.y);
windowManager.updateViewLayout(dragImageView, windowParams);
}
int tempScrollX = x - dragPointX + dragOffsetX;
int tempScrollY = y - dragPointY + dragOffsetY;
if (tempScrollY +itemHeight> 600)
{
this.scrollTo(0, tempScrollY);
}
else
if (pointToPosition(x, y) > 2)
{
this.scrollTo(0, tempScrollY - 300);
}
}
2.Is it possible to get the position where the enlarged tile is dropped?
If your class is extends GridView or AbsListView,this API `pointToPosition(x, y) will return the position of the whole view.
When you drop tile A,then you calculate where tile A is now,if above tile C,then start a Animation(C move to side and A take place position of C) and update the Adapter end of the Animation.
I ended up editting http://code.google.com/p/android-gridview-drag-and-drop/ to fit my problem.
I want crop image like Facebook profile image selection on Android, where the user can move and scale an image, causing it to be resized and/or cropped:
How might I accomplish this?
I had the same requirement. I solved it combining PhotoView and Cropper by replacing the ImageView with PhotoView in cropper lib.
I had to modify the CropWindow class in order to avoid touch events not being correctly handled:
public void setImageView(PhotoView pv){
mPhotoView = pv;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// If this View is not enabled, don't allow for touch interactions.
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
boolean dispatch = onActionDown(event.getX(), event.getY());
if(!dispatch)
mPhotoView.dispatchTouchEvent(event);
return dispatch;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
onActionUp();
return true;
case MotionEvent.ACTION_MOVE:
onActionMove(event.getX(), event.getY());
getParent().requestDisallowInterceptTouchEvent(true);
return true;
default:
return false;
}
}
In CropImageView class changed few things as well:
private void init(Context context) {
final LayoutInflater inflater = LayoutInflater.from(context);
final View v = inflater.inflate(R.layout.crop_image_view, this, true);
mImageView = (PhotoView) v.findViewById(R.id.ImageView_image2);
setImageResource(mImageResource);
mCropOverlayView = (CropOverlayView) v.findViewById(R.id.CropOverlayView);
mCropOverlayView.setInitialAttributeValues(mGuidelines, mFixAspectRatio, mAspectRatioX, mAspectRatioY);
mCropOverlayView.setImageView(mImageView);
}
You can notice that I have replaced ImageView with PhotoView inside R.layout.crop_image_view in Cropper library.
Cropper library supports fixed size and PhotoView allows you to move and scale the photo, giving you the best from both worlds. :)
Hope it helps.
Edit, for those that asked how to get the image that is only inside the crop area:
private Bitmap getCurrentDisplayedImage(){
Bitmap result = Bitmap.createBitmap(mImageView.getWidth(), mImageView.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(result);
mImageView.draw(c);
return result;
}
public Bitmap getCroppedImage() {
Bitmap mCurrentDisplayedBitmap = getCurrentDisplayedImage();
final Rect displayedImageRect = ImageViewUtil2.getBitmapRectCenterInside(mCurrentDisplayedBitmap, mImageView);
// Get the scale factor between the actual Bitmap dimensions and the
// displayed dimensions for width.
final float actualImageWidth =mCurrentDisplayedBitmap.getWidth();
final float displayedImageWidth = displayedImageRect.width();
final float scaleFactorWidth = actualImageWidth / displayedImageWidth;
// Get the scale factor between the actual Bitmap dimensions and the
// displayed dimensions for height.
final float actualImageHeight = mCurrentDisplayedBitmap.getHeight();
final float displayedImageHeight = displayedImageRect.height();
final float scaleFactorHeight = actualImageHeight / displayedImageHeight;
// Get crop window position relative to the displayed image.
final float cropWindowX = Edge.LEFT.getCoordinate() - displayedImageRect.left;
final float cropWindowY = Edge.TOP.getCoordinate() - displayedImageRect.top;
final float cropWindowWidth = Edge.getWidth();
final float cropWindowHeight = Edge.getHeight();
// Scale the crop window position to the actual size of the Bitmap.
final float actualCropX = cropWindowX * scaleFactorWidth;
final float actualCropY = cropWindowY * scaleFactorHeight;
final float actualCropWidth = cropWindowWidth * scaleFactorWidth;
final float actualCropHeight = cropWindowHeight * scaleFactorHeight;
// Crop the subset from the original Bitmap.
final Bitmap croppedBitmap = Bitmap.createBitmap(mCurrentDisplayedBitmap,
(int) actualCropX,
(int) actualCropY,
(int) actualCropWidth,
(int) actualCropHeight);
return croppedBitmap;
}
public RectF getActualCropRect() {
final Rect displayedImageRect = ImageViewUtil.getBitmapRectCenterInside(mBitmap, mImageView);
final float actualImageWidth = mBitmap.getWidth();
final float displayedImageWidth = displayedImageRect.width();
final float scaleFactorWidth = actualImageWidth / displayedImageWidth;
// Get the scale factor between the actual Bitmap dimensions and the displayed
// dimensions for height.
final float actualImageHeight = mBitmap.getHeight();
final float displayedImageHeight = displayedImageRect.height();
final float scaleFactorHeight = actualImageHeight / displayedImageHeight;
// Get crop window position relative to the displayed image.
final float displayedCropLeft = Edge.LEFT.getCoordinate() - displayedImageRect.left;
final float displayedCropTop = Edge.TOP.getCoordinate() - displayedImageRect.top;
final float displayedCropWidth = Edge.getWidth();
final float displayedCropHeight = Edge.getHeight();
// Scale the crop window position to the actual size of the Bitmap.
float actualCropLeft = displayedCropLeft * scaleFactorWidth;
float actualCropTop = displayedCropTop * scaleFactorHeight;
float actualCropRight = actualCropLeft + displayedCropWidth * scaleFactorWidth;
float actualCropBottom = actualCropTop + displayedCropHeight * scaleFactorHeight;
// Correct for floating point errors. Crop rect boundaries should not exceed the
// source Bitmap bounds.
actualCropLeft = Math.max(0f, actualCropLeft);
actualCropTop = Math.max(0f, actualCropTop);
actualCropRight = Math.min(mBitmap.getWidth(), actualCropRight);
actualCropBottom = Math.min(mBitmap.getHeight(), actualCropBottom);
final RectF actualCropRect = new RectF(actualCropLeft,
actualCropTop,
actualCropRight,
actualCropBottom);
return actualCropRect;
}
private boolean onActionDown(float x, float y) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
mPressedHandle = HandleUtil.getPressedHandle(x, y, left, top, right, bottom, mHandleRadius);
if (mPressedHandle == null)
return false;
mTouchOffset = HandleUtil2.getOffset(mPressedHandle, x, y, left, top, right, bottom);
invalidate();
return true;
}
I have some additions to #Nikola Despotoski answer.
Firstly, you don't have to change ImageView in R.layout.crop_image_view to PhotoView, because PhotoView logic can be simply attached in code as new PhotoViewAttacher(mImageView).
Also in default logic, a CropView's overlay size calculates only on its initialization according to imageView bitmap size. So it is not appropriate logic for us, becouse we change bitmap size by touches according to the requirement. So, we should change stored bitmap sizes in CropOverlayView and invalidate it each time when we change the main image.
And the last is that a range, where user can make cropping normally based on the image size, but if we made image bigger, it can go beyond the border of screen, so it will be possible to user to move a cropping view beyond the border, which is incorrect. So we also should handle this situation and provide limitation.
And the corresponding part of code for this three issues:
In CropImageView:
private void init(Context context) {
final LayoutInflater inflater = LayoutInflater.from(context);
final View v = inflater.inflate(R.layout.crop_image_view, this, true);
mImageView = (ImageView) v.findViewById(R.id.ImageView_image);
setImageResource(mImageResource);
mCropOverlayView = (CropOverlayView) v.findViewById(R.id.CropOverlayView);
mCropOverlayView.setInitialAttributeValues(mGuidelines, mFixAspectRatio, mAspectRatioX, mAspectRatioY);
mCropOverlayView.setOutlineTouchEventReceiver(mImageView);
final PhotoViewAttacher photoAttacher = new PhotoViewAttacher(mImageView);
photoAttacher.setOnMatrixChangeListener(new PhotoViewAttacher.OnMatrixChangedListener() {
#Override
public void onMatrixChanged(RectF imageRect) {
final Rect visibleRect = ImageViewUtil.getBitmapRectCenterInside(photoAttacher.getVisibleRectangleBitmap(), photoAttacher.getImageView());
imageRect.top = Math.max(imageRect.top, visibleRect.top);
imageRect.left = Math.max(imageRect.left, visibleRect.left);
imageRect.right = Math.min(imageRect.right, visibleRect.right);
imageRect.bottom = Math.min(imageRect.bottom, visibleRect.bottom);
Rect bitmapRect = new Rect();
imageRect.round(bitmapRect);
mCropOverlayView.changeBitmapRectInvalidate(bitmapRect);
}
});
}
In CropOverlayView:
#Override
public boolean onTouchEvent(MotionEvent event) {
// If this View is not enabled, don't allow for touch interactions.
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return onActionDown(event.getX(), event.getY());
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
return onActionUp();
case MotionEvent.ACTION_MOVE:
boolean result = onActionMove(event.getX(), event.getY());
getParent().requestDisallowInterceptTouchEvent(true);
return result;
default:
return false;
}
}
public void changeBitmapRectInvalidate(Rect bitmapRect) {
mBitmapRect = bitmapRect;
invalidate();
}
private boolean onActionDown(float x, float y) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
mPressedHandle = HandleUtil.getPressedHandle(x, y, left, top, right, bottom, mHandleRadius);
if (mPressedHandle == null){
return false;
}
// Calculate the offset of the touch point from the precise location
// of the handle. Save these values in a member variable since we want
// to maintain this offset as we drag the handle.
mTouchOffset = HandleUtil.getOffset(mPressedHandle, x, y, left, top, right, bottom);
invalidate();
return true;
}
/**
* Handles a {#link MotionEvent#ACTION_UP} or
* {#link MotionEvent#ACTION_CANCEL} event.
* #return true if event vas handled, else - false
*/
private boolean onActionUp() {
if (mPressedHandle == null)
return false;
mPressedHandle = null;
invalidate();
return true;
}
/**
* Handles a {#link MotionEvent#ACTION_MOVE} event.
*
* #param x the x-coordinate of the move event
* #param y the y-coordinate of the move event
*/
private boolean onActionMove(float x, float y) {
if (mPressedHandle == null)
return false;
// Adjust the coordinates for the finger position's offset (i.e. the
// distance from the initial touch to the precise handle location).
// We want to maintain the initial touch's distance to the pressed
// handle so that the crop window size does not "jump".
x += mTouchOffset.first;
y += mTouchOffset.second;
// Calculate the new crop window size/position.
if (mFixAspectRatio) {
mPressedHandle.updateCropWindow(x, y, mTargetAspectRatio, mBitmapRect, mSnapRadius);
} else {
mPressedHandle.updateCropWindow(x, y, mBitmapRect, mSnapRadius);
}
invalidate();
return true;
}
For properly getting cropped image you should use the second part of #Nikola Despotoski answer
what you want can be exactly achieved by this lib simple-crop-image-lib
Thanks to all. Able to achieve this using the answers above, using Photoview and Cropper library. Added options to pick images from Camera or Gallery. Sharing the project on Github. Added an apk file in the project. Use real device for testing camera as emulator doesn't handle camera well. Here's the link to my project.
https://github.com/ozeetee/AndroidImageZoomCrop
I just need to set a high res image, but wants to know if its possible to set it in xml. I want to be able to set it even if it will be bigger than the mobile screen.
EDIT: I want to add a huge image, so the user will have to scroll it. Something like a map, but need to add clickable areas on it and I only know how to do this in xml. That's why I asked to add the image using xml not in java. But if someone can tell me how to add clickable areas in java. That will do it better, since I already have the image but using java and its able to scroll.
EDIT: Heres where I got the code from : Anddev.org I'm using exactly the same code, just with other image.
What are you trying for? In android the resolutions are based on like below -
Screen Support
MultipleResolution
Just read out this. Hope these two links enough for your query.
Have a look at this sample code to see how to scroll an image larger than the screen. It also does bitmap caching to speed up drawing of a complex image, and shows how to respond to long-taps and double-taps at any point on the image.
Ok I'm posting this as an answer, just because it will be easier to post this. (It's not working how I want it to, but I got something) I want to share this so anyone else, who knows how to, can help me out here to find a solution for this one. This is the code
package com.example.largeimagescroller;
import android.app.Activity;
import android.os.Bundle;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
public class LargeImageScroller extends Activity {
// Physical display width and height.
private static int displayWidth = 0;
private static int displayHeight = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// displayWidth and displayHeight will change depending on screen
// orientation. To get these dynamically, we should hook
// onSizeChanged().
// This simple example uses only landscape mode, so it's ok to get them
// once on startup and use those values throughout.
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
// SampleView constructor must be constructed last as it needs the
// displayWidth and displayHeight we just got.
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static Bitmap bmLargeImage; // bitmap large enough to be
// scrolled
private static Rect displayRect = null; // rect we display to
private Rect scrollRect = null; // rect we scroll over our bitmap with
private int scrollRectX = 0; // current left location of scroll rect
private int scrollRectY = 0; // current top location of scroll rect
private float scrollByX = 0; // x amount to scroll by
private float scrollByY = 0; // y amount to scroll by
private float startX = 0; // track x from one ACTION_MOVE to the next
private float startY = 0; // track y from one ACTION_MOVE to the next
public SampleView(Context context) {
super(context);
// Destination rect for our main canvas draw. It never changes.
displayRect = new Rect(0, 0, displayWidth, displayHeight);
// Scroll rect: this will be used to 'scroll around' over the
// bitmap in memory. Initialize as above.
scrollRect = new Rect(0, 0, displayWidth, displayHeight);
// Load a large bitmap into an offscreen area of memory.
bmLargeImage = BitmapFactory.decodeResource(getResources(),
R.drawable.alienware);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//This code define what to do if you touch the x and y coordinates.
float x1 = event.getX();
float y1 = event.getY();
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
if (x1>150 & x1<200 & y1>400 & y1<500){
Toast.makeText(getContext(), "Touched the coordinates.", Toast.LENGTH_SHORT).show();
}
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Remember our initial down event location.
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float x = event.getRawX();
float y = event.getRawY();
// Calculate move update. This will happen many times
// during the course of a single movement gesture.
scrollByX = x - startX; // move update x increment
scrollByY = y - startY; // move update y increment
startX = x; // reset initial values to latest
startY = y;
invalidate(); // force a redraw
break;
}
return true; // done with this event so consume it
}
#Override
protected void onDraw(Canvas canvas) {
// Our move updates are calculated in ACTION_MOVE in the opposite
// direction
// from how we want to move the scroll rect. Think of this as
// dragging to
// the left being the same as sliding the scroll rect to the right.
int newScrollRectX = scrollRectX - (int) scrollByX;
int newScrollRectY = scrollRectY - (int) scrollByY;
// Don't scroll off the left or right edges of the bitmap.
if (newScrollRectX < 0)
newScrollRectX = 0;
else if (newScrollRectX > (bmLargeImage.getWidth() - displayWidth))
newScrollRectX = (bmLargeImage.getWidth() - displayWidth);
// Don't scroll off the top or bottom edges of the bitmap.
if (newScrollRectY < 0)
newScrollRectY = 0;
else if (newScrollRectY > (bmLargeImage.getHeight() - displayHeight))
newScrollRectY = (bmLargeImage.getHeight() - displayHeight);
// We have our updated scroll rect coordinates, set them and draw.
scrollRect.set(newScrollRectX, newScrollRectY, newScrollRectX
+ displayWidth, newScrollRectY + displayHeight);
Paint paint = new Paint();
canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, paint);
// Reset current scroll coordinates to reflect the latest updates,
// so we can repeat this update process.
scrollRectX = newScrollRectX;
scrollRectY = newScrollRectY;
}
}
}
I rec the application in use. (Since touch events don't work with cpu emulators I did it on my phone)
http://www.youtube.com/watch?v=NrszZoDenXE&feature=youtube_gdata
As you can see its taking the touch, but it moves with the scroll rect, so the question will be: How can I do the touch to stay in the image, so it doesn't move with the scrolling rect?
Thanks
Can I get a View's x and y position relative to the root layout of my Activity in Android?
The Android API already provides a method to achieve that.
Try this:
Rect offsetViewBounds = new Rect();
//returns the visible bounds
childView.getDrawingRect(offsetViewBounds);
// calculates the relative coordinates to the parent
parentViewGroup.offsetDescendantRectToMyCoords(childView, offsetViewBounds);
int relativeTop = offsetViewBounds.top;
int relativeLeft = offsetViewBounds.left;
Here is the doc
This is one solution, though since APIs change over time and there may be other ways of doing it, make sure to check the other answers. One claims to be faster, and another claims to be easier.
private int getRelativeLeft(View myView) {
if (myView.getParent() == myView.getRootView())
return myView.getLeft();
else
return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}
private int getRelativeTop(View myView) {
if (myView.getParent() == myView.getRootView())
return myView.getTop();
else
return myView.getTop() + getRelativeTop((View) myView.getParent());
}
Let me know if that works.
It should recursively just add the top and left positions from each parent container.
You could also implement it with a Point if you wanted.
Please use view.getLocationOnScreen(int[] location); (see Javadocs). The answer is in the integer array (x = location[0] and y = location[1]).
View rootLayout = view.getRootView().findViewById(android.R.id.content);
int[] viewLocation = new int[2];
view.getLocationInWindow(viewLocation);
int[] rootLocation = new int[2];
rootLayout.getLocationInWindow(rootLocation);
int relativeLeft = viewLocation[0] - rootLocation[0];
int relativeTop = viewLocation[1] - rootLocation[1];
First I get the root layout then calculate the coordinates difference with the view.
You can also use the getLocationOnScreen() instead of getLocationInWindow().
No need to calculate it manually.
Just use getGlobalVisibleRect like so:
Rect myViewRect = new Rect();
myView.getGlobalVisibleRect(myViewRect);
float x = myViewRect.left;
float y = myViewRect.top;
Also note that for the centre coordinates, rather than something like:
...
float two = (float) 2
float cx = myViewRect.left + myView.getWidth() / two;
float cy = myViewRect.top + myView.getHeight() / two;
You can just do:
float cx = myViewRect.exactCenterX();
float cy = myViewRect.exactCenterY();
You can use `
view.getLocationOnScreen(int[] location)
;` to get location of your view correctly.
But there is a catch if you use it before layout has been inflated you will get wrong position.
Solution to this problem is adding ViewTreeObserver like this :-
Declare globally the array to store x y position of your view
int[] img_coordinates = new int[2];
and then add ViewTreeObserver on your parent layout to get callback for layout inflation and only then fetch position of view otherwise you will get wrong x y coordinates
// set a global layout listener which will be called when the layout pass is completed and the view is drawn
parentViewGroup.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
//Remove the listener before proceeding
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
parentViewGroup.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
parentViewGroup.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// measure your views here
fab.getLocationOnScreen(img_coordinates);
}
}
);
and then use it like this
xposition = img_coordinates[0];
yposition = img_coordinates[1];
I wrote myself two utility methods that seem to work in most conditions, handling scroll, translation and scaling, but not rotation. I did this after trying to use offsetDescendantRectToMyCoords() in the framework, which had inconsistent accuracy. It worked in some cases but gave wrong results in others.
"point" is a float array with two elements (the x & y coordinates), "ancestor" is a viewgroup somewhere above the "descendant" in the tree hierarchy.
First a method that goes from descendant coordinates to ancestor:
public static void transformToAncestor(float[] point, final View ancestor, final View descendant) {
final float scrollX = descendant.getScrollX();
final float scrollY = descendant.getScrollY();
final float left = descendant.getLeft();
final float top = descendant.getTop();
final float px = descendant.getPivotX();
final float py = descendant.getPivotY();
final float tx = descendant.getTranslationX();
final float ty = descendant.getTranslationY();
final float sx = descendant.getScaleX();
final float sy = descendant.getScaleY();
point[0] = left + px + (point[0] - px) * sx + tx - scrollX;
point[1] = top + py + (point[1] - py) * sy + ty - scrollY;
ViewParent parent = descendant.getParent();
if (descendant != ancestor && parent != ancestor && parent instanceof View) {
transformToAncestor(point, ancestor, (View) parent);
}
}
Next the inverse, from ancestor to descendant:
public static void transformToDescendant(float[] point, final View ancestor, final View descendant) {
ViewParent parent = descendant.getParent();
if (descendant != ancestor && parent != ancestor && parent instanceof View) {
transformToDescendant(point, ancestor, (View) parent);
}
final float scrollX = descendant.getScrollX();
final float scrollY = descendant.getScrollY();
final float left = descendant.getLeft();
final float top = descendant.getTop();
final float px = descendant.getPivotX();
final float py = descendant.getPivotY();
final float tx = descendant.getTranslationX();
final float ty = descendant.getTranslationY();
final float sx = descendant.getScaleX();
final float sy = descendant.getScaleY();
point[0] = px + (point[0] + scrollX - left - tx - px) / sx;
point[1] = py + (point[1] + scrollY - top - ty - py) / sy;
}
Incase someone is still trying to figure this out. This is how you get the center X and Y of the view.
int pos[] = new int[2];
view.getLocationOnScreen(pos);
int centerX = pos[0] + view.getMeasuredWidth() / 2;
int centerY = pos[1] + view.getMeasuredHeight() / 2;
I just found the answer here
It says:
It is possible to retrieve the location of a view by invoking the methods getLeft() and getTop(). The former returns the left, or X, coordinate of the rectangle representing the view. The latter returns the top, or Y, coordinate of the rectangle representing the view. These methods both return the location of the view relative to its parent. For instance, when getLeft() returns 20, that means the view is located 20 pixels to the right of the left edge of its direct parent.
so use:
view.getLeft(); // to get the location of X from left to right
view.getRight()+; // to get the location of Y from right to left
You can use the following the get the difference between parent and the view you interested in:
private int getRelativeTop(View view) {
final View parent = (View) view.getParent();
int[] parentLocation = new int[2];
int[] viewLocation = new int[2];
view.getLocationOnScreen(viewLocation);
parent.getLocationOnScreen(parentLocation);
return viewLocation[1] - parentLocation[1];
}
Dont forget to call it after the view is drawn:
timeIndicator.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
final int relativeTop = getRelativeTop(timeIndicator);
});
I'm trying to draw an image in a view but having problems trying to maintain the scale of the original image. Basically, I have a small view and I would like to show part of the image in the view. The intention then is to perform a translation on the image so that a different part appears in the view.
No matter what I try, either the image is down-scaled automatically to fit the view or the whole image is viewable. I've tried playing about with the settings on BitmapDrawable, ImageView and Layout to no avail.
Anyone know a good way to achieve this?
Hope This piece of code helps. I have googled it a month ago. It Scrolling performance for the Larger Images.Here whole display size is set as the Height and width of the view. You can change you know. and also can maintain the zoom controls too.
public class LargeImageScroller extends Activity {
// Physical display width and height.
private static int displayWidth = 0;
private static int displayHeight = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// displayWidth and displayHeight will change depending on screen
// orientation. To get these dynamically, we should hook onSizeChanged().
// This simple example uses only landscape mode, so it's ok to get them
// once on startup and use those values throughout.
Display display = ((WindowManager)
getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
// SampleView constructor must be constructed last as it needs the
// displayWidth and displayHeight we just got.
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static Bitmap bmLargeImage; //bitmap large enough to be scrolled
private static Rect displayRect = null; //rect we display to
private Rect scrollRect = null; //rect we scroll over our bitmap with
private int scrollRectX = 0; //current left location of scroll rect
private int scrollRectY = 0; //current top location of scroll rect
private float scrollByX = 0; //x amount to scroll by
private float scrollByY = 0; //y amount to scroll by
private float startX = 0; //track x from one ACTION_MOVE to the next
private float startY = 0; //track y from one ACTION_MOVE to the next
public SampleView(Context context) {
super(context);
// Destination rect for our main canvas draw. It never changes.
displayRect = new Rect(0, 0, displayWidth, displayHeight);
// Scroll rect: this will be used to 'scroll around' over the
// bitmap in memory. Initialize as above.
scrollRect = new Rect(0, 0, displayWidth, displayHeight);
// Load a large bitmap into an offscreen area of memory.
bmLargeImage = BitmapFactory.decodeResource(getResources(),
R.drawable.testlargeimage);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Remember our initial down event location.
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float x = event.getRawX();
float y = event.getRawY();
// Calculate move update. This will happen many times
// during the course of a single movement gesture.
scrollByX = x - startX; //move update x increment
scrollByY = y - startY; //move update y increment
startX = x; //reset initial values to latest
startY = y;
invalidate(); //force a redraw
break;
}
return true; //done with this event so consume it
}
#Override
protected void onDraw(Canvas canvas) {
// Our move updates are calculated in ACTION_MOVE in the opposite direction
// from how we want to move the scroll rect. Think of this as dragging to
// the left being the same as sliding the scroll rect to the right.
int newScrollRectX = scrollRectX - (int)scrollByX;
int newScrollRectY = scrollRectY - (int)scrollByY;
// Don't scroll off the left or right edges of the bitmap.
if (newScrollRectX < 0)
newScrollRectX = 0;
else if (newScrollRectX > (bmLargeImage.getWidth() - displayWidth))
newScrollRectX = (bmLargeImage.getWidth() - displayWidth);
// Don't scroll off the top or bottom edges of the bitmap.
if (newScrollRectY < 0)
newScrollRectY = 0;
else if (newScrollRectY > (bmLargeImage.getHeight() - displayHeight))
newScrollRectY = (bmLargeImage.getHeight() - displayHeight);
// We have our updated scroll rect coordinates, set them and draw.
scrollRect.set(newScrollRectX, newScrollRectY,
newScrollRectX + displayWidth, newScrollRectY + displayHeight);
Paint paint = new Paint();
canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, paint);
// Reset current scroll coordinates to reflect the latest updates,
// so we can repeat this update process.
scrollRectX = newScrollRectX;
scrollRectY = newScrollRectY;
}
}
}
This probably won't be the most efficient way, but if you're not going to be moving it too much, it'll do.
Treat it like a spritesheet, use Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height) to get the section you want from the original bitmap as its own bitmap, then pass that in instead. Then pass that in rather than the whole bitmap, and it'll only be working with the part you want it to show.
Sounds like you want to have a big ImageView control inside a smaller View (acting like a window), and then move the ImageView control around inside that, so only part of it is visible at a time.
Not exactly sure how you'd do that though, but pretty sure it's possible with AbsoluteLayout and a bit of tinkering.