2

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};
}
}
Community
  • 1
  • 1
hBrent
  • 1,696
  • 1
  • 17
  • 38
  • what do yuo mean by `when a different view is touched on` – Rod_Algonquin Aug 12 '14 at 19:02
  • @Rod_Algonquin I have a different view in a different layout that has an onClickListener associated with it. The code in my first snippet above is in `onClick()` for that listener. – hBrent Aug 12 '14 at 19:23

2 Answers2

0

You have a focus problem here, what you need to do is to call the requestFocusFromTouch() on the view where the onClick is used.

one more problem is that you are recreating the FrameLayout each time you click the button that is unnecessary, just create a instance varaible of frameLayout and each time you click on the button and use that instance to add the custom imageviews in it.

Rod_Algonquin
  • 26,074
  • 6
  • 52
  • 63
  • Thanks for catching the recreating of the FrameLayout. Re. the problem adding the view, I tried solutions here: http://stackoverflow.com/questions/19820846/android-requestfocus-not-working and also tried putting in the xml for the ImageView on which onClick is used, but no change. Thoughts? – hBrent Aug 12 '14 at 20:16
  • @hBrent can you post the whole class where onClick is used – Rod_Algonquin Aug 12 '14 at 20:22
  • Rod, I've posted the whole class. Thanks for your help. – hBrent Aug 12 '14 at 20:35
  • @hBrent can you try instead of `requestFocus` to `requestFocusFromTouch` – Rod_Algonquin Aug 12 '14 at 20:37
  • No luck. Is the fact that a "regular" ImageView doesn't have the problem significant? I'm thinking maybe it's something about my custom view. – hBrent Aug 12 '14 at 20:39
  • More info: if I add a 2nd non-custom ImageView (by onClicking a 2nd time), the second apparently fills the FrameLayout. (I changed onClick to create and add a non-custom ImageView to the FrameLayout, instead of the custom view, to do this testing.) – hBrent Aug 12 '14 at 20:45
  • @hBrent that is because you set the layout params to `MATCH_PARENT` – Rod_Algonquin Aug 12 '14 at 20:57
  • OK, that makes sense, though it seems odd that the first one added doesn't display that way. Apparently there is something weird about the first time a view is added to the FrameLayout. – hBrent Aug 12 '14 at 21:10
  • @hBrent you already set the size of the Imageview in the canvas thus it is not filling the whole screen. – Rod_Algonquin Aug 12 '14 at 21:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/59228/discussion-between-hbrent-and-rod-algonquin). – hBrent Aug 12 '14 at 22:39
0

It seems that the problem was somehow associated with the width and height of the FrameLayout or the width and height of the ImageView. (The problem went away as I was changing dimensions in both, for a different purpose.) I also set a scaleType for the ImageView.

Here's how the xml was when the problem was occurring:

<FrameLayout
    android:id="@+id/place_art_frame_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/black" >

    <ImageView
        android:id="@+id/place_art_imageview_wall"
        android:layout_width="@dimen/wall_large_size"
        android:layout_height="@dimen/wall_large_size"
        android:layout_gravity="center" />

</FrameLayout>

Here's the revised xml:

<FrameLayout
    android:id="@+id/place_art_frame_layout"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:background="@color/black" >

    <ImageView
        android:id="@+id/place_art_imageview_wall"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitCenter" />

</FrameLayout>
hBrent
  • 1,696
  • 1
  • 17
  • 38