下面列出了怎么用android.view.ViewDebug的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* Returns the view hierarchy and/or view properties starting at the provided view.
* Based on the input options, the return data may include:
* - just the view hierarchy
* - view hierarchy & the properties for each of the views
* - just the view properties for a specific view.
* TODO: Currently this only returns views starting at the root, need to fix so that
* it can return properties of any view.
*/
private Chunk dumpHierarchy(View rootView, ByteBuffer in) {
boolean skipChildren = in.getInt() > 0;
boolean includeProperties = in.getInt() > 0;
boolean v2 = in.hasRemaining() && in.getInt() > 0;
long start = System.currentTimeMillis();
ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024);
try {
if (v2) {
ViewDebug.dumpv2(rootView, b);
} else {
ViewDebug.dump(rootView, skipChildren, includeProperties, b);
}
} catch (IOException | InterruptedException e) {
return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+ e.getMessage());
}
long end = System.currentTimeMillis();
Log.d(TAG, "Time to obtain view hierarchy (ms): " + (end - start));
byte[] data = b.toByteArray();
return new Chunk(CHUNK_VURT, data, 0, data.length);
}
private void getStyleFromInteger(
String name,
Integer value,
@Nullable ViewDebug.ExportedProperty annotation,
StyleAccumulator styles) {
String intValueStr = IntegerFormatter.getInstance().format(value, annotation);
if (canIntBeMappedToString(annotation)) {
styles.store(
name,
intValueStr + " (" + mapIntToStringUsingAnnotation(value, annotation) + ")",
false);
} else if (canFlagsBeMappedToString(annotation)) {
styles.store(
name,
intValueStr + " (" + mapFlagsToStringUsingAnnotation(value, annotation) + ")",
false);
} else {
styles.store(name, intValueStr, isDefaultValue(value, annotation));
}
}
/**
* Returns the Theme dump of the provided view.
*/
private Chunk dumpTheme(View rootView) {
ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
try {
ViewDebug.dumpTheme(rootView, b);
} catch (IOException e) {
return createFailChunk(1, "Unexpected error while dumping the theme: "
+ e.getMessage());
}
byte[] data = b.toByteArray();
return new Chunk(CHUNK_VURT, data, 0, data.length);
}
private Chunk captureView(View rootView, View targetView) {
ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
try {
ViewDebug.capture(rootView, b, targetView);
} catch (IOException e) {
return createFailChunk(1, "Unexpected error while capturing view: "
+ e.getMessage());
}
byte[] data = b.toByteArray();
return new Chunk(CHUNK_VUOP, data, 0, data.length);
}
@Override
@ViewDebug.ExportedProperty
public View getSelectedView() {
if ( mItemCount > 0 && mSelectedPosition >= 0 ) {
return getChildAt( mSelectedPosition - mFirstPosition );
} else {
return null;
}
}
/**
* Obtain the view and add it to our list of children. The view can be made
* fresh, converted from an unused view, or used as is if it was in the
* recycle bin.
*
* @param position Logical position in the list
* @param childrenBottomOrTop Top or bottom edge of the view to add
* @param flow If flow is true, align top edge to y. If false, align bottom
* edge to y.
* @param childrenLeft Left edge where children should be positioned
* @param selected Is this position selected?
* @return View that was added
*/
@SuppressWarnings("deprecation")
private View makeAndAddView(int position, int childrenBottomOrTop, boolean flow,
boolean selected) {
View child;
int childrenLeft;
if (!mDataChanged) {
// Try to use an exsiting view for this position
child = mRecycler.getActiveView(position);
if (child != null) {
if (ViewDebug.TRACE_RECYCLER) {
ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,
position, getChildCount());
}
// Found it -- we're using an existing child
// This just needs to be positioned
childrenLeft = getItemLeft(position);
setupChild(child, position, childrenBottomOrTop, flow, childrenLeft, selected, true);
return child;
}
}
//Notify new item is added to view.
onItemAddedToList( position, flow );
childrenLeft = getItemLeft( position );
// Make a new view for this position, or convert an unused view if possible
child = obtainView(position, mIsScrap);
// This needs to be positioned and measured
setupChild(child, position, childrenBottomOrTop, flow, childrenLeft, selected, mIsScrap[0]);
return child;
}
/**
* Obtain the view and add it to our list of children. The view can be made
* fresh, converted from an unused view, or used as is if it was in the
* recycle bin.
*
* @param position Logical position in the list
* @param childrenBottomOrTop Top or bottom edge of the view to add
* @param flow If flow is true, align top edge to y. If false, align bottom
* edge to y.
* @param selected Is this position selected?
* @return View that was added
*/
@SuppressWarnings("deprecation")
private View makeAndAddView(int position, int childrenBottomOrTop, boolean flow,
boolean selected) {
View child;
int childrenLeft;
if (!mDataChanged) {
// Try to use an exsiting view for this position
child = mRecycler.getActiveView(position);
if (child != null) {
if (ViewDebug.TRACE_RECYCLER) {
ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,
position, getChildCount());
}
// Found it -- we're using an existing child
// This just needs to be positioned
childrenLeft = getItemLeft(position);
setupChild(child, position, childrenBottomOrTop, flow, childrenLeft, selected, true);
return child;
}
}
//Notify new item is added to view.
onItemAddedToList( position, flow );
childrenLeft = getItemLeft( position );
// Make a new view for this position, or convert an unused view if possible
child = obtainView(position, mIsScrap);
// This needs to be positioned and measured
setupChild(child, position, childrenBottomOrTop, flow, childrenLeft, selected, mIsScrap[0]);
return child;
}
private static String mapFlagsToStringUsingAnnotation(
int value,
@Nullable ViewDebug.ExportedProperty annotation) {
if (!canFlagsBeMappedToString(annotation)) {
throw new IllegalStateException("Cannot map using this annotation");
}
StringBuilder stringBuilder = null;
boolean atLeastOneFlag = false;
for (ViewDebug.FlagToString flagToString : annotation.flagMapping()) {
if (flagToString.outputIf() == ((value & flagToString.mask()) == flagToString.equals())) {
if (stringBuilder == null) {
stringBuilder = new StringBuilder();
}
if (atLeastOneFlag) {
stringBuilder.append(" | ");
}
stringBuilder.append(flagToString.name());
atLeastOneFlag = true;
}
}
if (atLeastOneFlag) {
return stringBuilder.toString();
} else {
return NONE_MAPPING;
}
}
private void getStyleFromValue(
View element,
String name,
Object value,
@Nullable ViewDebug.ExportedProperty annotation,
StyleAccumulator styles) {
if (name.equals(ID_NAME)) {
getIdStyle(element, styles);
} else if (value instanceof Integer) {
getStyleFromInteger(name, (Integer) value, annotation, styles);
} else if (value instanceof Float) {
styles.store(name, String.valueOf(value), ((Float) value) == 0.0f);
} else if (value instanceof Boolean) {
styles.store(name, String.valueOf(value), false);
} else if (value instanceof Short) {
styles.store(name, String.valueOf(value), ((Short) value) == 0);
} else if (value instanceof Long) {
styles.store(name, String.valueOf(value), ((Long) value) == 0);
} else if (value instanceof Double) {
styles.store(name, String.valueOf(value), ((Double) value) == 0.0d);
} else if (value instanceof Byte) {
styles.store(name, String.valueOf(value), ((Byte) value) == 0);
} else if (value instanceof Character) {
styles.store(name, String.valueOf(value), ((Character) value) == Character.MIN_VALUE);
} else if (value instanceof CharSequence) {
styles.store(name, String.valueOf(value), ((CharSequence) value).length() == 0);
} else {
getStylesFromObject(element, name, value, annotation, styles);
}
}
private void getStyleFromInteger(
String name,
Integer value,
@Nullable ViewDebug.ExportedProperty annotation,
StyleAccumulator styles) {
String intValueStr = IntegerFormatter.getInstance().format(value, annotation);
if (canIntBeMappedToString(annotation)) {
styles.store(
name,
intValueStr + " (" + mapIntToStringUsingAnnotation(value, annotation) + ")",
false);
} else if (canFlagsBeMappedToString(annotation)) {
styles.store(
name,
intValueStr + " (" + mapFlagsToStringUsingAnnotation(value, annotation) + ")",
false);
} else {
Boolean defaultValue = true;
// Mappable ints should always be shown, because enums don't necessarily have
// logical "default" values. Thus we mark all of them as not default, so that they
// show up in the inspector.
if (value != 0 ||
canFlagsBeMappedToString(annotation) ||
canIntBeMappedToString(annotation)) {
defaultValue = false;
}
styles.store(name, intValueStr, defaultValue);
}
}
public MethodBackedCSSProperty(
Method method,
String cssName,
@Nullable ViewDebug.ExportedProperty annotation) {
super(cssName, annotation);
mMethod = method;
mMethod.setAccessible(true);
}
private void getStyleFromInteger(
String name,
Integer value,
@Nullable ViewDebug.ExportedProperty annotation,
StyleAccumulator styles) {
String intValueStr = IntegerFormatter.getInstance().format(value, annotation);
if (canIntBeMappedToString(annotation)) {
styles.store(
name,
intValueStr + " (" + mapIntToStringUsingAnnotation(value, annotation) + ")",
false);
} else if (canFlagsBeMappedToString(annotation)) {
styles.store(
name,
intValueStr + " (" + mapFlagsToStringUsingAnnotation(value, annotation) + ")",
false);
} else {
Boolean defaultValue = true;
// Mappable ints should always be shown, because enums don't necessarily have
// logical "default" values. Thus we mark all of them as not default, so that they
// show up in the inspector.
if (value != 0 ||
canFlagsBeMappedToString(annotation) ||
canIntBeMappedToString(annotation)) {
defaultValue = false;
}
styles.store(name, intValueStr, defaultValue);
}
}
@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public String format(Integer integer, @Nullable ViewDebug.ExportedProperty annotation) {
if (annotation != null && annotation.formatToHexString()) {
return "0x" + Integer.toHexString(integer);
}
return super.format(integer, annotation);
}
private static String mapFlagsToStringUsingAnnotation(
int value,
@Nullable ViewDebug.ExportedProperty annotation) {
if (!canFlagsBeMappedToString(annotation)) {
throw new IllegalStateException("Cannot map using this annotation");
}
StringBuilder stringBuilder = null;
boolean atLeastOneFlag = false;
for (ViewDebug.FlagToString flagToString : annotation.flagMapping()) {
if (flagToString.outputIf() == ((value & flagToString.mask()) == flagToString.equals())) {
if (stringBuilder == null) {
stringBuilder = new StringBuilder();
}
if (atLeastOneFlag) {
stringBuilder.append(" | ");
}
stringBuilder.append(flagToString.name());
atLeastOneFlag = true;
}
}
if (atLeastOneFlag) {
return stringBuilder.toString();
} else {
return NONE_MAPPING;
}
}
public MethodBackedCSSProperty(
Method method,
String cssName,
@Nullable ViewDebug.ExportedProperty annotation) {
super(cssName, annotation);
mMethod = method;
mMethod.setAccessible(true);
}
@ViewDebug.ExportedProperty
public boolean isChecked() {
return checked;
}
/**
* @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}
* if nothing is selected.
*/
@ViewDebug.CapturedViewProperty
public long getSelectedItemId() {
return INVALID_ROW_ID;
}
/**
* @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}
* if nothing is selected.
*/
@ViewDebug.CapturedViewProperty
public long getSelectedItemId() {
return mNextSelectedRowId;
}
@ViewDebug.CapturedViewProperty
public int getSelectedItemPosition() {
return this.mNextSelectedPosition;
}
@ViewDebug.CapturedViewProperty
public CharSequence getTitle() {
return mTitle;
}
/**
* Move all views remaining in mActiveViews to mScrapViews.
*/
@SuppressWarnings("deprecation")
void scrapActiveViews() {
final View[] activeViews = mActiveViews;
final boolean hasListener = mRecyclerListener != null;
final boolean multipleScraps = mViewTypeCount > 1;
ArrayList<View> scrapViews = mCurrentScrap;
final int count = activeViews.length;
for (int i = count - 1; i >= 0; i--) {
final View victim = activeViews[i];
if (victim != null) {
int whichScrap = ((PLA_AbsListView.LayoutParams) victim.getLayoutParams()).viewType;
activeViews[i] = null;
if (!shouldRecycleViewType(whichScrap)) {
// Do not move views that should be ignored
if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
removeDetachedView(victim, false);
}
continue;
}
if (multipleScraps) {
scrapViews = mScrapViews[whichScrap];
}
//victim.dispatchStartTemporaryDetach();
dispatchFinishTemporaryDetach(victim);
scrapViews.add(victim);
if (hasListener) {
mRecyclerListener.onMovedToScrapHeap(victim);
}
if (ViewDebug.TRACE_RECYCLER) {
ViewDebug.trace(victim,
ViewDebug.RecyclerTraceType.MOVE_FROM_ACTIVE_TO_SCRAP_HEAP,
mFirstActivePosition + i, -1);
}
}
}
pruneScrapViews();
}
/**
* @return The number of items owned by the Adapter associated with this AdapterView. (This is the number of data items, which
* may be larger than the number of visible views.)
*/
@ViewDebug.CapturedViewProperty
public int getCount() {
return mItemCount;
}
@ViewDebug.ExportedProperty
public boolean isIndeterminate() {
return mIndeterminate;
}
@ViewDebug.ExportedProperty
public boolean isChecked() {
return mChecked;
}
@ViewDebug.ExportedProperty(category = "drawing")
public int getCacheColorHint() {
return mCacheColorHint;
}
/**
* @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}
* if nothing is selected.
*/
@ViewDebug.CapturedViewProperty
public long getSelectedItemId() {
return mNextSelectedRowId;
}
/**
* @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}
* if nothing is selected.
*/
@ViewDebug.CapturedViewProperty
public long getSelectedItemId() {
return INVALID_ROW_ID;
}
@ViewDebug.CapturedViewProperty
public int getItemId() {
return mId;
}
@Override
@ViewDebug.ExportedProperty
public boolean isChecked() {
return this.mChecked;
}
public ViewCSSProperty(String cssName, @Nullable ViewDebug.ExportedProperty annotation) {
mCSSName = cssName;
mAnnotation = annotation;
}