I have multiple images which is created dynamically in for loop.
for (int i = 0; i < 15; i++) {
ivFront = new TouchImageView(this);
ivFront.setId(i + 1);
ivFront.setBackgroundResource(frontImages[i]);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
frontWidth[i], frontHeight[i]);
((MarginLayoutParams) params).setMargins(frontX_axis[i],
frontY_axis[i], 0, 0);
ivFront.setAdjustViewBounds(true);
ivFront.setLayoutParams(params);
ivFront.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent arg1) {
Bitmap bmp = loadBitmapFromView(v);
return true;
}
});
//Bitmap bmp_new = loadBitmapFromView(ivFront);
rlMain.addView(ivFront, params);
}
public static Bitmap loadBitmapFromView(View v) {
Canvas bitmapCanvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(v.getWidth() * 2,
v.getHeight() * 2, Bitmap.Config.ARGB_8888);
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.scale(2.0f, 2.0f);
v.draw(bitmapCanvas);
return bitmap;
}
When i write ivFront.getWidth() & ivFront.getHeight() then it shows me 0(Height)-0(Width). I already tried to get ImageView height & width in onWindowFocusChanged() method, but the output is same 0(Height)-0(Width).
So when i open the comment then it shows me below error:
11-05 19:16:28.522: E/AndroidRuntime(9153): Caused by: java.lang.IllegalArgumentException: width and height must be > 0
If you have created multiple images dynamically than try this one:
// initialize your images array
private ImageView myImages[] = new ImageView[your_array_length];
// create programatically and add to parent view
for (int i = 0; i < your_array_length; i++) {
myImages[i] = new ImageView(this);
myImages[i].setId(i + 1);
myImages[i].setBackgroundResource(your_array[i]);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
frontWidth[i], frontHeight[i]);
((MarginLayoutParams) params).setMargins(frontX_axis[i],
frontY_axis[i], 0, 0);
myImages[i].setAdjustViewBounds(true);
myImages[i].setLayoutParams(params);
if (getIntent() != null && i != your_array,length) {
final int j = i;
myImages[j].getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
myImages[j].getViewTreeObserver().removeOnPreDrawListener(this);
finalHeight = myImages[j].getMeasuredHeight();
finalWidth = myImages[j].getMeasuredWidth();
your_textview.setText("Height: " + finalHeight + " Width: " + finalWidth);
return true;
}
});
}
your_parent_layout.addView(myImages[i], params);
}
// Run code
Use a PreDrawListener to get the dimensions once your view has been measured. OnWindowFocusChanged does not guarantee all views have been measured.
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this);
int width = view.getWidth();
}
});
Add this code for each one of the dynamic imageview :
ivFront.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
iv.getViewTreeObserver().removeOnPreDrawListener(this);
height = iv.getMeasuredHeight();
width = iv.getMeasuredWidth();
return true;
}
});
ivFront.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent arg1) {
Bitmap bmp = loadBitmapFromView(v);
return true;
}
});
If you want to save all of your dynamic imageview's width and height, you can use List of integer to save the width and height of each your imageview.
Related
I have been trying to make a gridview with drag and drop functionality along with one cell of different size. I have already made the the grid drag and drop and its working fine. you can check the code from here
but I want it to be like this and purely dynamic as I will be draging and dropping the other which will be replaced and resized automatically
Updated with new code that accommodates resizing of cells.
Your question refers to GridView but the code you supplied doesn't mention GridView but uses GridLayout instead, so I am assuming that GridLayout is the right layout.
I have put together a demo using a mocked-up layout with one 2x2 tile. I have modified the code that you have supplied to accommodate the 2x2 tile. Other than the code that I added to implement the 2x2 tile, the only other change to MainAcitivity was to the calculateNextIndex method that uses a different way of calculating the index at an (x, y) position. Layouts and the LongPressListener class were also mocked up since they were not supplied.
Here is a video of the demo:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int ITEMS = 10;
private GridLayout mGrid;
private ScrollView mScrollView;
private ValueAnimator mAnimator;
private Boolean isScroll = false;
private GridLayout.Spec m1xSpec = GridLayout.spec(GridLayout.UNDEFINED, 1);
private GridLayout.Spec m2xSpec = GridLayout.spec(GridLayout.UNDEFINED, 2);
private int mBaseWidth;
private int mBaseHeight;
private int mBaseMargin;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScrollView = (ScrollView) findViewById(R.id.scrollView);
mScrollView.setSmoothScrollingEnabled(true);
mGrid = (GridLayout) findViewById(R.id.grid);
mGrid.setOnDragListener(new DragListener());
final LayoutInflater inflater = LayoutInflater.from(this);
GridLayout.LayoutParams lp;
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
float dpiToPx = displayMetrics.density;
View view = inflater.inflate(R.layout.item, mGrid, false);
lp = (GridLayout.LayoutParams) view.getLayoutParams();
mBaseWidth = lp.width;
mBaseHeight = lp.height;
mBaseMargin = lp.rightMargin;
for (int i = 0; i < ITEMS; i++) {
final View itemView = inflater.inflate(R.layout.item, mGrid, false);
final TextView text = (TextView) itemView.findViewById(R.id.text);
text.setText(String.valueOf(i + 1));
itemView.setOnLongClickListener(new LongPressListener());
lp = (i == 0) ? make2x2LayoutParams(itemView) : make1x1LayoutParams(itemView);
mGrid.addView(itemView, lp);
}
}
private GridLayout.LayoutParams make2x2LayoutParams(View view) {
GridLayout.LayoutParams lp = (GridLayout.LayoutParams) view.getLayoutParams();
lp.width = mBaseWidth * 2 + 2 * mBaseMargin;
lp.height = mBaseHeight * 2 + 2 * mBaseMargin;
lp.rowSpec = m2xSpec;
lp.columnSpec = m2xSpec;
lp.setMargins(mBaseMargin, mBaseMargin, mBaseMargin, mBaseMargin);
return lp;
}
private GridLayout.LayoutParams make1x1LayoutParams(View view) {
GridLayout.LayoutParams lp = (GridLayout.LayoutParams) view.getLayoutParams();
lp.width = mBaseWidth;
lp.height = mBaseHeight;
lp.setMargins(mBaseMargin, mBaseMargin, mBaseMargin, mBaseMargin);
lp.rowSpec = m1xSpec;
lp.columnSpec = m1xSpec;
return lp;
}
private int mDraggedIndex;
class DragListener implements View.OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
final View view = (View) event.getLocalState();
int index = calculateNextIndex(event.getX(), event.getY());
View child;
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
mDraggedIndex = index;
break;
case DragEvent.ACTION_DRAG_LOCATION:
if (view == v) return true;
// get the new list index
final Rect rect = new Rect();
mScrollView.getHitRect(rect);
final int scrollY = mScrollView.getScrollY();
if (event.getY() - scrollY > mScrollView.getBottom() - 250) {
startScrolling(scrollY, mGrid.getHeight());
} else if (event.getY() - scrollY < mScrollView.getTop() + 250) {
startScrolling(scrollY, 0);
} else {
stopScrolling();
}
child = mGrid.getChildAt(0);
if (index == 0) {
child.setLayoutParams(make1x1LayoutParams(child));
view.setLayoutParams(make2x2LayoutParams(view));
} else if (mDraggedIndex == 0) {
view.setLayoutParams(make1x1LayoutParams(view));
child.setLayoutParams(make2x2LayoutParams(child));
} else {
child.setLayoutParams(make2x2LayoutParams(child));
view.setLayoutParams(make1x1LayoutParams(view));
}
mGrid.removeView(view);
mGrid.addView(view, index);
break;
case DragEvent.ACTION_DROP:
for (int i = 0; i < mGrid.getChildCount(); i++) {
child = mGrid.getChildAt(i);
child.setLayoutParams(make1x1LayoutParams(child));
}
mGrid.removeView(view);
if (index == 0) {
view.setLayoutParams(make2x2LayoutParams(view));
}
mGrid.addView(view, index);
view.setVisibility(View.VISIBLE);
mGrid.getChildAt(0).setLayoutParams(make2x2LayoutParams(mGrid.getChildAt(0)));
break;
case DragEvent.ACTION_DRAG_ENDED:
if (!event.getResult()) {
view.setVisibility(View.VISIBLE);
}
break;
}
return true;
}
}
private void startScrolling(int from, int to) {
if (from != to && mAnimator == null) {
isScroll = true;
mAnimator = new ValueAnimator();
mAnimator.setInterpolator(new OvershootInterpolator());
mAnimator.setDuration(Math.abs(to - from));
mAnimator.setIntValues(from, to);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mScrollView.smoothScrollTo(0, (int) valueAnimator.getAnimatedValue());
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
isScroll = false;
mAnimator = null;
}
});
mAnimator.start();
}
}
private void stopScrolling() {
if (mAnimator != null) {
mAnimator.cancel();
}
}
private int calculateNextIndexOld(float x, float y) {
// calculate which column to move to
final float cellWidth = mGrid.getWidth() / mGrid.getColumnCount();
final int column = (int) (x / cellWidth);
final float cellHeight = mGrid.getHeight() / mGrid.getRowCount();
final int row = (int) Math.floor(y / cellHeight);
int index = row * mGrid.getColumnCount() + column;
if (index >= mGrid.getChildCount()) {
index = mGrid.getChildCount() - 1;
}
Log.d("MainActivity", "<<<<index=" + index);
return index;
}
private int calculateNextIndex(float x, float y) {
// calculate which column to move to
int index;
for (index = 0; index < mGrid.getChildCount(); index++) {
View child = mGrid.getChildAt(index);
Rect rect = new Rect();
child.getHitRect(rect);
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
break;
}
}
if (index >= mGrid.getChildCount()) {
// Move into empty cell? Calculate based upon uniform cell sizes.
index = calculateNextIndexOld(x, y);
}
if (index >= mGrid.getChildCount()) {
// Can't determine where to put it? Add it to the end.
index = mGrid.getChildCount() - 1;
}
return index;
}
}
If you work with the demo a little, you will see that it is possible to move tiles such that a 1x1 tile gap is opened up. This may be OK, but the code may need to be reworked a little if not.
You can try :
https://github.com/askerov/DynamicGrid
I hope it can help your problem!
I'm new to building apps on Android. I do have Java and android studio working experience, and build basic android apps but the project I'm working on now is a little bit complicated.
I want to move a image inside a imageview corresponding to touch pointer, in other words to direction of touch but inside a specific area(circular).
Just like in android app pou i.e. pou eyes move corresponding to touch pointer.
I started with below code:
public class TouchActivity extends Activity {
private ViewGroup mainLayout;
private ImageView image;
private int xDelta;
private int yDelta;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_touch);
mainLayout = (RelativeLayout) findViewById(R.id.main);
image = (ImageView) findViewById(R.id.image);
image.setOnTouchListener(onTouchListener());
}
private OnTouchListener onTouchListener() {
return new OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View view, MotionEvent event) {
final int x = (int) event.getRawX();
final int y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams)
view.getLayoutParams();
xDelta = x - lParams.leftMargin;
yDelta = y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
Toast.makeText(TouchActivity.this,
"thanks for new location!", Toast.LENGTH_SHORT)
.show();
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
.getLayoutParams();
layoutParams.leftMargin = x - xDelta;
layoutParams.topMargin = y - yDelta;
layoutParams.rightMargin = 0;
layoutParams.bottomMargin = 0;
view.setLayoutParams(layoutParams);
break;
}
mainLayout.invalidate();
return true;
}
};
}
}
... and successfully managed to move an image corresponding to touch pointer but I want to implement something like this:
(eyes are moving according to touch but inside a specific area)
Any ideas?
(by kalu) Move click image inside imageview ajust height or with depends the size of resource(image) and translate x or y direction.
Try This:
// your image view
ImageView mCoverView = findViewById(R.id.imageview);
Glide.with(aActivity).load(cover) //set img url
.asBitmap()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(new BitmapImageViewTarget(mCoverView) {
#Override
protected void setResource(Bitmap resource) {
final Bitmap f_res = resource;
// just if you want change for a drawable res
// final Bitmap f_res = BitmapFactory.decodeResource(aActivity.getResources(),R.drawable.ic_ampliada);
//k ajust center but ajust height or with depend the image
mCoverView.setScaleType(ImageView.ScaleType.CENTER_CROP);
mCoverView.setImageBitmap(f_res);
final String dir;
if(f_res.getWidth()>f_res.getHeight())
dir="x";
else
dir="y";
mCoverView.setOnClickListener(new View.OnClickListener() {
// size of move
int value = 25;
double resh=f_res.getHeight();
double resw=f_res.getWidth();
double coverh =mCoverView.getHeight();
double coverw = mCoverView.getWidth();
int xTranslate = 0;
double sacale_w_from_h = coverh / resh * resw;
//k wtotal distancia do centro as bordas (x=0 (centro imagem))
double wtotal = (sacale_w_from_h/2);
int yTranslate = 0;
double sacale_h_from_w = coverw / resw * resh;
//k wtotal distancia do centro as bordas (y=0 (centro imagem))
double htotal = sacale_h_from_w / 2;
#Override
public void onClick(View v) {
if (dir == "x"){
if (xTranslate>wtotal || xTranslate<-wtotal)
value=-value;
xTranslate += value;
}else if(dir == "y"){
if (yTranslate>htotal || yTranslate<-htotal)
value=-value;
yTranslate += value;
}
this.translateImage(xTranslate,yTranslate);
}
private void translateImage(int xTranslate, int yTranslate)
{
Bitmap translateBitmap = Bitmap.createBitmap(f_res.getWidth() + xTranslate, f_res.getHeight() + yTranslate, f_res.getConfig());
Canvas translateCanvas = new Canvas(translateBitmap);
Matrix translateMatrix = new Matrix();
translateMatrix.setTranslate(xTranslate, yTranslate);
Paint paint = new Paint();
translateCanvas.drawBitmap(f_res, translateMatrix, paint);
mCoverView.setImageBitmap(translateBitmap);
}
});
}
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
}
}
});
//-------------------------------------------------------------------
As a example,have to draw a layout of images.
I inflated an ImageView into a layout. When I swipe on the screen I need to change the color of the each circle to green. How should I do it. I tried with gesture detector, but I can't get what I need.
and this is my code.. I create this view by inflating an image view..
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(1024);
setContentView(R.layout.test);
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
height = displaymetrics.heightPixels;
width = displaymetrics.widthPixels;
count = width/30;
totalCircles = count * (height/30);
horizontalSpace = width%30;
verticalSpace = height%30;
mRelativeLayout = (RelativeLayout)findViewById(R.id.fl);
mRelativeLayout.setPadding(horizontalSpace/2, verticalSpace/2, horizontalSpace/2, verticalSpace/2);
for(int i=1;i<totalCircles+1;i++){
myButton = new ImageView(this);
myButton.setId(i);
myButton.setImageResource(R.drawable.circle_grey);
LayoutParams lp = new LayoutParams(0,0);
if(i%count != 1){
lp.addRule(RelativeLayout.RIGHT_OF, imgViews.get(i-2).getId());
if(imView != null){
lp.addRule(RelativeLayout.BELOW, imView.getId());
}
if(i%count == 0){
imView = myButton;
}
}else{
if(imView != null){
lp.addRule(RelativeLayout.BELOW, imView.getId());
}
}
mRelativeLayout.addView(myButton,lp);
myButton.getLayoutParams().width = 30;
myButton.getLayoutParams().height = 30;
imgViews.add(myButton);
myButton.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
System.out.println("Inside touch listener");
ImageView imV = (ImageView)v;
imV.setImageResource(R.drawable.circle_green);
}
return false;
}
});
}
}
Jack K Fouani's answer is partially correct. Instead of imagView.onTouch you need to use gridview.onTouch.
Please check this sample.
gv.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
float currentXPosition = event.getX();
float currentYPosition = event.getY();
int position = gv.pointToPosition((int)currentXPosition, (int) currentYPosition);
//using the position obtained you can change the imageview color.
}
return true;
}
});
Instead of inflating one ImageView. You should fill a GridView where every GridCell contains a circle image. Now
set the OnTouchEvent of those Gridcells to change the ImageView within the GridCell.
my advice is to use GrideView with adapter , inside the adapter you will have position for each imagView the position allow you to check if image is touched or not depending on that you can change ImageView color to white or green
I am a programmer with a Windows background and I am new to Java and Android stuff.
I want to create a widget (not an app) which displays a chart. After a long research I know I can do this with Canvas, imageviews and Bitmaps. The canvas which I paint on should be the same as the Widget Size.
How do I know the widget size (or imageview size) so that I can supply it to the function?
Bitmap.createBitmap(width_xx, height_yy, Config.ARGB_8888);
Code Snippet:
In the timer run method:
#Override
public void run() {
Bitmap bitmap = Bitmap.createBitmap(??, ??, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// Create a new paint
Paint p = new Paint();
p.setAntiAlias(true);
p.setStrokeWidth(1);
// Draw circle
// Here I can use the width and height to scale the circle
canvas.drawCircle(50, 50, 7, p);
remoteViews.setImageViewBitmap(R.id.imageView, bitmap);
From what I've learnt, you can only calculate widget dimensions on Android 4.1+.
When on a lower API, you'll have to use static dimensions.
About widget dimensions: App Widget Design Guidelines
int w = DEFAULT_WIDTH, h = DEFAULT_HEIGHT;
if ( Build.VERSION.SDK_INT >= 16 ) {
Bundle options = appWidgetManager.getAppWidgetOptions(widgetId);
int maxW = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
int maxH = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
int minW = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
int minH = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
if ( context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ) {
w = maxW;
h = minH;
} else {
w = minW;
h = maxH;
}
}
Have a look at the method:
public void onAppWidgetOptionsChanged (Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)
It will be called each time you start/resize the widget.
Getting the widget width/height can be done as follows:
newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
I am currently using this:
private void run() {
int width = 400, height = 400;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
Paint p = new Paint();
p.setColor(Color.WHITE);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(1);
p.setAntiAlias(true);
c.drawCircle(width/2, height/2, radius, p);
remoteViews.setImageViewBitmap(R.id.imageView, bitmap);
ComponentName clockWidget = new ComponentName(context,
Clock_22_analog.class);
AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(context);
appWidgetManager.updateAppWidget(clockWidget, remoteViews);
}
You can use this
Bitmap image1, image2;
Bitmap bitmap = Bitmap.createBitmap(image1.getWidth(), image1.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
You can create a custom widget and set the size of wight on its onMeasure() method. And also save the size at that time so that you can use it further for image creation...
I've not worked on Widgets, but I have some experience getting ImageView's size.
Here is some code I use:
public class ViewSizes {
public int width;
public int height;
public boolean isEmpty() {
boolean result = false;
if (0 >= width || 0 >= height) {
result = true;
}
return result;
}
}
That's just a dummy class containing the size parameters.
public static ViewSizes getSizes(View view) {
ViewSizes sizes = new ViewSizes();
sizes.width = view.getWidth();
sizes.height = view.getHeight();
if (sizes.isEmpty()) {
LayoutParams params = view.getLayoutParams();
if (null != params) {
int widthSpec = View.MeasureSpec.makeMeasureSpec(params.width, View.MeasureSpec.AT_MOST);
int heightSpec = View.MeasureSpec.makeMeasureSpec(params.height, View.MeasureSpec.AT_MOST);
view.measure(widthSpec, heightSpec);
}
sizes.width = view.getMeasuredWidth();
sizes.height = view.getMeasuredHeight();
}
return sizes;
}
This method calculates the width forcing a measure cycle if such has not already happened.
public static boolean loadPhoto(ImageView view, String url, float aspectRatio) {
boolean processed = false;
ViewSizes sizes = ViewsUtils.getSizes(view);
if (!sizes.isEmpty()) {
int width = sizes.width - 2;
int height = sizes.height - 2;
if (ASPECT_RATIO_UNDEFINED != aspectRatio) {
if (height * aspectRatio > width) {
height = (int) (width / aspectRatio);
} else if (height * aspectRatio < width) {
width = (int) (height * aspectRatio);
}
}
// Do you bitmap processing here
processed = true;
}
return processed;
}
This one is probably useless for you. I give just as an example - I have an ImageView and image URL, which should be parametrized with image and height.
public class PhotoLayoutListener implements OnGlobalLayoutListener {
private ImageView view;
private String url;
private float aspectRatio;
public PhotoLayoutListener(ImageView view, String url, float aspectRatio) {
this.view = view;
this.url = url;
this.aspectRatio = aspectRatio;
}
boolean handled = false;
#Override
public void onGlobalLayout() {
if (!handled) {
PhotoUtils.loadPhoto(view, url, aspectRatio);
handled = true;
}
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
removeLayoutListenerPre16(viewTreeObserver, this);
} else {
removeLayoutListenerPost16(viewTreeObserver, this);
}
}
}
#SuppressWarnings("deprecation")
private void removeLayoutListenerPre16(ViewTreeObserver observer, OnGlobalLayoutListener listener){
observer.removeGlobalOnLayoutListener(listener);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void removeLayoutListenerPost16(ViewTreeObserver observer, OnGlobalLayoutListener listener){
observer.removeOnGlobalLayoutListener(listener);
}
}
This is just a layout listener - I want to process the image loading once the layout phase has finished.
public static void setImage(ImageView view, String url, boolean forceLayoutLoading, float aspectRatio) {
if (null != view && null != url) {
final ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (forceLayoutLoading || !PhotoUtils.loadPhoto(view, url, aspectRatio)) {
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new PhotoLayoutListener(view, url, aspectRatio));
}
}
}
}
This is the method I actually call. I give it the view and URL. The methods takes care of loading - if it can determine the view's size it starts loading immediately. Otherwise it just assigns a layout listener and start the loading process once the layout is finished.
You could strip away some parameters - forceLoading / aspectRatio should be irrelevant for you. After that change the PhotoUtils.loadPhoto method in order to create the bitmap with the width / height it has calculated.
Like Julian told us, you can get them like that with a bitmap of your image:
int width = bitmap.getWidth();
int height = bitmap.getHeight();
I'm new with Android.
In my project I have the custom View MyView with the follow code
public class MyView extends View {
private final Bitmap baseBitmap = BitmapFactory.decodeResource(
getResources(), R.drawable.myImage);
private final Matrix matrix;
private boolean active = true;
public MyView(Context context, Matrix matrix) {
super(context);
this.matrix = matrix;
this.setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (active) {
System.out.println("draw "+this.getId());
canvas.drawBitmap(baseBitmap, matrix, null);
} else {
...
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
System.out.println("--------->"+this.getId());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
this.matrix.setTranslate(event.getX()-(baseBitmap.getWidth()/2), event.getY()-(baseBitmap.getHeight()/2));
this.invalidate();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
this.active = false;
}
return true;
}
In my Activity, I instantiate MyView many times and then add them to the main layout. This is its code:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display display = getWindowManager().getDefaultDisplay();
float cx = display.getWidth() / 2, cy = display.getHeight() / 2;
int radius = 80;
double distance = 0, distancePoint = 0;
final int flags = PathMeasure.POSITION_MATRIX_FLAG
| PathMeasure.TANGENT_MATRIX_FLAG;
float length = 0;
setContentView(R.layout.main);
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_view);
Path pathCircle = new Path();
pathCircle.addCircle(cx, cy, radius, Direction.CW);
PathMeasure meas = new PathMeasure(pathCircle, false);
int nObject = 10;
length = meas.getLength();
distance = length/nObject;
int i = 0;
while(i<nObject){
Matrix m = new Matrix();
meas.getMatrix((float)distancePoint, m, flags);
MyView myView = new MyView(this, m);
System.out.println(myView.toString());
myView.setId(i);
mainLayout.addView(myView,i);
i++;
distancePoint = distance*i;
}
}
}
At runtime, when I touch any MyView element I always get the last. With "System.out.println("--------->"+this.getId());" I can see that the id of the touched element is always the last, even if I toch the first or any other element. Actualy, I just can move the last element.
Does anyone know why can't I get the event of the right istance of MyView touched?
(I hope my question is clear)
Thanks
I changed the code adding the onMeasure method. I used the code of a tutorial, dimensions are not specific for my image. The views are drawn and the result is the same, unfortunately with the same problem. I post the layout xml too, maybe could be useful.
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display display = getWindowManager().getDefaultDisplay();
float cx = display.getWidth() / 2, cy = display.getHeight() / 2;
int radius = 80;
double distance = 0, distancePoint = 0;
final int flags = PathMeasure.POSITION_MATRIX_FLAG
| PathMeasure.TANGENT_MATRIX_FLAG;
float length = 0;
setContentView(R.layout.main);
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_view);
Path pathCircle = new Path();
pathCircle.addCircle(cx, cy, radius, Direction.CW);
PathMeasure meas = new PathMeasure(pathCircle, false);
int nObject = 10;
length = meas.getLength();
distance = length/nObject;
int i = 0;
while(i<nObject){
Matrix m = new Matrix();
meas.getMatrix((float)distancePoint, m, flags);
MyView myView = new MyView(this, m);
System.out.println(myView.toString());
myView.setId(i);
nt spec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
myView.measure(spec, spec);
mainLayout.addView(myView,i);
i++;
distancePoint = distance*i;
}
}
}
public class MyView extends View {
private final Bitmap baseBitmap = BitmapFactory.decodeResource(
getResources(), R.drawable.myImage);
private final Matrix matrix;
private boolean active = true;
public MyView(Context context, Matrix matrix) {
super(context);
this.matrix = matrix;
this.setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (active) {
System.out.println("draw "+this.getId());
canvas.drawBitmap(baseBitmap, matrix, null);
} else {
...
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
System.out.println("--------->"+this.getId());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
this.matrix.setTranslate(event.getX()-(baseBitmap.getWidth()/2), event.getY()-(baseBitmap.getHeight()/2));
this.invalidate();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
this.active = false;
}
return true;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
setMeasuredDimension(chosenDimension, chosenDimension);
}
private int chooseDimension(int mode, int size) {
if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
return size;
} else { // (mode == MeasureSpec.UNSPECIFIED)
return getPreferredSize();
}
}
// in case there is no size specified
private int getPreferredSize() {
return 300;
}
}
The main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="#+id/main_view"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF66FF33">
</RelativeLayout>
I'm pretty sure that it's because you're basically piling up your views at the top left corner of your RelativeLayout. So, only the uppermost (the last one added) is touchable.
I think that if you try adding them to a LinearLayout, as a test, you'll see that your view works. Setting LayoutParams for a RelativeLayout programmatically is not very comfy IMHO.
EDIT
I tried your code. The fact is that your views are just made to be drawn one over the other, or else the overall drawing wouldn't come, so my first guess is right (the uppermost covers the others - even in its transparent parts)(btw try Hierarchy Viewer and you can see that yourself). So you need to do your job in a single view, or handle the touches like this:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if(!isPetaloTouched()) {// check if the actual drawing was touched
return false; // discard the event so that it reaches
// the underlying view
}
//......
See this post for an explanation of how events work in Android.
Both ways would need an isPetaloTouched() logic to detect if/which drawing must be moved, but the first would be more efficient of course.
Also, forget about the onMeasure() thing, I thought that could help giving the view a size around which to wrap, so that it wouldn't fill its parent and aligning views aside would make sense. However, be sure that the touch would work if the views were not piled up.
(...allora mPetali stava proprio per petali!)