首页 > 其他分享 >Android 实现下拉界面一种方式

Android 实现下拉界面一种方式

时间:2023-06-06 11:56:04浏览次数:34  
标签:界面 方式 LayoutParams WindowManager mListeners event listener Android public

需要是按的效果
从屏幕顶端下拉出来一个界面跟随手势滑动

效果如下
image

先看一下实现之后的window 层级

$ dumpsys window windows
Window #0 Window{2710e08 u0 SystemUI_smallPanel}:
    mDisplayId=0 rootTaskId=1 mSession=Session{4739268 1310:1000} mClient=android.os.BinderProxy@69125fa
    mOwnerUid=1000 showForAllUsers=true package=com.android.systemui appop=NONE
    mAttrs={(0,0)(1440x35) gr=TOP LEFT CENTER sim={adjust=pan} ty=2100 fmt=TRANSLUCENT
      fl=NOT_FOCUSABLE LAYOUT_IN_SCREEN LAYOUT_NO_LIMITS FULLSCREEN HARDWARE_ACCELERATED
      pfl=FIT_INSETS_CONTROLLED
      bhv=SHOW_TRANSIENT_BARS_BY_SWIPE
      fitTypes=STATUS_BARS NAVIGATION_BARS CAPTION_BAR
      fitSides=}

可以看到有一个高度为35的view,放置在屏幕的顶部,这个就是当前用来响应下拉的一个入口,相对应的参数配置如下

     @RequiresApi(api = Build.VERSION_CODES.R)
    protected WindowManager.LayoutParams createSmallPanelWindowParams(Context context, Display display, int w, int h, int gravity) {
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                GlobalConstants.STATUS_PANEL_WINDOW_TYPE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_FULLSCREEN
                        | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                        & ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);
        params.packageName = context.getPackageName();

        params.gravity = gravity;
        params.width = w;
        params.height = h;
        params.setFitInsetsSides(0);
        params.setTitle("SystemUI_smallPanel");
        return params;
    }

然后对该view 监听触摸事件 onTouch(View v, MotionEvent event),把拿到的 event 抛出给到真正需要跟随下拉实现的view 来做对应事件处理

接下来看真正下拉显示的view,也是一开始就已经添加在了window 里面

  Window #1 Window{390618f u0 SystemUI_Container}:
    mDisplayId=0 rootTaskId=1 mSession=Session{4739268 1310:1000} mClient=android.os.BinderProxy@6b08669
    mOwnerUid=1000 showForAllUsers=true package=com.android.systemui appop=NONE
    mAttrs={(0,0)(1440x1920) gr=START CENTER_HORIZONTAL sim={adjust=pan} ty=2100 fmt=TRANSLUCENT
      fl=DIM_BEHIND NOT_FOCUSABLE LAYOUT_IN_SCREEN LAYOUT_NO_LIMITS FULLSCREEN HARDWARE_ACCELERATED
      pfl=FIT_INSETS_CONTROLLED
      bhv=SHOW_TRANSIENT_BARS_BY_SWIPE
      fitTypes=STATUS_BARS NAVIGATION_BARS CAPTION_BAR
      fitSides=}

然后再这个view 中去处理 上一个传过来的 MotionEvent

public void postOpenTouch(MotionEvent event) {
 // 对event 各个状态 适配当前view 的滑动就可完成此效果
}

方案小结

能达到系统需要的效果,弊端在于多添加了一个view到window上,有一个MotionEvent传递的过程,需要把这个步骤处理好,一面出现下拉无响应的情况

其他实现思路
若是能拿到系统的后门InputManager,直接监听系统input 事件来实现,就能避免以上方案的弊端
大概实现落实如下:

private InputManager mInputManager;
private InputMonitor mInputMonitor;

    public void init() {
        mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
        mInputMonitor = mInputManager.monitorGestureInput(
                "gesture_monitor",
                Display.DEFAULT_DISPLAY
        );
       MyMonitor m = new MyMonitor(context, Display.DEFAULT_DISPLAY);
        mPointerEventDispatcher = new PointerEventDispatcher(mInputMonitor.getInputChannel());
        mPointerEventDispatcher.registerInputEventListener(m);
    }

定义的 MyMonitor

public class MyMonitor implements WindowManagerPolicyConstants.PointerEventListener{
    @Override
    public void onPointerEvent(MotionEvent motionEvent) {
        // do sth
    }
}
public class PointerEventDispatcher extends InputEventReceiver {
    private static final String TAG = "PointerEventDispatcher";
    private final ArrayList<PointerEventListener> mListeners = new ArrayList<>();
    private PointerEventListener[] mListenersArray = new PointerEventListener[0];

    public PointerEventDispatcher(InputChannel inputChannel) {
        super(inputChannel, Looper.getMainLooper());
        Log.d(TAG, "PointerEventDispatcher:");
    }

    @Override
    public void onInputEvent(InputEvent event) {
        try {
            if (event instanceof MotionEvent && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                final MotionEvent motionEvent = (MotionEvent) event;
                PointerEventListener[] listeners;
                synchronized (mListeners) {
                    if (mListenersArray == null) {
                        mListenersArray = new PointerEventListener[mListeners.size()];
                        mListeners.toArray(mListenersArray);
                    }
                    listeners = mListenersArray;
                }
                for (int i = 0; i < listeners.length; ++i) {
                    listeners[i].onPointerEvent(motionEvent);
                }
            }
        } finally {
            finishInputEvent(event, false);
        }
    }

    /**
     * Add the specified listener to the list.
     *
     * @param listener The listener to add.
     */
    public void registerInputEventListener(PointerEventListener listener) {
        Log.d(TAG, "registerInputEventListener: ");
        synchronized (mListeners) {
            if (mListeners.contains(listener)) {
                Log.w(TAG, "registerInputEventListener: trying to register" + listener + " twice.");
                return;
            }
            Log.d(TAG, "register " + listener);
            mListeners.add(listener);
            mListenersArray = null;
        }
    }

    /**
     * Remove the specified listener from the list.
     *
     * @param listener The listener to remove.
     */
    public void unregisterInputEventListener(PointerEventListener listener) {
        synchronized (mListeners) {
            if (!mListeners.contains(listener)) {
                Log.w(TAG, "unregisterInputEventListener: " + listener + " not registered.");
                return;
            }
            Log.d(TAG, "unRegister " + listener);
            // 直接清空
            mListeners.clear();
            mListenersArray = null;
        }
    }

    /**
     * Dispose the associated input channel and clean up the listeners.
     */
    @Override
    public void dispose() {
        super.dispose();
        synchronized (mListeners) {
            mListeners.clear();
            mListenersArray = null;
        }
    }
}

拿到的 MotionEvent ,就能实现需求所述功能

标签:界面,方式,LayoutParams,WindowManager,mListeners,event,listener,Android,public
From: https://www.cnblogs.com/qiyuexiaxun/p/17460145.html

相关文章

  • 41.QT-多线程与界面之间交互总结
    1.线程与界面组件需要注意的地方在QThread线程中不能直接创建QWidget之类的界面组件.因为在QT中,所有界面组件相关的操作都必须在主线程中(也就是GUIthread)所以,QThread线程不能直接操作界面组件.2.QThread线程如何操作界面组件-方法1将多线程类对象封装为GUI界面类的类成员然......
  • post请求方式 - 抖音生活服务 使用restTemplate而不使用httpClient
    publicstaticStringdoPostForJson(Stringurl,Stringjson,StringbyteAuthorization){RestTemplaterestTemplate=newRestTemplate();logger.info("restTemplateinvokepostmethod.url:[{}],json:[{}]",url,json);long......
  • vue之三种与后端交互的方式
    目录一、vue与后端交互之Ajax情况一:出现了跨域问题前端:index.html后端:main.py情况二:解决了跨域问题前端:index.html二、vue与后端交互之fetch简介fetch介绍后端:main.py前端:index.html三、vue与后端交互之axios1.简介html前端一、vue与后端交互之Ajax情况一:出现了跨域问题前端:in......
  • Vue——表单控制、购物车案例、v-model进阶、与后端交互三种方式、箭头函数
    表单控制//1checkbox 单选 多选//2radio 单选<body><divid="app"><h1>checkbox单选</h1><p>用户名:<inputtype="text"v-model="username"></p><p>密码:<inputtype="p......
  • android接入云平台的两种方法
    通过jar包接入:将jar包复制下来将安卓切换到project下app——lib下将jar包粘贴进来,选中jar包右击addaslibrary通过SDK接入:File——New——importModule将SDK导入进来添加依赖库: 记得添加网络权限:<uses-permissionandroid:name="android.permission.INTERNET"/> ......
  • Java中为什么禁止把SimpleDateFormat定位为static变量以及如果非要使用static定位Simp
    场景Java中ExecutorService线程池的使用(Runnable和Callable多线程实现):https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126242904Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/art......
  • 表单控制,购物车案例,v-model进阶,与后端交互的三种方式
    1表单控制#1checkebox: -单选-多选#2radio -单选<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><scriptsrc="./js/vue.js"></scrip......
  • 资源释放发方式二:try-with-resource
        ......
  • 纯注解方式整合Spring和Mybatis框架
    一、配置信息介绍application-dao.xml  application-dao.xml配置文件中配置的内容包含以下4项:读取jdbc.properties文件中的数据连接信息。创建Druid对象,并将读取的数据连接信息注入到Druid数据连接池对象中。创建SqlSessionFactoryBean对象,将并将Druid对象注入到SqlSessi......
  • 【GiraKoo】adb.exe频繁崩溃,与Android设备连接不稳定
    【解决方案】adb.exe频繁崩溃,与Android设备连接不稳定在使用AndroidStudio时,发现adb.exe连接非常不稳定。通过EveryThing工具搜索关键字adb.exe。发现了大量的AppCrash_adb文件夹,adb.exe.xxx.dmp文件等情况。初步判断,应当是adb在运行时,程序频繁崩溃。遇到此类问题,尝试了以下几......