Add support for GradientColor in layoutlib

Change-Id: Ia9a55a9e00d7ddb5263f3dbe46b5da8dde457526
This commit is contained in:
Diego Perez
2016-03-01 16:20:43 +00:00
parent bf8ff76fee
commit 566b303365
9 changed files with 225 additions and 105 deletions

View File

@@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="com.android.tools.layoutlib.create.Main" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/icu4j-icudata-jarjar_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-jarjar_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" />
<option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icudata-host-jarjar_intermediates/classes-jarjar.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-host-jarjar_intermediates/classes-jarjar.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/../../../../" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" value="" />

View File

@@ -25,14 +25,9 @@ import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.internal.util.XmlUtils;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.ResourceType;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.Nullable;
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
@@ -42,7 +37,6 @@ import android.util.TypedValue;
import android.view.LayoutInflater_Delegate;
import android.view.ViewGroup.LayoutParams;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
@@ -305,77 +299,22 @@ public final class BridgeTypedArray extends TypedArray {
return defValue;
}
@Override
public ComplexColor getComplexColor(int index) {
// TODO: Support GradientColor
return getColorStateList(index);
}
/**
* Retrieve the ColorStateList for the attribute at <var>index</var>.
* The value may be either a single solid color or a reference to
* a color or complex {@link android.content.res.ColorStateList} description.
*
* @param index Index of attribute to retrieve.
*
* @return ColorStateList for the attribute, or null if not defined.
*/
@Override
public ColorStateList getColorStateList(int index) {
if (!hasValue(index)) {
return null;
}
ResourceValue resValue = mResourceData[index];
String value = resValue.getValue();
return ResourceHelper.getColorStateList(mResourceData[index], mContext);
}
if (value == null) {
@Override
public ComplexColor getComplexColor(int index) {
if (!hasValue(index)) {
return null;
}
try {
// Get the state list file content from callback to parse PSI file
XmlPullParser parser = mContext.getLayoutlibCallback().getXmlFileParser(value);
if (parser == null) {
// If used with a version of Android Studio that does not implement getXmlFileParser
// fall back to reading the file from disk
File f = new File(value);
if (f.isFile()) {
parser = ParserFactory.create(f);
}
}
if (parser != null) {
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, mContext, resValue.isFramework());
try {
return ColorStateList.createFromXml(mContext.getResources(), blockParser,
mContext.getTheme());
} finally {
blockParser.ensurePopped();
}
}
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
"Failed to configure parser for " + value, e, null);
return null;
} catch (Exception e) {
// this is an error and not warning since the file existence is checked before
// attempting to parse it.
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
"Failed to parse file " + value, e, null);
return null;
}
try {
int color = ResourceHelper.getColor(value);
return ColorStateList.valueOf(color);
} catch (NumberFormatException e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null);
}
return null;
return ResourceHelper.getComplexColor(mResourceData[index], mContext);
}
/**

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.res;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources.Theme;
import android.util.AttributeSet;
import java.io.IOException;
/**
* Class that provides access to the {@link GradientColor#createFromXmlInner(Resources,
* XmlPullParser, AttributeSet, Theme)} and {@link ColorStateList#createFromXmlInner(Resources,
* XmlPullParser, AttributeSet, Theme)} methods
*/
public class ComplexColor_Accessor {
public static GradientColor createGradientColorFromXmlInner(@NonNull Resources r,
@NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
throws IOException, XmlPullParserException {
return GradientColor.createFromXmlInner(r, parser, attrs, theme);
}
public static ColorStateList createColorStateListFromXmlInner(@NonNull Resources r,
@NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
throws IOException, XmlPullParserException {
return ColorStateList.createFromXmlInner(r, parser, attrs, theme);
}
}

View File

@@ -227,6 +227,10 @@ public class Paint_Delegate {
mColorFilter = ColorFilter_Delegate.getDelegate(colorFilterPtr);
}
public void setShader(long shaderPtr) {
mShader = Shader_Delegate.getDelegate(shaderPtr);
}
/**
* Returns the {@link Shader} delegate or null if none have been set
*

View File

@@ -211,12 +211,16 @@ public class VectorDrawable_Delegate {
@LayoutlibDelegate
static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) {
VFullPath_Delegate path = getDelegate(pathPtr);
path.setFillGradient(fillGradientPtr);
}
@LayoutlibDelegate
static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) {
VFullPath_Delegate path = getDelegate(pathPtr);
path.setStrokeGradient(strokeGradientPtr);
}
@LayoutlibDelegate
@@ -540,6 +544,8 @@ public class VectorDrawable_Delegate {
float mStrokeWidth = 0;
int mFillColor = Color.TRANSPARENT;
long mStrokeGradient = 0;
long mFillGradient = 0;
float mStrokeAlpha = 1.0f;
float mFillAlpha = 1.0f;
float mTrimPathStart = 0;
@@ -569,6 +575,9 @@ public class VectorDrawable_Delegate {
mStrokeLineCap = copy.mStrokeLineCap;
mStrokeLineJoin = copy.mStrokeLineJoin;
mStrokeMiterlimit = copy.mStrokeMiterlimit;
mStrokeGradient = copy.mStrokeGradient;
mFillGradient = copy.mFillGradient;
}
private int getStrokeLineCap() {
@@ -637,7 +646,7 @@ public class VectorDrawable_Delegate {
return mStrokeColor;
}
private void setStrokeColor(int strokeColor) {
private void setStrokeColor(int strokeColor) {
mStrokeColor = strokeColor;
}
@@ -704,6 +713,14 @@ public class VectorDrawable_Delegate {
private float getStrokeMiterlimit() {
return mStrokeMiterlimit;
}
private void setStrokeGradient(long gradientPtr) {
mStrokeGradient = gradientPtr;
}
private void setFillGradient(long gradientPtr) {
mFillGradient = gradientPtr;
}
}
private static class VGroup_Delegate implements VNativeObject {
@@ -1046,11 +1063,11 @@ public class VectorDrawable_Delegate {
final Paint fillPaint = mFillPaint;
fillPaint.setColor(applyAlpha(fullPath.mFillColor, fullPath.mFillAlpha));
Paint_Delegate fillPaintDelegate = Paint_Delegate.getDelegate(fillPaint
.getNativeInstance
());
.getNativeInstance());
// mFillPaint can not be null at this point so we will have a delegate
assert fillPaintDelegate != null;
fillPaintDelegate.setColorFilter(filterPtr);
fillPaintDelegate.setShader(fullPath.mFillGradient);
Canvas_Delegate.native_drawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
.getNativeInstance());
}
@@ -1080,6 +1097,7 @@ public class VectorDrawable_Delegate {
strokePaintDelegate.setColorFilter(filterPtr);
final float finalStrokeScale = minScale * matrixScale;
strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
strokePaintDelegate.setShader(fullPath.mStrokeGradient);
Canvas_Delegate.native_drawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
.getNativeInstance());
}

View File

@@ -33,7 +33,11 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.ComplexColor;
import android.content.res.ComplexColor_Accessor;
import android.content.res.GradientColor;
import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -47,6 +51,7 @@ import android.util.TypedValue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@@ -119,53 +124,127 @@ public final class ResourceHelper {
throw new NumberFormatException();
}
public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) {
/**
* Returns a {@link ComplexColor} from the given {@link ResourceValue}
*
* @param resValue the value containing a color value or a file path to a complex color
* definition
* @param context the current context
* @param theme the theme to use when resolving the complex color
* @param allowGradients when false, only {@link ColorStateList} will be returned. If a {@link
* GradientColor} is found, null will be returned.
*/
@Nullable
private static ComplexColor getInternalComplexColor(@NonNull ResourceValue resValue,
@NonNull BridgeContext context, @Nullable Theme theme, boolean allowGradients) {
String value = resValue.getValue();
if (value != null && !RenderResources.REFERENCE_NULL.equals(value)) {
// first check if the value is a file (xml most likely)
if (value == null || RenderResources.REFERENCE_NULL.equals(value)) {
return null;
}
// first check if the value is a file (xml most likely)
XmlPullParser parser = context.getLayoutlibCallback().getXmlFileParser(value);
if (parser == null) {
File f = new File(value);
if (f.isFile()) {
// let the framework inflate the color from the XML file, by
// providing an XmlPullParser
try {
// let the framework inflate the ColorStateList from the XML file, by
// providing an XmlPullParser
XmlPullParser parser = ParserFactory.create(f);
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, context, resValue.isFramework());
try {
return ColorStateList.createFromXml(context.getResources(), blockParser);
} finally {
blockParser.ensurePopped();
}
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
"Failed to configure parser for " + value, e, null /*data*/);
// we'll return null below.
} catch (Exception e) {
// this is an error and not warning since the file existence is
// checked before attempting to parse it.
parser = ParserFactory.create(f);
} catch (XmlPullParserException | FileNotFoundException e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
"Failed to parse file " + value, e, null /*data*/);
return null;
}
} else {
// try to load the color state list from an int
try {
int color = ResourceHelper.getColor(value);
return ColorStateList.valueOf(color);
} catch (NumberFormatException e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
"Failed to convert " + value + " into a ColorStateList", e,
null /*data*/);
return null;
}
}
}
if (parser != null) {
try {
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, context, resValue.isFramework());
try {
// Advance the parser to the first element so we can detect if it's a
// color list or a gradient color
int type;
//noinspection StatementWithEmptyBody
while ((type = blockParser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
// Seek parser to start tag.
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
final String name = blockParser.getName();
if (allowGradients && "gradient".equals(name)) {
return ComplexColor_Accessor.createGradientColorFromXmlInner(
context.getResources(),
blockParser, blockParser,
theme);
} else if ("selector".equals(name)) {
return ComplexColor_Accessor.createColorStateListFromXmlInner(
context.getResources(),
blockParser, blockParser,
theme);
}
} finally {
blockParser.ensurePopped();
}
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
"Failed to configure parser for " + value, e, null /*data*/);
// we'll return null below.
} catch (Exception e) {
// this is an error and not warning since the file existence is
// checked before attempting to parse it.
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
"Failed to parse file " + value, e, null /*data*/);
return null;
}
} else {
// try to load the color state list from an int
try {
int color = getColor(value);
return ColorStateList.valueOf(color);
} catch (NumberFormatException e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
"Failed to convert " + value + " into a ColorStateList", e,
null /*data*/);
}
}
return null;
}
/**
* Returns a {@link ColorStateList} from the given {@link ResourceValue}
*
* @param resValue the value containing a color value or a file path to a complex color
* definition
* @param context the current context
*/
@Nullable
public static ColorStateList getColorStateList(@NonNull ResourceValue resValue,
@NonNull BridgeContext context) {
return (ColorStateList) getInternalComplexColor(resValue, context, context.getTheme(),
false);
}
/**
* Returns a {@link ComplexColor} from the given {@link ResourceValue}
*
* @param resValue the value containing a color value or a file path to a complex color
* definition
* @param context the current context
*/
@Nullable
public static ComplexColor getComplexColor(@NonNull ResourceValue resValue,
@NonNull BridgeContext context) {
return getInternalComplexColor(resValue, context, context.getTheme(), true);
}
/**
* Returns a drawable from the given value.
* @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,24 @@
<!--
~ Copyright (C) 2016 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<gradient xmlns:android="http://schemas.android.com/apk/res/android"
android:startX="10"
android:startY="10"
android:endX="50"
android:endY="50"
android:startColor="#ffff0000"
android:endColor="#ff00ff00" />

View File

@@ -53,6 +53,16 @@
android:trimPathStart="0.2"
android:trimPathEnd="0.8"
/>
<!--
Draw a line with gradient stroke color
-->
<path
android:strokeWidth="1"
android:strokeColor="#FF00FF"
android:fillColor="@color/gradient"
android:pathData="M-20,-20 l0, 10 l10, 0 l0, -10 l-10,0 "
/>
</group>
</vector>