Android视图调整大小和WindowManager:子视图放置期间不需要的转换

时间:2017-05-28 10:19:54

标签: android viewgroup android-transitions android-windowmanager onmeasure

我在父视图调整大小后在自定义ViewGroup中放置子视图期间避免不必要的转换时遇到了一些问题。只有在使用WindowManager而不是setContentView()调用时才会出现此问题。这是一个简短的例子:

MainActivity

public class MainActivity extends AppCompatActivity {

    private static final int MY_PERMISSIONS_DRAW_OVER_OTHER_APPS = 1;
    CustomViewGroup mCustomViewGroup;
    WindowManager mWindowManager;

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


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {

            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, MY_PERMISSIONS_DRAW_OVER_OTHER_APPS);
        }


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            if (ContextCompat.checkSelfPermission(this,
                    android.Manifest.permission.SYSTEM_ALERT_WINDOW)
                    != PackageManager.PERMISSION_GRANTED) {

                if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                        android.Manifest.permission.SYSTEM_ALERT_WINDOW)) {


                } else {

                    ActivityCompat.requestPermissions(this,
                            new String[]{android.Manifest.permission.SYSTEM_ALERT_WINDOW},
                            MY_PERMISSIONS_DRAW_OVER_OTHER_APPS);
                }
            }
        }

        mCustomViewGroup =
                (CustomViewGroup) LayoutInflater.from(this).inflate(R.layout.activity_main,null);

        final WindowManager.LayoutParams floatingViewLayoutParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );
        floatingViewLayoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT;
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mWindowManager.addView(mCustomViewGroup,floatingViewLayoutParams);


    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == MY_PERMISSIONS_DRAW_OVER_OTHER_APPS) {
            //Check if the permission is granted or not.
            if (resultCode == RESULT_OK) {
            } else {
                Toast.makeText(this,
                        "Draw over other app permission not available. Closing the application",
                        Toast.LENGTH_SHORT).show();

                finish();
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

}

CustomViewGroup

public class CustomViewGroup extends ViewGroup {

    private boolean mIsTouchEventActive;

    public CustomViewGroup(Context context) {
        this(context,null,0);
    }

    public CustomViewGroup(Context context, AttributeSet attrs) {
        this(context,attrs,0);
    }

    public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {

        mIsTouchEventActive = false;
        setWillNotDraw(false);

        // Add the fab view
        LayoutInflater layoutInflater = LayoutInflater.from(context);
        View fabView = layoutInflater.inflate(R.layout.fab_view,null,false);
        this.addView(fabView);
    }

    @Override
    public boolean shouldDelayChildPressedState() {
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {


        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                mIsTouchEventActive = true;
                requestLayout();
                return true;

            case MotionEvent.ACTION_UP:
                mIsTouchEventActive = false;
                requestLayout();
                return true;

        }
        return false;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {


        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        final View child = getChildAt(0);
        if (child.getVisibility() != GONE) {

            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
            maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
            childState = combineMeasuredStates(childState, child.getMeasuredState());

        }

        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
        if (mIsTouchEventActive == true) {

            final Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
            Point deviceDisplay = new Point();
            display.getSize(deviceDisplay);

            maxHeight = deviceDisplay.x;
            maxWidth = deviceDisplay.y;
        }


        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));

    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        final Rect fabChildRect = new Rect();
        final View child = getChildAt(0);
        if (child.getVisibility() != GONE) {

            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight();

            fabChildRect.left = 0;
            fabChildRect.right = width;
            fabChildRect.top = 0;
            fabChildRect.bottom = height;

            child.layout(fabChildRect.left, fabChildRect.top,
                    fabChildRect.right, fabChildRect.bottom);

        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<packagename.CustomViewGroup
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/custom_view_group"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"/>

fab_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:orientation="vertical"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content">

    <android.support.design.widget.FloatingActionButton
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        app:srcCompat="@android:drawable/ic_dialog_email"/>

</LinearLayout>

清单文件应包含android.permission.SYSTEM_ALERT_WINDOW权限,build.gradle中的依赖关系列表如下:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.android.support:design:24.2.1'
    testCompile 'junit:junit:4.12'
}

以下是我正在做的事情:

我有一个自定义视图CustomViewGroup继承自ViewGroup,在我的简短示例中包含FloatingActionButton,位于父视图的左下角。 CustomViewGroup的默认大小是包含FloatingActionButton:264x264的大小。

我已在onClick中实施了onMeasureCustomViewGroup方法,以便在ACTION_DOWN事件的情况下扩展CustomViewGroup的大小到整个显示器。如果发生ACTION_UP事件,则大小将恢复为默认值。 FloatingActionButtonCustomViewGroup的位置始终为

fabChildRect.left = 0;
fabChildRect.right = width;
fabChildRect.top = 0;
fabChildRect.bottom = height;

其中widthheight都等于264,因此当用户触摸后父视图大小发生变化时,按钮本身会放置在不同的位置。

我的问题是:在用户触摸事件的情况下,如何在动作按钮放置期间删除动画?我的意思是按钮视图的垂直运动。我已经使用disableTransitionType方法尝试了所有转换类型但没有任何更改。如果我尝试禁用xml文件中的转换,则相同。如果CustomViewGroup视图通过从xml而不是WindowManager充气而添加到主活动视图中,则不会出现此转换,例如使用以下activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/activity_main"
    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"
    tools:context="packagename.MainActivity">

    <packagename.CustomViewGroup
    xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/custom_view_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"/>

</RelativeLayout>

您对如何解决我的问题有任何建议吗?也许对我观察到的内容有解释?正如你可以从我的代码中猜到的那样,我是Android编程的初学者,所以一般来说,任何建议都与这个主题的主题没有严格的关系,这是非常受欢迎的!

谢谢。

更新:我发现使用WindowManager可以打电话

int relayoutResult = mWindowSession.relayout(
    mWindow, mSeq, params,
    (int) (mView.getMeasuredWidth() * appScale + 0.5f),
    (int) (mView.getMeasuredHeight() * appScale + 0.5f),
    viewVisibility, 
    insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,mWinFrame, 
    mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
    mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);

ViewRootImpl(第5415行)内,并且在执行此方法期间执行不需要的可见转换。如果我在主要活动中从xml中膨胀CustomViewGroup,则在触摸事件的情况下不会调用上述方法,并且因为在这种情况下,转换也不可见,我想我可以考虑{{1} relayout数据成员的方法(类型为mWindowSession)其负责。

1 个答案:

答案 0 :(得分:1)

我在这里找到了我的问题的答案:

http://stackoverflow.com/questions/31338359/avoid-layout-change-animations-in-custom-view-thats-updated-in-the-windowmanag?rq=1

在发布我的问题之前,我似乎没有进行足够的搜索......