1. java files:
Package us.eiyou.financial_management_service;
Import java.util.ArrayList;
Import java.util.Collections;
Import Java.util.Comparator;
Import Android.animation.Animator;
Import Android.animation.AnimatorListenerAdapter;
Import Android.animation.AnimatorSet;
Import Android.animation.ObjectAnimator;
Import Android.animation.ValueAnimator;
Import Android.animation.ValueAnimator.AnimatorUpdateListener;
Import Android.content.Context;
Import Android.util.AttributeSet;
Import android.view.MotionEvent;
Import Android.view.VelocityTracker;
Import Android.view.View;
Import Android.view.ViewTreeObserver;
Import Android.view.animation.DecelerateInterpolator;
Import Android.widget.FrameLayout;
Rotatecard (custom rotation view)
The item is clicked with a magnified animation effect
Quick swipe and finger release will spin
public class Rotatecard extends Framelayout {
Private arraylist<rotatecardviewholder> viewholderlist;
Private arraylist<view> viewlist;
private int Ovala, ovalb;//long half axis of elliptical trajectory, short half axis
private int parentwidth, parentheight;//Rotatecard's wide height
private int Viewwidth, width of view in viewheight;//Viewset
Layoutparams viewpparams;//View's Layoutparams
Private float angleoffset;//The degree of rotation around the center of the ellipse for each sliding pixel of a finger
Private Rotatecardontouchlistener mtouchlistener;//for Rotatecard and child view, handles sliding events
Increase monitoring before drawing to obtain rotatecard width and height
Private Viewtreeobserver.onpredrawlistener Onpredrawlistener = new Viewtreeobserver.onpredrawlistener () {
@Override
public Boolean Onpredraw () {
Parentwidth = RotateCard.this.getMeasuredWidth ();
Parentheight = RotateCard.this.getMeasuredHeight ();
Angleoffset = (float) ((180.0/parentwidth) * 1.5);
LOG.I (Info_tag, "parentwidth:" + parentwidth);
LOG.I (Info_tag, "parentheight:" + parentheight);
LOG.I (Info_tag, "Angleoffset:" + angleoffset);
if (viewlist! = null && viewlist.size () > 0) {
Aftergetparamsandviews ();
}
Remove monitoring
RotateCard.this.getViewTreeObserver (). Removeonpredrawlistener (
Onpredrawlistener);
return true;
}
};
Public Rotatecard (Context context) {
Super (context);
TODO auto-generated Constructor stub
Init ();
}
Public Rotatecard (context context, AttributeSet Attrs) {
Super (context, attrs);
TODO auto-generated Constructor stub
Init ();
}
Public Rotatecard (context context, AttributeSet attrs, int defstyle) {
Super (context, attrs, Defstyle);
TODO auto-generated Constructor stub
Init ();
}
private void init () {
Increase the listening before the Rotatecard is drawn
RotateCard.this.getViewTreeObserver (). Addonpredrawlistener (
Onpredrawlistener);
Increased rotatecard of sliding monitoring
Mtouchlistener = new Rotatecardontouchlistener ();
This.setontouchlistener (Mtouchlistener);
}
/**
* You must ensure that each view of the incoming view collection is the same size, otherwise the display may not be accurate
*
* @param viewset
* View Collection
*/
public void Commitviews (arraylist<view> viewlist, int viewwidth,
int viewheight) {
This.removeallviews ();
This.viewlist = viewlist;
This.viewwidth = Viewwidth;
This.viewheight = Viewheight;
Viewpparams = new Layoutparams (viewwidth, viewheight);
LOG.I (Info_tag, "viewwidth:" + viewwidth);
LOG.I (Info_tag, "viewheight:" + viewheight);
if (parentwidth! = 0 && Parentheight! = 0) {
Aftergetparamsandviews ();
}
}
/**
* You must ensure that each view of the incoming view collection is the same size, otherwise the display may not be accurate
* There should be no one whose view is not a circle or a square? The input diameter or the length of the side is fine.
*
* @param viewset
* View Collection
*/
public void Commitviews (arraylist<view> viewlist, int viewwidthheight) {
This.removeallviews ();
This.viewlist = viewlist;
This.viewwidth = Viewwidthheight;
This.viewheight = Viewwidthheight;
Viewpparams = new Layoutparams (viewwidth, viewheight);
LOG.I (Info_tag, "viewwidth:" + viewwidth);
LOG.I (Info_tag, "viewheight:" + viewheight);
if (parentwidth! = 0 && Parentheight! = 0) {
Aftergetparamsandviews ();
}
}
/**
* This function is called after Rotatecard has been obtained and the Commitviews function has been called
*/
private void Aftergetparamsandviews () {
Ovala = (parentwidth-viewwidth)/2;
OVALB = (int) ((Parentheight-viewheight * 1.5)/2);
int viewnum = Viewlist.size ();
float Angleperview = (float) (360.0/viewnum);
LOG.I (Info_tag, "Ovala:" + Ovala);
LOG.I (Info_tag, "ovalb:" + ovalb);
LOG.I (Info_tag, "viewnum:" + viewnum);
LOG.I (Info_tag, "Angleperview:" + Angleperview);
if (viewholderlist = = null) {
Viewholderlist = new arraylist<rotatecardviewholder> ();
} else {
Viewholderlist.clear ();
}
float Tmpangle = 270.0f;//The first view at 270 degrees, which is the top position
for (int i = 0; i < Viewnum; ++i) {
int index = i + 1;
View view = Viewlist.get (i);
Adding a slide event listener to a child view
View.setontouchlistener (Mtouchlistener);
Click event Listener to add sub view
View.setonclicklistener (New Itemonclicklistener (index));
Create a rotatecardviewholder for a child view
Rotatecardviewholder holder = new Rotatecardviewholder (view,
Tmpangle, index);
Bundle the holder on the child view and get the holder directly from the sub view
View.settag (holder);
Viewholderlist.add (holder);
Increase the angle of the angleperview to determine the position of the next view
Tmpangle + = Angleperview;
}
The handle view is added to the Rotatecard
Loadviewbysequence ();
}
/**
* Angle of all view rotation Anglediff
*
* @param Anglediff
* Angle of rotation
*/
private void Rotateviewsbyangle (float anglediff) {
If an animation is in progress, no rotation is made
if (set! = null && set.isrunning ()) {
Return
}
Rotate each view
for (Rotatecardviewholder holder:viewholderlist) {
Holder.setangle (Holder.getangle () + Anglediff);
}
Reload view to ensure they are properly occluded
Loadviewbysequence ();
}
/**
* To determine their occlusion relationship by re-adding the view to the Rotatecard after sorting the vew, the more slowly the previous view is added
*/
private void Loadviewbysequence () {
Collections.sort (Viewholderlist, New Rotatecardviewholdercomparator ());
This.removeallviews ();
for (Rotatecardviewholder holder:viewholderlist) {
This.addview (Holder.getview (), viewpparams);
}
}
/**
* Encapsulate some actions on view
*/
public class Rotatecardviewholder {
Private View MView;
private float MAngle;
private int index;
Public Rotatecardviewholder (view view, float angle, int index) {
This.mview = view;
This.mangle = angle;
This.index = index;
Setangle (angle);
}
/**
* Set Location and size
*/
public void Resetpositionandscale () {
Set size
float Anglediff = GETANGLEDIFFWITH90 ();
Float scale = (float) ((1.0 * anglediff/180) * 0.75 + 0.25);
Mview.setscalex (scale);
Mview.setscaley (scale);
Set Location
float x = (float) (Ovala * Math.Cos (mangle/180.0 * Math.PI)
+ PARENTWIDTH/2-VIEWWIDTH/2);
Float y = (float) (PARENTHEIGHT/2-Ovalb
* Math.sin (mangle/180.0 * math.pi)-VIEWHEIGHT/2);
Mview.setx (x);
Mview.sety (y);
}
/**
* Set the view angle while changing the size and position of his display
*
* @param angle
* Angle of view
*/
public void Setangle (float angle) {
Guarantee the mangle between 0~360
MAngle = angle;
while (MAngle < 0 | | mAngle > 360) {
if (MAngle < 0) {
MAngle = (mAngle + 360)% 360;
} else {
MAngle = mAngle% 360;
}
}
Resetpositionandscale ();
}
/**
* Get angle of view
*
* Angle of @return View
*/
public float Getangle () {
return mAngle;
}
/**
* Get the first sequence number of a view
*
* Number of @return view
*/
public int GetIndex () {
return index;
}
/**
* Get an instance of view
*
* Example of @return view
*/
Public View GetView () {
return mView;
}
/**
* Gets the degree of difference between the current position of the view and the 90 degree position
*
* @return degrees of difference from 90 degrees
*/
public float getAngleDiffWith90 () {
float Tmpangle = mAngle > 270? Mangle-360:mangle;
float Anglediff = Math.Abs (tmpAngle-90);
return Anglediff;
}
/**
* Gets the degree of difference between the current position of the view and the 270 degree position
*
* @return degrees of difference from 270 degrees
*/
public float getAngleDiffWith270 () {
float Tmpangle = MAngle < 90? MAngle + 360:mangle;
float Anglediff = Math.Abs (tmpAngle-270);
return Anglediff;
}
}
/**
* Comparator, which is used to sort the view in the order of 270 degrees more
*
*/
public class Rotatecardviewholdercomparator implements
comparator<rotatecardviewholder> {
@Override
public int Compare (Rotatecardviewholder A, Rotatecardviewholder b) {
TODO auto-generated Method Stub
return a.getanglediffwith270 () > b.getanglediffwith270 ()? -1:1;
}
}
Private float LASTX, nowx;//the x-coordinate position of the last finger and the x-coordinate position of the current finger
Private Velocitytracker vtracker;//to monitor finger slip utilization
Private Animatorset set;//Motion album
Private Boolean justcancelanimation = false;//has just clicked the screen so that the in-progress animation is canceled, the Click event that was used to cancel the animation of the colleague
@Override
public boolean dispatchtouchevent (Motionevent event) {
int action = Event.getaction ();
Switch (action) {
Case Motionevent.action_down: {
If the animation is in progress, cancel the animation
if (set! = null && set.isrunning ()) {
Set.cancel ();
set = NULL;
Justcancelanimation = true;
}
Get an instance of Velocitytracker
Vtracker = Velocitytracker.obtain ();
Record Lastx
LASTX = Event.getx ();
}
Break
Case Motionevent.action_move: {
Justcancelanimation = false;
Send the action to Vtracker.
Vtracker.addmovement (event);
Record Nowx
NOWX = Event.getx ();
}
Break
Case MOTIONEVENT.ACTION_UP: {
if (justcancelanimation) {
Justcancelanimation = false;
Return true;//used to cancel a click event
}
Set the calculation unit to 1 seconds
Vtracker.computecurrentvelocity (1000);
Gets the number of pixels across the x-axis for 1 seconds
float xspeed = vtracker.getxvelocity ();
The activity is too fast, then perform the animation, let the Rotatecard rotate for a while to stop
if (Math.Abs (xspeed) > 800.0f) {
and the angle to rotate.
float Anglediff = XSPEED/10;
And the time to rotate.
int duration = (int) (Math.Abs (ANGLEDIFF)/180 * 1000);
Set up a motion album
set = new Animatorset ();
Valueanimator animation = Valueanimator.offloat (0f, 1f)
. setduration (duration);
Animation.addupdatelistener (New Animatorupdatelistener () {
@Override
public void Onanimationupdate (Valueanimator animation) {
TODO auto-generated Method Stub
Loadviewbysequence ();
}
});
Set.play (animation);
for (Rotatecardviewholder _holder:viewholderlist) {
Objectanimator OA = objectanimator.offloat (_holder,
"Angle", _holder.getangle (),
_holder.getangle () + Anglediff). Setduration (
Duration);
Oa.setinterpolator (New Decelerateinterpolator ());
Set.play (OA). with (animation);
}
Start Animation Collection
Set.start ();
}
}
Break
}
Return Super.dispatchtouchevent (event);
}
Private class Rotatecardontouchlistener implements Ontouchlistener {
@Override
public boolean OnTouch (View V, motionevent event) {
TODO auto-generated Method Stub
int action = Event.getaction ();
Switch (action) {
Case Motionevent.action_down: {
}
Break
Case Motionevent.action_move: {
The degree of rotation required for this finger slide
float Anglediff = (nowx-lastx) * ANGLEOFFSET;
Angle of rotation Anglediff
Rotateviewsbyangle (Anglediff);
Record Lastx
LASTX = nowx;
}
Break
Case MOTIONEVENT.ACTION_UP: {
}
Break
}
if (v instanceof rotatecard) {
If the position of the finger is Rotatecard, return true to let the event continue to pass to Rotatecard
return true;
} else {
If the position of the finger is a child view, return False to allow the Click event of the child view to respond
return false;
}
}
}
public class Itemonclicklistener implements Onclicklistener {
private int index;
public itemonclicklistener (int index) {
This.index = index;
}
/**
* * Turn the clicked view to the front of the rotation
*/
@Override
public void OnClick (View v) {
TODO auto-generated Method Stub
Final View TView = v;
Final Rotatecardviewholder holder = (rotatecardviewholder) v
. Gettag ();
float angle = Holder.getangle ();
Degree of rotation required
float Anglediff = Angle >= 0.0f && angle <= 90.0f? -90.0f-angle
: 270.0f-angle;
Whether the child executes the Click event of the view (if the view is clicked in front, then the Click event is executed after the rotation)
Final Boolean PerformClick = Math.Abs (Anglediff) < 45.0f;
int duration = (int) (Math.Abs (ANGLEDIFF)/180 * 1000);
Initial Motion Album
set = new Animatorset ();
Valueanimator animation = Valueanimator.offloat (0f, 1f)
. setduration (duration);
Animation.addupdatelistener (New Animatorupdatelistener () {
@Override
public void Onanimationupdate (Valueanimator animation) {
TODO auto-generated Method Stub
Loadviewbysequence ();
}
});
Set.play (animation);
if (PerformClick) {
Objectanimator AA = Objectanimator.offloat (TView, "Alpha",
1.0f, 0.5f, 1.0f). Setduration (800);
Objectanimator SaX = objectanimator.offloat (TView, "ScaleX",
1.0f, 1.5f, 1.0f). Setduration (800);
Objectanimator SaY = objectanimator.offloat (TView, "ScaleY",
1.0f, 1.5f, 1.0f). Setduration (800);
Set.play (AA). with (animation);
Set.play (SaX). with (animation);
Set.play (SaY). with (animation);
}
for (Rotatecardviewholder _holder:viewholderlist) {
Objectanimator OA = objectanimator.offloat (_holder, "angle",
_holder.getangle (), _holder.getangle () + Anglediff)
. setduration (duration);
Oa.setinterpolator (New Decelerateinterpolator ());
Set.play (OA). with (animation);
}
Set.addlistener (New Animatorlisteneradapter () {
@Override
public void Onanimationend (Animator animation) {
TODO auto-generated Method Stub
if (Monitemclicklistener! = null && PerformClick) {
Perform external settings for click events
Monitemclicklistener.onitemclicklistener (TView, index);
}
}
@Override
public void Onanimationcancel (Animator animation) {
TODO auto-generated Method Stub
Tview.setalpha (1.0f);
Holder.resetpositionandscale ();
}
});
Set.start ();
}
}
/**
* Sub View Click event Listener Onitemclicklistener
*
* Rotatecard internal need to handle the child Vew click events, Onitemclicklistener Listener for external code use
*/
Public interface Onitemclicklistener {
public void Onitemclicklistener (view view, int index);
}
Private Onitemclicklistener Monitemclicklistener;
/**
* Add Sub View Click event Listener
*
* @param onitemclicklistener
*/
public void Setonitemclicklistener (Onitemclicklistener onitemclicklistener) {
This.monitemclicklistener = Onitemclicklistener;
}
}
2. Layout reference:
<us.eiyou.financial_management_service. Rotatecard
Android:id= "@+id/card"
Android:layout_width= "Match_parent"
android:layout_height= "400DP"
Android:layout_alignparentleft= "true"
Android:layout_centervertical= "true"/>
3. Activity Call:
/**
* Rotate View
*/
Rotatecard Card;
arraylist<view> views;
/**
* Rotate View
*/
Card = (Rotatecard) Findviewbyid (R.id.card);
views = new arraylist<view> ();
img = new ImageView (Getapplicationcontext ());
Img.setimageresource (R.DRAWABLE.HUILV);
Views.add (IMG);
img = new ImageView (Getapplicationcontext ());
Img.setimageresource (R.drawable.cunkuan);
Views.add (IMG);
img = new ImageView (Getapplicationcontext ());
Img.setimageresource (R.drawable.fangdai);
Views.add (IMG);
menu selection for rotary effects