自定义视图最初不显示/绘制

时间:2014-08-12 18:37:35

标签: android android-layout android-custom-view

我有一个自定义视图,扩展了ImageView,当我触摸到不同的视图时,我以编程方式添加到FrameLayout。由于某些原因,此自定义视图不会显示在屏幕上,直到第二次触摸触发视图,尽管后续触摸工作正常(自定义视图的新实例不断添加)。编辑:当我昨天发布这个问题时,我忽略了提到在第二次触摸时第一个视图显示出来(即屏幕上有2个视图,当其中一个被拖离另一个时可以看到)。因此,似乎所有点击都得到了妥善处理,问题在于布局的刷新或其他方面的问题。

我已经使用非自定义ImageView进行了测试,并且没有看到这个问题,我也改变了自定义视图以扩展View而不是ImageView,这个问题消失了但是一个新的取而代之 - 将视图添加到FrameLayout对整体布局做了奇怪的事情(FrameLayout似乎向下移动或增长,并且FrameLayout下面的布局变得不再可见)。

我在这里尝试了解决方案:Refreshing a LinearLayout after adding a view,但这没有用。

这是将自定义视图添加到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);

我在上面的代码之后尝试在视图和FrameLayout上调用invalidate,但都没有帮助。

以下是自定义视图的代码:

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;
    }
}
}

提前感谢您的帮助。

编辑:这里是在onClick on其他视图时将自定义视图添加到FrameLayout的完整代码:

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};
}
}

2 个答案:

答案 0 :(得分:0)

此处存在焦点问题,您需要做的是在使用 onClick 的视图上调用requestFocusFromTouch()

另外一个问题是,每次单击不必要的按钮时都要重新创建FrameLayout,只需创建frameLayout的实例变量,每次单击按钮并使用该实例在其中添加自定义图像视图。

答案 1 :(得分:0)

似乎问题以某种方式与FrameLayout的宽度和高度或ImageView的宽度和高度相关联。 (问题随之消失,因为我改变了两者的尺寸,出于不同的目的。)我还为ImageView设置了scaleType。

以下是问题发生时xml的用法:

<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>

这是修订后的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>