android: qq 5.0 demo学习笔记(主 粒子爆炸效果+ViewDragHelper)

demo下载地址:http://download.csdn.net/detail/u012764110/9435650 eclipse版的
PS:本文可作为demo解析 or 琐碎的知识点整理

知识点:

1、xml中定义的anim,在ImageView上显示

ImageView imageView = (ImageView)findViewById(R.id.imageView1);
        imageView.setImageResource(R.drawable.anim_bubble_pop);
        AnimationDrawable mAnimDrawable = (AnimationDrawable) imageView
                .getDrawable();
        mAnimDrawable.start();

xml中动画:android:oneshot=”false”循环 为true 单次

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false" >

    <item
        android:drawable="@drawable/pop1"
        android:duration="100"/>
    <item
        android:drawable="@drawable/pop2"
        android:duration="100"/>
    <item
        android:drawable="@drawable/pop3"
        android:duration="100"/>
    <item
        android:drawable="@drawable/pop4"
        android:duration="100"/>
    <item
        android:drawable="@drawable/pop5"
        android:duration="100"/>

</animation-list>

2、ArrayAdapter中改写TextView样式

simple_list_item_1中TextView的id是text1,哎,得看源码

mLeftList.setAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, Cheeses.sCheeseStrings){
            @Override
            public View getView(int position, View convertView,
                    ViewGroup parent) {
                View view = super.getView(position, convertView, parent);
                TextView mText = (TextView) view.findViewById(android.R.id.text1);
                mText.setTextColor(Color.WHITE);
                return view;
            }
        });

3、substring

public String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。。。我总是记不清

    String a = "一二三四五六七八九十".substring(0, 4);
        myTv.setText(a); //一二三四

4、经常有设置子View在父布局的中间位置,固定的写法如下

太常见了

             /**
             * 设置为中心位置,给出两个点坐标,左上,右下 
             * 父布局中心点:mCenterX、mCenterY 
             * 子View长宽:width、height
             */
            child.layout(
                    (int) (mCenterX - width / 2.0f), 
                    (int) (mCenterY - height / 2.0f),
                    (int) (mCenterX + width / 2.0f), 
                    (int) (mCenterY + height / 2.0f));

5、requestLayout()方法和invalidate()方法

  • requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent
    view重新调用他的onMeasureonLayout来对重新设置自己位置。特别的当view的layoutparameter发生改变,并且它的值还没能应用到view上,这时候适合调用这个方法

  • invalidate:View本身调用迫使view重画。

  • -

- 6、粒子爆炸效果(源码+使用)

使用:

        tvPoint = (TextView)findViewById(R.id.point);
        tvPoint.setText(String.valueOf(22));
        tvPoint.setTag(22);//必须设置Tag
        GooViewListener mGooListener = new GooViewListener(mContext, tvPoint) {
            @Override
            public void onDisappear(PointF mDragCenter) {
                super.onDisappear(mDragCenter);

                Utils.showToast(mContext,
                        "Cheers! We have get rid of it!");
            }

            @Override
            public void onReset(boolean isOutOfRange) {
                super.onReset(isOutOfRange);

                Utils.showToast(mContext,
                        isOutOfRange ? "Are you regret?" : "Try again!");
            }
        };
        tvPoint.setOnTouchListener(mGooListener);

源码:

package com.anna.tencentqq.reminder;

import android.content.Context;
import android.view.View;
import android.widget.FrameLayout;

/**
 * @author PoplarTang To show the end animation(bubble burst)
 *
 */
public class BubbleLayout extends FrameLayout {

    public BubbleLayout(Context context) {
        super(context);
    }

    private int mCenterX, mCenterY;

    public void setCenter(int x, int y) {
        mCenterX = x;
        mCenterY = y;
        requestLayout();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        View child = getChildAt(0);
        // 设置View到指定位置
        if (child != null && child.getVisibility() != GONE) {
            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight();
            /**
             * 设置为中心位置,给出两个点坐标,左上,右下 
             * 父布局中心点:mCenterX、mCenterY 
             * 子View长宽:width、height
             */
            child.layout(
                    (int) (mCenterX - width / 2.0f), 
                    (int) (mCenterY - height / 2.0f),
                    (int) (mCenterX + width / 2.0f), 
                    (int) (mCenterY + height / 2.0f));
        }
    }
}
package com.anna.tencentqq.reminder;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.support.v4.view.MotionEventCompat;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.OvershootInterpolator;

import com.anna.tencentqq.util.GeometryUtil;
import com.anna.tencentqq.util.Utils;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;

/**
 * This view should be added to WindowManager, so we can drag it to anywhere.
 * @author PoplarTang
 *
 */
public class GooView extends View {

    interface OnDisappearListener {
        void onDisappear(PointF mDragCenter);
        void onReset(boolean isOutOfRange);
    }

    protected static final String TAG = "GooView_TAG";

    private PointF mInitCenter;
    private PointF mDragCenter;
    private PointF mStickCenter;
    /**
     * 拖拽的圆半径
     */
    float dragCircleRadius = 0;

    float stickCircleRadius = 0;

    float stickCircleMinRadius = 0;

    float stickCircleTempRadius = stickCircleRadius;

    float farest = 0;
    String text = "";

    private Paint mPaintRed;
    private Paint mTextPaint;

    private ValueAnimator mAnim;
    private boolean isOutOfRange = false;
    private boolean isDisappear = false;

    private OnDisappearListener mListener;
    private Rect rect;
    private int mStatusBarHeight;

    private float resetDistance;


    public GooView(Context context) {
        super(context);

        rect = new Rect(0, 0, 50, 50);

        stickCircleRadius = Utils.dip2Dimension(10.0f, context);
        dragCircleRadius = Utils.dip2Dimension(10.0f, context);
        stickCircleMinRadius = Utils.dip2Dimension(3.0f, context);

        farest = Utils.dip2Dimension(80.0f, context);
        resetDistance = Utils.dip2Dimension(40.0f, getContext());

        mPaintRed = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintRed.setColor(Color.RED);

        //拖拽的圆圈中的字体
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextAlign(Align.CENTER);
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTextSize(dragCircleRadius * 1.2f);
    }

    /**
     * 设置固定圆的半径
     * @param r
     */
    public void setDargCircleRadius(float r){
        dragCircleRadius = r;
    }

    /**
     * 设置拖拽圆的半径
     * @param r
     */
    public void setStickCircleRadius(float r){
        stickCircleRadius = r;
    }

    /**
     * 设置数字
     * @param num
     */
    public void setNumber(int num){
        text = String.valueOf(num);
    }

    /**
     * 初始化圆的圆心坐标
     * @param x
     * @param y
     */
    public void initCenter(float x, float y){
        mDragCenter = new PointF(x, y);
        mStickCenter = new PointF(x, y);
        mInitCenter = new PointF(x, y);
        invalidate();
    }

    /**
     * 更新拖拽圆的圆心坐标,重绘View
     * @param x
     * @param y
     */
    private void updateDragCenter(float x, float y) {
        this.mDragCenter.x = x;
        this.mDragCenter.y = y;
        invalidate();
    }

    /**
     * 通过绘制Path构建一个ShapeDrawable,用来绘制到画布Canvas上
     * @return
     */
    private ShapeDrawable drawGooView() {
        Path path = new Path();


        //1. 根据当前两圆圆心的距离计算出固定圆的半径
        float distance = (float) GeometryUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
        stickCircleTempRadius = getCurrentRadius(distance);

        //2. 计算出经过两圆圆心连线的垂线的dragLineK(对边比临边)。求出四个交点坐标
        float xDiff = mStickCenter.x - mDragCenter.x;
        Double dragLineK = null;
        if(xDiff != 0){
            dragLineK = (double) ((mStickCenter.y - mDragCenter.y) / xDiff);
        }

            //分别获得经过两圆圆心连线的垂线与圆的交点(两条垂线平行,所以dragLineK相等)。
        PointF[] dragPoints = GeometryUtil.getIntersectionPoints(mDragCenter, dragCircleRadius, dragLineK);
        PointF[] stickPoints = GeometryUtil.getIntersectionPoints(mStickCenter, stickCircleTempRadius, dragLineK);

        //3. 以两圆连线的0.618处作为 贝塞尔曲线 的控制点。(选一个中间点附近的控制点)
        PointF pointByPercent = GeometryUtil.getPointByPercent(mDragCenter, mStickCenter, 0.618f);


        // 绘制两圆连接
        //此处参见示意图{@link https://github.com/PoplarTang/DragGooView }
        path.moveTo((float)stickPoints[0].x, (float)stickPoints[0].y);
        path.quadTo((float)pointByPercent.x, (float)pointByPercent.y,
                (float)dragPoints[0].x, (float)dragPoints[0].y);
        path.lineTo((float)dragPoints[1].x, (float)dragPoints[1].y);
        path.quadTo((float)pointByPercent.x, (float)pointByPercent.y,
                (float)stickPoints[1].x, (float)stickPoints[1].y);
        path.close();

        //将四个交点画到屏幕上
//      path.addCircle((float)dragPoints[0].x, (float)dragPoints[0].y, 5, Direction.CW);
//      path.addCircle((float)dragPoints[1].x, (float)dragPoints[1].y, 5, Direction.CW);
//      path.addCircle((float)stickPoints[0].x, (float)stickPoints[0].y, 5, Direction.CW);
//      path.addCircle((float)stickPoints[1].x, (float)stickPoints[1].y, 5, Direction.CW);

        //构建ShapeDrawable
        ShapeDrawable shapeDrawable = new ShapeDrawable(new PathShape(path, 50f, 50f));
        shapeDrawable.getPaint().setColor(Color.RED);
        return shapeDrawable;
    }

    /**
     * 根据距离获得当前固定圆的半径
     * @param distance
     * @return
     */
    private float getCurrentRadius(float distance) {

        distance = Math.min(distance, farest);

        // Start from 20%
        float fraction = 0.2f + 0.8f * distance / farest;

        // Distance -> Farthest
        // stickCircleRadius -> stickCircleMinRadius
        float evaluateValue = (float) GeometryUtil.evaluateValue(fraction, stickCircleRadius, stickCircleMinRadius);
        return evaluateValue;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if(isAnimRunning()){
            return false;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int actionMasked = MotionEventCompat.getActionMasked(event);
        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN:{

                if(isAnimRunning()){
                    return false;
                }

                isDisappear = false;
                isOutOfRange = false;
                updateDragCenter(event.getRawX() , event.getRawY());

                break;
            }
            case MotionEvent.ACTION_MOVE:{

                //如果两圆间距大于最大距离farest,执行拖拽结束动画
                PointF p0 = new PointF(mDragCenter.x, mDragCenter.y);
                PointF p1 = new PointF(mStickCenter.x, mStickCenter.y);
                if (GeometryUtil.getDistanceBetween2Points(p0, p1) > farest) {
                    isOutOfRange = true;

                    updateDragCenter(event.getRawX(), event.getRawY());
                    return false;
                }

                updateDragCenter(event.getRawX() , event.getRawY());
                break;
            }
            case MotionEvent.ACTION_UP:{
                handleActionUp();

                break;
            }
            default:{
                isOutOfRange = false;
                break;
            }
        }
        return true;
    }

    private boolean isAnimRunning() {
        if(mAnim != null && mAnim.isRunning()){
            return true;
        }
        return false;
    }
    /**
     * 清除
     */
    private void disappeared() {
        isDisappear = true;
        invalidate();

        if(mListener != null){
            mListener.onDisappear(mDragCenter);
        }
    }

    private void handleActionUp() {
        if(isOutOfRange){
            // When user drag it back, we should call onReset().
            if(GeometryUtil.getDistanceBetween2Points(mDragCenter, mInitCenter) < resetDistance){
                if(mListener != null)
                    mListener.onReset(isOutOfRange);
                return;
            }

            // Otherwise
            disappeared();
        }else {

            //手指抬起时,弹回动画
            mAnim = ValueAnimator.ofFloat(1.0f);
            mAnim.setInterpolator(new OvershootInterpolator(4.0f));

            final PointF startPoint = new PointF(mDragCenter.x, mDragCenter.y);
            final PointF endPoint = new PointF(mStickCenter.x, mStickCenter.y);
            mAnim.addUpdateListener(new AnimatorUpdateListener() {

                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float fraction = animation.getAnimatedFraction();
                    PointF pointByPercent = GeometryUtil.getPointByPercent(startPoint, endPoint, fraction);
                    updateDragCenter((float)pointByPercent.x, (float)pointByPercent.y);
                }
            });
            mAnim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    if(mListener != null)
                        mListener.onReset(isOutOfRange);
                }
            });
            if(GeometryUtil.getDistanceBetween2Points(startPoint,endPoint) < 10)    {
                mAnim.setDuration(10);
            }else {
                mAnim.setDuration(500);
            }
            mAnim.start();
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {

        canvas.save();
        //去除状态栏高度偏差
        canvas.translate(0, -mStatusBarHeight);

        if(!isDisappear){
            if(!isOutOfRange){
                // 画两圆连接处
                ShapeDrawable drawGooView = drawGooView();
                drawGooView.setBounds(rect);
                drawGooView.draw(canvas);

                // 画固定圆
                canvas.drawCircle(mStickCenter.x, mStickCenter.y, stickCircleTempRadius, mPaintRed);
            }
            // 画拖拽圆
            canvas.drawCircle(mDragCenter.x , mDragCenter.y, dragCircleRadius, mPaintRed);
            // 画数字
            canvas.drawText(text, mDragCenter.x , mDragCenter.y + dragCircleRadius /2f, mTextPaint);
        }

        canvas.restore();

    }

    public OnDisappearListener getOnDisappearListener() {
        return mListener;
    }

    public void setOnDisappearListener(OnDisappearListener mListener) {
        this.mListener = mListener;
    }

    public void setStatusBarHeight(int statusBarHeight) {
        this.mStatusBarHeight = statusBarHeight;
    }

}
package com.anna.tencentqq.reminder;

import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.drawable.AnimationDrawable;
import android.os.Handler;
import android.support.v4.view.MotionEventCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.anna.tencentqq.reminder.GooView.OnDisappearListener;
import com.anna.tencentqq.util.Utils;
import com.anna.tencentqq.R;

public class GooViewListener implements OnTouchListener, OnDisappearListener {

    private WindowManager mWm;
    private WindowManager.LayoutParams mParams;
    private GooView mGooView;
    private View point;
    private int number;
    private final Context mContext;

    private Handler mHandler;

    public GooViewListener(Context mContext, View point) {
        this.mContext = mContext;
        this.point = point;
        this.number = (Integer) point.getTag();

        mGooView = new GooView(mContext);

        mWm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        mParams = new WindowManager.LayoutParams();
        mParams.format = PixelFormat.TRANSLUCENT;//指定图像中每个像素的颜色数据的格式

        mHandler= new Handler(mContext.getMainLooper());//表示放到主UI线程去处理。
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        int action = MotionEventCompat.getActionMasked(event);
        if (action == MotionEvent.ACTION_DOWN) {
            // 当按下时,将自定义View添加到WindowManager中

            ViewParent parent = v.getParent();
            // 请求其父级View不拦截Touch事件.这样即使ViewGroup在MOVE的时候return true,子View依然可以捕获到MOVE以及UP事件。
            parent.requestDisallowInterceptTouchEvent(true);

            //隐藏该View
            point.setVisibility(View.INVISIBLE);

            Log.d("TAG",
                    "rawX: " + event.getRawX() + " rawY: " + event.getRawY());

            // 初始化当前点击的item的信息,数字及坐标
            mGooView.setStatusBarHeight(Utils.getStatusBarHeight(v));
            mGooView.setNumber(number);
            mGooView.initCenter(event.getRawX(), event.getRawY());
            mGooView.setOnDisappearListener(this);

            // 执行添加方法
            mWm.addView(mGooView, mParams);
        }
        // 将所有touch事件转交给GooView处理
        mGooView.onTouchEvent(event);
        return true;
    }

    /**
     * 以下是自定义的两个接口方法的实现
     */
    @Override
    public void onDisappear(PointF mDragCenter) {
        if (mWm != null && mGooView.getParent() != null) {
            mWm.removeView(mGooView);

            //播放气泡爆炸动画
            ImageView imageView = new ImageView(mContext);
            imageView.setImageResource(R.drawable.anim_bubble_pop);
            AnimationDrawable mAnimDrawable = (AnimationDrawable) imageView
                    .getDrawable();

            final BubbleLayout bubbleLayout = new BubbleLayout(mContext);
            bubbleLayout.setCenter((int) mDragCenter.x, (int) mDragCenter.y
                    - Utils.getStatusBarHeight(mGooView));

            bubbleLayout.addView(imageView, new FrameLayout.LayoutParams(
                    android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
                    android.widget.FrameLayout.LayoutParams.WRAP_CONTENT));

            mWm.addView(bubbleLayout, mParams);

            mAnimDrawable.start();

            // 播放结束后,删除该bubbleLayout
            mHandler.postDelayed(new Runnable() {

                @Override
                public void run() {
                    mWm.removeView(bubbleLayout);
                }
            }, 501);
        }

    }

    @Override
    public void onReset(boolean isOutOfRange) {
        // 当气泡弹回时,去除该View,等下次ACTION_DOWN的时候再添加
        if (mWm != null && mGooView.getParent() != null) {
            mWm.removeView(mGooView);
        }
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值