知足是天赋的财富;奢侈是人为的贫穷。——苏格拉底
threeddemoActivity.java代码:
package com.siso.crazyworld;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class threeddemoActivity extends AppCompatActivity {
private Button btnLogin;
private Button btnSetting;
private Button btnImage;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threeddemo);
btnLogin = (Button) findViewById(R.id.btn_login);
btnSetting = (Button) findViewById(R.id.btn_setting);
btnImage = (Button) findViewById(R.id.btn_image);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(threeddemoActivity.this, LoginActivity.class);
startActivity(intent);
}
});
btnSetting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(threeddemoActivity.this, SettingActivity.class);
startActivity(intent);
}
});
btnImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(threeddemoActivity.this, ImageActivity.class);
startActivity(intent);
}
});
}
}
SettingActivity.java代码:
package com.siso.crazyworld;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.BounceInterpolator;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import com.siso.crazyworld.custom.StereoView;
public class SettingActivity extends AppCompatActivity {
private StereoView stereoView1;
private StereoView stereoView2;
private SeekBar sbAngle;
private TextView tvAngle;
private Button btnNext;
private Button btnPre;
private Button btnSelect;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setting);
initView();
initStereoView();
sbAngle.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
stereoView1.setAngle(i);
tvAngle.setText("设置3D旋转页面夹角,当前夹角 " + i);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
btnPre.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stereoView1.toPre();
}
});
btnNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stereoView1.toNext();
}
});
btnSelect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stereoView1.setItem(3);
}
});
}
private void initView() {
stereoView1 = (StereoView) findViewById(R.id.stereoView1);
stereoView2 = (StereoView) findViewById(R.id.stereoView2);
sbAngle = (SeekBar) findViewById(R.id.sb_angle);
tvAngle = (TextView) findViewById(R.id.tv_angle);
btnPre = (Button) findViewById(R.id.btn_pre);
btnNext = (Button) findViewById(R.id.btn_next);
btnSelect = (Button) findViewById(R.id.btn_select);
}
private void initStereoView() {
stereoView1.setResistance(4f)
.setInterpolator(new BounceInterpolator())
.setStartScreen(2);
stereoView2.setCan3D(false);
}
}
LoginActivity.java代码:
package com.siso.crazyworld;
import android.animation.ObjectAnimator;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.siso.crazyworld.custom.RippleView;
import com.siso.crazyworld.custom.StereoView;
import com.siso.crazyworld.utils.LogUtil;
import com.siso.crazyworld.utils.ToastUtil;
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
private EditText etUsername;
private EditText etEmail;
private EditText etPassword;
private RippleView rvUsername;
private RippleView rvEmail;
private RippleView rvPassword;
private StereoView stereoView;
private LinearLayout lyWelcome;
private TextView tvWelcome;
private int translateY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
stereoView.setStartScreen(2);
stereoView.post(new Runnable() {
@Override
public void run() {
int[] location = new int[2];
stereoView.getLocationOnScreen(location);
translateY = location[1];
}
});
stereoView.setiStereoListener(new StereoView.IStereoListener() {
@Override
public void toPre(int curScreen) {
LogUtil.m("跳转到前一页 " + curScreen);
}
@Override
public void toNext(int curScreen) {
LogUtil.m("跳转到下一页 " + curScreen);
}
});
}
private void initView() {
stereoView = (StereoView) findViewById(R.id.stereoView);
etUsername = (EditText) findViewById(R.id.et_username);
etEmail = (EditText) findViewById(R.id.et_email);
etPassword = (EditText) findViewById(R.id.et_password);
rvUsername = (RippleView) findViewById(R.id.rv_username);
rvEmail = (RippleView) findViewById(R.id.rv_email);
rvPassword = (RippleView) findViewById(R.id.rv_password);
lyWelcome = (LinearLayout) findViewById(R.id.ly_welcome);
tvWelcome = (TextView) findViewById(R.id.tv_welcome);
rvUsername.setOnClickListener(this);
rvEmail.setOnClickListener(this);
rvPassword.setOnClickListener(this);
tvWelcome.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.rv_username:
rvUsername.setiRippleAnimListener(new RippleView.IRippleAnimListener() {
@Override
public void onComplete(View view) {
stereoView.toPre();
}
});
break;
case R.id.rv_email:
rvEmail.setiRippleAnimListener(new RippleView.IRippleAnimListener() {
@Override
public void onComplete(View view) {
stereoView.toPre();
}
});
break;
case R.id.rv_password:
rvPassword.setiRippleAnimListener(new RippleView.IRippleAnimListener() {
@Override
public void onComplete(View view) {
stereoView.toPre();
}
});
break;
case R.id.tv_welcome:
if (TextUtils.isEmpty(etUsername.getText())) {
ToastUtil.showInfo(LoginActivity.this, "请输入用户名!");
stereoView.setItem(2);
return;
}
if (TextUtils.isEmpty(etEmail.getText())) {
ToastUtil.showInfo(LoginActivity.this, "请输入邮箱!");
stereoView.setItem(1);
return;
}
if (TextUtils.isEmpty(etPassword.getText())) {
ToastUtil.showInfo(LoginActivity.this, "请输入密码!");
stereoView.setItem(0);
return;
}
startExitAnim();
break;
}
}
private void startExitAnim() {
ObjectAnimator animator = ObjectAnimator.ofFloat(stereoView, "translationY", 0, 100, -translateY);
animator.setDuration(500).start();
ToastUtil.showInfo(LoginActivity.this, "登录成功 =.=");
}
}
ImageActivity.java代码:
package com.siso.crazyworld;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class ImageActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
}
}
utils文件夹下LogUtil.java和ToastUtil.java
package com.siso.crazyworld.utils;
import android.util.Log;
/**
*/
public class LogUtil {
/**
* 默认的tag
*/
public static final String defaultTag = "dota";
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int NOTHING = 6;
/**
* 下面这个变量定义日志级别
*/
public static final int LEVEL = VERBOSE;
public static void v(String tag, String msg) {
if (LEVEL <= VERBOSE) {
Log.v(tag, msg);
}
}
public static void d(String tag, String msg) {
if (LEVEL <= DEBUG) {
Log.d(tag, msg);
}
}
public static void i(String tag, String msg) {
if (LEVEL <= INFO) {
Log.i(tag, msg);
}
}
public static void w(String tag, String msg) {
if (LEVEL <= WARN) {
Log.w(tag, msg);
}
}
public static void e(String tag, String msg) {
if (LEVEL <= ERROR) {
Log.e(tag, msg);
}
}
public static void v(String msg) {
if (LEVEL <= VERBOSE) {
Log.v(defaultTag, msg);
}
}
public static void d(String msg) {
if (LEVEL <= DEBUG) {
Log.d(defaultTag, msg);
}
}
public static void i(String msg) {
if (LEVEL <= INFO) {
Log.i(defaultTag, msg);
}
}
public static void w(String msg) {
if (LEVEL <= WARN) {
Log.w(defaultTag, msg);
}
}
public static void e(String msg) {
if (LEVEL <= ERROR) {
Log.e(defaultTag, msg);
}
}
public static void m(String msg){
String methodName = new Exception().getStackTrace()[1].getMethodName();
Log.v(defaultTag,methodName+": "+msg);
}
public static void m(int msg){
String methodName = new Exception().getStackTrace()[1].getMethodName();
Log.v(defaultTag,methodName+": "+msg+"");
}
public static void m(){
String methodName = new Exception().getStackTrace()[1].getMethodName();
Log.v(defaultTag,methodName);
}
public static void v(int msg) {
LogUtil.v(msg + "");
}
public static void d(int msg) {
LogUtil.d(msg + "");
}
public static void i(int msg) {
LogUtil.i(msg + "");
}
public static void w(int msg) {
LogUtil.w(msg + "");
}
public static void e(int msg) {
LogUtil.e(msg + "");
}
}
package com.siso.crazyworld.utils;
import android.content.Context;
import android.widget.Toast;
public class ToastUtil {
public static void showInfo(Context context, String info) {
Toast.makeText(context, info, Toast.LENGTH_SHORT).show();
}
}
custom文件夹下RippleView.java和StereoView.java
package com.siso.crazyworld.custom;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import com.siso.crazyworld.R;
public class RippleView extends View {
private Paint maxPaint;
private Paint minPaint;
private Paint bgPaint;
private Drawable bgNormal;
private Drawable bgPressed;
private Bitmap bgBitmapNormal;
private Bitmap bgBitmapPressed;
private int mWidth;
private int mHeight;
private int radius;
private int maxRadius;
private int minRadius;
private RippleAnim rippleAnim;
private float mInterpolatedTime;
private Region region;
private Rect rect;
private boolean isPressed = false;
private IRippleAnimListener iRippleAnimListener;
public RippleView(Context context) {
this(context, null);
}
public RippleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RippleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
bgPressed = ta.getDrawable(R.styleable.RippleView_pressed);
bgNormal = ta.getDrawable(R.styleable.RippleView_normal);
ta.recycle();
init();
}
private void init() {
maxPaint = new Paint();
maxPaint.setColor(getResources().getColor(R.color.bg_gray));
minPaint = new Paint();
minPaint.setColor(getResources().getColor(R.color.bg_white));
bgPaint = new Paint();
bgPaint.setAntiAlias(true);
if (bgNormal != null) {
bgBitmapNormal = ((BitmapDrawable) bgNormal).getBitmap();
}
if (bgPressed != null) {
bgBitmapPressed = ((BitmapDrawable) bgPressed).getBitmap();
}
region = new Region();
rect = new Rect();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
radius = Math.min(mWidth / 2, mHeight / 2);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.translate(getPaddingLeft(), 0);
maxRadius = (int) (mInterpolatedTime * radius * 1.2f);
if (maxRadius > radius * 0.8f) {
minRadius = (int) ((mInterpolatedTime - 0.6) * radius * 3);
}
if (mInterpolatedTime != 1.0f) {
canvas.drawCircle(mWidth / 2, mHeight / 2, maxRadius, maxPaint);
canvas.drawCircle(mWidth / 2, mHeight / 2, minRadius, minPaint);
} else {
clearState();
}
if (bgBitmapNormal != null && !isPressed) {
Rect rect = new Rect();
rect.set(mWidth * 5 / 16, mHeight * 5 / 16, mWidth * 11 / 16, mHeight * 11 / 16);
canvas.drawBitmap(bgBitmapNormal, null, rect, bgPaint);
} else if (bgBitmapPressed != null && isPressed) {
Rect rect = new Rect();
rect.set(mWidth * 5 / 16, mHeight * 5 / 16, mWidth * 11 / 16, mHeight * 11 / 16);
canvas.drawBitmap(bgBitmapPressed, null, rect, bgPaint);
}
}
private void clearState() {
minRadius = 0;
maxRadius = 0;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (isContain(event)) {
isPressed = true;
startRipple();
} else {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(event);
}
private boolean isContain(MotionEvent event) {
rect.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
region.set(rect);
if (region.contains((int) event.getX(), (int) event.getY())) {
return true;
}
return false;
}
private void startRipple() {
if (rippleAnim == null) {
rippleAnim = new RippleAnim();
}
rippleAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (iRippleAnimListener != null) {
iRippleAnimListener.onComplete(RippleView.this);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
rippleAnim.cancel();
rippleAnim.setDuration(600);
startAnimation(rippleAnim);
}
private class RippleAnim extends Animation {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
mInterpolatedTime = interpolatedTime;
invalidate();
}
}
public interface IRippleAnimListener{
void onComplete(View view);
}
public void setiRippleAnimListener(IRippleAnimListener iRippleAnimListener) {
this.iRippleAnimListener = iRippleAnimListener;
}
}
package com.siso.crazyworld.custom;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.Scroller;
import com.siso.crazyworld.utils.LogUtil;
public class StereoView extends ViewGroup {
private int mStartScreen = 1;
private float resistance = 1.8f;
private Scroller mScroller;
private float mAngle = 90;
private boolean isCan3D = true;
private Context mContext;
private int mTouchSlop;
private VelocityTracker mVelocityTracker;
private Camera mCamera;
private Matrix mMatrix;
private int mWidth;
private int mHeight;
private static final int standerSpeed = 2000;
private static final int flingSpeed = 800;
private int addCount;
private int alreadyAdd = 0;
private boolean isAdding = false;
private int mCurScreen = 1;
private IStereoListener iStereoListener;
private float mDownX, mDownY, mTempY;
private boolean isSliding = false;
private State mState = State.Normal;
public StereoView(Context context) {
this(context, null);
}
public StereoView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public StereoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
init(mContext);
}
/**
* 初始化数据
*/
private void init(Context context) {
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mCamera = new Camera();
mMatrix = new Matrix();
if (mScroller == null) {
mScroller = new Scroller(context);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
scrollTo(0, mStartScreen * mHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childTop = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
child.layout(0, childTop,
child.getMeasuredWidth(), childTop + child.getMeasuredHeight());
childTop = childTop + child.getMeasuredHeight();
}
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
float x = ev.getX();
float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
isSliding = false;
mDownX = x;
mTempY = mDownY = y;
if (!mScroller.isFinished()) {
mScroller.setFinalY(mScroller.getCurrY());
mScroller.abortAnimation();
scrollTo(0, getScrollY());
isSliding = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (!isSliding) {
isSliding = isCanSliding(ev);
}
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isSliding;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
if (isSliding) {
int realDelta = (int) (mDownY - y);
mDownY = y;
if (mScroller.isFinished()) {
recycleMove(realDelta);
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (isSliding) {
isSliding = false;
mVelocityTracker.computeCurrentVelocity(1000);
float yVelocity = mVelocityTracker.getYVelocity();
if (yVelocity > standerSpeed || ((getScrollY() + mHeight / 2) / mHeight < mStartScreen)) {
mState = State.ToPre;
} else if (yVelocity < -standerSpeed || ((getScrollY() + mHeight / 2) / mHeight > mStartScreen)) {
mState = State.ToNext;
} else {
mState = State.Normal;
}
changeByState(yVelocity);
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
return super.onTouchEvent(event);
}
public boolean isCanSliding(MotionEvent ev) {
float moveX;
float moveY;
moveX = ev.getX();
mTempY = moveY = ev.getY();
if (Math.abs(moveY - mDownX) > mTouchSlop && (Math.abs(moveY - mDownY) > (Math.abs(moveX - mDownX)))) {
return true;
}
return false;
}
private void changeByState(float yVelocity) {
alreadyAdd = 0;
if (getScrollY() != mHeight) {
switch (mState) {
case Normal:
toNormalAction();
break;
case ToPre:
toPreAction(yVelocity);
break;
case ToNext:
toNextAction(yVelocity);
break;
}
invalidate();
}
}
/**
* mState = State.Normal 时进行的动作
*/
private void toNormalAction() {
int startY;
int delta;
int duration;
mState = State.Normal;
addCount = 0;
startY = getScrollY();
delta = mHeight * mStartScreen - getScrollY();
duration = (Math.abs(delta)) * 4;
mScroller.startScroll(0, startY, 0, delta, duration);
}
/**
* mState = State.ToPre 时进行的动作
*
* @param yVelocity 竖直方向的速度
*/
private void toPreAction(float yVelocity) {
int startY;
int delta;
int duration;
mState = State.ToPre;
addPre();
int flingSpeedCount = (yVelocity - standerSpeed) > 0 ? (int) (yVelocity - standerSpeed) : 0;
addCount = flingSpeedCount / flingSpeed + 1;
startY = getScrollY() + mHeight;
setScrollY(startY);
delta = -(startY - mStartScreen * mHeight) - (addCount - 1) * mHeight;
duration = (Math.abs(delta)) * 3;
mScroller.startScroll(0, startY, 0, delta, duration);
addCount--;
}
/**
* mState = State.ToNext 时进行的动作
*
* @param yVelocity 竖直方向的速度
*/
private void toNextAction(float yVelocity) {
int startY;
int delta;
int duration;
mState = State.ToNext;
addNext();
int flingSpeedCount = (Math.abs(yVelocity) - standerSpeed) > 0 ? (int) (Math.abs(yVelocity) - standerSpeed) : 0;
addCount = flingSpeedCount / flingSpeed + 1;
startY = getScrollY() - mHeight;
setScrollY(startY);
delta = mHeight * mStartScreen - startY + (addCount - 1) * mHeight;
LogUtil.m("多后一页startY " + startY + " yVelocity " + yVelocity + " delta " + delta + " getScrollY() " + getScrollY() + " addCount " + addCount);
duration = (Math.abs(delta)) * 3;
mScroller.startScroll(0, startY, 0, delta, duration);
addCount--;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
if (mState == State.ToPre) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY() + mHeight * alreadyAdd);
if (getScrollY() < (mHeight + 2) && addCount > 0) {
isAdding = true;
addPre();
alreadyAdd++;
addCount--;
}
} else if (mState == State.ToNext) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY() - mHeight * alreadyAdd);
if (getScrollY() > (mHeight) && addCount > 0) {
isAdding = true;
addNext();
addCount--;
alreadyAdd++;
}
} else {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
}
postInvalidate();
}
if (mScroller.isFinished()) {
alreadyAdd = 0;
addCount = 0;
}
}
/**
* 把第一个item移动到最后一个item位置
*/
private void addNext() {
mCurScreen = (mCurScreen + 1) % getChildCount();
int childCount = getChildCount();
View view = getChildAt(0);
removeViewAt(0);
addView(view, childCount - 1);
if (iStereoListener != null) {
iStereoListener.toNext(mCurScreen);
}
}
/**
* 把最后一个item移动到第一个item位置
*/
private void addPre() {
mCurScreen = ((mCurScreen - 1) + getChildCount()) % getChildCount();
int childCount = getChildCount();
View view = getChildAt(childCount - 1);
removeViewAt(childCount - 1);
addView(view, 0);
if (iStereoListener != null) {
iStereoListener.toPre(mCurScreen);
}
}
private void recycleMove(int delta) {
delta = delta % mHeight;
delta = (int) (delta / resistance);
if (Math.abs(delta) > mHeight / 4) {
return;
}
scrollBy(0, delta);
if (getScrollY() < 5 && mStartScreen != 0) {
addPre();
scrollBy(0, mHeight);
} else if (getScrollY() > (getChildCount() - 1) * mHeight - 5) {
addNext();
scrollBy(0, -mHeight);
}
}
public enum State {
Normal, ToPre, ToNext
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (!isAdding && isCan3D) {
for (int i = 0; i < getChildCount(); i++) {
drawScreen(canvas, i, getDrawingTime());
}
} else {
isAdding = false;
super.dispatchDraw(canvas);
}
}
private void drawScreen(Canvas canvas, int i, long drawingTime) {
int curScreenY = mHeight * i;
if (getScrollY() + mHeight < curScreenY) {
return;
}
if (curScreenY < getScrollY() - mHeight) {
return;
}
float centerX = mWidth / 2;
float centerY = (getScrollY() > curScreenY) ? curScreenY + mHeight : curScreenY;
float degree = mAngle * (getScrollY() - curScreenY) / mHeight;
if (degree > 90 || degree < -90) {
return;
}
canvas.save();
mCamera.save();
mCamera.rotateX(degree);
mCamera.getMatrix(mMatrix);
mCamera.restore();
mMatrix.preTranslate(-centerX, -centerY);
mMatrix.postTranslate(centerX, centerY);
canvas.concat(mMatrix);
drawChild(canvas, getChildAt(i), drawingTime);
canvas.restore();
}
/**
* 设置第一页展示的页面
*
* @param startScreen (0,getChildCount-1)
* @return
*/
public StereoView setStartScreen(int startScreen) {
if (startScreen <= 0 || startScreen >= (getChildCount() - 1)) {
throw new IndexOutOfBoundsException("请输入规定范围内startScreen位置号");
}
this.mStartScreen = startScreen;
this.mCurScreen = startScreen;
return this;
}
/**
* 设置移动阻力
*
* @param resistance (0,...)
* @return
*/
public StereoView setResistance(float resistance) {
this.resistance = resistance;
return this;
}
/**
* 设置滚动时interpolator插补器
*
* @param mInterpolator
* @return
*/
public StereoView setInterpolator(Interpolator mInterpolator) {
mScroller = new Scroller(mContext, mInterpolator);
return this;
}
/**
* 设置滚动时两个item的夹角度数
*
* @param mAngle [0f,180f]
* @return
*/
public StereoView setAngle(float mAngle) {
this.mAngle = 180f - mAngle;
return this;
}
/**
* 是否开启3D效果
*
* @param can3D
* @return
*/
public StereoView setCan3D(boolean can3D) {
isCan3D = can3D;
return this;
}
/**
* 跳转到指定的item
*
* @param itemId [0,getChildCount-1]
* @return
*/
public StereoView setItem(int itemId) {
LogUtil.m("之前curScreen " + mCurScreen);
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
LogUtil.m("强制完成");
}
if (itemId < 0 || itemId > (getChildCount() - 1)) {
throw new IndexOutOfBoundsException("请输入规定范围内item位置号");
}
if (itemId > mCurScreen) {
toNextAction(-standerSpeed - flingSpeed * (itemId - mCurScreen - 1));
} else if (itemId < mCurScreen) {
toPreAction(standerSpeed + (mCurScreen - itemId - 1) * flingSpeed);
}
LogUtil.m("之后curScreen " + mCurScreen + " getScrollY " + getScrollY());
return this;
}
/**
* 上一页
*
* @return
*/
public StereoView toPre() {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
LogUtil.m("强制完成");
}
toPreAction(standerSpeed);
return this;
}
/**
* 下一页
*
* @return
*/
public StereoView toNext() {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
LogUtil.m("强制完成");
}
toNextAction(-standerSpeed);
return this;
}
public interface IStereoListener {
void toPre(int curScreen);
void toNext(int curScreen);
}
public void setiStereoListener(IStereoListener iStereoListener) {
this.iStereoListener = iStereoListener;
}
}
activity_threeddemo.xml内容:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="登录"/>
<Button
android:id="@+id/btn_setting"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="调节样式"/>
<Button
android:id="@+id/btn_image"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="图片展示"/>
</LinearLayout>
activity_setting.xml内容:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_blue_deep"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="3D效果"
android:textColor="@color/font_white"/>
<com.siso.crazyworld.custom.StereoView
android:id="@+id/stereoView1"
android:layout_width="300dp"
android:layout_height="60dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_password"/>
<EditText
android:id="@+id/et_password"
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:hint="0"
android:inputType="textPassword"
android:textColorHint="@color/font_gray"/>
<com.siso.crazyworld.custom.RippleView
android:id="@+id/rv_password"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_email"/>
<EditText
android:id="@+id/et_email"
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:hint="1"
android:textColorHint="@color/font_gray"/>
<com.siso.crazyworld.custom.RippleView
android:id="@+id/rv_email"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_user"/>
<EditText
android:id="@+id/et_username"
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:hint="2"
android:textColorHint="@color/font_gray"
android:textSize="18sp"/>
<com.siso.crazyworld.custom.RippleView
android:id="@+id/rv_username"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_love"/>
<TextView
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:gravity="center"
android:hint="3"
android:textColorHint="@color/font_gray"
android:textSize="18sp"/>
<com.siso.crazyworld.custom.RippleView
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_love"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_love"/>
<TextView
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:gravity="center"
android:hint="4"
android:textColorHint="@color/font_gray"
android:textSize="18sp"/>
<com.siso.crazyworld.custom.RippleView
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_love"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ly_welcome"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_love"/>
<TextView
android:id="@+id/tv_welcome"
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:gravity="center"
android:hint="5"
android:textColorHint="@color/font_gray"
android:textSize="18sp"/>
<com.siso.crazyworld.custom.RippleView
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_love"/>
</LinearLayout>
</com.siso.crazyworld.custom.StereoView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="平面效果"
android:textColor="@color/font_white"/>
<com.siso.crazyworld.custom.StereoView
android:id="@+id/stereoView2"
android:layout_width="300dp"
android:layout_height="60dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_password"/>
<EditText
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:hint="0"
android:inputType="textPassword"
android:textColorHint="@color/font_gray"/>
<com.siso.crazyworld.custom.RippleView
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_email"/>
<EditText
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:hint="1"
android:textColorHint="@color/font_gray"/>
<com.siso.crazyworld.custom.RippleView
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_user"/>
<EditText
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:hint="2"
android:textColorHint="@color/font_gray"
android:textSize="18sp"/>
<com.siso.crazyworld.custom.RippleView
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_love"/>
<TextView
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:gravity="center"
android:hint="3"
android:textColorHint="@color/font_gray"
android:textSize="18sp"/>
<com.siso.crazyworld.custom.RippleView
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_love"/>
</LinearLayout>
</com.siso.crazyworld.custom.StereoView>
<TextView
android:id="@+id/tv_angle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="设置3D旋转页面夹角"
android:textColor="@color/font_white"/>
<SeekBar
android:id="@+id/sb_angle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:thumbOffset="0dip"
android:max="180"/>
<Button
android:text="上一页"
android:id="@+id/btn_pre"
android:layout_width="match_parent"
android:layout_height="60dp"/>
<Button
android:text="下一页"
android:id="@+id/btn_next"
android:layout_width="match_parent"
android:layout_height="60dp"/>
<Button
android:text="跳转到第4页"
android:id="@+id/btn_select"
android:layout_width="match_parent"
android:layout_height="60dp"/>
</LinearLayout>
activity_login.xml内容:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_blue_deep"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<com.siso.crazyworld.custom.StereoView
android:id="@+id/stereoView"
android:layout_width="300dp"
android:layout_height="60dp"
android:layout_marginTop="50dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_password"/>
<EditText
android:id="@+id/et_password"
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:hint="Password"
android:inputType="textPassword"
android:textColorHint="@color/font_gray"/>
<com.siso.crazyworld.custom.RippleView
android:id="@+id/rv_password"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_email"/>
<EditText
android:id="@+id/et_email"
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:singleLine="true"
android:hint="Email"
android:textColorHint="@color/font_gray"/>
<com.siso.crazyworld.custom.RippleView
android:id="@+id/rv_email"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_user"/>
<EditText
android:id="@+id/et_username"
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:singleLine="true"
android:background="@null"
android:hint="UserName"
android:textColorHint="@color/font_gray"
android:textSize="18sp"/>
<com.siso.crazyworld.custom.RippleView
android:id="@+id/rv_username"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_next_gray"
app:pressed="@drawable/icon_next"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ly_welcome"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:paddingLeft="20dp">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:background="@drawable/icon_love"/>
<TextView
android:id="@+id/tv_welcome"
android:layout_width="180dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:singleLine="true"
android:gravity="center"
android:hint="You are welcome !"
android:textColorHint="@color/font_gray"
android:textSize="18sp"/>
<com.siso.crazyworld.custom.RippleView
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="10dp"
app:normal="@drawable/icon_love"/>
</LinearLayout>
</com.siso.crazyworld.custom.StereoView>
</LinearLayout>
activity_image.xml内容:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_blue_deep"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<com.siso.crazyworld.custom.StereoView
android:id="@+id/stereoView"
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_marginTop="50dp">
<ImageView
android:background="@drawable/p10"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:background="@drawable/p14"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:background="@drawable/p12"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:background="@drawable/p11"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.siso.crazyworld.custom.StereoView>
</LinearLayout>
运行结果如图:


