【问题标题】:Custom view doesn't display/draw at first自定义视图一开始不显示/绘制
【发布时间】:2014-08-12 18:37:34
【问题描述】:

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

我已经用非自定义 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 不同视图发生时将自定义视图添加到 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};
}
}

【问题讨论】:

  • 你所说的when a different view is touched on是什么意思
  • @Rod_Algonquin 我在不同的布局中有一个不同的视图,它有一个与之关联的 onClickListener。我上面第一个 sn-p 中的代码在 onClick() 中,用于该侦听器。

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


【解决方案1】:

你这里有一个焦点问题,你需要做的是在使用onClick的视图上调用requestFocusFromTouch()

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

【讨论】:

  • 感谢您重新创建 FrameLayout。关于。添加视图的问题,我在这里尝试了解决方案:stackoverflow.com/questions/19820846/… 并且还尝试将 放在使用 onClick 的 ImageView 的 xml 中,但没有更改。想法?
  • @hBrent 你能把使用onClick的全班贴出来吗
  • 罗德,我已经发布了整个班级。感谢您的帮助。
  • @hBrent 你可以试试把requestFocus 换成requestFocusFromTouch
  • 不走运。 “常规” ImageView 没有问题的事实是否重要?我在想这可能与我的自定义视图有关。
【解决方案2】:

问题似乎与 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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-22
    • 1970-01-01
    • 1970-01-01
    • 2016-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多