您的位置:首页 > 移动开发

android AppWidget中添加自定义View

2017-07-24 19:54 471 查看
最近接了个任务,在小部件(AppWidget)中播放动画。android中小部件是使用RemoteViews加载布局。但RemoteViews支持的View很少,并且不支持自定义View。

如果让RemoteViews支持自定义View,需要在将该View放到framewoke下编译。现记录实现过程如下:

1.自定义View,将class文件放到\frameworks\base\core\java\android\widget/目录下,并在代码第一行添加@RemoteViews.RemoteView标签。代码如下:

自定义的RobotAnimView是一个动画类,主要增加播放动画方法。

package android.widget;

import android.R;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.RemoteViews;

@RemoteViews.RemoteView
public class RobotAnimView extends View {

private WaveRobotAnimator mAnimator;

public RobotAnimView(Context context) {
super(context);
init();
}

public RobotAnimView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public RobotAnimView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

public void startRobotAnimator(boolean start) {
if (start) {
startRobotAnimator();
} else {
endRobotAnimator();
}
}

public void startRobotAnimator() {
init();
mAnimator.start();
}

public void endRobotAnimator() {
if (mAnimator != null) {
if (mAnimator.isRunning()) {
mAnimator.end();
}
mAnimator = null;
}

}

private void init() {
endRobotAnimator();
mAnimator = new WaveRobotAnimator(getContext(), false);
this.setBackground(mAnimator.getDrawable());
}

private class WaveRobotAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {

private RobotDrawable mDrawable;

WaveRobotAnimator(Context context, boolean left) {
this.setDuration(1700);
this.setIntValues(0, 1700);
this.setInterpolator(new LinearInterpolator());
mDrawable = new RobotDrawable(context, left);
addUpdateListener(this);
}

@Override
public void onAnimationUpdate(ValueAnimator animation) {
mDrawable.update((int) (animation.getAnimatedValue()));
mDrawable.invalidateSelf();
}

Drawable getDrawable() {
return mDrawable;
}

private class RobotDrawable extends Drawable {

private int mTime;
private Body mBody;
private EyeLeft mEyeLeft;
private EyeRight mEyeRight;
private HandLeft mHandLeft;
private HandRight mHandRight;
private Head mHead;
private Mouth mMouth;
private int mWidth;
private int mHeight;
private boolean mIsLeft;

RobotDrawable(Context context, boolean left) {
mIsLeft = left;
mBody = new Body(context);
mEyeLeft = new EyeLeft(context);
mEyeRight = new EyeRight(context);
mHandLeft = new HandLeft(context);
mHandRight = new HandRight(context);
mHead = new Head(context);
mMouth = new Mouth(context);

}

@Override
public void setAlpha(int alpha) {

}

private void update(int time) {
mTime = time;
}

@Override
public void draw(Canvas canvas) {
if (mWidth == 0 || mHeight == 0) {
mWidth = canvas.getWidth();
mHeight = canvas.getHeight();
}
canvas.save();
if (mIsLeft) {
canvas.scale(-1, 1, canvas.getWidth() / 2f, canvas.getHeight() / 2f);
}
canvas.translate(mWidth / 2f - 126 / 2, mHeight / 2f - 164 / 2);
float bodyProgress = mBody.getProgress(mTime);
canvas.rotate(bodyProgress, 63, 164);

canvas.save();
float headProgress = mHead.getProgress(mTime);
canvas.rotate(headProgress, 63, 103);

mHead.draw(canvas);
mEyeLeft.draw(canvas, mTime);
mEyeRight.draw(canvas, mTime);
mMouth.draw(canvas);
canvas.restore();

mBody.draw(canvas);
mHandLeft.draw(canvas, mTime);
mHandRight.draw(canvas, mTime);
canvas.restore();
}

@Override
public void setColorFilter(ColorFilter colorFilter) {

}

@Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
}

private class Body extends Basic {
private final Interpolator[] mInterpolator = {
new EaseCubicInterpolator(0.48f, 0.06f, 0.52f, 0.94f),
new EaseCubicInterpolator(0.47f, 0.00f, 0.77f, 0.63f),
new EaseCubicInterpolator(0.34f, -0.68f, 0.55f, -1.14f),
new EaseCubicInterpolator(0.48f, 0.00f, 0.52f, 1.00f)
};
private final float[] mProgress = {0f, 18f, 17.7f, 18.0f};

Body(Context context) {
mBitmap = BitmapFactory.decodeResource(context.getResources(), com.android.internal.R.drawable.anim_robot_body);
mBitmap = Bitmap.createScaledBitmap(mBitmap, 57, 59, true);
}

private void draw(Canvas canvas) {
mMatrix.setTranslate(34, 103);
canvas.drawBitmap(mBitmap, mMatrix, null);
}

float getProgress(int time) {
return getProgress(time, mTimes, mProgress, mInterpolator);
}
}

private class EyeLeft extends Eye {

EyeLeft(Context context) {
mTransX = 28;
mTransY = 57;
mBitmap = BitmapFactory.decodeResource(context.getResources(), com.android.internal.R.drawable.anim_robot_eye_left);
mBitmap = Bitmap.createScaledBitmap(mBitmap, 18, 22, true);
}
}

private class EyeRight extends Eye {

EyeRight(Context context) {
mTransX = 80;
mTransY = 57;
mBitmap = BitmapFactory.decodeResource(context.getResources(), com.android.internal.R.drawable.anim_robot_eye_right);
mBitmap = Bitmap.createScaledBitmap(mBitmap, 18, 22, true);
}
}

class Eye extends Basic {
final float[] mTimes = {50f, 200f};
final float[] mProgress = {1f, 0.15f, 1f};
final Interpolator[] mInterpolator = {
new EaseCubicInterpolator(0.33f, 0.00f, 0.67f, 1.00f),
new EaseCubicInterpolator(0.33f, 0.00f, 0.67f, 1.00f)
};
int mTransX;
int mTransY;

void draw(Canvas canvas, int time) {
mMatrix.setTranslate(mTransX, mTransY);

if (time >= 1000 && time <= 1250) {
float scale = getProgress(time - 1000, mTimes, mProgress, mInterpolator);
mMatrix.postScale(1, scale, mTransX + mBitmap.getWidth() / 2f, mTransY + mBitmap.getHeight() / 2f);
}
canvas.drawBitmap(mBitmap, mMatrix, null);
}
}

private class HandLeft extends Basic {
private final float[] mProgress = {0f, 116f, 80f, 116f};
private final Interpolator[] mInterpolator = {
new EaseCubicInterpolator(0.44f, 0.20f, 0.71f, 1.00f),
new EaseCubicInterpolator(0.48f, 0.00f, 0.44f, 1.00f),
new EaseCubicInterpolator(0.53f, 0.00f, 0.61f, 1.00f),
new EaseCubicInterpolator(0.17f, 0.00f, 0.52f, 1.00f)
};

HandLeft(Context context) {
mBitmap = BitmapFactory.decodeResource(context.getResources(), com.android.internal.R.drawable.anim_robot_hand_left);
mBitmap = Bitmap.createScaledBitmap(mBitmap, 19, 27, true);
}

private void draw(Canvas canvas, int time) {
float progress = getProgress(time, mTimes, mProgress, mInterpolator);
mMatrix.setTranslate(16, 116);
mMatrix.postRotate(progress, 37, 114);
canvas.drawBitmap(mBitmap, mMatrix, null);
}
}

private class HandRight extends Basic {
private final float[] mProgress = {0f, -25f, -24.6f, -25f};
private final Interpolator[] mInterpolator = {
new EaseCubicInterpolator(0.48f, 0.06f, 0.52f, 0.94f),
new EaseCubicInterpolator(0.47f, 0.00f, 0.77f, 0.63f),
new EaseCubicInterpolator(0.34f, -0.68f, 0.55f, -1.14f),
new EaseCubicInterpolator(0.48f, 0.00f, 0.52f, 1.00f)
};

HandRight(Context context) {
mBitmap = BitmapFactory.decodeResource(context.getResources(), com.android.internal.R.drawable.anim_robot_hand_right);
mBitmap = Bitmap.createScaledBitmap(mBitmap, 19, 27, true);
}

private void draw(Canvas canvas, int time) {
float progress = getProgress(time, mTimes, mProgress, mInterpolator);
mMatrix.setTranslate(90, 116);
mMatrix.postRotate(progress, 88, 114);
canvas.drawBitmap(mBitmap, mMatrix, null);
}
}

private class Head extends Basic {
private final float[] mProgress = {0f, 18f, 17.7f, 18f};
private final Interpolator[] mInterpolator = {
new EaseCubicInterpolator(0.48f, 0.60f, 0.52f, 0.94f),
new EaseCubicInterpolator(0.47f, 0.00f, 0.77f, 0.63f),
new EaseCubicInterpolator(0.34f, -0.68f, 0.55f, -1.14f),
new EaseCubicInterpolator(0.48f, 0.00f, 0.52f, 1.00f)
};

Head(Context context) {
mBitmap = BitmapFactory.decodeResource(context.getResources(), com.android.internal.R.drawable.anim_robot_head);
mBitmap = Bitmap.createScaledBitmap(mBitmap, 126, 103, true);
}

private void draw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, null);
}

float getProgress(int time) {
return getProgress(time, mTimes, mProgress, mInterpolator);
}

}

private class Mouth extends Basic {

Mouth(Context context) {
mBitmap = BitmapFactory.decodeResource(context.getResources(), com.android.internal.R.drawable.anim_robot_mouth);
mBitmap = Bitmap.createScaledBitmap(mBitmap, 12, 8, true);
}

private void draw(Canvas canvas) {
mMatrix.setTranslate(57, 81);
canvas.drawBitmap(mBitmap, mMatrix, null);

}
}

private class Basic {

final float[] mTimes = {700f, 200f, 250f, 550f};

Bitmap mBitmap;
Matrix mMatrix = new Matrix();

float getProgress(int time, float[] times, float[] progresss, Interpolator[] interpolators) {
float progress = 0f;
float t;
if (time < times[0]) {
t = 1f * time / times[0];
progress = progresss[0] + interpolators[0].getInterpolation(t) * (progresss[1] - progresss[0]);
} else if (time < times[0] + times[1]) {
t = 1f * (time - times[0]) / times[1];
progress = progresss[1] + interpolators[1].getInterpolation(t) * (progresss[2] - progresss[1]);
} else if (time < times[0] + times[1] + times[2]) {
t = 1f * (time - times[1] - times[0]) / times[2];
progress = progresss[2] + interpolators[2].getInterpolation(t) * (progresss[3] - progresss[2]);
} else if (time < times[0] + times[1] + times[2] + times[3]) {
t = 1f * (time - times[2] - times[1] - times[0]) / times[3];
progress = progresss[3] + interpolators[3].getInterpolation(t) * (0 - progresss[3]);
}
return progress;
}
}
}

private class EaseCubicInterpolator implements Interpolator {
private final static int ACCURACY = 4096;
private static final float STEP_SIZE = 1.0f / ACCURACY;
private int mLastI = 0;

private final PointF mControlPoint1 = new PointF();
private final PointF mControlPoint2 = new PointF();

EaseCubicInterpolator(float x1, float y1, float x2, float y2) {
mControlPoint1.x = x1;
mControlPoint1.y = y1;
mControlPoint2.x = x2;
mControlPoint2.y = y2;
}

@Override
public float getInterpolation(float input) {
float t = input;
for (int i = mLastI; i < ACCURACY; i++) {
t = i * STEP_SIZE;
double x = cubicCurves(t, 0, mControlPoint1.x, mControlPoint2.x, 1);
if (x >= input) {
mLastI = i;
break;
}
}
double value = cubicCurves(t, 0, mControlPoint1.y, mControlPoint2.y, 1);
if (value == 1) {
mLastI = 0;
}
return (float) value;
}

double cubicCurves(double t, double value0, double value1, double value2, double value3) {
double value;
double u = 1 - t;
double tt = t * t;
double uu = u * u;
double uuu = uu * u;
double ttt = tt * t;

value = uuu * value0;
value += 3 * uu * t * value1;
value += 3 * u * tt * value2;
value += ttt * value3;
return value;
}

}
}

二、自定义View中需要引入bitmap资源。使用R.drawable时,参考TextView源码,使用com.android.internal.R.drawable.形式引入。

三、png资源文件放到\frameworks\base\core\res\res\drawable\目录下。 

只将图片放到drawable目录下编译时编不过,会找不到图片资源。参考TextView源码,将用到的资源在frameworks/base/core/res/res/values/symbols.xml中声明。查资料得知,自己添加的资源要在public.xml(资源可对外使用)或symbols.xml(资源私有)中声明。

代码片段:

  <java-symbol type="drawable" name="anim_robot_body" />

  <java-symbol type="drawable" name="anim_robot_eye_left" />

  <java-symbol type="drawable" name="anim_robot_eye_right" />

  <java-symbol type="drawable" name="anim_robot_hand_left" />

  <java-symbol type="drawable" name="anim_robot_hand_right" />

  <java-symbol type="drawable" name="anim_robot_head" />

  <java-symbol type="drawable" name="anim_robot_mouth" />

四、编译framework代码,将生成的framework.jar和framew-res.apk push到手机中。Remoteview就可以使用自定义View了。小部件如何用RemoteView后面再更新。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: