public class PullToRefresh extends ListView {
private static final int PULL_TO_REFRESH = 0;
private static final int PULL_RELEASE_REFRESH = 1;
private static final int PULL_REFRESHING = 2;
private View headerView;
private int headerHeight;
private DashBoard db;
private ImageView needle;
private TextView title;
private TextView time;
private int state;
private boolean isRecord;
private int downY;
private OnRefreshListener listener;
public PullToRefresh(Context context, AttributeSet attrs) {
super(context, attrs);
headerView = View.inflate(context, R.layout.view_header, null);
addHeaderView(headerView);
headerView.measure(0, 0);
headerHeight = headerView.getMeasuredHeight();
db = (DashBoard) headerView.findViewById(R.id.db);
needle = (ImageView) headerView.findViewById(R.id.neddle);
title = (TextView) headerView.findViewById(R.id.title);
time = (TextView) headerView.findViewById(R.id.time);
finishRefresh();
}
public void finishRefresh() {
db.setVisibility(View.VISIBLE);
db.isShowNeedle(true);
//清除动画
needle.clearAnimation();
needle.setVisibility(View.GONE);
title.setText("下拉刷新");
state = PULL_TO_REFRESH;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
time.setText("最后刷新时间:"+dateFormat.format(new Date()));
headerView.setPadding(0, -headerHeight, 0, 0);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!isRecord && getFirstVisiblePosition() == 0) {
isRecord = true;
downY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
if (!isRecord && getFirstVisiblePosition() == 0) {
isRecord = true;
downY = (int) ev.getY();
}
int moveY = (int) ev.getY();
int dy = moveY - downY;
if (dy > 0 && getFirstVisiblePosition() == 0 && state != PULL_REFRESHING) {
int top = dy / 3 - headerHeight;
headerView.setPadding(0, top, 0, 0);
db.setProgress(top + 85);
if (top > 0 && state == PULL_TO_REFRESH) {
title.setText("释放刷新");
state = PULL_RELEASE_REFRESH;
}
if (top <= 0 && state == PULL_RELEASE_REFRESH) {
title.setText("下拉刷新");
state = PULL_TO_REFRESH;
}
return true;
}
break;
case MotionEvent.ACTION_UP:
isRecord = false;
if (state == PULL_TO_REFRESH) {
headerView.setPadding(0, -headerHeight, 0, 0);
}
if (state == PULL_RELEASE_REFRESH) {
headerView.setPadding(0, 0, 0, 0);
title.setText("正在刷新...");
state = PULL_REFRESHING;
db.isShowNeedle(false);
needle.setVisibility(View.VISIBLE);
startAnimation();
if (listener != null) {
listener.onRefresh();
}
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
public void startAnimation(){
RotateAnimation ra = new RotateAnimation(180, 280, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
ra.setDuration(200);
ra.setRepeatCount(RotateAnimation.INFINITE);
ra.setRepeatMode(RotateAnimation.REVERSE);
AlphaAnimation aa = new AlphaAnimation(0.5f, 1);
aa.setDuration(200);
aa.setRepeatCount(AlphaAnimation.INFINITE);
aa.setRepeatMode(AlphaAnimation.REVERSE);
AnimationSet as = new AnimationSet(false);
as.addAnimation(ra);
as.addAnimation(aa);
needle.startAnimation(as);
}
public interface OnRefreshListener{
void onRefresh();
}
public void setOnRefreshListener(OnRefreshListener listener){
this.listener = listener;
}
}
上述为下拉刷新控件,继承的是ListView,控件里面的子元素调用startAnimation方法时,必须保证子元素为可见状态(即setVisibility(View.Visible)),否则动画不可见,然后再调用setVisibility()方法设置不可见或其他时,需要先清除动画才能设置成功,即使动画为无线模式(即INFINITE),也能够清除掉.
public class ArcMenuCenter extends ViewGroup implements OnClickListener {
private int[] imgs = {
R.drawable.composer_icn_plus,
R.drawable.composer_camera,
R.drawable.composer_music,
R.drawable.composer_place,
R.drawable.composer_sleep,
R.drawable.composer_sun,
R.drawable.composer_thought,
R.drawable.composer_with
};
private static final int WRAP_CONTENT = 100;
private static final int DX_DY = 5;
private int raduis = WRAP_CONTENT;
private ArcMenuCenter mArcMenuCenter = this;
private int measuredCenter;
private boolean isOpen;
private OnItemClickListener listener;
public ArcMenuCenter(Context context, AttributeSet attrs) {
super(context, attrs);
ImageView iv_menuButton = new ImageView(context);
iv_menuButton.setImageResource(imgs[0]);
iv_menuButton.setBackgroundResource(R.drawable.composer_button);
iv_menuButton.setScaleType(ScaleType.CENTER);
addView(iv_menuButton);
for (int i = 1; i < imgs.length; i++) {
ImageView child = new ImageView(context);
child.setImageResource(imgs[i]);
addView(child);
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams params = child.getLayoutParams();
params.width = LayoutParams.WRAP_CONTENT;
params.height = LayoutParams.WRAP_CONTENT;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measuredWidth = getMeasuredSize(widthMeasureSpec);
int measuredHeight = getMeasuredSize(heightMeasureSpec);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(0, 0);
child.setOnClickListener(this);
}
measuredCenter = measuredHeight > measuredWidth ? measuredWidth / 2 : measuredHeight / 2;
raduis = measuredCenter - DX_DY - getChildAt(0).getMeasuredHeight();
setMeasuredDimension(measuredWidth, measuredHeight);
}
private int getMeasuredSize(int measureSpec) {
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
int result = -1;
switch (mode) {
case MeasureSpec.EXACTLY:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
result = WRAP_CONTENT + 3*getChildAt(0).getMeasuredHeight() + DX_DY;
break;
}
return result;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View child0 = getChildAt(0);
child0.layout(measuredCenter - child0.getMeasuredWidth() / 2, measuredCenter - child0.getMeasuredHeight() / 2, measuredCenter + child0.getMeasuredWidth() / 2, measuredCenter + child0.getMeasuredHeight() / 2);
for (int i = 0; i < getChildCount() -1; i++) {
View child = getChildAt(i+1);
l = (int) (measuredCenter - Math.cos(2*Math.PI / (getChildCount()-1) * i) * raduis) - child.getMeasuredWidth() / 2;
t = (int) (measuredCenter - Math.sin(2*Math.PI / (getChildCount()-1) * i) * raduis) - child.getMeasuredHeight() / 2;
r = l + child.getMeasuredWidth();
b = t + child.getMeasuredHeight();
child.layout(l, t, r, b);
child.setVisibility(View.GONE);
}
}
@Override
public void onClick(View v) {
final View child0 = getChildAt(0);
if (v == child0) {
isOpen = !isOpen;
clickStartAnimation(child0);
}
if (isOpen) {
for (int i = 0; i < getChildCount() - 1; i++) {
View child = getChildAt(i + 1);
if (v == child) {
if (listener != null) {
listener.onItemClick(child, i);
isOpen = false;
clickStartAnimation(child0);
break;
}
}
}
}
}
private void clickStartAnimation(View child0) {
RotateAnimation ra = startRotateAnimation();
child0.startAnimation(ra);
for (int i = 0; i < getChildCount() - 1; i++) {
final ImageView child = (ImageView) getChildAt(i + 1);
RotateAnimation childRa = startRotateAnimation();
AlphaAnimation childaa = startAlphaAnimation();
TranslateAnimation childta = startTranlateAnimation(child0.getLeft() - child.getLeft(), child0.getTop() - child.getTop());
ScaleAnimation childsa = startScaleAnimation();
AnimationSet as = new AnimationSet(false);
as.addAnimation(childsa);
as.addAnimation(childta);
as.addAnimation(childaa);
as.addAnimation(childRa);
as.setFillAfter(true);
as.setStartOffset(200 * i);
as.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (!isOpen) {
child.clearAnimation();
}
}
});
child.startAnimation(as);
}
}
private ScaleAnimation startScaleAnimation() {
ScaleAnimation sa = new ScaleAnimation(1, 1.2f, 1, 1.2f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
sa.setDuration(500);
sa.setRepeatCount(!isOpen ? 0 : ScaleAnimation.INFINITE);
sa.setRepeatMode(ScaleAnimation.REVERSE);
return sa;
}
private AlphaAnimation startAlphaAnimation() {
AlphaAnimation aa = new AlphaAnimation(!isOpen ? 1 : 0.3f, !isOpen ? 0.3f : 1);
aa.setDuration(500);
aa.setRepeatCount(!isOpen ? 0 : AlphaAnimation.INFINITE);
aa.setRepeatMode(AlphaAnimation.REVERSE);
return aa;
}
private TranslateAnimation startTranlateAnimation(int x,int y) {
TranslateAnimation ta = new TranslateAnimation(TranslateAnimation.ABSOLUTE, isOpen ? x : 0, TranslateAnimation.ABSOLUTE, isOpen ? 0 : x, TranslateAnimation.ABSOLUTE, isOpen ? y : 0, TranslateAnimation.ABSOLUTE, isOpen ? 0 : y);
ta.setDuration(2000);
ta.setInterpolator(new OvershootInterpolator());
return ta;
}
private RotateAnimation startRotateAnimation() {
RotateAnimation ra = new RotateAnimation(!isOpen ? 0 : 1080, !isOpen ? 1080 : 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
ra.setDuration(2000);
return ra;
}
public interface OnItemClickListener{
void onItemClick(View itemView,int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
this.listener = listener;
}
}
上述为行星菜单自定义控件,继承的是ViewGroup,空间中的子元素调用startAnimation方法时,即使子元素不可见也可以启动动画并可见,当在onLayout方法中将子元素设置为不可见时,只要移除动画,子元素就会不可见(个人观点:存在OnLayoutChangedListener,每次布局改变都会回调onLayout方法),但当动画中存在无线模式时(INFINITE),即使调用clearAnimation方法,也无法去除动画,所以onLayout方法中的setVisibility(View.GONE)会调用失败