1

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

您是否真的很好解释为什么按钮标签对其容器的大小调整和定位具有定位作用,而一个位置是由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; 
     } 
    } 
} 

main.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); 
    } 
} 

left - that I have; right - that I need

回答

0

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

的一点是,在我的情况(含自定义仪表板的每个子项中遇到大小ell_size)在onMeasure()身体我只是用ell_size设置childWidthMeasureSpecchildHeightMeasureSpec而不是使用widthMeasureSpecheightMeasureSpec。 我不知道这是否是一个大胆的想法,但这帮助我,我得到了我的需要。

上面的代码的片段:

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()中的子度量值 完成。派生类不应该 重写此方法。带孩子的派生类应覆盖onLayout上的 。在这种方法中,他们应该为每个 孩子调用布局。