利用DecorView实现播放视频开灯/关灯效果和仿QQ消息提示框

写这篇文章前得先来了解一下activity.getWindow().getDecorView()获取到的DecorView视图

首先我们通过强大的Android Studio开发神器,获取我们当前app界面的布局层次(结构)来帮助

我们了解DecorView是个什么?

依次点击菜单Tools——>Android——>Layout Inspector然后选择一个app所在的进程就可以抓取界面的布局结构了一览无余。

分析界面的布局结构

通过上面截图我们发现最顶层的视图就是PhoneWindow中的DecorView了,也就是这篇文章使用到的重点。 
DecorView是一个FrameLayout不信你instanceof一下 
我们每次通过Activity的setContentView()函数加载布局时,都是加在图中的content(ContentFrameLayout)

咳咳咳废话不多说了,直接进入今天要说的仿QQ消息提示框ActionBarToast(名字瞎起的不要在意),楼下放效果图

这里写图片描述

实现的思路:

  1. 首先获取DecorView根视图
  2. 初始化展示的视图 高度为顶部状态栏的高度加上ActionBar(标题栏)的高度 
    让显示的视图在ActionBar中垂直居中(布局放一个高度为状态栏高度的View占位即可)
  3. 往DecorView根视图 添加/移除 我们要展示的视图(使用属性动画)

1.首先获取DecorView根视图

//仅需一行代码即可获取
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
  • 1
  • 2

2. 初始化展示的视图

我们要展示的布局item_toast.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white">

    <!--用来占位(状态栏高度)
    这样让显示的内容正好在actionBar的中间-->
    <View
        android:id="@+id/status_bar"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/icon"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginLeft="16dp"
        android:src="@drawable/icon"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/status_bar" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="12dp"
        android:text="每天最多给她点10个赞哦。"
        android:textColor="#131313"
        android:textSize="14sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/icon"
        app:layout_constraintTop_toBottomOf="@+id/status_bar" />
</android.support.constraint.ConstraintLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

获取状态栏和ActionBar的高度

/**
 * 获取状态栏 + 标题栏 的高度
 */
private int getHeight(Context context, View v) {
    //标题栏
    TypedArray values = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.actionBarSize});
    int actionBarHeight = values.getDimensionPixelSize(0, 0);
    values.recycle();
    //状态栏
    int statusBarHeight = 0;
    int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        //设置布局 占位视图的高度
        statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
        v.setLayoutParams(new ConstraintLayout.LayoutParams(
                ConstraintLayout.LayoutParams.MATCH_PARENT, statusBarHeight));
    }
    return actionBarHeight + statusBarHeight;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

初始化好要展示的视图

View view = LayoutInflater.from(activity).inflate(R.layout.item_toast, null);
//设置View的高度
int totalHeight = getHeight(activity, view.findViewById(R.id.status_bar));
view.setLayoutParams(new ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT, totalHeight));
  • 1
  • 2
  • 3
  • 4
  • 5

3. 往DecorView根视图 添加/移除 我们要展示的视图(使用属性动画)

/**
 * 显示
 *
 * @param duration 显示时长
 */
public void showToast(@Duration int duration) {
    if (!showing) {
        showing = true;
        decorView.addView(view);
        ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationY", -totalHeight, 0f);
        animator.setDuration(animationDuration);
        animator.start();
        if (duration != LENGTH_SHOW) {
            //一段时间后隐藏
            view.postDelayed(runnable, duration);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

/**
 * 延时执行取消操作
 */
private Runnable runnable = new Runnable() {
    @Override
    public void run() {
        cancel();
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

重要的操作也就上面这些,主要还是实现的思路。

ActionBarToast完整源码(使用方法可以下载文末Demo查看)

同样使用DecorView实现一个观看视频时 开灯/关灯 的效果,如下图:

这里写图片描述

实现的思路:

  1. 往DecorView添加自定义的视图(黑色蒙版)
  2. 获取我们不加蒙版View在屏幕上的坐标(左上角)
  3. 使用path进行绘制蒙版区域
  4. 移除蒙版

添加我们自定义的View蒙版,设置大小充满屏幕,获取View在屏幕上的坐标。

public class MaskView extends View {

    /**
     * activity 根视图
     */
    private ViewGroup decorView;
    /**
     * 不加遮罩的视图
     */
    private View brightView;
    /**
     * 遮罩画笔
     */
    private Paint paint;
    /**
     * 不遮罩的view
     */
    private RectF brightRectF;
    /**
     * 遮罩的路径
     */
    private Path path;

    /**
     * 是否已经添加了
     */
    private boolean showing = false;

    public MaskView(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        //获取activity顶层视图
        decorView = (ViewGroup) ((Activity) context).getWindow().getDecorView();
        //初始化蒙版视图,充满全屏幕
        setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        paint = new Paint();
        //设置为黑色 在加点透明度
        paint.setColor(Color.argb(230, 0, 0, 0));
        path = new Path();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (decorView == null || brightView == null) {
            return;
        }
        //路径恢复
        path.reset();
        //A
        path.moveTo(brightRectF.left, brightRectF.top);
        //B
        path.lineTo(brightRectF.right, brightRectF.top);
        //C
        path.lineTo(brightRectF.right, brightRectF.bottom);
        //D
        path.lineTo(brightRectF.left, brightRectF.bottom);
        //A
        path.lineTo(brightRectF.left, brightRectF.top);
        //E
        path.lineTo(0, brightRectF.top);
        //F
        path.lineTo(0, decorView.getHeight());
        //F
        path.lineTo(decorView.getWidth(), decorView.getHeight());
        //H
        path.lineTo(decorView.getWidth(), 0);
        //I
        path.lineTo(0, 0);
        //E
        path.lineTo(0, brightRectF.top);
        //A
        path.lineTo(brightRectF.left, brightRectF.top);
        //闭合曲线
        path.close();
        canvas.drawPath(path, paint);
    }

    /**
     * 添加不加蒙版的View
     *
     * @param view 视图
     */
    public void attachView(View view) {
        if (!showing) {
            this.brightView = view;
            //没有添加,才可以添加
            initMask();
        }
    }

    /**
     * 移除遮罩
     */
    public void removeMask() {
        if (!showing) {
            //没有添加
            return;
        }
        //使用动画消失
        AlphaAnimation animation = new AlphaAnimation(1f, 0f);
        animation.setDuration(500);
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                ViewParent parent = MaskView.this.getParent();
                if (parent != null && parent instanceof ViewGroup) {
                    ((ViewGroup) parent).removeView(MaskView.this);
                    showing = false;
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        startAnimation(animation);

    }

    /**
     * 初始化不加遮罩view的位置
     */
    private void initMask() {
        int[] location = new int[2];
        //获取view在屏幕上的坐标
        brightView.getLocationOnScreen(location);
        brightRectF = new RectF(location[0], location[1], location[0] + brightView.getWidth(),
                location[1] + brightView.getHeight());
        //添加蒙版到Activity之上
        decorView.addView(this);
        invalidate();
        showing = true;
    }

    /**
     * 是否已经添加
     *
     * @return
     */
    public boolean isShowing() {
        return showing;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155

使用当然也是很简单的了

MaskView maskView = new MaskView(this);
maskView.attachView(view);
//maskView.removeMask();
  • 1
  • 2
  • 3

为了更好理解onDraw()里的每个点,这里做了张图来辅助理解:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebView可以通过以下步骤实现视频全屏播放: 1. 在WebViewClient中重写shouldOverrideUrlLoading方法,拦截视频链接。 2. 在拦截到视频链接时,创建一个全屏的Activity,并将WebView添加到该Activity中。 3. 隐藏状态栏、导航栏等UI元素,让WebView占据整个屏幕。 4. 在WebView播放视频。 5. 在Activity销毁时,将WebView从该Activity中移除,并恢复原来的UI状态。 下面是一个示例代码: ``` public class VideoWebViewClient extends WebViewClient { private Activity mActivity; private WebView mWebView; private View mCustomView; private WebChromeClient.CustomViewCallback mCustomViewCallback; public VideoWebViewClient(Activity activity, WebView webView) { mActivity = activity; mWebView = webView; } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.endsWith(".mp4") || url.endsWith(".avi") || url.endsWith(".3gp") || url.endsWith(".wmv")) { Intent intent = new Intent(mActivity, FullScreenVideoActivity.class); intent.putExtra("url", url); mActivity.startActivity(intent); return true; } return false; } public void onHideCustomView() { if (mCustomView == null) { return; } mCustomView.setVisibility(View.GONE); mWebView.setVisibility(View.VISIBLE); mCustomViewCallback.onCustomViewHidden(); mCustomView = null; mCustomViewCallback = null; // Show the status bar. View decorView = mActivity.getWindow().getDecorView(); decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) { if (mCustomView != null) { onHideCustomView(); return; } mCustomView = view; mWebView.setVisibility(View.GONE); mCustomView.setVisibility(View.VISIBLE); mCustomViewCallback = callback; // Hide the status bar. View decorView = mActivity.getWindow().getDecorView(); int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN; decorView.setSystemUiVisibility(uiOptions); mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } } public class FullScreenVideoActivity extends Activity { private WebView mWebView; private String mUrl; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_full_screen_video); mWebView = findViewById(R.id.webview); mUrl = getIntent().getStringExtra("url"); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.setWebViewClient(new VideoWebViewClient(this, mWebView)); mWebView.setWebChromeClient(new WebChromeClient() { public void onShowCustomView(View view, CustomViewCallback callback) { ((VideoWebViewClient) mWebView.getWebViewClient()).onShowCustomView(view, callback); } public void onHideCustomView() { ((VideoWebViewClient) mWebView.getWebViewClient()).onHideCustomView(); } }); mWebView.loadUrl(mUrl); } @Override public void onBackPressed() { if (mWebView.canGoBack()) { mWebView.goBack(); } else { super.onBackPressed(); } } @Override protected void onDestroy() { super.onDestroy(); ViewGroup parent = (ViewGroup) mWebView.getParent(); if (parent != null) { parent.removeView(mWebView); } mWebView.destroy(); } } ``` 在这个示例中,VideoWebViewClient拦截视频链接并启动FullScreenVideoActivity,该Activity中包含了一个WebView,用于播放视频。在播放视频时,将WebView占据整个屏幕并隐藏状态栏、导航栏等UI元素,让视频全屏播放。在Activity销毁时,将WebView从该Activity中移除,并恢复原来的UI状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值