am ce5a79da: am d717682d: am 5c597c1d: am a8854564: am ce4a9d91: Merge "New custom widgets library" into lmp-mr1-dev
* commit 'ce5a79dacfb152317429cb69c28fe8bb39c02f4c': New custom widgets library
This commit is contained in:
8
tools/layoutlib/.idea/artifacts/studio_android_widgets_jar.xml
generated
Normal file
8
tools/layoutlib/.idea/artifacts/studio_android_widgets_jar.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="studio-android-widgets:jar">
|
||||||
|
<output-path>$PROJECT_DIR$/out/artifacts/studio_android_widgets_jar</output-path>
|
||||||
|
<root id="archive" name="studio-android-widgets.jar">
|
||||||
|
<element id="module-output" name="studio-android-widgets" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
tools/layoutlib/.idea/artifacts/studio_android_widgets_src_jar.xml
generated
Normal file
8
tools/layoutlib/.idea/artifacts/studio_android_widgets_src_jar.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="studio-android-widgets-src:jar">
|
||||||
|
<output-path>$PROJECT_DIR$/out/artifacts/studio_android_widgets_src_jar</output-path>
|
||||||
|
<root id="archive" name="studio-android-widgets-src.jar">
|
||||||
|
<element id="dir-copy" path="$PROJECT_DIR$/studio-custom-widgets/src" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
4
tools/layoutlib/.idea/modules.xml
generated
4
tools/layoutlib/.idea/modules.xml
generated
@@ -4,7 +4,7 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/bridge/bridge.iml" filepath="$PROJECT_DIR$/bridge/bridge.iml" />
|
<module fileurl="file://$PROJECT_DIR$/bridge/bridge.iml" filepath="$PROJECT_DIR$/bridge/bridge.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/create/create.iml" filepath="$PROJECT_DIR$/create/create.iml" />
|
<module fileurl="file://$PROJECT_DIR$/create/create.iml" filepath="$PROJECT_DIR$/create/create.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" filepath="$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 com.android.tools.idea.editors.theme.widgets;
|
||||||
|
|
||||||
|
import com.android.ide.common.rendering.api.LayoutLog;
|
||||||
|
import com.android.layoutlib.bridge.Bridge;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ViewGroup} that wraps another view and catches any possible exceptions that the child view
|
||||||
|
* might generate.
|
||||||
|
* This is used by the theme editor to stop custom views from breaking the preview.
|
||||||
|
*/
|
||||||
|
// TODO: This view is just a temporary solution that will be replaced by adding a try / catch
|
||||||
|
// for custom views in the ClassConverter
|
||||||
|
public class ErrorCatcher extends ViewGroup {
|
||||||
|
public ErrorCatcher(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorCatcher(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorCatcher(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorCatcher(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
assert getChildCount() == 1 : "ErrorCatcher can only have one child";
|
||||||
|
|
||||||
|
View child = getChildAt(0);
|
||||||
|
try {
|
||||||
|
measureChild(child, widthMeasureSpec, heightMeasureSpec);
|
||||||
|
|
||||||
|
setMeasuredDimension(resolveSize(child.getMeasuredWidth(), widthMeasureSpec),
|
||||||
|
resolveSize(child.getMeasuredHeight(), heightMeasureSpec));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Bridge.getLog().warning(LayoutLog.TAG_BROKEN, "Failed to do onMeasure for view " +
|
||||||
|
child.getClass().getCanonicalName(), t);
|
||||||
|
setMeasuredDimension(resolveSize(0, widthMeasureSpec),
|
||||||
|
resolveSize(0, heightMeasureSpec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||||
|
try {
|
||||||
|
return super.drawChild(canvas, child, drawingTime);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Bridge.getLog().warning(LayoutLog.TAG_BROKEN, "Failed to draw for view " +
|
||||||
|
child.getClass().getCanonicalName(), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
assert getChildCount() == 1 : "ErrorCatcher can only have one child";
|
||||||
|
|
||||||
|
View child = getChildAt(0);
|
||||||
|
try {
|
||||||
|
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Bridge.getLog().warning(LayoutLog.TAG_BROKEN, "Failed to do onLayout for view " +
|
||||||
|
child.getClass().getCanonicalName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 com.android.tools.idea.editors.theme.widgets;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class PressedButton extends Button {
|
||||||
|
public PressedButton(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
setPressed(true);
|
||||||
|
jumpDrawablesToCurrentState();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 com.android.tools.idea.editors.theme.widgets;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom layout used in the theme editor to display the component preview. It arranges the child
|
||||||
|
* Views as a grid of cards.
|
||||||
|
* <p/>
|
||||||
|
* The Views are measured and the maximum width and height are used to dimension all the child
|
||||||
|
* components. Any margin attributes from the children are ignored and only the item_margin element
|
||||||
|
* is used.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class ThemePreviewLayout extends ViewGroup {
|
||||||
|
private final int mMaxColumns;
|
||||||
|
private final int mMaxColumnWidth;
|
||||||
|
private final int mMinColumnWidth;
|
||||||
|
private final int mItemHorizontalMargin;
|
||||||
|
private final int mItemVerticalMargin;
|
||||||
|
|
||||||
|
/** Item width to use for every card component. This includes margins. */
|
||||||
|
private int mItemWidth;
|
||||||
|
/** Item height to use for every card component. This includes margins. */
|
||||||
|
private int mItemHeight;
|
||||||
|
|
||||||
|
/** Calculated number of columns */
|
||||||
|
private int mNumColumns;
|
||||||
|
|
||||||
|
public ThemePreviewLayout(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThemePreviewLayout(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThemePreviewLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
|
if (attrs == null) {
|
||||||
|
mMaxColumnWidth = Integer.MAX_VALUE;
|
||||||
|
mMinColumnWidth = 0;
|
||||||
|
mMaxColumns = Integer.MAX_VALUE;
|
||||||
|
mItemHorizontalMargin = 0;
|
||||||
|
mItemVerticalMargin = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||||
|
int maxColumnWidth = attrs.getAttributeIntValue(null, "max_column_width", Integer
|
||||||
|
.MAX_VALUE);
|
||||||
|
int minColumnWidth = attrs.getAttributeIntValue(null, "min_column_width", 0);
|
||||||
|
int itemHorizontalMargin = attrs.getAttributeIntValue(null, "item_horizontal_margin", 0);
|
||||||
|
int itemVerticalMargin = attrs.getAttributeIntValue(null, "item_vertical_margin", 0);
|
||||||
|
|
||||||
|
mMaxColumnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
maxColumnWidth,
|
||||||
|
dm);
|
||||||
|
mMinColumnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
minColumnWidth,
|
||||||
|
dm);
|
||||||
|
mItemHorizontalMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
itemHorizontalMargin,
|
||||||
|
dm);
|
||||||
|
mItemVerticalMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
itemVerticalMargin,
|
||||||
|
dm);
|
||||||
|
mMaxColumns = attrs.getAttributeIntValue(null, "max_columns", Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
// Measure the column size.
|
||||||
|
// The column has a minimum width that will be used to calculate the maximum number of
|
||||||
|
// columns that we can fit in the available space.
|
||||||
|
//
|
||||||
|
// Once we have the maximum number of columns, we will span all columns width evenly to fill
|
||||||
|
// all the available space.
|
||||||
|
int wSize = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
|
||||||
|
|
||||||
|
// Calculate the desired width of all columns and take the maximum.
|
||||||
|
// This step can be skipped if we have a fixed column height so we do not have to
|
||||||
|
// dynamically calculate it.
|
||||||
|
int childWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||||
|
int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||||
|
int itemWidth = 0;
|
||||||
|
int itemHeight = 0;
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
View v = getChildAt(i);
|
||||||
|
|
||||||
|
if (v.getVisibility() == GONE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
measureChild(v, childWidthSpec, childHeightSpec);
|
||||||
|
|
||||||
|
itemWidth = Math.max(itemWidth, v.getMeasuredWidth());
|
||||||
|
itemHeight = Math.max(itemHeight, v.getMeasuredHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
itemWidth = Math.min(Math.max(itemWidth, mMinColumnWidth), mMaxColumnWidth);
|
||||||
|
mNumColumns = Math.min((int) Math.ceil((double) wSize / itemWidth), mMaxColumns);
|
||||||
|
|
||||||
|
// Check how much space this distribution would take taking into account the margins.
|
||||||
|
// If it's bigger than what we have, remove one column.
|
||||||
|
int wSizeNeeded = mNumColumns * itemWidth + (mNumColumns - 1) * mItemHorizontalMargin;
|
||||||
|
if (wSizeNeeded > wSize && mNumColumns > 1) {
|
||||||
|
mNumColumns--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getChildCount() < mNumColumns) {
|
||||||
|
mNumColumns = getChildCount();
|
||||||
|
}
|
||||||
|
if (mNumColumns == 0) {
|
||||||
|
mNumColumns = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inform each child of the measurement
|
||||||
|
childWidthSpec = MeasureSpec.makeMeasureSpec(itemWidth, MeasureSpec.EXACTLY);
|
||||||
|
childHeightSpec = MeasureSpec.makeMeasureSpec(itemHeight, MeasureSpec.EXACTLY);
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
View v = getChildAt(i);
|
||||||
|
|
||||||
|
if (v.getVisibility() == GONE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
measureChild(v, childWidthSpec, childHeightSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the height of the first column to measure our own size
|
||||||
|
int firstColumnItems = getChildCount() / mNumColumns + ((getChildCount() % mNumColumns) > 0
|
||||||
|
? 1 : 0);
|
||||||
|
|
||||||
|
int horizontalMarginsTotalWidth = (mNumColumns - 1) * mItemHorizontalMargin;
|
||||||
|
int verticalMarginsTotalHeight = (firstColumnItems - 1) * mItemVerticalMargin;
|
||||||
|
int totalWidth = mNumColumns * itemWidth + horizontalMarginsTotalWidth +
|
||||||
|
mPaddingRight + mPaddingLeft;
|
||||||
|
int totalHeight = firstColumnItems * itemHeight + verticalMarginsTotalHeight +
|
||||||
|
mPaddingBottom + mPaddingTop;
|
||||||
|
|
||||||
|
setMeasuredDimension(resolveSize(totalWidth, widthMeasureSpec),
|
||||||
|
resolveSize(totalHeight, heightMeasureSpec));
|
||||||
|
|
||||||
|
mItemWidth = itemWidth;
|
||||||
|
mItemHeight = itemHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
int itemsPerColumn = getChildCount() / mNumColumns;
|
||||||
|
// The remainder items are distributed one per column.
|
||||||
|
int remainderItems = getChildCount() % mNumColumns;
|
||||||
|
|
||||||
|
int x = mPaddingLeft;
|
||||||
|
int y = mPaddingTop;
|
||||||
|
int position = 1;
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
View v = getChildAt(i);
|
||||||
|
v.layout(x,
|
||||||
|
y,
|
||||||
|
x + mItemWidth,
|
||||||
|
y + mItemHeight);
|
||||||
|
|
||||||
|
if (position == itemsPerColumn + (remainderItems > 0 ? 1 : 0)) {
|
||||||
|
// Break column
|
||||||
|
position = 1;
|
||||||
|
remainderItems--;
|
||||||
|
x += mItemWidth + mItemHorizontalMargin;
|
||||||
|
y = mPaddingTop;
|
||||||
|
} else {
|
||||||
|
position++;
|
||||||
|
y += mItemHeight + mItemVerticalMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
|
||||||
|
<orderEntry type="library" name="framework.jar" level="project" />
|
||||||
|
<orderEntry type="module" module-name="bridge" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Reference in New Issue
Block a user