Android12-Toast行为变更+底部导航栏适配

前言

事情是这样的,因为我们是做游戏产品,关于未成年政策相关的限制,又有新的指示,因此服务端改了接口后,前端有个适配问题就非常影响UI。

 咦,用Toast展示长文本,怎么显示不全了。

一查才发现,原来谷歌在安卓12,targetSdkVersion>31后,做了调整。

 对于长文本的展示,不建议用Toast了,但是没办法,我又不太能和项目组解释,改用弹窗也不合适,只能想着适配去改改了,既然别的实现方法都走不通,那就想办法吧,自定义Toast?

也对,毕竟Toast提供了setView方法,做一个layout放进去,就行了。这里直接贴代码

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

public class BToast extends Toast {
    private BToast(Context context) {
        super(context);
    }
    private static BToast toast;
    private static void cancelToast() {
        //为什么把这个if语句去掉,就会异常终止呢?
        //因为程序第一次进来时, toast时为空的,第二次及以后, 它才不为空,才能执行toast.cancel()。
        if (toast != null) {
            toast.cancel();
        }
    }
    public void cancel() {
        super.cancel();
    }
    public void show() {
        super.show();
    }

    public static void initToast( Context context, CharSequence text,boolean NavigationBar) {
        //第二次点击及以后,它会先取消上一次的Toast, 然后show本次的Toast。
        cancelToast();
        toast = new BToast(context);

        View mView = LayoutInflater.from(context).inflate(RGet.$L("tc_activity_toast"), null);
        TextView mText = mView.findViewById(RGet.$ID("toast_show"));
        mText.setText(text);
        toast.setView(mView);
        int  height =  0;
        if(NavigationBar){
            height =  getNavigationBarHeight(context)/2;
        }
        Log.d("BToast",height+"");
        toast.setGravity(Gravity.TOP, -height, 70);
        toast.setDuration(Toast.LENGTH_LONG);
        toast.show();
    }

    //屏幕高度
    public  static int getHeight(Activity activity) {
        DisplayMetrics dm = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(dm);
        return dm.widthPixels;
    }

    //真实高度
    public static int getRealHeight(Activity activity) {
        WindowManager windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics dm = new DisplayMetrics();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            display.getRealMetrics(dm);
        } else {
            display.getMetrics(dm);
        }
        int realHeight = dm.widthPixels;
        return realHeight;
    }



    public static int getNavigationBarHeight(Context context) {
        Resources resources = context.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        return resourceId != 0 ? resources.getDimensionPixelSize(resourceId) : 0;
    }

layout布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/toast_backgound"
    >
    <TextView
        android:id="@+id/toast_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:padding="8dp"
        android:text=""
        android:textColor="#000000"
        android:gravity="center"
        android:textSize="14sp" />
</LinearLayout>

 toast_backgound.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="10dp"/>
    <solid android:color="#ffffffff"/>
</shape>

调用

BToast.initToast(this,"",isshowNavigationBar);

就是这么简单,但是到这里还没有结束。文本过长的展示是解决了,但是会发现,在有底部导航栏的手机上,Toast 的View没有居中,这……,那再查查资料,发现网上搜的底部导航栏适配也全都返回true,无论有的没有,这可烦死了。在看了这位大佬博客后,最终找到了解决方案。

https://blog.csdn.net/qq_20451879/article/details/79109501

底部虚拟导航栏

是否存在虚拟按键

注意:指的是当前手机是否设置有虚拟按键!而不是指虚拟按键是否显示!

 public static boolean isHaveSoftKey(Activity activity) {
     Display d = activity.getWindowManager().getDefaultDisplay();
     DisplayMetrics realDisplayMetrics = new DisplayMetrics();
     d.getRealMetrics(realDisplayMetrics);
     int realHeight = realDisplayMetrics.heightPixels;
     int realWidth = realDisplayMetrics.widthPixels;
     DisplayMetrics displayMetrics = new DisplayMetrics();
     d.getMetrics(displayMetrics);
     int displayHeight = displayMetrics.heightPixels;
     int displayWidth = displayMetrics.widthPixels;
     return (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;
 }

底部虚拟按键的高度

 public static int getBottomSoftKeysHeight(Activity activity) {
     Display d = activity.getWindowManager().getDefaultDisplay();
     DisplayMetrics realDisplayMetrics = new DisplayMetrics();
     d.getRealMetrics(realDisplayMetrics);
     int realHeight = realDisplayMetrics.heightPixels;
     DisplayMetrics displayMetrics = new DisplayMetrics();
     d.getMetrics(displayMetrics);
     int displayHeight = displayMetrics.heightPixels;
     return (realHeight - displayHeight);
 }

隐藏、显示 - 底部虚拟导航栏

  /**
     * @param state   true 隐藏  false 还原显示
     */
    public static void hideBottomMenu(Context context,boolean state){
        Intent barIntent = new Intent();
        barIntent.setAction("com.alf.switch_status_bar");
        barIntent.putExtra("hide", state);
        context.sendBroadcast(barIntent);
    }
————————————————
版权声明:本文为CSDN博主「Modu_Liu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_20451879/article/details/79109501

隐藏 - 底部虚拟导航栏

    private void hideBottomMenu() {
        int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                View.SYSTEM_UI_FLAG_FULLSCREEN |
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(flags);
    }

隐藏 - 底部虚拟导航栏

隐藏底部虚拟按键,但是如果你在底部网上滑动还是显示的出来的

    private void hideBottomMenu() {
        //android 隐藏底部虚拟按键
        //隐藏虚拟按键,并且全屏
        if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
            View v = this.getWindow().getDecorView();
            v.setSystemUiVisibility(View.GONE);
        } else if (Build.VERSION.SDK_INT >= 19) {
            //for new api versions.
            View decorView = getWindow().getDecorView();
            int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    | View.SYSTEM_UI_FLAG_FULLSCREEN;
            decorView.setSystemUiVisibility(uiOptions);
        }
    }

关于这个功能我在网上找了很多半成品,大部分都无效,所幸拼拼凑凑实现了此项功能,亲测可用!

下面来贴代码。

NavigationActivity
建议:可以将NavigationActivity的关键代码移植到我们项目的BaseActivity,同时此类中的一些Log完全可以删掉,这里打Log只是为了看一些执行流程 ~


import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;

public class NavigationActivity extends Activity implements ViewTreeObserver.OnGlobalLayoutListener {
    FrameLayout content;
    private static final String TAG = "BaseActivityForAuto";
    private boolean mLayoutComplete = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        content = (FrameLayout) findViewById(android.R.id.content);
        content.post(new Runnable() {
            @Override
            public void run() {
                mLayoutComplete = true;
                Log.e(TAG, "content 布局完成");
            }
        });
        content.getViewTreeObserver().addOnGlobalLayoutListener(NavigationActivity.this);
    }


    @Override
    public void onGlobalLayout() {
        Log.e(TAG, "onGlobalLayout");
        if (!mLayoutComplete)
            return;
        onNavigationBarStatusChanged();
    }

    protected void onNavigationBarStatusChanged() {
        // 子类重写该方法,实现自己的逻辑即可。
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
}

MainActivity
此处的MainActivity替代到对应的需求类即可,但是要记得继承上方的父类 - - ~

因为我们的游戏是横屏,所以这里的判断高度,实质上是宽度。

import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;

public class MainActivity extends NavigationActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    boolean isshowNavigationBar = true;

    @Override
    protected void onNavigationBarStatusChanged() {
        super.onNavigationBarStatusChanged();
        int displayWidth = BToast.getHeight(this)+BToast.getNavigationBarHeight(this);
        int basicWidth = BToast.getRealHeight(this);
        Log.e("tag", "宽度" + displayWidth);
        Log.e("tag", "真实宽度" + basicWidth);
        if (displayWidth < basicWidth) {
            isshowNavigationBar = true;
            Log.e("tag", "底部导航显示");
        } else {
            isshowNavigationBar = false;
            Log.e("tag", "底部导航隐藏");
        }
    }
}

总结

通过这个小问题,发现随着安卓版本的更新,行为也发生了很多改变,还是要与时俱进。网上关于底部导航栏的适配有很多文章,但是一试,发现都是一样效果,没什么太大用,可能自己的业务和对方的不一致。总之,还是要感谢Modu_Liu这位大哥,让我成功解决了难题。respect!

猜你喜欢

转载自blog.csdn.net/qq_34123324/article/details/131230802