下面列出了怎么用androidx.annotation.StyleableRes的API类实例代码及写法,或者点击链接到github查看源代码。
private void setSelectorDrawable(StateListDrawable stateListDrawable, @StyleableRes int solidAttr, @StyleableRes int strokeAttr, @AttrRes int functionId) throws Exception {
if (typedArray.hasValue(solidAttr) || typedArray.hasValue(strokeAttr)) {
GradientDrawable tmpDrawable = DrawableFactory.getDrawable(typedArray);
if (typedArray.hasValue(solidAttr)) {
tmpDrawable.setColor(typedArray.getColor(solidAttr, 0));
}
if (typedArray.hasValue(strokeAttr)) {
int strokeWidth = typedArray.getDimensionPixelSize(R.styleable.background_bl_stroke_width, 0);
int strokeColor = typedArray.getColor(strokeAttr, 0);
float strokeDashWidth = typedArray.getDimension(R.styleable.background_bl_stroke_dashWidth, 0f);
float strokeGap = typedArray.getDimension(R.styleable.background_bl_stroke_dashGap, 0f);
tmpDrawable.setStroke(strokeWidth, strokeColor, strokeDashWidth, strokeGap);
}
stateListDrawable.addState(new int[]{functionId}, tmpDrawable);
}
}
/**
* Returns the {@link ColorStateList} from the given {@link TypedArray} attributes. The resource
* can include themeable attributes, regardless of API level.
*/
@Nullable
public static ColorStateList getColorStateList(
@NonNull Context context, @NonNull TypedArray attributes, @StyleableRes int index) {
if (attributes.hasValue(index)) {
int resourceId = attributes.getResourceId(index, 0);
if (resourceId != 0) {
ColorStateList value = AppCompatResources.getColorStateList(context, resourceId);
if (value != null) {
return value;
}
}
}
// Reading a single color with getColorStateList() on API 15 and below doesn't always correctly
// read the value. Instead we'll first try to read the color directly here.
if (VERSION.SDK_INT <= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
int color = attributes.getColor(index, -1);
if (color != -1) {
return ColorStateList.valueOf(color);
}
}
return attributes.getColorStateList(index);
}
/**
* Returns the {@link ColorStateList} from the given {@link TintTypedArray} attributes. The
* resource can include themeable attributes, regardless of API level.
*/
@Nullable
public static ColorStateList getColorStateList(
@NonNull Context context, @NonNull TintTypedArray attributes, @StyleableRes int index) {
if (attributes.hasValue(index)) {
int resourceId = attributes.getResourceId(index, 0);
if (resourceId != 0) {
ColorStateList value = AppCompatResources.getColorStateList(context, resourceId);
if (value != null) {
return value;
}
}
}
// Reading a single color with getColorStateList() on API 15 and below doesn't always correctly
// read the value. Instead we'll first try to read the color directly here.
if (VERSION.SDK_INT <= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
int color = attributes.getColor(index, -1);
if (color != -1) {
return ColorStateList.valueOf(color);
}
}
return attributes.getColorStateList(index);
}
/**
* Safely retrieve styled attribute information in this Context's theme, after checking whether
* the theme is compatible with the component's given style.
*
* <p>Set a component's {@link R.attr#enforceMaterialTheme enforceMaterialTheme} attribute to
* <code>true</code> to ensure that the Context's theme inherits from {@link
* R.style#Theme_MaterialComponents Theme.MaterialComponents}. For example, you'll want to do this
* if the component uses a new attribute defined in <code>Theme.MaterialComponents</code> like
* {@link R.attr#colorSecondary colorSecondary}.
*
* <p>If {@link R.attr#enforceTextAppearance} attribute is set to <code>true</code> and
* textAppearanceResIndices parameter is specified and has non-negative values, this will also
* check that a valid TextAppearance is set on this component for the text appearance resources
* passed in.
*/
@NonNull
public static TypedArray obtainStyledAttributes(
@NonNull Context context,
AttributeSet set,
@NonNull @StyleableRes int[] attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes,
@StyleableRes int... textAppearanceResIndices) {
// First, check for a compatible theme.
checkCompatibleTheme(context, set, defStyleAttr, defStyleRes);
// Then, check that a textAppearance is set if enforceTextAppearance attribute is true
checkTextAppearance(context, set, attrs, defStyleAttr, defStyleRes, textAppearanceResIndices);
// Then, safely retrieve the styled attribute information.
return context.obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes);
}
private static boolean isCustomTextAppearanceValid(
@NonNull Context context,
AttributeSet set,
@NonNull @StyleableRes int[] attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes,
@NonNull @StyleableRes int... textAppearanceResIndices) {
TypedArray componentAttrs =
context.obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes);
for (int customTextAppearanceIndex : textAppearanceResIndices) {
if (componentAttrs.getResourceId(customTextAppearanceIndex, -1) == -1) {
componentAttrs.recycle();
return false;
}
}
componentAttrs.recycle();
return true;
}
@ColorInt
private static int[] resolveThemeColors(@NonNull Context context, @AttrRes @StyleableRes int[] attrs, @ColorInt int[] defaultColors) {
if (attrs.length != defaultColors.length)
throw new IllegalArgumentException("Argument attrs must be the same size as defaultColors");
TypedValue typedValue = new TypedValue();
TypedArray a = context.obtainStyledAttributes(typedValue.data, attrs);
for (int i = 0; i < attrs.length; i++) {
defaultColors[i] = a.getColor(0, defaultColors[i]);
}
a.recycle();
return defaultColors;
}
private void addFrame(@StyleableRes int itemDrawableId, @StyleableRes int itemDurationId){
if(animationTa.hasValue(itemDrawableId)){
Drawable itemDrawable = animationTa.getDrawable(itemDrawableId);
if(itemDrawable != null){
if(animationTa.hasValue(itemDurationId)){
drawable.addFrame(itemDrawable, animationTa.getInt(itemDurationId, 0));
}else {
drawable.addFrame(itemDrawable, duration);
}
}
}
}
/**
* Fetches and returns a Styleable value
* @param context
* @param styleable The styleable to fetch
* @param style Property of the styleable to fetch
* @param defaultAttr default attr to return if styleable can't be fetched
* @return
*/
public static int fetchStyleableAttr(Context context, @StyleRes int style, @StyleableRes int[] styleable, @StyleableRes int styleableStyle, @AttrRes int defaultAttr) {
TypedArray textColors = context.obtainStyledAttributes(style, styleable);
int color = textColors.getColor(styleableStyle, fetchAttrColor(context, defaultAttr));
textColors.recycle();
return color;
}
/**
* Gets a boolean from an attr resource value
*
* @param context Context
* @return @ColorRes
*/
public static boolean fetchAttrBool(Context context, @StyleRes int style, @StyleableRes int boolRes) {
TypedArray bool = context.obtainStyledAttributes(style, R.styleable.BaseTwistyTheme);
boolean finalBool = bool.getBoolean(boolRes, false);
bool.recycle();
return finalBool;
}
@SuppressLint("ResourceType")
public ColorPalette(@StyleRes int themeOverlay, @StyleableRes int[] themeOverlayAttrs) {
super(themeOverlay);
TypedArray a = getContext().obtainStyledAttributes(themeOverlay, themeOverlayAttrs);
main = a.getColor(0, Color.TRANSPARENT);
a.recycle();
}
/**
* Inflates an instance of MotionSpec from the animator resource indexed in the given attributes
* array.
*/
@Nullable
public static MotionSpec createFromAttribute(
@NonNull Context context, @NonNull TypedArray attributes, @StyleableRes int index) {
if (attributes.hasValue(index)) {
int resourceId = attributes.getResourceId(index, 0);
if (resourceId != 0) {
return createFromResource(context, resourceId);
}
}
return null;
}
/**
* Returns the drawable object from the given attributes.
*
* <p>This method supports inflation of {@code <vector>} and {@code <animated-vector>} resources
* on devices where platform support is not available.
*/
@Nullable
public static Drawable getDrawable(
@NonNull Context context, @NonNull TypedArray attributes, @StyleableRes int index) {
if (attributes.hasValue(index)) {
int resourceId = attributes.getResourceId(index, 0);
if (resourceId != 0) {
Drawable value = AppCompatResources.getDrawable(context, resourceId);
if (value != null) {
return value;
}
}
}
return attributes.getDrawable(index);
}
/**
* Returns a TextAppearanceSpan object from the given attributes.
*
* <p>You only need this if you are drawing text manually. Normally, TextView takes care of this.
*/
@Nullable
public static TextAppearance getTextAppearance(
@NonNull Context context, @NonNull TypedArray attributes, @StyleableRes int index) {
if (attributes.hasValue(index)) {
int resourceId = attributes.getResourceId(index, 0);
if (resourceId != 0) {
return new TextAppearance(context, resourceId);
}
}
return null;
}
/**
* Returns the @StyleableRes index that contains value in the attributes array. If both indices
* contain values, the first given index takes precedence and is returned.
*/
@StyleableRes
static int getIndexWithValue(
@NonNull TypedArray attributes, @StyleableRes int a, @StyleableRes int b) {
if (attributes.hasValue(a)) {
return a;
}
return b;
}
private static int readFirstAvailableDimension(
@NonNull Context context,
@NonNull TypedArray attributes,
@NonNull @StyleableRes int... indices) {
int lineHeight = -1;
for (int index = 0; index < indices.length && lineHeight < 0; ++index) {
lineHeight = MaterialResources.getDimensionPixelSize(context, attributes, indices[index], -1);
}
return lineHeight;
}
@NonNull
@Override
public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs)
{
return mActivity.obtainStyledAttributes(resId, attrs);
}
@NonNull
@Override
public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs)
{
return mStyledAttributes;
}
private void initializeThemingValues(
RadioGroup group,
@ArrayRes int overlays,
@ArrayRes int contentDescriptions,
@StyleableRes int[] themeOverlayAttrs,
@IdRes int overlayId,
ThemingType themingType) {
TypedArray themingValues = getResources().obtainTypedArray(overlays);
TypedArray contentDescriptionArray = getResources().obtainTypedArray(contentDescriptions);
if (themingValues.length() != contentDescriptionArray.length()) {
throw new IllegalArgumentException(
"Feature array length doesn't match its content description array length.");
}
for (int i = 0; i < themingValues.length(); i++) {
@StyleRes int valueThemeOverlay = themingValues.getResourceId(i, 0);
ThemeAttributeValues themeAttributeValues = null;
// Create RadioButtons for themeAttributeValues values
switch (themingType) {
case COLOR:
themeAttributeValues = new ColorPalette(valueThemeOverlay, themeOverlayAttrs);
break;
case SHAPE_CORNER_FAMILY:
themeAttributeValues = new ThemeAttributeValues(valueThemeOverlay);
break;
case SHAPE_CORNER_SIZE:
themeAttributeValues =
new ThemeAttributeValuesWithContentDescription(
valueThemeOverlay, contentDescriptionArray.getString(i));
break;
}
// Expect the radio group to have a RadioButton as child for each themeAttributeValues value.
AppCompatRadioButton button =
themingType.radioButtonType == RadioButtonType.XML
? ((AppCompatRadioButton) group.getChildAt(i))
: createCompatRadioButton(group, contentDescriptionArray.getString(i));
button.setTag(themeAttributeValues);
themeAttributeValues.customizeRadioButton(button);
int currentThemeOverlay = ThemeOverlayUtils.getThemeOverlay(overlayId);
if (themeAttributeValues.themeOverlay == currentThemeOverlay) {
group.check(button.getId());
}
}
themingValues.recycle();
contentDescriptionArray.recycle();
}
@StyleableRes
public int[] getPrimaryThemeOverlayAttrs() {
return PRIMARY_THEME_OVERLAY_ATTRS;
}
@StyleableRes
public int[] getSecondaryThemeOverlayAttrs() {
return SECONDARY_THEME_OVERLAY_ATTRS;
}
/**
* Retrieve a dimensional unit attribute at <var>index</var> for use as a size in raw pixels. A
* size conversion involves rounding the base value, and ensuring that a non-zero base value is at
* least one pixel in size.
*
* <p>This method will throw an exception if the attribute is defined but is not a dimension.
*
* @param context The Context the view is running in, through which the current theme, resources,
* etc can be accessed.
* @param attributes array of typed attributes from which the dimension unit must be read.
* @param index Index of attribute to retrieve.
* @param defaultValue Value to return if the attribute is not defined or not a resource.
* @return Attribute dimension value multiplied by the appropriate metric and truncated to integer
* pixels, or defaultValue if not defined.
* @throws UnsupportedOperationException if the attribute is defined but is not a dimension.
* @see TypedArray#getDimensionPixelSize(int, int)
*/
public static int getDimensionPixelSize(
@NonNull Context context,
@NonNull TypedArray attributes,
@StyleableRes int index,
final int defaultValue) {
TypedValue value = new TypedValue();
if (!attributes.getValue(index, value) || value.type != TypedValue.TYPE_ATTRIBUTE) {
return attributes.getDimensionPixelSize(index, defaultValue);
}
TypedArray styledAttrs = context.getTheme().obtainStyledAttributes(new int[] {value.data});
int dimension = styledAttrs.getDimensionPixelSize(0, defaultValue);
styledAttrs.recycle();
return dimension;
}
/**
* Safely retrieve styled attribute information in this Context's theme using {@link
* androidx.appcompat.widget.TintTypedArray}, after checking whether the theme is compatible with
* the component's given style.
*
* <p>Set a component's {@link R.attr#enforceMaterialTheme enforceMaterialTheme} attribute to
* <code>true</code> to ensure that the Context's theme inherits from {@link
* R.style#Theme_MaterialComponents Theme.MaterialComponents}. For example, you'll want to do this
* if the component uses a new attribute defined in <code>Theme.MaterialComponents</code> like
* {@link R.attr#colorSecondary colorSecondary}.
*
* <p>New components should prefer to use {@link #obtainStyledAttributes(Context, AttributeSet,
* int[], int, int, int...)}, and use
* {@link com.google.android.material.resources.MaterialResources}
* as a replacement for the functionality in {@link androidx.appcompat.widget.TintTypedArray}.
*
* <p>If {@link R.attr#enforceTextAppearance} attribute is set to <code>true</code> and
* textAppearanceResIndices parameter is specified and has non-negative values, this will also
* check that a valid TextAppearance is set on this component for the text appearance resources
* passed in.
*/
public static TintTypedArray obtainTintedStyledAttributes(
@NonNull Context context,
AttributeSet set,
@NonNull @StyleableRes int[] attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes,
@StyleableRes int... textAppearanceResIndices) {
// First, check for a compatible theme.
checkCompatibleTheme(context, set, defStyleAttr, defStyleRes);
// Then, check that a textAppearance is set if enforceTextAppearance attribute is true
checkTextAppearance(context, set, attrs, defStyleAttr, defStyleRes, textAppearanceResIndices);
// Then, safely retrieve the styled attribute information.
return TintTypedArray.obtainStyledAttributes(context, set, attrs, defStyleAttr, defStyleRes);
}
private static void checkTextAppearance(
@NonNull Context context,
AttributeSet set,
@NonNull @StyleableRes int[] attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes,
@Nullable @StyleableRes int... textAppearanceResIndices) {
TypedArray themeEnforcementAttrs =
context.obtainStyledAttributes(
set, R.styleable.ThemeEnforcement, defStyleAttr, defStyleRes);
boolean enforceTextAppearance =
themeEnforcementAttrs.getBoolean(R.styleable.ThemeEnforcement_enforceTextAppearance, false);
if (!enforceTextAppearance) {
themeEnforcementAttrs.recycle();
return;
}
boolean validTextAppearance;
if (textAppearanceResIndices == null || textAppearanceResIndices.length == 0) {
// No custom TextAppearance attributes passed in, check android:textAppearance
validTextAppearance =
themeEnforcementAttrs.getResourceId(
R.styleable.ThemeEnforcement_android_textAppearance, -1)
!= -1;
} else {
// Check custom TextAppearances are valid
validTextAppearance =
isCustomTextAppearanceValid(
context, set, attrs, defStyleAttr, defStyleRes, textAppearanceResIndices);
}
themeEnforcementAttrs.recycle();
if (!validTextAppearance) {
throw new IllegalArgumentException(
"This component requires that you specify a valid TextAppearance attribute. Update your "
+ "app theme to inherit from Theme.MaterialComponents (or a descendant).");
}
}
private static int readColorFromAttributes(
Context context, @NonNull TypedArray a, @StyleableRes int index) {
return MaterialResources.getColorStateList(context, a, index).getDefaultColor();
}
@ColorInt
private static int resolveThemeColor(@NonNull Context context, @AttrRes @StyleableRes int attr) {
return resolveThemeColors(context, new int[]{attr}, new int[]{0})[0];
}
/**
* Retrieve styled attribute information in {@link #getContext()} theme. See
* {@link android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])}
* for more information.
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
*/
@NonNull
TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs);