【问题标题】:Custom dashboard buttons text positioning自定义仪表板按钮文本定位
【发布时间】:2016-12-15 21:51:29
【问题描述】:

我已经使用下面的代码实现了扩展 ViewGroup 的仪表板。但是结果并没有像我预期的那样显示。 Dashboard 将按钮作为其子视图正确显示,但按钮内的文本有异常对齐。

你能解释一下为什么按钮标签对调整其容器的大小和定位有定位效果,而一个位置是由XML属性'gravity'设置的吗?

如何使按钮内的文本居中?

可能,我需要对 onMeasure() 和 onLayout() 方法的解释。正如我猜想的那样,第一种方法通过其子视图大小测量容器(仪表板)的大小,第二种方法更改这些子视图在容器内的位置并调整其大小。不是吗?但是为什么子视图的位置和大小对onMeasure()有影响?

提前致谢。

MainDashboard.java

public class MainDashboard extends ViewGroup {

    /*Attributes: */
    private static final float DEFAULT_FACTOR = 0.2f;
    private static final float DEFAULT_SPACING = 0.025f;
    private static final int DEFAULT_ROW = 1;
    private static final int DEFAULT_COL = 1;

    private int mMaxChildWidth = 0;
    private int mMaxChildHeight = 0;

    /*Attribute variables: */
    private float mFactor;
    private float mSpacing;
    private int mRow;
    private int mCol;

    private DeviceScreenSize dss;
    private SCApplication app;
    private int ell_size;

    public MainDashboard(Context context) {
        super(context, null);
    }

    public MainDashboard(Context context, AttributeSet attrs) {
        super(context, attrs, 0);
        init(context,attrs);
    }

    public MainDashboard(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs){
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MainDashboard);

        mFactor = a.getFloat(R.styleable.MainDashboard_factor, DEFAULT_FACTOR);
        mSpacing = a.getFloat(R.styleable.MainDashboard_spacing, DEFAULT_SPACING);
        mRow = a.getInteger(R.styleable.MainDashboard_row, DEFAULT_ROW);
        mCol = a.getInteger(R.styleable.MainDashboard_col, DEFAULT_COL);

        app = (SCApplication)context.getApplicationContext();
        dss= app.getScreenSize();
        ell_size = (int) (Math.min(dss.getWidth(),dss.getHeight())*mFactor);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mMaxChildWidth = 0;
        mMaxChildHeight = 0;

        // Measure once to find the maximum child size.
        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);

        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
            mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight());
        }
        // Measure again for each child to be exactly the same size.
        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                mMaxChildWidth, MeasureSpec.EXACTLY);
        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                mMaxChildHeight, MeasureSpec.EXACTLY);

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }

        setMeasuredDimension(
                resolveSize(mMaxChildWidth, widthMeasureSpec),
                resolveSize(mMaxChildHeight, heightMeasureSpec));
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int container_width = r - l;
        int container_height = b - t;
        final int count = getChildCount();

        int visibleCount = 0;
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            ++visibleCount;
        }

        if (visibleCount == 0) {
            return;
        }

        int left, top;
        int hSpace=(int)(mSpacing *container_width),
            vSpace=(int)(2 * mSpacing * container_width);
        int col, row;
        int visibleIndex = 0;
        for (int i = 0; i < visibleCount; i++) {
            final View child = getChildAt(i);
            row = visibleIndex / mCol;
            col = visibleIndex % mCol;
            left = hSpace * (col + 1) + ell_size * col;
            top = vSpace * (row + 1) + ell_size * row;
            child.layout(left, top, left + ell_size, top + ell_size);
            ++visibleIndex;
        }
    }
}

ma​​in.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:whatever="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/llRoot"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:background = "#000000" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="7"
        android:gravity="center">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/tvMainTitle"
            style="@android:style/TextAppearance.Large"
            android:text="@string/main_title"
            android:textColor="#4169E1"
            />
    </LinearLayout>

    <com.sample.myapp.MainDashboard
        xmlns:android = "http://schemas.android.com/apk/res/android"
        android:id="@+id/dash"
        android:layout_width = "match_parent"
        android:layout_height = "match_parent"
        android:layout_weight = "1"
        android:background = "#000000"
        whatever:col="4">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn1"
                android:text="1"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn2"
                android:text="2"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn3"
                android:text="3"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn4"
                android:text="4"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn5"
                android:text="5"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn6"
                android:text="6"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn7"
                android:text="7"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn8"
                android:text="8"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn9"
                android:text="9"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn10"
                android:text="10"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn11"
                android:text="11"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn12"
                android:text="12"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn13"
                android:text="13"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn14"
                android:text="14"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn15"
                android:text="15"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/DashboardButton"
                android:id="@+id/btn16"
                android:text="16"
                />
    </com.sample.myapp.MainDashboard>
</LinearLayout>

styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name = "DashboardButton">
        <item name = "android:textSize">14sp</item >
        <item name = "android:textStyle">bold</item >
        <item name = "android:textColor">#FFFFFF</item>
        <item name = "android:gravity">center_horizontal</item >
        <item name = "android:layout_gravity">center_vertical</item >
        <item name = "android:background">@drawable/defaultbuttonselector</item >
        <item name = "android:layout_width">wrap_content</item >
        <item name = "android:layout_height">wrap_content</item >
    </style>

</resources>

defaultbuttonselector.xml

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

    <item>
        <shape
            android:shape="rectangle" >

            <corners android:radius="10.0dp" />

            <stroke
                android:width="2dp"
                android:color="#4169E1" />   
        </shape>
    </item>

    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <corners android:radius="10.0dip" />
            <stroke
                android:width="3dp"
                android:color="#00f2ff" />
        </shape>
    </item>

</selector>

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="MainDashboard">
        <attr name="factor" format="float" />
        <attr name="spacing" format="float" />
        <attr name="row" format="integer" />
        <attr name="col" format="integer" />
    </declare-styleable>

</resources>

MainActivity.java

public class MainActivity extends AppCompatActivity{

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

【问题讨论】:

    标签: android-layout android-custom-view dashboard viewgroup android-viewgroup


    【解决方案1】:

    经过一些有根据的猜测和撕裂我的头发,我找到了这个问题的解决方案。

    关键是在我的情况下(自定义仪表板的每个子项遇到大小ell_size)在onMeasure() 的正文中我只使用ell_size 来设置childWidthMeasureSpecchildHeightMeasureSpec 而不是使用widthMeasureSpecheightMeasureSpec。 我不知道这是否是个好主意,但它帮助了我,我得到了我需要的东西。

    上面代码的sn-p:

    private void init(Context context, AttributeSet attrs){
        ...
        ell_size = (int) (Math.min(dss.getWidth(),dss.getHeight())*mFactor);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
        // Measure once to find the maximum child size.
        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                ell_size, MeasureSpec.AT_MOST);
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                ell_size, MeasureSpec.AT_MOST);
    
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    
        // Measure again for each child to be exactly the same size.
        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                ell_size, MeasureSpec.EXACTLY);
        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                ell_size, MeasureSpec.EXACTLY);
    
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    
        setMeasuredDimension(
                resolveSize(ell_size, widthMeasureSpec),
                resolveSize(ell_size, heightMeasureSpec));
    }
    

    我从函数layout()的描述中想到了这个想法:

    为视图及其所有后代分配大小和位置。这 是布局机制的第二阶段。 (首先是测量)。 在此阶段,每个父级调用其所有子级的布局以 定位它们。这通常使用子测量来完成 存储在度量 pass() 中。派生类不应该 覆盖此方法。有孩子的派生类应该覆盖 布局。在这种方法中,他们应该在他们的每一个上调用布局 孩子们。

    【讨论】:

      猜你喜欢
      • 2020-10-23
      • 2015-03-29
      • 2020-07-07
      • 1970-01-01
      • 1970-01-01
      • 2017-12-31
      • 2015-01-27
      • 2020-02-15
      • 1970-01-01
      相关资源
      最近更新 更多