自定义动画绘图的电池消耗
时间久了,终于抽出时间在 StackOverflow 上注册了!
经过很长时间的寻找在 Android 中实现 ViewGroup 的滚动背景的方法后,我开发了以下方法:
public class SlidingDrawable extends Drawable implements Drawable.Callback {
private static final String TAG = "SlidingDraw";
private static float STEP_SIZE = 1.0f;
private BitmapDrawable mBitmap;
private Context mContext;
private float mPosX;
private int mBitmapWidth;
private Runnable mInvalidater;
private Handler mHandler;
public SlidingDrawable(Context c){
mContext = c;
// use this as the callback as we're implementing the interface
setCallback(this);
mHandler = new Handler();
mInvalidater = new Runnable(){
@Override
public void run(){
// decrement the drawables step size
mPosX -= SlidingDrawable.STEP_SIZE;
/*
* Check to see if the current position is at point where it should
* loop. If so, reset back to 0 to restart
*/
if(Math.abs(mPosX) >= mBitmapWidth) mPosX = 0;
// redraw
invalidateDrawable(null);
}
};
}
public static void setStepSize(float newSize){
SlidingDrawable.STEP_SIZE = newSize;
}
public void createBitmap(String path, ViewGroup parent){
// height of the parent container
int height = parent.getHeight();
/* Initialize local variables
* bgBitmap - the resulting bitmap to send into SlidingDrawable instance
* imageStream - raw bitmap data to be decoded into bgBitmap
*/
WindowManager wMgr = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
int mScreenWidth = wMgr.getDefaultDisplay().getWidth();
InputStream imageStream;
Matrix imgMatrix = new Matrix();
Bitmap bitmap = null;
try {
imageStream = mContext.getAssets().open(path);
// create a temporary bitmap object for basic data
Bitmap temp = BitmapFactory.decodeStream(imageStream);
int width = temp.getWidth();
// find the width difference as a percentage to apply to the
// transformation matrix
float widthDifference = ((float)mScreenWidth) / (float)(width / 2);
imgMatrix.postScale(widthDifference, 0, 0f, 0f);
// create a copy of the bitmap, scaled correctly to maintain loop
bitmap = Bitmap.createScaledBitmap(temp, (int)(width * widthDifference), height, true);
// recycle the temp bitmap
temp.recycle();
} catch (IOException e) {
e.printStackTrace();
}
mBitmap = new BitmapDrawable(bitmap);
// required
mBitmapWidth = getIntrinsicWidth() / 2;
Rect bounds = new Rect(0, 0, getIntrinsicWidth(), getIntrinsicHeight());
setBounds(bounds);
}
@Override
public void draw(Canvas canvas) {
canvas.drawBitmap(mBitmap.getBitmap(), mPosX, 0f, null);
scheduleDrawable(this, mInvalidater, SystemClock.uptimeMillis());
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
mHandler.postAtTime(what, who, when);
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
mHandler.removeCallbacks(what, who);
}
@Override
public void invalidateDrawable(Drawable who) {
invalidateSelf();
}
/*
* Methods not directly used or called omitted
*
*/
}
它在 Activity 中使用如下:
@Override
public void onWindowFocusChanged(boolean focus){
// set the background of the root view of main.xml
SlidingDrawable drawable = new SlidingDrawable(getApplicationContext());
drawable.createBitmap("bgimg/basebg.jpg", mRoot);
mRoot.setBackgroundDrawable(drawable);
}
长话短说,basebg.jpg 图像是一个可平铺的图像大约 1600x480。 SlidingDrawable 的构造函数可以缩放和移动,yaddah yaddah。有用。
现在的问题是,这样做似乎效率很低。我似乎找不到关于这种实现的太多信息,所以我对在哪里可以减少 CPU 周期一无所知,或者即使我正确使用方法调用也是如此。
我的问题包括:
- 与使用 setTranslate() 或 postTranslate 并使用矩阵绘制位图相比,绘制位图是否更好?
- 是使用drawBitmap更好,还是使用translate()、save()和restore()等canvas函数更好?
- 调用draw()方法的速率是多少?有没有办法将其限制为24 FPS或限制重绘?
- 这类事情的“何时”参数到底是什么?传入 SystemClock.uptimeMillis() 是唯一有效的方法,尝试通过添加“+ 100”或每 100 毫秒触发一次的东西来延迟它只会让它变得卡顿。
我已经尽可能多地研究了这个问题......我现在把它留给 StackOverflow :)
Loooong time viewer, finally getting round to signing up here at StackOverflow!
After a very long time searching for a way to do a scrolling background of a ViewGroup in Android, I've developed the following:
public class SlidingDrawable extends Drawable implements Drawable.Callback {
private static final String TAG = "SlidingDraw";
private static float STEP_SIZE = 1.0f;
private BitmapDrawable mBitmap;
private Context mContext;
private float mPosX;
private int mBitmapWidth;
private Runnable mInvalidater;
private Handler mHandler;
public SlidingDrawable(Context c){
mContext = c;
// use this as the callback as we're implementing the interface
setCallback(this);
mHandler = new Handler();
mInvalidater = new Runnable(){
@Override
public void run(){
// decrement the drawables step size
mPosX -= SlidingDrawable.STEP_SIZE;
/*
* Check to see if the current position is at point where it should
* loop. If so, reset back to 0 to restart
*/
if(Math.abs(mPosX) >= mBitmapWidth) mPosX = 0;
// redraw
invalidateDrawable(null);
}
};
}
public static void setStepSize(float newSize){
SlidingDrawable.STEP_SIZE = newSize;
}
public void createBitmap(String path, ViewGroup parent){
// height of the parent container
int height = parent.getHeight();
/* Initialize local variables
* bgBitmap - the resulting bitmap to send into SlidingDrawable instance
* imageStream - raw bitmap data to be decoded into bgBitmap
*/
WindowManager wMgr = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
int mScreenWidth = wMgr.getDefaultDisplay().getWidth();
InputStream imageStream;
Matrix imgMatrix = new Matrix();
Bitmap bitmap = null;
try {
imageStream = mContext.getAssets().open(path);
// create a temporary bitmap object for basic data
Bitmap temp = BitmapFactory.decodeStream(imageStream);
int width = temp.getWidth();
// find the width difference as a percentage to apply to the
// transformation matrix
float widthDifference = ((float)mScreenWidth) / (float)(width / 2);
imgMatrix.postScale(widthDifference, 0, 0f, 0f);
// create a copy of the bitmap, scaled correctly to maintain loop
bitmap = Bitmap.createScaledBitmap(temp, (int)(width * widthDifference), height, true);
// recycle the temp bitmap
temp.recycle();
} catch (IOException e) {
e.printStackTrace();
}
mBitmap = new BitmapDrawable(bitmap);
// required
mBitmapWidth = getIntrinsicWidth() / 2;
Rect bounds = new Rect(0, 0, getIntrinsicWidth(), getIntrinsicHeight());
setBounds(bounds);
}
@Override
public void draw(Canvas canvas) {
canvas.drawBitmap(mBitmap.getBitmap(), mPosX, 0f, null);
scheduleDrawable(this, mInvalidater, SystemClock.uptimeMillis());
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
mHandler.postAtTime(what, who, when);
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
mHandler.removeCallbacks(what, who);
}
@Override
public void invalidateDrawable(Drawable who) {
invalidateSelf();
}
/*
* Methods not directly used or called omitted
*
*/
}
It is used in the Activity like so:
@Override
public void onWindowFocusChanged(boolean focus){
// set the background of the root view of main.xml
SlidingDrawable drawable = new SlidingDrawable(getApplicationContext());
drawable.createBitmap("bgimg/basebg.jpg", mRoot);
mRoot.setBackgroundDrawable(drawable);
}
Long story short, the basebg.jpg image is a tileable image roughly 1600x480. The constructor for SlidingDrawable scales and moves and yaddah yaddah. It works.
Now, the problem is, it seems really inefficient to do it like this. I can't seem to find much information on this sort of implementation, so I'm in the dark on where I can cut CPU cycles, or even if I'm using the method calls correctly.
My questions include:
- Is it better to drawBitmap as opposed to using setTranslate() or postTranslate and draw the bitmap using a Matrix?
- Is it better to use drawBitmap, or the canvas functions such as translate(), save(), and restore()?
- What rate does the draw() method get called at, and is there a way to limit it to, say, 24 FPS o limit redraws?
- What the heck is the "when" parameter of these sorts of things? Passing in SystemClock.uptimeMillis() is the only one that worked, and trying to delay it by adding a " + 100" or something to fire every 100ms just made it stutter.
I've researched this as much as I can... I'm leaving it to StackOverflow now :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
经过一段时间的绘图板的使用,我简化了功能。本质上,它在每个
SystemClock.uptimeMillis()
上发送一个invalidate()
调用,为每个步长更改执行一次重绘。因此,我删除了Drawable.Callback接口并直接从Handler中传递了invalidateSelf()调用,删除了中间接口方法,这似乎没有做任何特别的事情反正。
CPU 使用率略有不同
drawBitmap(Bitmap source, int X, int Y, Paint p)
与drawBitmap(Bitmap source, Matrix matrix, Paint p)
,所以我选择后者来节省周期。新方法如下:
我通过拔掉手机,运行应用程序大约一分钟,然后在 Moto Droid 上打开电池使用情况来测试电池结果。以前,电池使用量超过了显示屏,现在它轻松地位于显示屏下方。
《愤怒的小鸟》也是一个基准,通过运行打开屏幕(背景滚动和小鸟到处飞)相同的时间,在某些情况下,我的应用程序位于《愤怒的小鸟》之下,但并非总是如此。
使用 ADB shell 命令
dumpsys cpuinfo
检查 CPU 使用情况,因为似乎存在问题 在运行 2.2 的设备上通过 DDMS 查看 CPU 信息。我仍然想听听对此的其他想法,但现在,它已经解决了。
After some time with the drawing board, I simplified the functions down. Essentially, it was sending an
invalidate()
call on everySystemClock.uptimeMillis()
, doing one redraw for each step change.So, I removed the
Drawable.Callback
interface and passed theinvalidateSelf()
call directly from the Handler, removing the intermediary interface methods, which didn't seem to do anything special anyway.There was a slight difference in the CPU usage using
drawBitmap(Bitmap source, int X, int Y, Paint p)
vs.drawBitmap(Bitmap source, Matrix matrix, Paint p)
, so I opted for the latter to save cycles.New methods are as follows:
I tested the battery results by unplugging the phone, running the application for around a minute, then opening the battery usage on my Moto Droid. Before, the battery usage surpassed the Display, now it sits comfortably below.
Angry Birds was also a benchmark, by running the opening screen (where the bg scrolls and the birds fly everywhere) for the same amount of time, in some cases my app sat below Angry Birds, but not always.
CPU usage was checked using the ADB shell command
dumpsys cpuinfo
as there seems to be a problem viewing CPU info on through the DDMS on devices running 2.2.I'd still be up to hear other thoughts on this, but for now, it's solved.