ViewOverlay与ViewGroupOverlay

有关ViewOverlay的官方文档非常清晰,您应该阅读它

它是什么?

ViewOverlay 是存在于API 18+(Android 4.3)内的类,它是位于View视图层顶部的一个附加层,在View所有内容绘制完成后被绘制。 ViewGroupOverlay 是ViewOverlay的子类,对应于ViewGroup,同样地,这个层在ViewGroup中所有内容(包括childView)绘制完毕后才会被绘制。这个层和我们的View拥有同样的尺寸,可看做浮在View上方的一个二维空间。

Overlay对象可通过 View.getOverlay() 或 ViewGroup.getOverlay() 方法获得,获得Overlay对象后,就可以通过add、clear和remove方法操作其中的元素,这也是Overlay仅有的几个方法。

要记住重叠的重要点:

  • 两种覆盖类型:ViewOverlay(用于视图)和ViewGroupOverlay(用于ViewGroup
  • 两种检索视图叠加的方法:View.getViewOverlay/ViewGroup.getViewGroupOverlay
  • 您可以添加/删除一个drawable 在ViewOverlay和ViewGroupOverlay
  • 您可以在ViewGroupOverlay中添加/删除View
  • 文档上说 An overlay [...] on top of a View [...] drawn after all other content in that view [...]

我们先看看View.getViewOverlayView.getViewGroupOverlay方法。

public ViewOverlay getOverlay() {
    if (mOverlay == null) {
        mOverlay = new ViewOverlay(mContext, this);
    }
    return mOverlay;
}

public ViewGroupOverlay getOverlay() {
    if (mOverlay == null) {
        mOverlay = new ViewGroupOverlay(mContext, this);
    }
    return (ViewGroupOverlay) mOverlay;
}

调用get方法时会创建ViewOverlay / ViewGroupOverlay。

ViewOverlay / ViewGroupOverlay如何工作? 请注意,ViewGroupOverlay继承自ViewOverlay,因此我们可以专注于ViewOverlay实现。


ViewOverlay实现基于OverlayViewGroup(ViewOverlay中的静态内部类)。OverlayViewGroup继承自ViewGroup并包含一个drawable列表 正如你所看到的,没有任何方法来布置drawables或view,这就是你负责定位/调整它们的原因。

它怎么用?

带横幅的按钮(VIEWOVERLAY)

在这里,我们希望在按钮的右上角添加一些带有文字的横幅,如下所示。


主要困难是我们不希望按钮的行为被横幅(即点击,触摸等)改变。

基本的xml布局

这里我们有一个按钮的基本布局。


<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/button"
android:text="Click me even if I'm a pirate"
android:background="@drawable/badged"
android:layout_width="250dp"
android:layout_height="250dp"
android:textColor="#FFF"
android:fontFamily="sans-serif-condensed"
android:layout_centerHorizontal="true"/>

</RelativeLayout>

自定义Drawable

为了获得更大的灵活性(更改横幅颜色,文本等),您必须实施自定义绘图。

class BannerDrawable extends Drawable {

private static final double SQRT_2 = Math.sqrt(2);
private final Rect mTextBounds;
private Paint mPaintFill;
private Paint mPaintText;
private String mMessage = "I'M A PIRATE BANNER";
private int mBannerWidth = 50;
private int mTextSize;

public BannerDrawable() {
  initPaintFill();
  initPaintText();
  mTextBounds = new Rect();
}

private void initPaintFill() {
  mPaintFill = new Paint(ANTI_ALIAS_FLAG);
  mPaintFill.setStyle(Paint.Style.FILL);
  mPaintFill.setColor(getResources().getColor(R.color.banner));
}

private void initPaintText() {
  mPaintText = new Paint(ANTI_ALIAS_FLAG);
  mPaintText.setStyle(Paint.Style.FILL);
  mPaintText.setColor(Color.WHITE);
  mPaintText.setTextSize(20);
  mPaintText.setShadowLayer(4.0f, 2.0f, 2.0f, Color.BLACK);
}

@Override
public void draw(Canvas canvas) {
  Rect bounds = getBounds();
            if (bounds.isEmpty()) {
                bounds = canvas.getClipBounds();
            }
            float width = bounds.width();

            adaptTextSize((int) (width * 0.9), (int) (mBannerWidth * 0.9));

            float bannerHyp = (float) (mBannerWidth * SQRT_2);

            canvas.translate(0, bounds.centerY() - mBannerWidth);
            canvas.rotate(45, bounds.centerX(), bounds.centerY() - mBannerWidth);
            canvas.drawRect(bounds.left - bannerHyp, bounds.top, bounds.right + bannerHyp, bounds.top + mBannerWidth, mPaintFill);

            canvas.drawText(mMessage, bounds.centerX() - mTextBounds.centerX(), mBannerWidth / 2 + mTextBounds.height() / 2, mPaintText);
}

private void adaptTextSize(float width, int height) {
  if (mTextSize > 0) {
      mPaintText.setTextSize(mTextSize);
      return;
  }
  int textSize = 10;
  int textHeight;
  int textWidth;
  boolean stop = false;
  while (!stop) {
      mTextSize = textSize++;
      mPaintText.setTextSize(mTextSize);
      mPaintText.getTextBounds(mMessage, 0, mMessage.length(), mTextBounds);

      textHeight = mTextBounds.height();
      textWidth = mTextBounds.width();

      stop = textHeight >= height || textWidth >= width;
  }
}

@Override
public void setAlpha(int alpha) {
}

@Override
public void setColorFilter(ColorFilter cf) {
}

@Override
public int getOpacity() {
  return PixelFormat.OPAQUE;
}
}

最后,使用ViewOverlay

最后一步很简单。它包括调整和定位BannerDrawable,并在按钮已完全布局时将其添加到按钮的ViewOverlay。

请注意,可绘制边界是按钮的右上方。

final View button = findViewById(R.id.button);
final ViewOverlay overlay = button.getOverlay();
final BannerDrawable bannerDrawable = new BannerDrawable();
button.post(new Runnable() {
  @Override
  public void run() {
      //top right square
      bannerDrawable.setBounds(button.getWidth() / 2, 0, button.getWidth(), button.getHeight() / 2);
      overlay.add(bannerDrawable);
  }
});



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值