安卓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>
运行结果如图:
相关文章推荐
- 安卓Andriod使用入门(六)【动态GridView控件】
- 安卓Andriod使用入门(十七)【卡片视图的形式管理Activity】
- 安卓Andriod使用入门(八)【联动ListView】
- 安卓Andriod使用入门(二十五)【爬新闻】
- 安卓Andriod使用入门(十三)【九宫格展示图片】
- 安卓Andriod使用入门(二十三)【百度地图API】
- 安卓Andriod使用入门(十九)【视频播放列表】
- 安卓Andriod使用入门(二)【高仿安卓微信6.0】
- 安卓Andriod使用入门(十六)【地图定位 选址】
- Android 3D立体无限旋转滚动容器
- 安卓Andriod使用入门(二十二)【漂亮酷炫的引导页】
- iOS 使用 CATransform3D 处理 3D 影像、制做互动立体旋转的效果
- 安卓Andriod使用入门(二十一)【视差效果引导页】
- 安卓Andriod使用入门(十九)【网络视频播放器】
- 安卓Andriod使用入门(九)【悬浮窗菜单】
- Android 3D 立体无限旋转滚动容器
- 安卓Andriod使用入门(一)【圆形Menu菜单】
- 安卓Andriod使用入门(二十四)【爬取网络小说】
- 针对安卓java入门:方法的使用
- [原]OpenGL ES 2.0 -- 制作 3D 彩色旋转三角形 - 顶点着色器 片元着色器 使用详解