Skip to content

Instantly share code, notes, and snippets.

@lucasr
Created October 9, 2014 20:39
Show Gist options
  • Save lucasr/45eaf48a7485dfe7494b to your computer and use it in GitHub Desktop.
Save lucasr/45eaf48a7485dfe7494b to your computer and use it in GitHub Desktop.
Lazy measurement with Probe (Experimental)
/*
* Copyright (C) 2014 Lucas Rocha
*
* 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 org.lucasr.probe.interceptors;
import android.util.Log;
import android.util.LongSparseArray;
import android.view.View;
import org.lucasr.probe.Interceptor;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.KITKAT;
public class LazyMeasurementInterceptor extends Interceptor {
private static final String LOGTAG = "LazyMeasurement";
private static final boolean POST_KITKAT = (SDK_INT >= KITKAT);
private static class MeasureState {
final LongSparseArray<Long> cache;
boolean needsOnMeasureBeforeLayout;
int lastWidthMeasureSpec;
int lastHeightMeasureSpec;
public MeasureState() {
cache = new LongSparseArray<Long>(2);
}
void clear() {
cache.clear();
needsOnMeasureBeforeLayout = false;
lastWidthMeasureSpec = 0;
lastHeightMeasureSpec = 0;
}
}
private void clearMeasureState(View view) {
final MeasureState measureState = (MeasureState) view.getTag(R.id.measure_state);
if (measureState != null) {
measureState.clear();
}
}
@Override
public void onMeasure(View view, int widthMeasureSpec, int heightMeasureSpec) {
if (POST_KITKAT) {
Log.d(LOGTAG, "Post-KitKat, onMeasure() on " + view.getTag());
super.onMeasure(view, widthMeasureSpec, heightMeasureSpec);
return;
}
MeasureState measureState = (MeasureState) view.getTag(R.id.measure_state);
if (measureState== null) {
measureState = new MeasureState();
view.setTag(R.id.measure_state, measureState);
}
final long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
final int cacheIndex = measureState.cache.indexOfKey(key);
if (cacheIndex < 0) {
Log.d(LOGTAG, "Pre-KitKat, onMeasure() on " + view.getTag());
super.onMeasure(view, widthMeasureSpec, heightMeasureSpec);
measureState.needsOnMeasureBeforeLayout = false;
} else {
final long value = measureState.cache.valueAt(cacheIndex);
setMeasuredDimension(view, (int) (value >> 32), (int) value);
measureState.needsOnMeasureBeforeLayout = true;
}
measureState.lastWidthMeasureSpec = widthMeasureSpec;
measureState.lastHeightMeasureSpec = heightMeasureSpec;
measureState.cache.put(key, ((long) view.getMeasuredWidth()) << 32 |
(long) view.getMeasuredHeight() & 0xffffffffL);
}
@Override
public void onLayout(View view, boolean changed, int l, int t, int r, int b) {
if (POST_KITKAT) {
super.onLayout(view, changed, l, t, r, b);
return;
}
final MeasureState measureState = (MeasureState) view.getTag(R.id.measure_state);
if (measureState.needsOnMeasureBeforeLayout) {
invokeOnMeasure(view, measureState.lastWidthMeasureSpec,
measureState.lastHeightMeasureSpec);
measureState.needsOnMeasureBeforeLayout = false;
}
super.onLayout(view, changed, l, t, r, b);
}
@Override
public void requestLayout(View view) {
if (!POST_KITKAT) {
clearMeasureState(view);
}
super.requestLayout(view);
}
@Override
public void forceLayout(View view) {
if (!POST_KITKAT) {
clearMeasureState(view);
}
super.forceLayout(view);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment