您的位置:首页 > 其它

安卓Andriod使用入门(十)【3D立体无限旋转】

2016-11-15 14:49 579 查看

知足是天赋的财富;奢侈是人为的贫穷。——苏格拉底

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);//设置开始时item,注意不能是开头和结尾
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;//开始时的item位置
private float resistance = 1.8f;//滑动阻力
private Scroller mScroller;
private float mAngle = 90;//两个item间的夹角
private boolean isCan3D = true;//是否开启3D效果

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;//fling时正在添加新页面,在绘制时不需要开启camera绘制效果,否则页面会有闪动
private int mCurScreen = 1;//记录当前item
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();
//滑动到设置的StartScreen位置
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();
//滑动的速度大于规定的速度,或者向上滑动时,上一页页面展现出的高度超过1/2。则设定状态为State.ToPre
if (yVelocity > standerSpeed || ((getScrollY() + mHeight / 2) / mHeight < mStartScreen)) {
mState = State.ToPre;
} else if (yVelocity < -standerSpeed || ((getScrollY() + mHeight / 2) / mHeight > mStartScreen)) {
//滑动的速度大于规定的速度,或者向下滑动时,下一页页面展现出的高度超过1/2。则设定状态为State.ToNext
mState = State.ToNext;
} else {
mState = State.Normal;
}
//根据mState进行相应的变化
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();//增加新的页面
//计算松手后滑动的item个数
int flingSpeedCount = (yVelocity - standerSpeed) > 0 ? (int) (yVelocity - standerSpeed) : 0;
addCount = flingSpeedCount / flingSpeed + 1;
//mScroller开始的坐标
startY = getScrollY() + mHeight;
setScrollY(startY);
//mScroller移动的距离
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 {
//mState == State.Normal状态
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) {
//当开启3D效果并且当前状态不属于 computeScroll中 addPre() 或者addNext()
//如果不做这个判断,addPre() 或者addNext()时页面会进行闪动一下
//我当时写的时候就被这个坑了,后来通过log判断,原来是computeScroll中的onlayout,和子Child的draw触发的顺序导致的。
//知道原理的朋友希望可以告知下
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) {
//setScrollY(mStartScreen * mHeight);
toNextAction(-standerSpeed - flingSpeed * (itemId - mCurScreen - 1));
} else if (itemId < mCurScreen) {
//setScrollY(mStartScreen * mHeight);
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>


运行结果如图:





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: