(二十五)3D 翻轉效果
一、ViewPager 的 3D 效果
1.ViewPager
先直接上個簡單的 ViewPager 的 demo。
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<android.support.v4.view.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
TranslateFragment
public class TranslateFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Bundle bundle = getArguments();
int layoutId = bundle.getInt("layoutId");
View view = inflater.inflate(layoutId, null);
return view;
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private ViewPager vp;
//這三個佈局介面就不貼出來,隨便一個介面都可以
private int[] layouts = {
R.layout.welcome1,
R.layout.welcome2,
R.layout.welcome3
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vp = (ViewPager) findViewById(R.id.vp);
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
vp.setAdapter(adapter);
}
class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Fragment f = new TranslateFragment();
Bundle bundle = new Bundle();
bundle.putInt("layoutId", layouts[position]);
f.setArguments(bundle );
return f;
}
@Override
public int getCount() {
return 3;
}
}
}
使用 v4 支援包下的 ViewPager,可以很方便的做出分介面滑動效果。執行效果:
2.TransFormer
ViewPager 在切換介面的時候想要使用 3D 效果,需要用到一個介面轉換器 PageTransformer,也是在 v4 支援包裡面(v4 包會不斷更新,較早的 v4 包沒有 PageTransformer),PageTransformer 是一個介面,需要實現。
實現 PageTransformer 介面,需要實現 transformPage(View page, float position) 方法,在滑動過程中,每一個頁面 View 都會不停呼叫這個方法。
transformPage(View page, float position)
page:當前頁
position:當前頁的位置,在螢幕正中間為 0,在左邊為負,右邊為正。
來個簡單的 PageTransformer,滑動的時候,當前介面慢慢縮小滑出去,下一個介面慢慢放大進入螢幕。
WelcomeTransFormer
public class WelcomeTransFormer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
page.setScaleX(1 - Math.abs(position));
page.setScaleY(1 - Math.abs(position));
}
}
記得要呼叫 ViewPager 的 setPageTransformer。
MainActivity
public class MainActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vp = (ViewPager) findViewById(R.id.vp);
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
vp.setPageTransformer(true, new WelcomeTransFormer());
vp.setAdapter(adapter);
}
...
}
效果:
3.3D 翻轉
在切換過程中,讓當前介面與下一個介面繞公共邊進行旋轉即可。
WelcomeTransFormer
public class WelcomeTransFormer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
//0~45度
if (position > -1 && position < 1){
page.setPivotX(position < 0f?page.getWidth() : 0f);//左邊頁面:0~-1;右邊的頁面:1~0
//不進行設定旋轉點的 Y 也可以,預設中點。
page.setPivotY(page.getHeight()*0.5f);
//0~45度
page.setRotationY(position*45f);
}
}
效果:
注:個人測試時候在華為安卓7.0的手機,旋轉效果直接白屏,原因未知。有人說是華為手機在 3D 這塊效果處理一直有問題,但是使用三星的模擬器,在一些 3D 效果偶爾也碰到問題。
二、Camera
android.graphics.Camera.java,系統還有個是硬體的 Camera,別導錯了。
原始碼裡面的 Camera 備註:用來計算 3D 變換,以及生成一個用於畫布繪製的 matrix 矩陣。
/**
* A camera instance can be used to compute 3D transformations and
* generate a matrix that can be applied, for instance, on a
* {@link Canvas}.
*/
Camera 內部實際也是用上 OpenGL。很多時候我們用 OpenGL 做 3D 特效,其實 Camera 可以滿足絕大部分的需要。 Camera 內部很多方法是 native 的,由底層 C 去實現呼叫。
1.Camera 的方向
Camera X 的正方向是水平向右,Y 的正方向是向上的,這點與螢幕的方向不一樣,Z 的正方向是遠離眼睛的方向(效果就是 Z 為正,縮小; Z 為負,放大)。
2.Camera 簡單 Demo
自定義 Camera 類,直接畫一個圓。
CameraView
public class CameraView extends View {
private Paint paint;
public CameraView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(400, 400, 300, paint);
}
}
在佈局檔案引用改控制元件。
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.xiaoyue.camera.MainActivity">
<com.xiaoyue.camera.CameraView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
效果:
3.使用 Camera 進行變化
CameraView
public class CameraView extends View {
private Camera camera;
private Matrix matrix;
private Paint paint;
public CameraView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
camera = new Camera();
matrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
camera.save();
camera.translate(100, 100, 0);
camera.getMatrix(matrix);
camera.restore();
canvas.concat(matrix);
canvas.drawCircle(400, 400, 300, paint);
}
}
效果:
這邊在 Y 方向上是正的,但是往上平移,再次說明一下:Camera 裡面的座標系跟 Android 的螢幕座標系不一樣。
4.Camera 的 Z 方向
CameraView
public class CameraView extends View {
...
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
camera.save();
camera.translate(100, 100, 400);
...
}
}
效果:
Z 方向為正,即遠離眼睛方向,所以對比上一個沒有進行 Z 方向變化的圓,這個圓縮小了。
三、Camera 的旋轉
新建一個 CameraView2 的自定義控制元件,對圖片進行旋轉。
CameraView2 :
public class CameraView2 extends View {
private final Camera camera;
private final Matrix matrix;
private Paint paint;
Bitmap bitmap;
public CameraView2(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
camera = new Camera();
matrix = new Matrix();
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.zuobiao);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
camera.save();
camera.rotateX(45);
camera.getMatrix(matrix);
camera.restore();
canvas.drawBitmap(bitmap,matrix,paint);
}
}
效果:
繞 X 軸旋轉,則圖片的下方離眼睛較近,所以放大。有時候我們需要對圖片進行中心旋轉,我們沒辦法把圖片的中點設為旋轉點,所以需要在旋轉前把圖片的中點移到左上角,旋轉後在移動回來。
Camera 的旋轉最終是通過矩陣來實現的,可以直接對矩陣進行前乘和後乘進行設定。
CameraView2 :
public class CameraView2 extends View {
...
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
camera.save();
camera.rotateX(45);//繞著X軸旋轉
camera.getMatrix(matrix);
camera.restore();
//前乘矩陣---先平移矩陣,再進行變換
matrix.preTranslate(-bitmap.getWidth()/2,-bitmap.getHeight()/2);
//變換玩之後,再恢復到原來的位置
matrix.postTranslate(bitmap.getWidth()/2,bitmap.getHeight()/2);
canvas.drawBitmap(bitmap,matrix,paint);
}
}
效果:
四、Camera 的翻轉動畫
先來看一下效果:
2D 滾動:
3D 整體滾動:
尾部逐漸分離再合併:
百葉窗:
各模組依次滾動:
注:這些效果我在華為 7.0 系統執行還是有一些異常,各型號手機不確定是否有效果,應該跟手機的底層對 Carema 處理有關。
這邊採用一個自定義控制元件實現,翻轉模式在 xml 中根據自定義屬性進行設定。這邊直接上程式碼了,具體內容都解除安裝註釋裡面。
Camera3DView
public class Camera3DView extends View {
private Context context;
//畫筆
private Paint paint;
//畫布
private Camera camera;
//矩陣
private Matrix matrix;
//圖片集合
private List<Bitmap> bitmapList;
//每個圖片要切割成小塊的個數
private int partNumber = 1;
//切割後圖片的儲存(有多張圖片)
private List<Bitmap[]> bitmapsList;
//滾動方向:1豎直方向 其他為水平方向
private int direction = 1;
//滾動模式
private RollMode rollMode = RollMode.SepartConbine;
//圖片下標,前一個,當前個、下一個
private int preIndex = 0, currIndex = 0, nextIndex = 0;
//控制元件的寬高
private int viewWidth, viewHeight;
//切割小塊的寬高
int averageWidth = 0, averageHeight = 0;
//動畫執行的進度
private float progress = 0;
//各模組依次滾動 RollInTurn 下情況下,各小模組的動畫執行時間佔總動畫時間比
private float partAnimatorPercent = 0;
//小模組開始執行動畫時間差
private float partSpaceTime = 0;
public Camera3DView(Context context) {
this(context, null);
}
public Camera3DView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Camera3DView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public Camera3DView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.Camera3DView_Param);
int caremaRollMode = a.getInt(R.styleable.Camera3DView_Param_camera_roll_mode, 0);
partAnimatorPercent = a.getFloat(R.styleable.Camera3DView_Param_part_animator_percent, 1);
partNumber = a.getInt(R.styleable.Camera3DView_Param_partNumber, 3);
direction = a.getInt(R.styleable.Camera3DView_Param_camera_roll_direction, 1);
if (caremaRollMode == 0) {
rollMode = RollMode.Roll2D;
} else if (caremaRollMode == 1) {
rollMode = RollMode.Whole3D;
} else if (caremaRollMode == 2) {
rollMode = RollMode.SepartConbine;
} else if (caremaRollMode == 3) {
rollMode = RollMode.Jalousie;
} else {
if (partAnimatorPercent <= 0 || partAnimatorPercent >= 1) {
rollMode = RollMode.SepartConbine;
}else {
rollMode = RollMode.RollInTurn;
partSpaceTime = (1 - partAnimatorPercent) / partNumber;
}
}
//初始化
init(context);
}
/**
* 初始化一些引數
* @param context
*/
private void init(Context context) {
bitmapList = new ArrayList<>();
bitmapsList = new ArrayList<>();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
camera = new Camera();
matrix = new Matrix();
this.context = context;
}
/**
* 新增圖片
* @param bitmap 要新增的圖片
*/
public void addBitmap(Bitmap bitmap){
bitmapList.add(bitmap);
//這是為了處理佈局完之後新增的圖片
if (viewWidth != 0 && viewHeight != 0) {
//縮放處理bitmap
bitmap = scaleBitmap(bitmap);
//切割操作
initBitmap(bitmap);
}
}
/**
* 根據給定的寬和高進行拉伸
* @param origin 原圖
* @return new Bitmap
*/
private Bitmap scaleBitmap(Bitmap origin) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) viewWidth) / width;
float scaleHeight = ((float) viewHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);// 使用後乘
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
return newBM;
}
private void initBitmap(Bitmap bitmap) {
//初始化
initIndex();
Bitmap partBitmap;
Bitmap[] partBitmaps = new Bitmap[partNumber];
for (int i = 0; i < partNumber; i ++){
Rect rect;
if(rollMode != RollMode.Jalousie){
if(direction == 1){
//縱向切割
rect = new Rect(i * averageWidth, 0, (i + 1) * averageWidth, viewHeight);
//按照矩形區域切割圖片
partBitmap = getPartBitmap(bitmap, i * averageWidth, 0, rect);
}else{
//橫向切割
rect = new Rect(0, i * averageHeight, viewWidth, (i + 1) * averageHeight);
partBitmap = getPartBitmap(bitmap, 0, i * averageHeight, rect);
}
}else{
if (direction == 1) {
//橫向切割
rect = new Rect(0, i * averageHeight, viewWidth, (i + 1) * averageHeight);
partBitmap = getPartBitmap(bitmap, 0, i * averageHeight, rect);
} else {
//縱向切割
rect = new Rect(i * averageWidth, 0, (i + 1) * averageWidth, viewHeight);
partBitmap = getPartBitmap(bitmap, i * averageWidth, 0, rect);
}
}
partBitmaps[i] = partBitmap;
}
bitmapsList.add(partBitmaps);
}
/**
* 切割圖片
* @param bitmap
* @param left
* @param top
* @param rect
* @return
*/
private Bitmap getPartBitmap(Bitmap bitmap, int left, int top, Rect rect) {
return Bitmap.createBitmap(bitmap,left,top,rect.width(),rect.height());
}
/**
* 根據當前圖片的下標,計算前一個與後一個的下標
*/
private void initIndex() {
int listSize = bitmapList.size();
nextIndex = currIndex +1;
preIndex = currIndex -1;
if(nextIndex > listSize -1){
nextIndex = 0;//當到了邊界,再迴圈變換
}
if(preIndex < 0){
preIndex = listSize -1;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取控制元件寬高
viewWidth = getMeasuredWidth();
viewHeight = getMeasuredHeight();
//獲取切割後小塊寬高
averageWidth = (int)(viewWidth/partNumber);
averageHeight = (int)(viewHeight/partNumber);
//處理在佈局前新增的圖片
if (viewWidth != 0 && viewHeight != 0) {
for (int i = 0; i < bitmapList.size(); i ++) {
//縮放處理bitmap
bitmapList.set(i, scaleBitmap(bitmapList.get(i)));
//切割操作
initBitmap(bitmapList.get(i));
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmapList.size() == 0) {
return;
}
switch (rollMode){
case Roll2D:
drawRoll2D(canvas);
break;
case Whole3D:
drawWhole3D(canvas);
break;
case SepartConbine:
drawSepartConbine(canvas);
break;
case Jalousie:
drawJalousie(canvas);
break;
case RollInTurn:
drawRollInTurn(canvas);
break;
}
}
/**
* 2D 滾動繪製
* @param canvas
*/
private void drawRoll2D(Canvas canvas) {
canvas.save();
Bitmap currWholeBitmap = bitmapList.get(currIndex);
Bitmap nextWholeBitmap = bitmapList.get(nextIndex);
if(direction == 1){
//豎直方向滑動
//當前圖片滑動
matrix.postTranslate(0, progress * viewHeight);
canvas.drawBitmap(currWholeBitmap, matrix, paint);
matrix.reset();
//下一張圖片滑動
matrix.postTranslate(0, -(1 - progress) * viewHeight);
canvas.drawBitmap(nextWholeBitmap, matrix, paint);
matrix.reset();
} else {
//水平方向滑動
//當前圖片滑動
matrix.postTranslate(-progress * viewHeight, 0);
canvas.drawBitmap(currWholeBitmap, matrix, paint);
matrix.reset();
//下一張圖片滑動
matrix.postTranslate((1 - progress) * viewHeight, 0);
canvas.drawBitmap(nextWholeBitmap, matrix, paint);
matrix.reset();
}
canvas.restore();
}
/**
* 3D 整體滾動
* @param canvas
*/
private void drawWhole3D(Canvas canvas) {
canvas.save();
Bitmap currWholeBitmap = bitmapList.get(currIndex);
Bitmap nextWholeBitmap = bitmapList.get(nextIndex);
if(direction == 1){
//豎直方向滑動
//兩張分別進行旋轉,繞著同一個軸旋轉
float axisY = progress * viewHeight;
//第一張圖片旋轉
camera.save();
camera.rotateX(-progress * 90);
camera.getMatrix(matrix);
camera.restore();
//下面的view繞著自己的top旋轉,Y 方向不需要進行平移
matrix.preTranslate(-viewWidth/2, 0);
//轉完之後顯示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
canvas.drawBitmap(currWholeBitmap, matrix, paint);
//第二張圖片旋轉
camera.save();
camera.rotateX((1 - progress) * 90);
camera.getMatrix(matrix);
camera.restore();
//下面的view繞著自己的buttom旋轉,Y 向上平移 viewHeight
matrix.preTranslate(-viewWidth/2, -viewHeight);
//轉完之後顯示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
canvas.drawBitmap(nextWholeBitmap, matrix, paint);
}else{
//水平方向滑動
float axisX = (1 - progress) * viewWidth;
camera.save();
camera.rotateY(-progress * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth, -viewHeight / 2);
matrix.postTranslate(axisX, viewHeight / 2);
canvas.drawBitmap(currWholeBitmap, matrix, paint);
camera.save();
camera.rotateY((1 - progress) * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0, -viewHeight / 2);
matrix.postTranslate(axisX, viewHeight / 2);
canvas.drawBitmap(nextWholeBitmap, matrix, paint);
}
canvas.restore();
}
/**
* 尾部逐漸分離再合併
* @param canvas
*/
private void drawSepartConbine(Canvas canvas) {
//跟 3D 整體滾動對比,
//3D 整體滾動是一整張圖片進行翻轉
//這邊是切割後的一張張小圖片進行翻轉
canvas.save();
Bitmap[] currWholeBitmaps = bitmapsList.get(currIndex);
Bitmap[] nextWholeBitmaps = bitmapsList.get(nextIndex);
if(direction == 1){
//豎直方向滑動
//兩張分別進行旋轉,繞著同一個軸旋轉
float axisY = progress * viewHeight;
//第一張圖片旋轉
camera.save();
camera.rotateX(-progress * 90);
camera.getMatrix(matrix);
camera.restore();
//下面的view繞著自己的top旋轉,Y 方向不需要進行平移
matrix.preTranslate(-viewWidth/2, 0);
//轉完之後顯示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
for (int i = 0; i < partNumber; i ++) {
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
//每繪製一張小圖片,需要往下平移,否則會覆蓋住
matrix.postTranslate(averageWidth, 0);
}
//第二張圖片旋轉
camera.save();
camera.rotateX((1 - progress) * 90);
camera.getMatrix(matrix);
camera.restore();
//下面的view繞著自己的buttom旋轉,Y 向上平移 viewHeight
matrix.preTranslate(-viewWidth/2, -viewHeight);
//轉完之後顯示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
for (int i = 0; i < partNumber; i ++) {
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
matrix.postTranslate(averageWidth, 0);
}
}else{
//水平方向滑動
float axisX = (1 - progress) * viewWidth;
camera.save();
camera.rotateY(-progress * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth, -viewHeight / 2);
matrix.postTranslate(axisX, viewHeight / 2);
for (int i = 0; i < partNumber; i ++) {
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
matrix.postTranslate(0, averageHeight);
}
camera.save();
camera.rotateY((1 - progress) * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0, -viewHeight / 2);
matrix.postTranslate(axisX, viewHeight / 2);
for (int i = 0; i < partNumber; i ++) {
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
matrix.postTranslate(0, averageHeight);
}
}
canvas.restore();
}
/**
* 百葉窗
* @param canvas
*/
private void drawJalousie(Canvas canvas) {
canvas.save();
Bitmap[] currWholeBitmaps = bitmapsList.get(currIndex);
Bitmap[] nextWholeBitmaps = bitmapsList.get(nextIndex);
if(direction == 1){
//豎直方向翻轉
if (progress < 0.5) {
//第一張圖片旋轉
for (int i = 0; i < partNumber; i ++) {
//每一小部分旋轉軸不一樣
float axisY = (i + 0.5f) * averageHeight;
camera.save();
camera.rotateX(-progress * 180);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2, -0.5f * averageHeight);
matrix.postTranslate(viewWidth/2, axisY);
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
}
} else {
//第二張圖片旋轉
for (int i = 0; i < partNumber; i ++) {
//每一小部分旋轉軸不一樣
float axisY = (i + 0.5f) * averageHeight;
camera.save();
camera.rotateX((1 - progress) * 180);
camera.getMatrix(matrix);
camera.restore();
//下面的view繞著自己的top旋轉,Y 方向不需要進行平移
matrix.preTranslate(-viewWidth/2, -0.5f * averageHeight);
//轉完之後顯示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
}
}
}else{
//水平方向滑動
if (progress < 0.5) {
//第一張圖片旋轉
for (int i = 0; i < partNumber; i ++) {
//每一小部分旋轉軸不一樣
float axisY = (i + 0.5f) * averageWidth;
camera.save();
camera.rotateY(-progress * 180);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-0.5f * averageWidth, -viewHeight/2);
matrix.postTranslate(axisY, viewHeight/2);
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
}
} else {
//第二張圖片旋轉
for (int i = 0; i < partNumber; i ++) {
//每一小部分旋轉軸不一樣
float axisY = (i + 0.5f) * averageWidth;
camera.save();
camera.rotateY((1 - progress) * 180);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-0.5f * averageWidth, -viewHeight/2);
matrix.postTranslate(axisY, viewHeight/2);
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
}
}
}
canvas.restore();
}
/**
* 各模組依次滾動
* @param canvas
*/
private void drawRollInTurn(Canvas canvas) {
canvas.save();
Bitmap[] currWholeBitmaps = bitmapsList.get(currIndex);
Bitmap[] nextWholeBitmaps = bitmapsList.get(nextIndex);
if(direction == 1){
//豎直方向滑動
float partProgress;
float axisY;
for (int i = 0; i < partNumber; i ++) {
//計算當前小模組執行動畫進度
partProgress = getPartProgress(i);
axisY = partProgress * viewHeight;
//第一張圖片旋轉
camera.save();
camera.rotateX(-partProgress * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2, 0);
matrix.postTranslate(viewWidth/2 + averageWidth * i, axisY);
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
//第二張圖片旋轉
camera.save();
camera.rotateX((1 - partProgress) * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2, -viewHeight);
matrix.postTranslate(viewWidth/2 + averageWidth * i, axisY);
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
}
}else{
float partProgress;
float axisX;
for (int i = 0; i < partNumber; i ++) {
//計算當前小模組執行動畫進度
partProgress = getPartProgress(i);
axisX = partProgress * viewWidth;
//第一張圖片旋轉
camera.save();
camera.rotateY(-partProgress * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0, -viewHeight/2);
matrix.postTranslate(axisX, viewHeight/2 + averageWidth * i);
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
//第二張圖片旋轉
camera.save();
camera.rotateY((1 - partProgress) * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth, -viewHeight/2);
matrix.postTranslate(axisX, viewHeight/2 + averageWidth * i);
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
}
}
canvas.restore();
}
/**
* 計算當前進度下,各小模組的進度
* @param index
* @return
*/
private float getPartProgress (int index) {
//計算當前小模組在什麼進度時候啟動
float begin = index * partSpaceTime;
if (progress <= begin) {
//還沒開始執行
return 0;
} else if (progress - begin >= partAnimatorPercent) {
//已經執行結束
return 1;
} else {
//正在執行
return (progress - begin) / partAnimatorPercent;
}
}
/**
* 根據切換進度進行重繪
* @param progress
*/
public void setProgress (float progress){
this.progress = progress;
invalidate();
}
//滾動模式
public enum RollMode {
Roll2D, //2D 滾動
Whole3D, //3D 整體滾動
SepartConbine, //尾部逐漸分離再合併
Jalousie, //百葉窗
RollInTurn; //各模組依次滾動
}
}
attrs.xml
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="Camera3DView_Param">
<!--翻轉模式-->
<attr name="camera_roll_mode"/>
<!--翻轉方向-->
<attr name="camera_roll_direction"/>
<!--分成小模組的個數-->
<attr name="partNumber" format="integer"/>
<!--每個小模組啟動的動畫佔總動畫比-->
<attr name="part_animator_percent" format="float"/>
</declare-styleable>
<attr name="camera_roll_mode">
<enum name="Roll2D" value="0"/>
<enum name="Whole3D" value="1"/>
<enum name="SepartConbine" value="2"/>
<enum name="Jalousie" value="3"/>
<enum name="RollInTurn" value="4"/>
</attr>
<attr name="camera_roll_direction">
<enum name="vertical" value="1"/>
<enum
相關推薦
(二十五)3D 翻轉效果
一、ViewPager 的 3D 效果
1.ViewPager
先直接上個簡單的 ViewPager 的 demo。
activity_main.xml
<RelativeLayout xmlns:android="http://s
SCPPO(二十五):從導資料看如何將一天過出多天的效果
【前言】
最近在微信朋友圈中看到一篇不錯的文章,無巧不成書,剛剛又在專案中做了一次導資料的任務,在做完後感覺收穫許多,於是總結一下,發現得出的結論結論可以用這篇文章竟標題來代替!
【有趣探索旅
【轉】JMeter學習(二十五)HTTP屬性管理器HTTP Cookie Manager、HTTP Request Defaults
agen 讀取 expired fault 範圍 運行時 ear 定制 只有一個 Test Plan的配置元件中有一些和HTTP屬性相關的元件:HTTP Cache Manager、HTTP Authorization Manager、HTTP Cookie Manager
2016集訓測試賽(二十五)小結
時間 第一題 是我 很多 分析 題目 ... 人的 簡單 這場測試賽有必要小結一下.
昨晚 1 點才睡, 今天狀態很差, 先睡了 1 個小時, 然後開始打比賽. 第一題不大會做, 寫了一個代碼後發現是錯的, 第二題看不懂題, 第三題簡單地分析了一下, 發現是一個樹形
C#編程(二十五)----------接口
目的 count() 聲明變量 form [] 類的繼承 計算機 構造 執行 接口
如果一個類派生自一個接口,聲明這個類就會實現某些函數.並不是所有的面向對象的語言都支持接口.
例如,有一個接口:IDispoable,包含一個方法Dispose(),該方法又類實現,用於清理
企業分布式微服務雲SpringCloud SpringBoot mybatis (二十五)集成swagger2構建Restful API
sel ima eth syn conf ring 但是 batis 關註 一、引入依賴
<dependency>
<groupId>io.springfox</groupId>
<
Linux學習筆記(二十五)shell特殊符號、 sort_wc_uniq、tee_tr_split
tee_tr_split命令一、特殊字符 1.‘* ‘任意個任意字符‘? ‘任意一個字符‘# ‘註釋字符‘ \ ‘脫義字符‘ | ‘管道符其中大部分之前都用過,對於脫義符‘\‘脫義符用法舉例如圖
2.幾個與管道有關的命令cut 分割,-d 分隔符 -f 指定段號 -c 指定第幾個字符 sort 排序,
Linux 學習總結(二十五) 系統管理4
iptables filter nat netfilter iptables 應用實例
一 filter表案例
1 需求:只針對filter表,預設INPUT 鏈DROP ,其他兩個鏈ACCEPT,然後針對192.169.188.0/24開通22端口,對所有網段開放80端口,21端口。我們編寫sh
大數據筆記(二十五)——Scala函數式編程
=== 情況 不能 nbsp 結構 map som class 編程 ===================== Scala函數式編程 ========================
一、Scala中的函數 (*) 函數是Scala中的頭等公民,就和數字一樣,可以在變量中
C之數組(二十五)
C語言 數組 在 C 語言中,我們不可避免的要接觸到數組。我們就來看看數組是什麽玩意,其實數組就是相同類型的變量的有序集合。下面這張圖更加形象的表示出數組的含義 數組在一片連續的內存空間中存儲元素,數組元素的個數是可以顯示或隱式指定的。比如: int a[5] = {1,
python2.7練習小例子(二十五)
點贊 實例 net mil 感覺 format 經典題目 tty 遞推 25):題目:有5個人坐在一起,問第五個人多少歲?他說比第4個人大2歲。問第4個人歲數,他說比第3個人大2歲。問第三個人,又說比第2人大兩歲。問第2個人,說比第一個人大兩歲。最後問第一個人,他說
完善的復數類(二十五)
C++ 復數類 賦值操作符 操作符重載 我們在之前已經是實現了復數類的相加操作,那麽我們今天就來完善下復數類。一個完整的復數類應該具備的操作有:運算(+, -, *, /);比較(==, !=);賦值(=);求模(modulus);利用的就是操作符重載來統一實現復數與實數的運算
(二十五)類的加載
main函數 img emp ima info 技術 admin file bubuko
jvm先找main函數,加載Demo34類
加載Car類,實例化對象進堆,成員變量都在堆中執行。類中的函數依舊在占中運行,遵循先入後出的原則,執行結束立即撤
Kafka:ZK+Kafka+Spark Streaming集群環境搭建(二十五)Structured Streaming:同一個topic中包含一組數據的多個部分,按照key它們拼接為一條記錄(以及遇到的問題)。
eas array 記錄 splay span ack timestamp b- each 需求:
目前kafka的topic上有一批數據,這些數據被分配到9個不同的partition中(就是發布時key:{m1,m2,m3,m4...m9},value:{records
MySQL數據庫8(二十五)變量作用域
圖片 沒有 http global 而且 局部作用域 數據庫 過程 會話 變量作用域
變量作用域:變量能夠使用的區域範圍
局部作用域
使用declare關鍵字聲明(在結構體內:函數/存儲過程/觸發器),而且只能在結構體內部使用。
declare關鍵字聲明的變量沒有任何符號修
【HHHOJ】NOIP2018 模擬賽(二十五) 解題報告
點此進入比賽
得分: \(100+100+20=220\)(\(T1\)打了兩個小時,以至於\(T3\)沒時間打了,無奈交暴力)
排名: \(Rank\ 8\)
\(Rating\):\(+19\)
\(T1\):【HHHOJ126】求和(點此看題面)
看到這道題,我不由得想到這道題目:【BZO
通證經濟大局觀(二十五):文藝復興與人文主義
十字軍東征 十字軍東征是教會主導的,但得到了上至國王,下至底層貴族的積極響應。這場戰爭的表面目的是為了宗教,為了奪回聖地。但根本的原因是整個西方的擴張衝動。 教會已經征服了西方,自然要聽從上帝的旨意,為更多的人帶去上帝的榮光,向東方傳教是理所當然之事。 大凡有野心的國王,都有擴張的慾望,就這一點來說
java基礎學習總結(二十五):logback詳解
為什麼使用logback
logback大約有以下的一些優點:
核心重寫、測試充分、初始化記憶體載入更小,這一切讓logback效能和log4j相比有諸多倍的提升
logback非常自然地直接實現了slf4j,這個嚴格來說算不上優點,只是這樣,再理解slf4j的前提下會很容易理解
ElasticSearch最佳入門實踐(二十五)mget批量查詢api
1、批量查詢的好處
就是一條一條的查詢,比如說要查詢100條資料,那麼就要傳送100次網路請求,這個開銷還是很大的 如果進行批量查詢的話,查詢100條資料,就只要傳送1次網路請求,網路請求的效能開銷縮減100倍
2、mget的語法
可以說mget是很重要
大資料(二十五):Sqoop的介紹和安裝
一、Sqoop簡介
Sqoop是一種旨在有效的Hadoop和關係型資料庫等結構化資料儲存之間傳輸大量資料的工具。Sqoop的原理就是將匯入或者匯出命令翻譯成mapreduce程式來實現。在翻譯出的mapreduce中主要就是對inputfor