简体   繁体   中英

How do I adjust my layout for a chin on Android Wear?

On Android Wear devices with a "chin", the screen size is still reported as square, but you get this WindowInsets object which contains information about what has been cropped from the screen.

I have a config screen which is already correctly fetching this value, easily confirmed because my views render an outline of the screen shape.

public class ConfigView extends FrameLayout {
    private Rect lastBounds = new Rect();
    private WindowInsets lastWindowInsets;

    //... omitting initialisation

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        requestApplyInsets();

        // omitting other stuff not relevant to this

        GridViewPager pager =
            (GridViewPager) findViewById(R.id.pager);
        DotsPageIndicator dotsPageIndicator =
            (DotsPageIndicator) findViewById(R.id.page_indicator);
        dotsPageIndicator.setPager(pager);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        WindowInsets result = super.onApplyWindowInsets(insets);

        lastWindowInsets = insets;
        maybeInitialiseViews();

        return result;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top,
                            int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        lastBounds.set(left, top, right, bottom);
        maybeInitialiseViews();
    }

    private void maybeInitialiseViews() {
        if (lastWindowInsets != null && !lastBounds.isEmpty()) {
            GridViewPager pager =
                (GridViewPager) findViewById(R.id.pager);
            if (pager.getAdapter() == null) {
                // TODO: Maybe there is a better callback I can rely on
                // being done on every layout but not before applying
                // insets, but a failure occurs later if we initialise
                // the adapter before we have both bits of info.
                pager.setAdapter(new ConfigGridPagerAdapter());
            }
        }
    }
}

My problem is now the positioning of the components. The layout XML:

<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <example.ConfigView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.wearable.view.GridViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:keepScreenOn="true"
            android:nestedScrollingEnabled="false"/>

        <android.support.wearable.view.DotsPageIndicator
            android:id="@+id/page_indicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal|bottom"
            app:dotFadeWhenIdle="false"/>

    </example.ConfigView>

</android.support.wearable.view.BoxInsetLayout>

Though the screen is truncated by the chin, the system has given the ConfigView the entire 320x320 space. This puts the bottom of the list as well as the page indicator off the bottom of the screen. I tried using padding to move them back up manually, but padding appeared to have no effect and I'm not quite sure why.

I have also tried making Config itself a BoxInsetLayout and messing with which side gets the insets, but that doesn't appear to change anything visually.

I tried putting app:layout="bottom" on the ConfigView in the XML, which does make it work for the chin layout, but then adds an unwanted border when there is no chin.

How is this supposed to work? It bugs me a bit that this doesn't just work. When I'm programming on a 1920x1080 monitor, I don't have to do anything special to crop down a 1920x1920 box.

I was also looking to pad the bottom of my layout to factor in the size of the chin. I managed to do this by wrapping everything in a customised FrameLayout and setting the padding in onApplyWindowInsets() .

public class ChinLayout extends FrameLayout {

    public ChinLayout(Context context) {
        super(context);
    }

    public ChinLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ChinLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        int chin = insets.getSystemWindowInsetBottom();
        setPadding(0, 0, 0, chin);
        return insets;
    }
}

Then I used ChinLayout at the root of my hierarchy:

<?xml version="1.0" encoding="utf-8"?>
<ChinLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- View hierarchy to fit above the chin goes here. -->

</ChinLayout>

The correct way to compensate for the chin is explained in the Android documentation .

You do not need a custom layout anymore, instead use the fitsSystemWindow attribute on the root layout to have the padding automatically adjusted.

Forewarned, this technique does not work if your root layout is a RelativeLayout in which case you need to replace it or wrap it in a FrameLayout .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM