如何正确绘制属于 ViewGroup 的 Android FloatingActionButton 实例
How to properly draw Android FloatingActionButton instances that are part of a ViewGroup
我正在尝试构建一个 Android 自定义视图,其中包含多个 FloatingActionButton
(命名空间 android.support.design.widget
)实例,这些实例排列成一个圆圈。
为此,我创建了一个继承自 ViewGroup
的新视图。代码如下所示:
package myapp;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ImageView;
import myapp.R;
public class ButtonOverlayView extends ViewGroup
{
private final float _radius = 200.0f;
private int _desiredSize;
public ButtonOverlayView(Context context)
{
super(context);
initializeViewGroup(context);
}
public ButtonOverlayView(Context context, AttributeSet attrs)
{
super(context, attrs);
initializeViewGroup(context);
}
public ButtonOverlayView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initializeViewGroup(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
_desiredSize = 600;
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(_desiredSize, _desiredSize);
}
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3)
{
layoutChildren();
}
private void initializeViewGroup(Context context)
{
createChildren(context, getIconIdentifiers(), getColorIdentifiers());
}
private void createChildren(Context context, int[] iconIdentifiers, int[] colorIdentifiers)
{
for(int i = 0; i < iconIdentifiers.length; i++)
{
final FloatingActionButton button = new FloatingActionButton(context);
button.setImageResource(iconIdentifiers[i]);
button.setSize(FloatingActionButton.SIZE_NORMAL);
button.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
button.setBackgroundTintList(ColorStateList.valueOf(colorIdentifiers[i]));
button.setClickable(true);
addView(button);
}
}
private void layoutChildren()
{
int buttonCount = getChildCount();
int center = _desiredSize / 2;
float angle = 0.0f;
float angleIncrement = 360.0f / (buttonCount - 1);
FloatingActionButton button = (FloatingActionButton)getChildAt(0);
int halfWidth = button.getMeasuredWidth() / 2;
int halfHeight = button.getMeasuredHeight() / 2;
button.layout(center - halfWidth, center - halfHeight, center + halfWidth, center + halfHeight);
for(int i = 1; i < buttonCount; i++)
{
button = (FloatingActionButton)getChildAt(i);
halfWidth = button.getMeasuredWidth() / 2;
halfHeight = button.getMeasuredHeight() / 2;
double radians = Math.toRadians(angle);
int x = (int)(Math.cos(radians) * _radius) + center;
int y = (int)(Math.sin(radians) * _radius) + center;
button.layout(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight);
angle += angleIncrement;
}
}
private int[] getIconIdentifiers()
{
final TypedArray icons = getResources().obtainTypedArray(R.array.icons);
int[] iconIdentifiers = new int[icons.length()];
try
{
for(int i = 0; i < icons.length(); i++)
{
iconIdentifiers[i] = icons.getResourceId(i, -1);
}
}
finally
{
icons.recycle();
}
return iconIdentifiers;
}
private int[] getColorIdentifiers()
{
final TypedArray colors = getResources().obtainTypedArray(R.array.colors);
int[] colorIdentifiers = new int[colors.length()];
try
{
for(int i = 0; i < colors.length(); i++)
{
colorIdentifiers[i] = colors.getResourceId(i, -1);
}
}
finally
{
colors.recycle();
}
return colorIdentifiers;
}
}
FloatingActionButton
的图标和颜色在专用 xml 文件中提供,其内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="icons">
<item>@android:drawable/ic_delete</item>
<item>@android:drawable/ic_input_add</item>
<item>@android:drawable/ic_menu_call</item>
<item>@android:drawable/ic_delete</item>
<item>@android:drawable/ic_input_add</item>
<item>@android:drawable/ic_menu_call</item>
<item>@android:drawable/ic_delete</item>
</array>
<array name="colors">
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorAccent2</item>
</array>
</resources>
从技术上讲,这一切都有效,即它在集成到 Android 应用程序时编译并显示。但是,渲染 FloatingActionButton
个实例时看起来 "strange".
下面的截图说明了我的意思 "strange":
Phone 1 (Android 8.1):
左边的截图显示所有按钮都处于未点击状态,而右边的截图显示右下角的按钮被点击。
Phone 2 (Android 9):
同上,左边为未点击状态,右边为点击右下按钮。
有人能解释为什么这些按钮看起来 "strange" 吗?我该如何解决这个问题?
编辑
受到 this SO 问题的启发,我仔细查看了我正在使用的依赖项,如下所示:
com.android.support:appcompat-v7:25.3.1
com.android.support:support-v4:25.3.1
com.android.support:support-annotations:+
com.android.support:design:25.3.1
com.android.support:support-vector-drawable:25.3.1
因为它可能是那些库中的一个错误,所以我将它们升级到 28.0.0
,但光学结果仍然与上面的屏幕截图相同。
好的,我找到问题了。屏幕截图中不太明显的是按钮实际上是透明的。发生这种情况是因为 getColorIdentifiers
没有从 xml 文件中正确检索颜色。
因此,方法 setBackgroundTintList
使按钮变得透明。解决办法是修正方法如下:
private int[] getColorIdentifiers()
{
return getContext().getResources().getIntArray(R.array.colors);
}
我正在尝试构建一个 Android 自定义视图,其中包含多个 FloatingActionButton
(命名空间 android.support.design.widget
)实例,这些实例排列成一个圆圈。
为此,我创建了一个继承自 ViewGroup
的新视图。代码如下所示:
package myapp;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ImageView;
import myapp.R;
public class ButtonOverlayView extends ViewGroup
{
private final float _radius = 200.0f;
private int _desiredSize;
public ButtonOverlayView(Context context)
{
super(context);
initializeViewGroup(context);
}
public ButtonOverlayView(Context context, AttributeSet attrs)
{
super(context, attrs);
initializeViewGroup(context);
}
public ButtonOverlayView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initializeViewGroup(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
_desiredSize = 600;
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(_desiredSize, _desiredSize);
}
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3)
{
layoutChildren();
}
private void initializeViewGroup(Context context)
{
createChildren(context, getIconIdentifiers(), getColorIdentifiers());
}
private void createChildren(Context context, int[] iconIdentifiers, int[] colorIdentifiers)
{
for(int i = 0; i < iconIdentifiers.length; i++)
{
final FloatingActionButton button = new FloatingActionButton(context);
button.setImageResource(iconIdentifiers[i]);
button.setSize(FloatingActionButton.SIZE_NORMAL);
button.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
button.setBackgroundTintList(ColorStateList.valueOf(colorIdentifiers[i]));
button.setClickable(true);
addView(button);
}
}
private void layoutChildren()
{
int buttonCount = getChildCount();
int center = _desiredSize / 2;
float angle = 0.0f;
float angleIncrement = 360.0f / (buttonCount - 1);
FloatingActionButton button = (FloatingActionButton)getChildAt(0);
int halfWidth = button.getMeasuredWidth() / 2;
int halfHeight = button.getMeasuredHeight() / 2;
button.layout(center - halfWidth, center - halfHeight, center + halfWidth, center + halfHeight);
for(int i = 1; i < buttonCount; i++)
{
button = (FloatingActionButton)getChildAt(i);
halfWidth = button.getMeasuredWidth() / 2;
halfHeight = button.getMeasuredHeight() / 2;
double radians = Math.toRadians(angle);
int x = (int)(Math.cos(radians) * _radius) + center;
int y = (int)(Math.sin(radians) * _radius) + center;
button.layout(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight);
angle += angleIncrement;
}
}
private int[] getIconIdentifiers()
{
final TypedArray icons = getResources().obtainTypedArray(R.array.icons);
int[] iconIdentifiers = new int[icons.length()];
try
{
for(int i = 0; i < icons.length(); i++)
{
iconIdentifiers[i] = icons.getResourceId(i, -1);
}
}
finally
{
icons.recycle();
}
return iconIdentifiers;
}
private int[] getColorIdentifiers()
{
final TypedArray colors = getResources().obtainTypedArray(R.array.colors);
int[] colorIdentifiers = new int[colors.length()];
try
{
for(int i = 0; i < colors.length(); i++)
{
colorIdentifiers[i] = colors.getResourceId(i, -1);
}
}
finally
{
colors.recycle();
}
return colorIdentifiers;
}
}
FloatingActionButton
的图标和颜色在专用 xml 文件中提供,其内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="icons">
<item>@android:drawable/ic_delete</item>
<item>@android:drawable/ic_input_add</item>
<item>@android:drawable/ic_menu_call</item>
<item>@android:drawable/ic_delete</item>
<item>@android:drawable/ic_input_add</item>
<item>@android:drawable/ic_menu_call</item>
<item>@android:drawable/ic_delete</item>
</array>
<array name="colors">
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorPrimary</item>
<item>@color/colorAccent2</item>
</array>
</resources>
从技术上讲,这一切都有效,即它在集成到 Android 应用程序时编译并显示。但是,渲染 FloatingActionButton
个实例时看起来 "strange".
下面的截图说明了我的意思 "strange":
Phone 1 (Android 8.1):
左边的截图显示所有按钮都处于未点击状态,而右边的截图显示右下角的按钮被点击。
Phone 2 (Android 9):
同上,左边为未点击状态,右边为点击右下按钮。
有人能解释为什么这些按钮看起来 "strange" 吗?我该如何解决这个问题?
编辑
受到 this SO 问题的启发,我仔细查看了我正在使用的依赖项,如下所示:
com.android.support:appcompat-v7:25.3.1
com.android.support:support-v4:25.3.1
com.android.support:support-annotations:+
com.android.support:design:25.3.1
com.android.support:support-vector-drawable:25.3.1
因为它可能是那些库中的一个错误,所以我将它们升级到 28.0.0
,但光学结果仍然与上面的屏幕截图相同。
好的,我找到问题了。屏幕截图中不太明显的是按钮实际上是透明的。发生这种情况是因为 getColorIdentifiers
没有从 xml 文件中正确检索颜色。
因此,方法 setBackgroundTintList
使按钮变得透明。解决办法是修正方法如下:
private int[] getColorIdentifiers()
{
return getContext().getResources().getIntArray(R.array.colors);
}