在Android中实现系统范围的侧面板(类似于导航抽屉)的更好方法是什么 [英] What is a better way to implement a system wide side panel (similar to navigation drawer) in Android

查看:118
本文介绍了在Android中实现系统范围的侧面板(类似于导航抽屉)的更好方法是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标:当用户从任意屏幕的左侧向内滑动时,Page滑动到屏幕上.

My aim: have a Page slide onto the screen when the user swipes in from the left side of any screen.

我当前的解决方案:

  1. 我启动我的服务(GlobalTouchService)
  2. 使用OnTouchListener创建狭窄的透明视图(floatingView)
  3. 然后使用WindowManager.addView将该视图添加到屏幕的一侧
  4. 在MotionEvent.ACTION_DOWN上,我将膨胀后的布局添加到此View(我称其为innerView)
  5. 在MotionEvent.ACTION_MOVE上,我通过event.getRawX()坐标调整窗口的宽度以及innerView的左右边界

问题:它可以工作,但是运动非常剧烈(缓慢).我敢肯定,有一种更好的方法可以使它像官方"导航抽屉一样平滑.

Problem: It works, but its movement is very jerky (slow). I am sure there is a much better way of doing this to make it as smooth as an "official" navigation drawer.

源文件摘要

AndroidManifest.xml (系统范围触摸检测所需的权限)

AndroidManifest.xml (permission necessary for system wide touch detection)

        <service android:name=".GlobalTouchService" />
</application>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

panel_layout.xml (这是要拖动的窗口的内容)

panel_layout.xml (this is the contents of the window that's to be dragged in)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#9900cc"
    android:orientation="vertical"
    android:gravity="right"
    tools:context=".GlobalTouchService" >
    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/dummy_content2" />
</LinearLayout>

GlobalTouchService.java (我在此处包括整个服务)

GlobalTouchService.java (I include the whole service here)

public class GlobalTouchService extends Service implements OnTouchListener{

    private String TAG = this.getClass().getSimpleName();
    private WindowManager mWindowManager;
    private LinearLayout floatingView;
    private LinearLayout innerView;
    private LinearLayout.LayoutParams floatingParams;
    private LinearLayout.LayoutParams innerParams;
    private WindowManager.LayoutParams windowParams;
    private int dw,dh;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {

        super.onCreate();

        // get screen dimensions

        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);  
        dw=mWindowManager.getDefaultDisplay().getWidth();
        dh=mWindowManager.getDefaultDisplay().getHeight();

        // prepare contents to show when ACTION_DOWN is detected

        innerView =new LinearLayout(this);
        innerParams =new LinearLayout.LayoutParams(dw,dh);
        innerParams.gravity=Gravity.RIGHT | Gravity.TOP;
        innerView.setLayoutParams(innerParams);
        View view=View.inflate(this, R.layout.panel_layout, new FrameLayout(this));
        innerView.addView(view);

        // create blank view to catch touches

        floatingView = new LinearLayout(this);
        floatingParams = new LinearLayout.LayoutParams(dw, dh);
        floatingView.setLayoutParams(floatingParams);    
        floatingView.setOnTouchListener(this);

        // add view to the windowmanager

        windowParams = new WindowManager.LayoutParams(
                dw/30,                                            // narrow stripe on the left
                dh,                                               // full height of the screen
                WindowManager.LayoutParams.TYPE_PHONE, 
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, 
                PixelFormat.TRANSLUCENT);
        windowParams.gravity = Gravity.LEFT | Gravity.TOP;
        mWindowManager.addView(floatingView, windowParams);
    }

    @Override
    public void onDestroy() {
        if(mWindowManager != null) {

            // remove window when service is finished

            if(floatingView != null) mWindowManager.removeView(floatingView);
        }
        super.onDestroy();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        if(event.getAction() == MotionEvent.ACTION_DOWN && windowParams.width<dw/3 ) {
            // add the inflated contents
            floatingView.addView(innerView);
            return true;

        } else if(event.getAction() == MotionEvent.ACTION_UP && event.getRawX()<dw/3) {
            // if released early collapses back to the left edge
            windowParams.width=dw/30;
            floatingView.removeView(innerView);
            mWindowManager.updateViewLayout(floatingView, windowParams);
            return true;

        } if(event.getAction()==MotionEvent.ACTION_MOVE) {
            windowParams.width=Math.max((int)event.getRawX(),dw/30);
            innerParams.leftMargin=-(dw-(int)event.getRawX());
            innerParams.rightMargin=(dw-(int)event.getRawX());
            mWindowManager.updateViewLayout(floatingView, windowParams);
            return true;
        }
        return false;
    }

}

推荐答案

尝试一下(奖励包是ValueAnimator,如果不需要在ACTION_UP上自动滚动,则可以将其删除):

try this (a bonus pack is the ValueAnimator, you can remove it if you don't need automatic scrolling on ACTION_UP):

public class MyService extends Service implements View.OnTouchListener, ValueAnimator.AnimatorUpdateListener {
    private static final int MIN_VISIBLE_WIDTH = 20;

    private WindowManager windowManager;
    private FrameLayout rootView;
    private Point displaySize = new Point();
    private WindowManager.LayoutParams params;
    private ValueAnimator animator = new ValueAnimator();

    @Override
    public void onCreate() {
        animator.addUpdateListener(this);
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getSize(displaySize);

        rootView = new FrameLayout(this);
        TextView tv = new TextView(this);
        tv.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
        int[] colors = {0xbb00ff00, 0x3300ff00};
        tv.setBackground(new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, colors));
        tv.setPadding(0, 0, 16, 0);
        tv.setText("text view");
        rootView.addView(tv);

        params = new WindowManager.LayoutParams(
            displaySize.x, displaySize.y / 4,       // width, height
            -displaySize.x + MIN_VISIBLE_WIDTH, 20, // left, top
            WindowManager.LayoutParams.TYPE_PHONE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
            PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.TOP | Gravity.LEFT;
        windowManager.addView(rootView, params);

        rootView.setOnTouchListener(this);
    }

    @Override
    public void onDestroy() {
        windowManager.removeView(rootView);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    float actionDownX;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            animator.cancel();
            actionDownX = event.getX();
        } else
        if (action == MotionEvent.ACTION_MOVE) {
            int newLeft = (int) (event.getRawX() - actionDownX);
            params.x = Math.max(-displaySize.x + MIN_VISIBLE_WIDTH, Math.min(newLeft, 0));
            windowManager.updateViewLayout(rootView, params);
        } else
        if (action == MotionEvent.ACTION_UP) {
            int startX = params.x;
            int endX = startX < (-displaySize.x + MIN_VISIBLE_WIDTH) / 2? -displaySize.x + MIN_VISIBLE_WIDTH : 0;
            animator.setIntValues(startX, endX);
            animator.setDuration(500 * Math.abs(endX - startX) / ((displaySize.x - MIN_VISIBLE_WIDTH) / 2))
                .start();
        }
        return true;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        params.x = (Integer) animation.getAnimatedValue();
        windowManager.updateViewLayout(rootView, params);
    }
}

这篇关于在Android中实现系统范围的侧面板(类似于导航抽屉)的更好方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆