* commit '5c597c1d89c529675096c2d326c452145403979d': 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>
|
||||
<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$/studio-custom-widgets/studio-android-widgets.iml" filepath="$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" />
|
||||
</modules>
|
||||
</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