0

任务:缩放布局以填充所有可用空间。我创建了容器,称为 ZoomViewGroup,它测量自身内部的单个子节点,并根据其自身大小设置 scaleX 和 scaleY。

package sample.andrew.myapplication;

import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import org.jetbrains.annotations.NotNull;

/**
 * Container class for single child to be zoomed to fill container. 
 */
public class ZoomViewGroup extends ViewGroup
{
    private static final String log = "mopo-zoom";

    @SuppressWarnings("UnusedDeclaration")
    public ZoomViewGroup(Context context)
    {
    super(context);
}

@SuppressWarnings("UnusedDeclaration")
public ZoomViewGroup(Context context, AttributeSet attrs){
    super(context, attrs);
}

@SuppressWarnings("UnusedDeclaration")
public ZoomViewGroup(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

private void checkCount() {
    if(getChildCount() > 1)
    {
        throw new IllegalStateException("ZoomViewGroup can host only one direct child");
    }
}

@Override
public void addView(@NotNull View child) {
    checkCount();
    super.addView(child);
}

@Override
public void addView(@NotNull View child, int index) {
    checkCount();
    super.addView(child, index);
}

@Override
public void addView(@NotNull View child, LayoutParams params) {
    checkCount();
    super.addView(child, params);
}

@Override
public void addView(@NotNull View child, int index, LayoutParams params) {
    checkCount();
    super.addView(child, index, params);
}

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

    View child = getChildAt(0);
    int fake_size = MeasureSpec.makeMeasureSpec(3000, MeasureSpec.UNSPECIFIED);

    assert child != null;
    assert child.getLayoutParams() != null;

    if(child.getLayoutParams().width == LayoutParams.MATCH_PARENT)
    {
        int w = MeasureSpec.getSize(widthMeasureSpec);

        int fake_w = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);

//            if(Log.ENABLED) Log.i(log, "measure child with width " + w + "x3000");

        measureChildren(fake_w, fake_size);

//            if(Log.ENABLED) Log.i(log, "measured child: " + child.getMeasuredWidth() + "x" + child.getMeasuredHeight());
    }
    else
    {
        //      By using fake size we will get child measured with
        //      wrap_content size, so we can calculate needed zoom to fit
        //      child in whole space
        measureChildren(fake_size, fake_size);
    }

    int w = MeasureSpec.getSize(widthMeasureSpec);
    int h = MeasureSpec.getSize(heightMeasureSpec);

//      ZoomViewGroup supports only match_parent layout params for itself,
//      so we don't modify income sizes and set them directly.
    setMeasuredDimension(w, h);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if(Log.ENABLED) Log.i(log, "onLayout " + l + " " + t + " " + r + " " +b);

        View child = getChildAt(0);

        assert child != null;

        if(child.getVisibility() != GONE) {
            int w = r - l;
            int h = b - t;

            int child_w = child.getMeasuredWidth();
            int child_h = child.getMeasuredHeight();

            if(child_w == w && child_h == h)
            {
                child.layout(l, t, r, b);
            }
            else
            {
                int dx = w - child_w;
                int dy = h - child_h;

                if(Log.ENABLED) Log.i(log, "dx, dy " + dx + " " + dy);

                int cl,ct,cr,cb;

                if(dx == 0) {
                    cl = 0;
                    cr = child_w;
                } else {
                    cl = (w-child_w)/2;
                    cr = cl + child_w;
                }

                if(dy == 0)
                {
                    ct = 0;
                    cb = child_h;
                }
                else
                {
                    ct = (h - child_h)/2;
                    cb = ct + child_h;
                }

                if(Log.ENABLED) Log.i(log, "set child bounds: " +
                        cl + " " + ct + " " + cr + " " + cb);

                child.layout(cl, ct, cr, cb);
            }
        }
}

@Override
public void onSizeChanged(int width, int height, int oldw, int oldh) {
    super.onSizeChanged(width, height, oldw, oldh);

    if(Log.ENABLED) Log.i(log, "onSizeChanged " + oldw + "x" + oldh +
            " -> " + width + "x" + height);

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    {
        View child = getChildAt(0);

        if(child != null)
        {
            float originScaleX = child.getScaleX();
            float originScaleY = child.getScaleY();

            if(originScaleX != 1 || originScaleY != 1)
            {
                if(Log.ENABLED) Log.i(log, "scale set: " + originScaleX + " " + originScaleY);
                if(Log.ENABLED) Log.i(log, "scale size: " + (int)((originScaleX*child.getMeasuredWidth())) + "x"
                        + (int)(originScaleY*child.getMeasuredHeight())
                        + " [" + child.getMeasuredWidth() + "x" + child.getMeasuredHeight() + "]" );
                return;
            }

            @SuppressWarnings("ConstantConditions")
            int originWidth = child.getMeasuredWidth();
            int originHeight = child.getMeasuredHeight();

            if(Log.ENABLED) Log.i(log, "child size: " + originWidth + " " + originHeight);

            if(originWidth > 0)
            {
                float zoomFactorX = findZoomCoef(width, originWidth);
                float zoomFactorY = findZoomCoef(height, originHeight);

                if(Log.ENABLED) Log.i(log, "calc zoom [" + zoomFactorX + ", " + zoomFactorY + "]");

                child.setScaleX(zoomFactorX);
                child.setScaleY(zoomFactorY);
            }
        }
    }
}

/**
 * Calculates such coefficient to meet rule:
 * size = (int)(sizeToZoom*coef)
 * @param size
 * @param sizeToZoom
 * @return coef
 */
public static float findZoomCoef(int size, int sizeToZoom)
{
    float zoomFactor = (size*100/sizeToZoom)/100.0f;

    float step = 0.001f;

    int count = 0;

    do
    {
        int reverse_size = (int) (sizeToZoom*zoomFactor);

        if(reverse_size == size)
            break;

        if(reverse_size < size)
        {
            zoomFactor += step;
        }
        else
        {
            zoomFactor -= step;
            step /= 10;
        }

        count++;
    }
    while (true);

    if(Log.ENABLED) Log.i(log, "calc zoom: s,c " + step + " " + count);

    return zoomFactor;
}

}

此实现适用于除 nexus 4 和 nexus 5 之外的主要设备数量。这是我第一次看到这个。通常某些东西在三星或索尼设备上不起作用,反之亦然。测试布局以显示问题:

<RelativeLayout 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=".MyActivity">


    <sample.andrew.myapplication.ZoomViewGroup
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#fff">

                    <RelativeLayout
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_centerInParent="true"
                            android:id="@+id/test_layout">

                            <View android:layout_width="200dp"
                                  android:layout_height="200dp"
                                  android:background="#70c0"
                                  android:id="@+id/anchor"
                                    />

                            <View android:layout_width="20dp"
                                  android:layout_height="20dp"
                                  android:background="#f00"
                                  android:layout_centerInParent="true"
                                  />

                            <RelativeLayout
                                    android:layout_width="match_parent"
                                    android:layout_height="match_parent"
                                    android:id="@+id/test_anchored_layout"
                                    android:layout_alignTop="@id/anchor"
                                    android:layout_alignLeft="@id/anchor"
                                    android:layout_alignRight="@id/anchor"
                                    android:layout_alignBottom="@id/anchor">

                            <!--<RelativeLayout-->
                                    <!--android:layout_width="200dp"-->
                                    <!--android:id="@+id/test_anchored_layout"-->
                                    <!--android:layout_height="200dp">-->

                                    <View android:layout_width="80dp"
                                          android:layout_height="20dp"
                                          android:layout_centerHorizontal="true"
                                          android:background="#00f"
                                            />

                            </RelativeLayout>
                    </RelativeLayout>

    </sample.andrew.myapplication.ZoomViewGroup>

</RelativeLayout>

测试样本的外观:1. android studio 设计器/预览 android 工作室设计师/预览

2 它应该是怎样的以及它是如何工作的,例如在所有 Galaxy 设备上

它应该是怎样的以及它是如何工作的,例如在所有 Galaxy 设备上

3 它在 nexus 4 和 nexus 5 上的外观

它在 nexus 4 和 nexus 5 上的外观

我发现如果 test_anchored_layout 使用固定大小(注释部分)而不是布局规则,那么即使在连接上也一切正常。

连接的错误或我不明白的东西?完整的项目档案:

项目压缩

.apk 文件

4

1 回答 1

0

尝试

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/test_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true" >

    <View
        android:id="@+id/anchor"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#70c0" />

    <View
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_centerInParent="true"
        android:background="#f00" />

    <RelativeLayout
        android:id="@+id/test_anchored_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignBottom="@id/anchor"
        android:layout_alignLeft="@id/anchor"
        android:layout_alignRight="@id/anchor"
        android:layout_alignTop="@id/anchor" >

        <!-- <RelativeLayout -->
        <!-- android:layout_width="200dp" -->
        <!-- android:id="@+id/test_anchored_layout" -->
        <!-- android:layout_height="200dp"> -->

        <View
            android:layout_width="80dp"
            android:layout_height="20dp"
            android:layout_centerHorizontal="true"
            android:background="#00f" />
    </RelativeLayout>

</RelativeLayout>
于 2014-07-14T11:26:38.343 回答