I have a custom view, extending ImageView, that I add to a FrameLayout programmatically when a different view is touched on. For some reason this custom view doesn't show up on the screen until the second touch on the triggering view, though subsequent touches work fine (new instances of the custom view keep getting added). EDIT: when I posted this question yesterday, I neglected to mention that on the second touch the first view shows up (i.e., there are 2 views on the screen, both of which can be seen when one is dragged off the other). Therefore, it seems that all clicks are being properly handled, that the problem is with the refreshing of the layout or something along those lines.
I've tested with a non-custom ImageView, and don't see this problem with that, and I've also changed the custom view to extend View, instead of ImageView, and this problem goes away but a new one takes its place--adding the view to the FrameLayout does weird things to the overall layout (the FrameLayout seems to shift down or grow, and the layout below the FrameLayout becomes no longer visible).
I tried the solution here: Refreshing a LinearLayout after adding a view, but that didn't help.
Here's the code that adds the custom view to the FrameLayout:
FrameLayout frameLayout = (FrameLayout) findViewById(R.id.place_art_frame_layout);
WallArtView wallArtView = new WallArtView(getApplicationContext());
wallArtView.initWallArt(artWork, 100, 300, viewBoundary(wall));
wallArtView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
frameLayout.addView(wallArtView);
I've tried calling invalidate on the view and the FrameLayout after the above code, but neither helped.
Here's the code for the custom view:
public class WallArtView extends ImageView {
Canvas canvas;
private int windowWidth;
private int windowHeight;
float mLastTouchX = 0;
float mLastTouchY = 0;
private int mActivePointerId = MotionEvent.INVALID_POINTER_ID;
WallArt wallArt;
Rect rect;
int movementBoundary[];
Paint paint;
public WallArtView(Context context) {
super(context);
setFocusable(true); // necessary for getting the touch events
canvas = new Canvas();
rect = new Rect();
paint = new Paint();
}
public WallArtView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public WallArtView(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true); // necessary for getting the touch events
canvas = new Canvas();
windowWidth = getContext().getResources().getDisplayMetrics().widthPixels;
windowHeight = getContext().getResources().getDisplayMetrics().heightPixels;
rect = new Rect();
paint = new Paint();
}
public float getWindowWidth() {
return windowWidth;
}
public float getWindowHeight() {
return windowHeight;
}
public void initWallArt(Bitmap b, float l, float t, int[] boundary) {
wallArt = new WallArt(b, l, t);
movementBoundary = boundary;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#44FFFFFF"));
paint.setStrokeWidth(0);
canvas.drawRect(200, 500, 500, 1000, paint);
// draw the art
rect.set((int) wallArt.getLeft(), (int) wallArt.getTop(),
(int) wallArt.getLeft() + wallArt.getWidthOfArt(),
(int) wallArt.getTop() + wallArt.getHeightOfArt());
canvas.drawBitmap(wallArt.getBitmap(), null, rect, null);
canvas.restore();
}
public boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
float X = event.getX();
float Y = event.getY();
switch (eventaction) {
case MotionEvent.ACTION_DOWN: // touch down so check if the finger is on
final int pointerIndex = MotionEventCompat.getActionIndex(event);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// Remember where we started (for dragging)
mLastTouchX = x;
mLastTouchY = y;
// Save the ID of this pointer (for dragging)
mActivePointerId = MotionEventCompat.getPointerId(event, 0);
break;
case MotionEvent.ACTION_MOVE: // touch drag with the ball
final int pointerIndex1 = MotionEventCompat.findPointerIndex(event, mActivePointerId);
final float x1 = MotionEventCompat.getX(event, pointerIndex1);
final float y1 = MotionEventCompat.getY(event, pointerIndex1);
// Calculate the distance moved
final float dx = x1 - mLastTouchX;
final float dy = y1 - mLastTouchY;
// Remember this touch position for the next move event
mLastTouchX = x1;
mLastTouchY = y1;
float artLeft = wallArt.getLeft();
float artRight = wallArt.getLeft() + wallArt.getWidthOfArt();
float artTop = wallArt.getTop();
float artBottom = wallArt.getTop() + wallArt.getHeightOfArt();
if (X > artLeft && X < artRight && Y > artTop && Y < artBottom
&& (artLeft > movementBoundary[0] || dx > 0)
&& (artTop > movementBoundary[1] || dy > 0)
&& (artRight < movementBoundary[2] || dx < 0)
&& (artBottom < movementBoundary[3] || dy < 0)) {
wallArt.setLeft(wallArt.getLeft() + dx);
wallArt.setTop(wallArt.getTop() + dy);
}
break;
case MotionEvent.ACTION_UP:
// touch drop - just do things here after dropping
break;
}
// redraw the canvas
invalidate();
return true;
}
public static class WallArt {
Bitmap bitmap;
float left, top;
int id;
static int count = 0;
public WallArt(Bitmap b, float l, float t) {
this.id = count++;
bitmap = b;
left = l;
top = t;
}
public int getWidthOfArt() {
return bitmap.getWidth();
}
public int getHeightOfArt() {
return bitmap.getHeight();
}
public Bitmap getBitmap() {
return bitmap;
}
public float getLeft() {
return left;
}
public float getTop() {
return top;
}
public int getID() {
return id;
}
public void setLeft(float l) {
left = l;
}
public void setTop(float t) {
top = t;
}
}
}
Thanks in advance for assistance.
Edit: Here's the full code in which the custom view is added to the FrameLayout when onClick on a different view happens:
public class ActivityWallPlaceArt extends Activity implements OnClickListener{
private ImageView thumbNail, wall;
private Bitmap artWork;
private WallArtView wallArtView;
private FrameLayout frameLayout;
private Button done;
private Context context;
private int mWallIndex = 0;
private int mRoomIndex = 0;
private String mThumbnailUrl;
ArrayList<Wall> mWallList;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wall_place_art);
artWork = bitmapFromFile("/storage/emulated/0/Pictures/Curate/IMG_20140808_132606.jpg");
wall = (ImageView) findViewById(R.id.place_art_imageview_wall);
wall.setImageBitmap(bitmapFromFile("/storage/emulated/0/Pictures/Curate/IMG_20140731_170538.jpg"));
thumbNail = (ImageView) findViewById(R.id.place_art_imageview_thumbnail);
thumbNail.setImageBitmap(artWork);
thumbNail.setOnClickListener(this);
thumbNail.requestFocus();
frameLayout = (FrameLayout) findViewById(R.id.place_art_frame_layout);
// -------------
context = getApplicationContext();
Intent intent = getIntent();
mWallIndex = intent.getIntExtra("wallIndex",0);
mRoomIndex = intent.getIntExtra("roomIndex",0);
mThumbnailUrl = intent.getStringExtra("thumbnailUrl");
mWallList = ActivityMain.sRoomList.getRoom(mRoomIndex).getWallList();
Wall currentWall = mWallList.get(mWallIndex);
done = (Button) findViewById(R.id.place_art_imageview_done);
done.setOnClickListener(this);
}
private Bitmap bitmapFromFile(String imageUrl) {
Bitmap bitmap = null;
try {
final File file = new File(imageUrl);
BitmapFactory.Options option = new BitmapFactory.Options();
option.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(file), null, option);
final int REQUIRED_WIDTH = 200;
final int REQUIRED_HIGHT = 200;
int scale = 1;
while (option.outWidth / scale / 2 >= REQUIRED_WIDTH
&& option.outHeight / scale / 2 >= REQUIRED_HIGHT)
scale *= 2;
BitmapFactory.Options optionObj = new BitmapFactory.Options();
optionObj.inSampleSize = scale;
Uri uri = Uri.parse(imageUrl);
ExifInterface exif;
try {
exif = new ExifInterface(uri.getPath());
int rotation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation);
Matrix mat = new Matrix();
mat.postRotate(rotationInDegrees);
Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(file), null, optionObj);
bitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(),
bmp.getHeight(), mat, true);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
}
return bitmap;
}
private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
return 0;
}
@Override
public void onClick(View v) {
if (v == thumbNail) {
wallArtView = new WallArtView(getApplicationContext());
wallArtView.initWallArt(artWork, 100, 300, viewBoundary(wall));
wallArtView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
frameLayout.addView(wallArtView);
}
else if (v == done) {
finish();
}
}
private int[] viewBoundary(View view) {
int[] l = new int[2];
view.getLocationOnScreen(l);
int x = l[0];
int y = l[1];
int w = view.getWidth();
int h = view.getHeight();
return new int[] {x, y, x + w, y + h};
}
}