/* * Copyright (C) 2012 Wu Tong * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cocoa4android.ui; import java.lang.reflect.Method; import org.cocoa4android.ca.CALayer; import org.cocoa4android.cg.CGAffineTransform; import org.cocoa4android.cg.CGPoint; import org.cocoa4android.cg.CGRect; import org.cocoa4android.ns.NSArray; import org.cocoa4android.ns.NSMutableArray; import org.cocoa4android.ns.NSSet; import android.R.anim; import android.content.Context; import android.graphics.Canvas; import android.graphics.Matrix; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import android.view.animation.Transformation; import android.view.animation.TranslateAnimation; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; public class UIView extends UIResponder{ protected Context context = UIApplication.sharedApplication().getContext(); protected LayoutInflater inflater; //================================================================================ // Constructor //================================================================================ private View view; public UIView(){ //if no setting fill the parent this.setView(new RelativeLayout(context)); params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); params.alignWithParent = true; params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); params.addRule(RelativeLayout.ALIGN_PARENT_TOP); params.leftMargin = 0; params.topMargin = 0; this.view.setLayoutParams(params); } public UIView(int viewid){ inflater = LayoutInflater.from(context); this.setView(inflater.inflate(viewid, null)); } public UIView(CGRect frame){ this.setView(new RelativeLayout(context)); this.setFrame(frame); } public UIView(View view){ this.setView(view); } //================================================================================ // Basic Method //================================================================================ private boolean isHidden; private UIColor backgroundColor; private int tag; public boolean isHidden() { return isHidden; } public void setHidden(boolean isHidden) { this.isHidden = isHidden; this.view.setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE); } public void setEnabled(boolean enabled) { this.view.setEnabled(enabled); } public boolean enabled() { return this.view.isEnabled(); } public UIColor backgroundColor() { return backgroundColor; } public void setBackgroundColor(UIColor backgroundColor) { this.backgroundColor = backgroundColor; this.view.setBackgroundColor(backgroundColor.getColor()); } public int tag() { return tag; } public void setTag(int tag) { this.tag = tag; } public void setBackgroundImage(UIImage backgroundImage){ if (backgroundImage==null) { this.getView().setBackgroundDrawable(null); }else { if(backgroundImage.getResId()!=0){ this.getView().setBackgroundResource(backgroundImage.getResId()); }else{ this.getView().setBackgroundDrawable(backgroundImage.getDrawable()); } } } public void bringSubviewToFront(UIView view){ if(this.isViewGroup()){ ViewGroup vg = (ViewGroup)this.view; vg.bringChildToFront(view.getView()); } } //================================================================================ // UIViewHierarchy //================================================================================ private UIView superView; public NSArray subViews(){ NSMutableArray subViews = null; if(this.isViewGroup()){ ViewGroup vg = (ViewGroup)this.view; subViews = NSMutableArray.arrayWithCapacity(vg.getChildCount()); for (int i = 0; i < vg.getChildCount(); i++) { subViews.addObject(vg.getChildAt(i).getTag()); } } return subViews; } public UIView superview() { return superView; } protected void setSuperview(UIView superView) { this.superView = superView; } public void addSubview(UIView child){ if(this.isViewGroup()){ ViewGroup vg = (ViewGroup)this.view; vg.addView(child.getView()); child.setSuperview(this); } } public void removeSubView(UIView child){ if(this.isViewGroup()){ ViewGroup vg = (ViewGroup)this.view; vg.removeView(child.getView()); child.setSuperview(null); } } public void removeFromSuperView(){ this.getView().clearAnimation(); if(this.superView!=null){ this.superView.removeSubView(this); } } protected boolean isViewGroup(){ return ViewGroup.class.isInstance(this.view); } //================================================================================ // Convention between View and UIView //================================================================================ private boolean hasTouchesBegan = NO; protected boolean canConsumeTouch = YES; public void setView(View view){ boolean isUIView = this.getClass().equals(UIView.class); if (!isUIView&&view!=null&&this.view!=view) { if (this.view!=null) { //release the previous view's Listener this.view.setOnTouchListener(null); } //check if the class override the method called touchesBegan try { Method began = this.getClass().getDeclaredMethod("touchesBegan", new Class[]{NSSet.class,UIEvent.class}); if (began!=null) { hasTouchesBegan = YES; } } catch (SecurityException e) { } catch (NoSuchMethodException e) { } if (!isUIView) { view.setOnTouchListener(new OnTouchListener(){ @Override public boolean onTouch(View v, MotionEvent event) { UIView.this.handleTouch(event); if(hasTouchesBegan){ UITouch[] toucheArray = new UITouch[event.getPointerCount()]; for(int i=0;i<toucheArray.length;i++){ float x = event.getX(i); float y = event.getY(i); float prevX = 0; float prevY = 0; if(event.getHistorySize()>0){ prevX = event.getHistoricalX(i, 0); prevY = event.getHistoricalY(i, 0); } toucheArray[i] = new UITouch(x,y,prevX,prevY,new UIView(v)); } NSSet touches = new NSSet(toucheArray); UIEvent ev = new UIEvent(event); if(event.getAction()==MotionEvent.ACTION_DOWN){ UIView.this.touchesBegan(touches,ev); }else if(event.getAction()==MotionEvent.ACTION_MOVE){ UIView.this.touchesMoved(touches,ev); }else if(event.getAction()==MotionEvent.ACTION_UP){ UIView.this.touchesEnded(touches,ev); }else if(event.getAction()==MotionEvent.ACTION_CANCEL){ UIView.this.touchesCancelled(touches,ev); } return canConsumeTouch; } return false; } }); } } this.view = view; view.setTag(this); } public View getView(){ return this.view; } //================================================================================ // Size&Position //================================================================================ protected CGRect frame; protected CGAffineTransform transform; private CGPoint center = null; protected boolean keepAspectRatio = NO; static float density = 1.0f; static float scaleFactorX = 1.0f; static float scaleFactorY = 1.0f; static float scaleDensityX = 1.0f; static float scaleDensityY = 1.0f; public CGRect frame() { if(frame==null){ float width = this.getView().getWidth()/scaleDensityX; float height = this.getView().getHeight()/scaleDensityY; LayoutParams params = (LayoutParams) this.getView().getLayoutParams(); float x = 0; float y = 0; if(params!=null){ x = params.leftMargin/scaleDensityX; y = params.topMargin/scaleDensityY; } frame = new CGRect(x,y,width,height); } return frame; } protected LayoutParams params; public void setFrame(CGRect frame) { this.frame = frame; float width = frame.size.width*scaleDensityX; float height = frame.size.height*scaleDensityY; float x = frame.origin.x*scaleDensityX; float y = frame.origin.y*scaleDensityY; //boolean isWidthFlexible = this.isAutoresizing(UIViewAutoresizing.UIViewAutoresizingFlexibleWidth); //boolean isHeightFlexible = this.isAutoresizing(UIViewAutoresizing.UIViewAutoresizingFlexibleHeight); boolean isLeftFlexible = this.isAutoresizing(UIViewAutoresizing.UIViewAutoresizingFlexibleLeftMargin); boolean isTopFlexible = this.isAutoresizing(UIViewAutoresizing.UIViewAutoresizingFlexibleTopMargin); /* boolean isRightFlexible = this.isAutoresizing(UIViewAutoresizing.UIViewAutoresizingFlexibleRightMargin); boolean isBottomFlexible = this.isAutoresizing(UIViewAutoresizing.UIViewAutoresizingFlexibleBottomMargin); */ if (keepAspectRatio&&this.frame!=null) { if (scaleFactorX>scaleFactorY) { float newWidth = frame.size.width*scaleDensityY; float deltaWidth = width - newWidth; x += deltaWidth/2; width = newWidth; }else{ float newHeight = frame.size.height*scaleDensityX; float deltaHeight = height - newHeight; y += deltaHeight/2; height = newHeight; } } if (isLeftFlexible) { x = frame.origin.x*scaleDensityX; } if (isTopFlexible) { y = frame.origin.y*scaleDensityY; } this.setViewFrame(x, y, width, height); this.center = null; } private void setViewFrame(float x,float y,float width,float height){ if (params==null) { params = new LayoutParams((int)(width), (int)(height)); params.alignWithParent = true; params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); params.addRule(RelativeLayout.ALIGN_PARENT_TOP); }else{ params.width = (int)(width); params.height = (int)(height); } params.leftMargin = (int)(x); params.topMargin = (int)(y); this.view.setLayoutParams(params); } public CGPoint center() { if (this.frame==null&¢er!=null) { return center; } CGRect frame = this.frame(); return CGPointMake(frame.size.width/2+frame.origin.x, frame.size.height/2+frame.origin.y); } /** * FIXME fail if UIImageView without setting frame * @param center */ public void setCenter(CGPoint center) { this.applyCenter(center); this.center = center; } public boolean isKeepAspectRatio() { return keepAspectRatio; } public void setKeepAspectRatio(boolean keepAspectRatio) { if (keepAspectRatio!=this.keepAspectRatio) { this.keepAspectRatio = keepAspectRatio; if (this.frame!=null) { this.setFrame(frame); } } } public CGAffineTransform transform() { return transform; } /** * Change the size and position of the shape by CGAffineTransform * @param transform transform matrix * @see CGAffineTransform */ public void setTransform(CGAffineTransform transform) { this.applyTransformation(transform); this.transform = transform; } //================================================================================ // AutoResizing //================================================================================ private int autoresizingMask; public int autoresizingMask() { return autoresizingMask; } protected void setAutoresizingMask(int autoresizingMask) { //TODO reCaculate it in layout method and change this to public /* if (autoresizingMask!=this.autoresizingMask) { this.autoresizingMask = autoresizingMask; if (this.frame!=null) { this.setFrame(frame); } } */ } private boolean isAutoresizing(int autoresizing){ return (this.autoresizingMask&autoresizing)>0; } public class UIViewAutoresizing{ public static final int UIViewAutoresizingNone = 0x00; public static final int UIViewAutoresizingFlexibleLeftMargin = 0x01; public static final int UIViewAutoresizingFlexibleWidth = 0x02; public static final int UIViewAutoresizingFlexibleRightMargin = 0x04; public static final int UIViewAutoresizingFlexibleTopMargin = 0x08; public static final int UIViewAutoresizingFlexibleHeight = 0x10; public static final int UIViewAutoresizingFlexibleBottomMargin = 0x20; } //================================================================================ // UIViewAnimation //================================================================================ private static NSMutableArray animations = null; private static boolean animationsEnabled = YES; private static boolean animationBegan = NO; private static double duation = 0; private static double delay=0; private static int repeatCount=1; private static UIViewAnimationCurve curve = null; /** * begin an animation transaction * frame bounds center transform alpha backgroundColor contentStretch will be recorded * @param animationID * @param context */ //FIXME didn't use the animationID,didn't support setFrame public static void beginAnimations(String animationID,Object context){ if (animationsEnabled) { animationBegan = YES; animations = NSMutableArray.array(); } } public static void setAnimationDuration(double duation){ UIView.duation = duation; } public static void setAnimationDelay(double delay){ UIView.delay = delay; } public static void setAnimationCurve(UIViewAnimationCurve curve){ UIView.curve = curve; } public static void setAnimationRepeatCount(int repeatCount){ UIView.repeatCount = repeatCount; } //FIXME empty method public static void setAnimationTransition(UIViewAnimationTransition transition,UIView view,boolean cache){ } public static void commitAnimations() { for (int i = 0; i < animations.count(); i++) { Animation animation = (Animation) animations.objectAtIndex(i); animation.setDuration((long) (UIView.duation*1000)); if (curve!=null) { switch (curve) { case UIViewAnimationCurveEaseInOut: animation.setInterpolator(new AccelerateDecelerateInterpolator()); break; case UIViewAnimationCurveEaseIn: animation.setInterpolator(new DecelerateInterpolator()); break; case UIViewAnimationCurveEaseOut: animation.setInterpolator(new AccelerateInterpolator()); break; case UIViewAnimationCurveLinear: animation.setInterpolator(new LinearInterpolator()); break; default: break; } } animation.setRepeatCount(repeatCount); if (UIView.delay>0) { animation.setStartTime((long) (AnimationUtils.currentAnimationTimeMillis()+UIView.delay*1000)); animation.start(); }else{ animation.startNow(); } //UIApplication.sharedApplication().getWindow().getView().postInvalidate(); } animationBegan = NO; animations = null; UIView.repeatCount = 1; UIView.duation = 0.0f; UIView.delay = 0.0f; } public static void setAnimationsEnabled(boolean enabled){ animationsEnabled = enabled; } private void applyTransformation(CGAffineTransform transform){ MatrixAnimation animation = new MatrixAnimation(this.transform,transform); //animation.setFillBefore(YES); animation.setFillAfter(YES); if (frame!=null) { animation.setAnchorPoint(CGPointMake(frame.size.width*scaleFactorX/2,frame.size.height*scaleFactorY/2)); } if (animationBegan) { this.getView().setAnimation(animation); animations.addObject(animation); }else{ animation.setDuration(0); this.getView().startAnimation(animation); } } private void applyCenter(CGPoint center){ if (animationBegan) { CGPoint beforeCenter = this.center(); float toXDelta = beforeCenter.x-center.x; float toYDelta = beforeCenter.y-center.y; TranslateAnimation animation = new TranslateAnimation(toXDelta*scaleDensityX, 0, toYDelta*scaleDensityY, 0); this.getView().setAnimation(animation); animation.setFillAfter(YES); animations.addObject(animation); } CGRect frame = this.frame(); frame.origin.x = (int) (center.x-frame.size.width/2); frame.origin.y = (int) (center.y-frame.size.height/2); this.setFrame(frame); } public static boolean areAnimationsEnabled(){ return animationsEnabled; } public enum UIViewAnimationCurve{ UIViewAnimationCurveEaseInOut, // slow at beginning and end UIViewAnimationCurveEaseIn, // slow at beginning UIViewAnimationCurveEaseOut, // slow at end UIViewAnimationCurveLinear } public enum UIViewAnimationTransition{ UIViewAnimationTransitionNone, UIViewAnimationTransitionFlipFromLeft, UIViewAnimationTransitionFlipFromRight, UIViewAnimationTransitionCurlUp, UIViewAnimationTransitionCurlDown, } protected class MatrixAnimation extends Animation{ private CGAffineTransform endTransform; private float startRotation = 0.0f; private float endRotation = 0.0f; private float deltaRotation = 0.0f; private float startScaleX = 0.0f; private float startScaleY = 0.0f; private float deltaScaleX = 0.0f; private float deltaScaleY = 0.0f; private float startTransX = 0.0f; private float startTransY = 0.0f; private float deltaTransX = 0.0f; private float deltaTransY = 0.0f; private CGPoint anchorPoint; public MatrixAnimation(CGAffineTransform startTransform,CGAffineTransform endTransform) { this.endTransform = endTransform; if (startTransform!=null) { startRotation = (float) Math.toDegrees(Math.atan2(startTransform.b, startTransform.a)); startScaleX = startTransform.a; startScaleY = startTransform.d; startTransX = startTransform.tx; startTransY = startTransform.ty; } endRotation = (float) Math.toDegrees(Math.atan2(this.endTransform.b, endTransform.a)); deltaRotation = endRotation - startRotation; deltaScaleX = endTransform.a - startScaleX; deltaScaleY = endTransform.d - startScaleY; deltaTransX = endTransform.tx - startTransX; deltaTransY = endTransform.ty - startTransY; } @Override protected void applyTransformation(float interpolatedTime, Transformation t){ //ͨ��Matrix.setScale���������ţ��ú�����������������X��Y���������ӣ�����interpolatedTime�Ǵ�0��1�仯��������ʵ�ֵ�Ч�����ǿؼ�����С�𽥱仯����� Matrix matrix = t.getMatrix(); matrix.setScale(startScaleX+deltaScaleX*interpolatedTime, startScaleY+deltaScaleY*interpolatedTime); matrix.setTranslate(startTransX+deltaTransX*interpolatedTime, startTransY+deltaTransY*interpolatedTime); if (anchorPoint!=null) { matrix.setRotate(startRotation+deltaRotation*interpolatedTime,anchorPoint.x,anchorPoint.y); }else{ matrix.setRotate(startRotation+deltaRotation*interpolatedTime); } //Matrix ����ʵ�ָ��ָ��ӵı任 //preTranslate������������ǰ�ƶ���postTranslate����������ɺ��ƶ��� } public void setAnchorPoint(CGPoint anchorPoint){ this.anchorPoint = anchorPoint; } } //================================================================================ // UIViewRendering //================================================================================ private CALayer layer = new CALayer(this); public CALayer layer() { return layer; } public void setLayer(CALayer layer) { this.layer = layer; } // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. protected void drawRect(CGRect rect) { } protected void draw(Canvas canvas){ this.drawRect(this.frame); } public class CocoaRelativeLayout extends RelativeLayout{ public CocoaRelativeLayout(Context context) { super(context); } @Override protected void onDraw(Canvas canvas){ super.onDraw(canvas); UIView.this.draw(canvas); } } }