Skip to content

Instantly share code, notes, and snippets.

@roman-mazur
Created December 3, 2012 13:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roman-mazur/4195038 to your computer and use it in GitHub Desktop.
Save roman-mazur/4195038 to your computer and use it in GitHub Desktop.
Code examples used at "Fine-tuning Android apps" talk: http://goo.gl/1fV7T
package com.example.testtest;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.Drawable;
public class AnimatedCrossDrawable extends Drawable {
static final int MAX_LEVEL = 100000;
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG), gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Path clipPath = new Path();
private Shader gradient;
{
paint.setColor(Color.BLUE);
}
private final FpsTracker fpsTracker = new FpsTracker("DrawableFps");
@Override
protected boolean onLevelChange(int level) {
super.onLevelChange(level);
invalidateSelf();
return true;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
gradient = new LinearGradient(bounds.left, bounds.top, bounds.right, bounds.top, Color.BLUE, Color.YELLOW, TileMode.CLAMP);
gradientPaint.setShader(gradient);
}
@Override
public void draw(final Canvas canvas) {
fpsTracker.trackFps();
Rect bounds = getBounds();
float part = (float)getLevel() / MAX_LEVEL;
float x = bounds.left + part * bounds.width();
float y = bounds.top + part * bounds.height();
canvas.drawLine(0f, 0f, x, y, paint);
canvas.drawLine(0f, bounds.bottom, x, bounds.bottom - y, paint);
float centerX = bounds.exactCenterX(), centerY = bounds.exactCenterY();
Path clipPath = this.clipPath;
clipPath.rewind();
clipPath.moveTo(0, 0);
clipPath.lineTo(Math.min(x, centerX), Math.min(y, centerY));
clipPath.lineTo(Math.min(x, centerX), Math.max(bounds.bottom - y, centerY));
clipPath.lineTo(0, bounds.bottom);
clipPath.lineTo(0, 0);
if (x > centerX) {
clipPath.moveTo(centerX, centerY);
clipPath.lineTo(x, bounds.bottom - y);
clipPath.lineTo(x, y);
clipPath.lineTo(centerX, centerY);
}
canvas.clipPath(clipPath);
canvas.drawRect(bounds, gradientPaint);
}
@Override
public void setAlpha(final int alpha) { }
@Override
public void setColorFilter(final ColorFilter cf) { }
@Override
public int getOpacity() { return 0; }
@Override
public int getIntrinsicHeight() { return 0; }
@Override
public int getIntrinsicWidth() { return 0; }
}
package com.example.testtest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
public class AnimationActivity extends Activity {
public static float line(float x1, float y1, float x2, float y2, float x) {
return (x - x1) / (x2 - x1) * (y2 - y1) + y1;
}
public static float interpolate(final float fraction) {
final float[] x = {0f, 0.2f, 0.4f, 0.6f, 0.8f, 1f};
final float[] y = {0f, 0.2f, 0.1f, 0.6f, 0.5f, 1f};
for (int i = 0; i < x.length; i++) {
if (fraction < x[i]) {
return line(x[i - 1], y[i - 1], x[i], y[i], fraction);
}
}
return fraction;
}
@SuppressWarnings("deprecation")
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final View view = new View(this);
final AnimatedCrossDrawable animDrawable = new AnimatedCrossDrawable();
view.setBackgroundDrawable(animDrawable);
setContentView(view);
new LevelAnimator(animDrawable) {
@Override
protected float interpolate(final float fraction) {
return AnimationActivity.interpolate(fraction);
}
}
.setDuration(4000)
.start(0, AnimatedCrossDrawable.MAX_LEVEL);
}
}
package com.example.testtest;
import android.animation.TimeInterpolator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class AnimationActivity2Views extends Activity {
@SuppressWarnings("deprecation")
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout layout = new FrameLayout(this);
layout.setBackgroundColor(Color.WHITE);
final View view = new View(this);
final AnimatedCrossDrawable animDrawable = new AnimatedCrossDrawable();
view.setBackgroundDrawable(animDrawable);
animDrawable.setLevel(AnimatedCrossDrawable.MAX_LEVEL);
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
layout.addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
View top = new View(this);
top.setBackgroundColor(Color.WHITE);
layout.addView(top, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
setContentView(layout);
//top.setLayerType(View.LAYER_TYPE_HARDWARE, null);
top.animate()
.translationX(getResources().getDisplayMetrics().widthPixels)
.setInterpolator(new TimeInterpolator() {
public float getInterpolation(float input) {
return AnimationActivity.interpolate(input);
}
})
.setDuration(4000);
// FPS...
final FpsTracker tracker = new FpsTracker("HWTracker");
top.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
public boolean onPreDraw() {
tracker.trackFps();
return true;
}
});
}
}
package com.example.testtest;
import android.app.ListActivity;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class BAD_FontsActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new TestFontsAdapter());
}
public class TestFontsAdapter extends BaseAdapter {
public int getCount() {
return Integer.MAX_VALUE;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView;
if (convertView == null) {
textView = new TextView(BAD_FontsActivity.this);
} else {
textView = (TextView) convertView;
}
textView.setText(String.valueOf(position));
textView.setTypeface(Typeface.createFromAsset(getAssets(), "Roboto-Light.ttf"));
return textView;
}
}
}
package com.example.testtest;
import android.os.SystemClock;
import android.util.Log;
public final class FpsTracker {
private long lastFpsTrackedTime, startTime, prevCallTime;
private int framesCount = 0, totalCount = 0;
private final String tag;
public FpsTracker(final String tag) {
this.tag = tag;
}
public void trackFps() {
long now = SystemClock.uptimeMillis();
totalCount++;
framesCount++;
if (lastFpsTrackedTime == 0) {
startTime = now;
lastFpsTrackedTime = now;
prevCallTime = now;
return;
}
long frameTime = now - prevCallTime;
prevCallTime = now;
Log.i(tag, "Frame time: " + frameTime);
long fpsTime = now - lastFpsTrackedTime;
if (fpsTime >= 1000) {
float avg = (float) totalCount * 1000 / (now - startTime);
float fps = (float) framesCount * 1000 / fpsTime;
Log.i(tag, "FPS = " + fps + ", avg FPS = " + avg);
framesCount = 0;
lastFpsTrackedTime = now;
}
}
}
package com.example.testtest;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
public class LevelAnimator extends Handler {
private static final int MSG_ANIMATE = 1;
private long startTime, duration;
private int begin, end;
private final Drawable drawable;
public LevelAnimator(final Drawable drawable) {
this.drawable = drawable;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ANIMATE:
final int curLevel = getLevelValue(interpolate(getFraction(SystemClock.uptimeMillis())));
drawable.setLevel(curLevel);
if (SystemClock.uptimeMillis() < startTime + duration) {
sendEmptyMessage(MSG_ANIMATE);
}
break;
default:
}
}
private float getFraction(final long curTime){
float passed = curTime - startTime;
final float fraction = passed /duration;
return Math.min(fraction, 1.0f);
}
protected float interpolate(final float fraction) {
return fraction;
}
private int getLevelValue(final float part) {
final int interval = end - begin;
return begin + (int) (part * interval);
}
public LevelAnimator setDuration(long duration) {
this.duration = duration;
return this;
}
public LevelAnimator start(final int begin, final int end){
startTime = SystemClock.uptimeMillis();
this.begin = begin;
this.end = end;
sendEmptyMessage(MSG_ANIMATE);
return this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment