android AppWidget中添加自定义View

最近接了个任务,在小部件(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(资源私有)中声明。

代码片段:

 
 
 
 
 
 
 

四、编译framework代码,将生成的framework.jar和framew-res.apk push到手机中。Remoteview就可以使用自定义View了。小部件如何用RemoteView后面再更新。


你可能感兴趣的:(Android)