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);
}
}
};