美文网首页
Android 引导页

Android 引导页

作者: 资本家大恶人 | 来源:发表于2020-08-23 18:38 被阅读0次

引导页

思路
1.判断当前系统版本号
2.如果与保存版本号不相同
启动引导页
跳转主页面
如果相同等待指定时间跳转启动页

  • 创建工具类判断版本是否一致如果不一致显示引导页

  // 是否显示引导页
    public static boolean isShowGuidePage() {
//        得到安装的版本号
        int versionCode = MvpSpUtils.getInt(SP_VERSION_CODE);
//     获取从SP保存的版本号与安装之后的版本对比如果不一致返回true
        if (versionCode == -1 || versionCode != MvpUtils.getAppVersionCode(mApplication)) {
            MvpSpUtils.saveApply(SP_VERSION_CODE, MvpUtils.getAppVersionCode(mApplication));
            return true;
        }

        return false;
    }

  • 得到pop自定工具类
package com.angle.mvplib.widgets;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.PopupWindow;

import com.angle.mvplib.util.MvpUtils;


/**
 * popupwindow 底层实际上是通过  windowmanager 添加 一个 叫 DecorView(FrameLayout)的方式实现的。
 * <p>
 * popupwindow 一般设计到三个 view,{1.mDecorView(FrameLayout),2.mBackgroundView(FrameLayout),3.mContentView(我们要显示的layout)}
 * <p>
 * 1 mDecorView: popupWindow 的根view,它的大小就是通过 设置popouWindow 的 宽高来决定的(setHeight,setWidth)
 * <p>
 * 2 mBackgroundView:它不一定会创建,取决于 是否调用了 setBackgroundDrawable 给  popupWindow 设置背景,如果设置了那么会创建一个 mBackgroundView
 * 然后添加到mDecorView 上,如下:
 * <p>
 * if (mBackground != null) { // 是否设置里背景
 * mBackgroundView = createBackgroundView(mContentView);
 * mBackgroundView.setBackground(mBackground);
 * } else {
 * mBackgroundView = mContentView;
 * }
 * <p>
 * //把我们的 mContentView 的传进来 创建 mBackgroundView
 * <p>
 * private PopupBackgroundView createBackgroundView(View contentView) {
 * final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
 * final int height;
 * if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
 * height = WRAP_CONTENT;
 * } else {
 * height = MATCH_PARENT;
 * }
 * <p>
 * final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
 * final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
 * MATCH_PARENT, height);
 * <p>
 * // 把我们的 mContentView 添加到 mBackgroundView上面,
 * // mContentView的宽度是 MATCH_PARENT(和mBackgroundView的宽度一致)。
 * // mContentView的高度 取决于  mContentView.getLayoutParams() ,要么是MATCH_PARENT,要么是 WRAP_CONTENT
 * backgroundView.addView(contentView, listParams);
 * <p>
 * return backgroundView;
 * }
 * <p>
 * mDecorView = createDecorView(mBackgroundView);
 * <p>
 * // 通过把mBackgroundView 传进来 创建 mDecorView,
 * private PopupDecorView createDecorView(View contentView) {
 * final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
 * final int height;
 * <p>
 * // 如果 mContentView 的 layoutParams != null && layoutParams.height == WRAP_CONTENT 时,高度为 wrap_content,否则为 match_parent
 * if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
 * height = WRAP_CONTENT;
 * } else {
 * height = MATCH_PARENT;
 * }
 * <p>
 * final PopupDecorView decorView = new PopupDecorView(mContext);
 * // 把 mBackgroundView添加到了 mDecorView上
 * // mBackgroundView 的宽度是 MATCH_PARENT(和 mDecorView 一样宽,也就是说等于 setWidth的值)
 * <p>
 * //mBackgroundView  的高度 取决于mBackgroundView.getLayoutParams(),要么是MATCH_PARENT,要么是 WRAP_CONTENT
 * <p>
 * decorView.addView(contentView, MATCH_PARENT, height);
 * decorView.setClipChildren(false);
 * decorView.setClipToPadding(false);
 * <p>
 * return decorView;
 * }
 * <p>
 * <p>
 * <p>
 * <p>
 * 3 mContentView,这就是我们调用popupwindow 的 setContentView 方法传进去我们要显示的内容view
 * <p>
 * <p>
 * 如果设置了pop 的backgrounddrawable 那么创建顺序是
 * <p>
 * 根据 contentview 创建 backgroundview,再根据backgroundview 创建 decorview,最后 windowmanager add  decorview
 * <p>
 * <p>
 * 如果没有设置pop 的backgrounddrawable的背景,那么创建顺序是
 * <p>
 * 直接把 contentview 赋值给 backgroundview。 更具 backgroundview 创建 decorview ,最后 windowmanager add  decorview
 * <p>
 * <p>
 * <p>
 * decorview 的宽高是由 setHeight 和 setWidth 决定的。
 * <p>
 * backgroundview 和 contentview 的 宽度都是不能修改的,都为 match_parent,高度要么是 wrap_content ,要么是 match_parent,只有当
 * <p>
 * contentview 的 layoutparams 不为 null, 并且其 layoutparams.height == wrap_content 时 ,高度为wrap_content 否则其他情况都为match_parent
 */


/**
 *
 *
 * 使用方式:
 * 1. 自己写一个子类继承  在子类内部去调用setContentView(view)以及对view 的初始化
 * 2. 直接new 一个 MvpCommonPopView 实例,调用 setContentView(view);。在调用者里面对 view  进行初始化
 *
 * 然后调用 showCenter(view)方法 进行显示,把pop 显示在屏幕中间,view 参数可以是任意一个 view .
 *
 * 该pop 可以显示 一下几个功能:<p/>
 *
 * 1. 可以设置pop 外部区域点击时是否关闭pop :{@link #setTouchOutsideDismiss(boolean)}<p/> // 默认不关闭
 * 2. 可以设置按返回键是否可以关闭pop : {@link #setOnBackKeyDismiss(boolean)}<p/> // 默认是按返回键关闭
 * 3. 可是设置点击pop 外部时事件是否传递到 pop 的后面 : {@link #setOutsideCanTouch(boolean)}<p/> // 默认不传递
 *
 *
 *
 */

public class MvpCommonPopView extends PopupWindow {


    private boolean isOutsideTouchDismiss; // 点击pop 外部是否消失
    private boolean isBackKeyDismiss = true;// 按返回键是否消失

    private Activity context;


    public MvpCommonPopView(Context context, int width, int height) {
        super(context);

        setHeight(height); //
        setWidth(width);
        this.context = (Activity) context;
        init(context);
    }

    // 这个构造方法,pop 的高度是WRAP_CONTENT,宽度是 MATCH_PARENT
    public MvpCommonPopView(Context context) {
        this(context, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);


    }

    protected void init(Context context) {
        // 在比较老的版本上,不设置背景就不能响应返回键和点击外部消失的,但是新版本google api 已经修改了,为了保险起见,设个背景最好
        setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

        backgroundAlpha( 0.5f);
        /**
         * 设置widow 是否获取焦点,默认false,如果为false,那么点击pop以外的区域,事件会传递到popu后面的(behind)界面上.
         * 点击pop 以外的区域pop 不会消失,注意如果pop 里面有输入框,那么输入框不能弹出键盘
         * 如果设置为true,pop 会有焦点,点击外部时 pop 会消失。
         */

        setFocusable(true);
        // 这个方法是控制是否当pop 以外的区域点击时是否发送 MotionEvent.ACTION_OUTSIDE 通知 pop,true 通知,false 不通知,但是 这个方法必须要 setFocusable = false ,setTouchable = true 时才有效果
        setOutsideTouchable(true);

        setTouchInterceptor(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                final int x = (int) event.getX();
                final int y = (int) event.getY();


                if ((event.getAction() == MotionEvent.ACTION_DOWN) // 当 setFocusable为 true 点击外部会收到这个事件
                        && ((x < 0) || (x >= v.getWidth()) || (y < 0) || (y >= v.getHeight()))) {
                    if (isOutsideTouchDismiss) {
                        dismiss();

                    }
                    return true;

                }

               /* if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { // ACTION_OUTSIDE 事件是 当 setOutsideTouchable(true) 方法 设置为true 时,才会被接受。否则不会接收这个事件
                    if (isOutsideTouchDismiss) {
                        CommonPopView.super.dismiss();
                        return true;
                    }
                }*/


                /**
                 * 返回 true 不拦截,走默认行为
                 *
                 *  @Override
                 *         public boolean onTouchEvent(MotionEvent event) {
                 *             final int x = (int) event.getX();
                 *             final int y = (int) event.getY();
                 *
                 *             if ((event.getAction() == MotionEvent.ACTION_DOWN)
                 *                     && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
                 *                 dismiss();
                 *                 return true;
                 *             } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                 *                 dismiss();
                 *                 return true;
                 *             } else {
                 *                 return super.onTouchEvent(event);
                 *             }
                 *         }
                 */
                return false;
            }
        });


    }


    /**
     * 设置点击pop 外部是否消失,这个方法只有在给pop 设置的
     *  宽高不是Match_parent 才有效,因为设置为了Match_parent这个都是pop,那就没有点击外部只说
     * @param dismiss
     */
    //
    public void setTouchOutsideDismiss(boolean dismiss) {
        isOutsideTouchDismiss = dismiss;


    }

    // view: 任意一个已经显示在界面上的view,注意如果这个view 没有添加的 window 上时,这个方法会报错。
    // 因为 pop 在显示的时候需要通过 view.getWidowToken.
    public void showCenter(View view){
        showAtLocation(view, Gravity.CENTER,0,0);
    }



    public void setOnBackKeyDismiss(boolean dismiss) {
        isBackKeyDismiss = dismiss;
    }

    @Override
    public void dismiss() {

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        StackTraceElement traceElement = stackTrace[3];
        String fileName = traceElement.getFileName(); // 拿到调用 dismiss 方法的类名
        fileName = fileName.substring(0, fileName.lastIndexOf("."));
        if (!fileName.equals(PopupWindow.class.getSimpleName())) { // 如果调用者不是从系统来的,那么就是我们认为的
            destroy();
            super.dismiss();
        } else {
            if (!isBackKeyDismiss) {
                return;
            } else {
                destroy();
                super.dismiss();
            }
        }


    }

    private void destroy(){
        backgroundAlpha(1);
        this.context = null;
    }


    /**
     * 设置pop 外部是否可点击,也就是点击pop 的外部 能把事件传递到pop 的 behind
     * <p>
     * 注意:如果 设置为true,那么再设置点击外部关闭 pop 将会无效,因此在为true 想关闭 pop ,必须手动关闭,调用close
     *
     * @param touch
     */
    public void setOutsideCanTouch(boolean touch) {

        // 设置为false, 点击pop 外部,事件会传递到 pop 的behind,设置为true ,不会。虽然设置为false ,但是 pop 里面如果有 输入框任然可以获取焦点,弹出键盘,这和 setFocusable = false 不一样。
        if(MvpUtils.hasQ()){
            setTouchModal(!touch);
        }

    }

    /**
     * 设置添加屏幕的背景透明度
     *
     * @param bgAlpha
     */
    public void backgroundAlpha( float bgAlpha) {
        WindowManager.LayoutParams lp = context.getWindow().getAttributes();
        lp.alpha = bgAlpha;
        context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        context.getWindow().setAttributes(lp);
    }
}

  • 实现自定义pop

public class AgreementPop extends MvpCommonPopView {

    private TextView mUnUser;
    private TextView mUser;

    private IPopClickListener mIPopClickListener;
//  设置监听
    public void setClickListener(IPopClickListener mIPopClickListener) {
        this.mIPopClickListener = mIPopClickListener;
    }

    public AgreementPop(Context context) {
        super(context);
        initView(context);
    }

    private void initView(Context context) {
//        找到两个按钮
        View view = LayoutInflater.from(context).inflate(R.layout.layout_splash_pop, null);
        mUnUser = view.findViewById(R.id.splash_pop_btn_stop);
        mUser = view.findViewById(R.id.splash_pop_btn_agree);
//     监听不能返回并利用接口回调传递该事件
        mUnUser.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mIPopClickListener.onCancel();
            }
        });
//     监听不能返回并利用接口回调传递该事件
        mUser.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mIPopClickListener.onAgree();
            }
        });
//        如文本框不允许复制内容,则setFocusable(false),不能被选中,就不可以复制。
        mUser.setFocusable(false);
        mUnUser.setFocusable(false);

//     设置布局
        setContentView(view);

//        调用MvpCommonPopView自定义popWindow工具类方法setOnBackKeyDismiss
//        按返回键不能返回
        setOnBackKeyDismiss(false);

    }

    //    创建监听接口接口
    public interface IPopClickListener {
        void onAgree();

        void onCancel();
    }
}

  • 具体使用

public class SplashActivity extends BaseActivity {
//   跳转时间
    private static final long WAIT_TIME = 300;
    private ActivitySplashBinding bind;

    //  先绑定xml ID
    @Override
    protected int getLayoutId() {
        return R.layout.activity_splash;
    }

    //   在使用Xml Id
    @Override
    protected void bindingView(View view) {
        super.bindingView(view);
        bind = ActivitySplashBinding.bind(view);

    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (MvpManager.isShowGuidePage()) {//判断是否为首次安装
            AgreementPop agreementPop = new AgreementPop(this);//得到popWindow
//            消费监听事件
            agreementPop.setClickListener(new AgreementPop.IPopClickListener() {
                @Override
                public void onAgree() {
                    agreementPop.dismiss();//关闭pop
                    showGildePage();//展示引导页
                }

                @Override
                public void onCancel() {
                    MvpManager.launchFail();//移除此次记录
                    finish();//关闭当前页面
                }
            });
//         显示在当前界面上
            getWindow().getDecorView().post(() -> {
                agreementPop.showCenter(getWindow().getDecorView());
            });

        } else {
//            设置为全屏
            SystemBarConfig config = new SystemBarConfig(this).enterFullScreen(SystemBarConfig.MODE_HIDE_LEAN_BACK);
            config.apply();

//post和postDelay都是Handler的方法,用以在子线程中发送Runnable对象的方法;
//
//其次,Android中post()方法可以直接在非UI线程中更新UI,不同与Handelr的Send类方法,需要进行切换;
//
//最后,两个方法在实现UI线程事件的时间上有所区别,postDelayed()方法用以延期执行,post则是立即执行;
            
//            跳转主页关闭引导页
            getWindow().getDecorView().postDelayed(() -> {
                startActivity(new Intent(SplashActivity.this, MainActivity.class));
                finish();
            }, WAIT_TIME);

        }
    }
//   显示引导页然后跳转主页
    private void showGildePage() {
        setContentView(R.layout.activity_splash);
        startActivity(new Intent(this, MainActivity.class));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

启动页

  • 问题
    启动页显示之前会有一段白屏

  • 原因
    在application初始化操作在application注册的事件越多白屏事件越长

  • 解决办法给当前Activity设置一个theme背景设置我们的启动页这样问题就完美解决了

在style文件中设置主题

<!--充满全屏-->
    <style name="Jiandao.FullScreen">
        <item name="android:windowFullscreen">true</item>
    </style>
<!--设置主题和背景-->
    <style name="Jiandao.FullScreen.Splash">
        <item name="android:windowFullscreen">true</item>
        <item name="android//:windowBackground">@drawable/splash_bg</item>
    </style>

  • 在Activity页使用该主题
   <activity
            android:name=".home.splash.SplashActivity"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:theme="@style/Jiandao.FullScreen.Splash">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

相关文章

网友评论

      本文标题:Android 引导页

      本文链接:https://www.haomeiwen.com/subject/sqlijktx.html