最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Android實現(xiàn)三維效果的動態(tài)旋轉圖

 更新時間:2025年09月09日 10:12:15   作者:Katie。  
在電商、相冊、視頻封面、海報展示、啟動頁 Logo 等場景里,帶真實透視感的 3D 旋轉能明顯提升界面質感,Android 自 3.x 起就支持基于屬性的 3D 旋轉,所以本文給大家介紹了如何基于Android實現(xiàn)三維效果的動態(tài)旋轉圖,需要的朋友可以參考下

一、項目背景詳細介紹

在電商、相冊、視頻封面、海報展示、啟動頁 Logo 等場景里,帶真實透視感的 3D 旋轉能明顯提升界面質感。常見需求:

  • 圖片繞 X/Y 軸持續(xù)旋轉(封面展示、加載動效)。
  • 卡片翻轉(繞 X 或 Y 軸 180° 翻面)。
  • 帶景深的 3D 旋轉(近大遠小,具備透視壓縮)。

Android 自 3.x 起就支持基于屬性的 3D 旋轉(rotationX/rotationY),配合 setCameraDistance() 能得到還不錯的透視感;而傳統(tǒng) Camera + Matrix 則能實現(xiàn)更精細的像素級控制。

二、項目需求詳細介紹

  1. 圖片能連續(xù)、平滑地 3D 旋轉(可配方向/速度)。
  2. 可選繞 X 或繞 Y 軸旋轉。
  3. 可設置景深強度(近大遠小的透視感)。
  4. 可暫停/恢復、重復/往返等播放控制。
  5. 兼容 Android 5.0+,盡量避免兼容雷區(qū)。

三、相關技術詳細介紹

  • 屬性動畫ObjectAnimator / ValueAnimator 控制 rotationX/rotationY。
  • 透視距離View.setCameraDistance(float),距離越大透視越弱,單位是像素乘以屏幕密度系數(shù)。
  • 插值器LinearInterpolator(勻速)、AccelerateDecelerateInterpolator(緩入緩出)。
  • Camera/Matrixandroid.graphics.Camera 做 3D 變換、Matrix 應用到 Canvas。
  • 硬件加速:屬性動畫天然兼容;Camera+Matrix 某些機型需要切換圖層類型。

四、實現(xiàn)思路詳細介紹

方案A(首選):
1)XML/代碼里設定較大的 cameraDistance;
2)用 ObjectAnimator 驅動 rotationY(或 rotationX)從 0 → 360 循環(huán);
3)可選 repeatCount/ModeInterpolator、時長。
優(yōu)點:簡單、兼容性好、硬件加速性能佳。

方案B(可精細控制):
1)自定義 Rotate3DImageView,在 onDraw() 里用 Camera.rotateX/rotateY + Matrix;
2)可在繪制前/后裁剪半區(qū),做上半/下半獨立翻轉;
3)ValueAnimator 驅動角度更新;
4)必要時設置 setLayerType(LAYER_TYPE_SOFTWARE/HARDWARE) 規(guī)避機型差異。

五、完整實現(xiàn)代碼

// ======================= A. 推薦方案:屬性動畫 + cameraDistance =======================
// 文件:res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:foregroundGravity="center">
 
    <ImageView
        android:id="@+id/ivSpin"
        android:layout_width="220dp"
        android:layout_height="220dp"
        android:scaleType="centerCrop"
        android:src="@drawable/sample" />
 
    <!-- 可加控制按鈕/文本,這里省略 -->
</FrameLayout>
 
// 文件:java/com/example/rotate3d/MainActivity.java
package com.example.rotate3d;
 
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
 
import androidx.appcompat.app.AppCompatActivity;
 
public class MainActivity extends AppCompatActivity {
 
    private ImageView ivSpin;
    private ObjectAnimator spinAnimatorY;
    private ObjectAnimator spinAnimatorX;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ivSpin = findViewById(R.id.ivSpin);
 
        // 1) 設置相機距離(越大透視越弱;數(shù)值過小會導致透視夸張/變形)
        // 建議:以屏幕密度為基準放大;經驗值:8000f ~ 20000f(按像素 * density)
        float density = getResources().getDisplayMetrics().density;
        ivSpin.setCameraDistance(12000 * density); // 試著改大/改小感受透視差別
 
        // 2) 繞Y軸無限旋轉(可換成 rotationX)
        spinAnimatorY = ObjectAnimator.ofFloat(ivSpin, "rotationY", 0f, 360f);
        spinAnimatorY.setDuration(3000); // 一圈3秒
        spinAnimatorY.setRepeatCount(ValueAnimator.INFINITE);
        spinAnimatorY.setInterpolator(new LinearInterpolator());
        spinAnimatorY.start();
 
        // 如需切換成繞X軸旋轉,改用下面這段(示例先不啟動)
        spinAnimatorX = ObjectAnimator.ofFloat(ivSpin, "rotationX", 0f, 360f);
        spinAnimatorX.setDuration(3000);
        spinAnimatorX.setRepeatCount(ValueAnimator.INFINITE);
        spinAnimatorX.setInterpolator(new LinearInterpolator());
 
        // 可依據(jù)交互,在按鈕點擊時:spinAnimatorY.pause()/resume()/cancel()
    }
 
    @Override
    protected void onPause() {
        super.onPause();
        if (spinAnimatorY != null && spinAnimatorY.isRunning()) {
            spinAnimatorY.pause();
        }
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        if (spinAnimatorY != null && spinAnimatorY.isPaused()) {
            spinAnimatorY.resume();
        }
    }
}
 
 
// ======================= B. 進階方案:自定義 View + Camera/Matrix =======================
// 亮點:可精細控制透視與局部翻轉;適合卡片翻頁、上半/下半獨立翻轉等
// 文件:java/com/example/rotate3d/Rotate3DImageView.java
package com.example.rotate3d;
 
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import androidx.annotation.Nullable;
 
public class Rotate3DImageView extends View {
 
    public static final int AXIS_Y = 0;
    public static final int AXIS_X = 1;
 
    private Drawable drawable;
    private Bitmap bitmap;
    private final Camera camera = new Camera();
    private final Matrix matrix = new Matrix();
    private float degree = 0f;  // 當前角度
    private int axis = AXIS_Y;  // 旋轉軸,默認Y
    private float cameraZ = -12_000f; // 相機Z,負值表示遠離屏幕(像素維度)
    private boolean autoStart = true;
    private long duration = 3000L;
 
    private ValueAnimator animator;
 
    public Rotate3DImageView(Context context) { this(context, null); }
    public Rotate3DImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public Rotate3DImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Rotate3DImageView);
            axis = a.getInt(R.styleable.Rotate3DImageView_axis, AXIS_Y);
            cameraZ = a.getFloat(R.styleable.Rotate3DImageView_cameraZ, -12000f);
            autoStart = a.getBoolean(R.styleable.Rotate3DImageView_autoStart, true);
            duration = a.getInt(R.styleable.Rotate3DImageView_durationMs, 3000);
            a.recycle();
        }
        setWillNotDraw(false);
        setLayerType(LAYER_TYPE_HARDWARE, null); // 也可嘗試 SOFTWARE 處理某些機型的Camera兼容
    }
 
    public void setImageDrawable(Drawable d) {
        this.drawable = d;
        if (d instanceof BitmapDrawable) {
            bitmap = ((BitmapDrawable) d).getBitmap();
        } else {
            bitmap = null;
        }
        requestLayout();
        invalidate();
    }
 
    public void setImageResource(int resId) {
        setImageDrawable(getResources().getDrawable(resId));
    }
 
    public void setAxis(int axis) {
        this.axis = axis;
        invalidate();
    }
 
    public void setDegree(float degree) {
        this.degree = degree;
        invalidate();
    }
 
    public void setCameraZ(float z) {
        this.cameraZ = z;
        invalidate();
    }
 
    public void setDuration(long durationMs) {
        this.duration = durationMs;
        if (animator != null) animator.setDuration(durationMs);
    }
 
    private void ensureAnimator() {
        if (animator != null) return;
        animator = ValueAnimator.ofFloat(0f, 360f);
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(duration);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.addUpdateListener(a -> {
            degree = (float) a.getAnimatedValue();
            invalidate();
        });
    }
 
    @Override protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (autoStart) start();
    }
 
    @Override protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stop();
    }
 
    public void start() {
        ensureAnimator();
        if (!animator.isStarted()) animator.start();
    }
 
    public void pause() {
        if (animator != null && animator.isRunning()) animator.pause();
    }
 
    public void resumeAnim() {
        if (animator != null && animator.isPaused()) animator.resume();
    }
 
    public void stop() {
        if (animator != null) {
            animator.cancel();
        }
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int w = resolveSize(
                drawable == null ? 200 : Math.max(drawable.getIntrinsicWidth(), 1),
                widthMeasureSpec);
        int h = resolveSize(
                drawable == null ? 200 : Math.max(drawable.getIntrinsicHeight(), 1),
                heightMeasureSpec);
        setMeasuredDimension(w, h);
        if (drawable != null) {
            drawable.setBounds(0, 0, w, h);
        }
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (drawable == null) return;
 
        final int cx = getWidth() / 2;
        final int cy = getHeight() / 2;
 
        // 保存畫布狀態(tài)
        int saveCount = canvas.save();
 
        matrix.reset();
        camera.save();
 
        // 設置相機位置(Z 軸),單位是像素。負值遠離屏幕,絕對值越大透視越弱
        // Camera#translate(0, 0, z) 不同廠商實現(xiàn)略有差異,必要時可按密度縮放
        camera.translate(0, 0, cameraZ);
 
        if (axis == AXIS_Y) {
            camera.rotateY(degree);
        } else {
            camera.rotateX(degree);
        }
        camera.getMatrix(matrix);
        camera.restore();
 
        // 將旋轉中心平移到控件中心(Camera/Matrix 默認以(0,0)為中心)
        matrix.preTranslate(-cx, -cy);
        matrix.postTranslate(cx, cy);
 
        // 應用矩陣到畫布
        canvas.concat(matrix);
 
        // 繪制圖片
        drawable.draw(canvas);
 
        // 恢復畫布
        canvas.restoreToCount(saveCount);
    }
}
 
 
// ======================= 自定義屬性聲明 =======================
// 文件:res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Rotate3DImageView">
        <!-- 0: Y軸;1: X軸 -->
        <attr name="axis" format="enum">
            <enum name="y" value="0"/>
            <enum name="x" value="1"/>
        </attr>
        <!-- Camera Z 位置(像素),負值表示遠離屏幕,絕對值越大透視越弱 -->
        <attr name="cameraZ" format="float"/>
        <!-- 自動開始動畫 -->
        <attr name="autoStart" format="boolean"/>
        <!-- 周期(毫秒) -->
        <attr name="durationMs" format="integer"/>
    </declare-styleable>
</resources>
 
// ======================= 使用自定義 View 的布局示例 =======================
// 文件:res/layout/activity_custom.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:foregroundGravity="center">
 
    <com.example.rotate3d.Rotate3DImageView
        android:id="@+id/iv3d"
        android:layout_width="240dp"
        android:layout_height="240dp"
        app:axis="y"
        app:cameraZ="-12000"
        app:autoStart="true"
        app:durationMs="2800" />
</FrameLayout>
 
// 文件:java/com/example/rotate3d/CustomActivity.java
package com.example.rotate3d;
 
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
 
public class CustomActivity extends AppCompatActivity {
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom);
 
        Rotate3DImageView v = findViewById(R.id.iv3d);
        v.setImageResource(R.drawable.sample);
        // 也可在代碼里切換軸/時長/相機Z
        // v.setAxis(Rotate3DImageView.AXIS_X);
        // v.setCameraZ(-16000f);
        // v.setDuration(3500);
    }
}

六、代碼與關鍵點解讀

相機距離(方案A)

  • setCameraDistance(12000 * density) 是非常關鍵的一步。
  • 距離越小透視越強(近大遠小更明顯),太小會變形嚴重;距離越大透視越弱,趨近于平面旋轉。
  • 常見經驗范圍:8000f ~ 20000f(乘以 density)。

屬性動畫控制

  • 使用 ObjectAnimator.ofFloat(view, "rotationY", 0f, 360f) 連續(xù)旋轉;
  • LinearInterpolator 讓轉動勻速;可換 AccelerateDecelerate 做“呼吸感”。
  • pause()/resume() 方便在 onPause/onResume 做生命周期管理,避免后臺耗電。

Camera/Matrix(方案B)

  • onDraw() 中使用 Camera.rotateY/rotateX 后要平移到控件中心旋轉:preTranslate(-cx,-cy) / postTranslate(cx,cy);
  • camera.translate(0,0,cameraZ) 控制景深;也可不平移僅靠旋轉,效果會更“緊”。
  • 如果遇到某些機型顯示異常,可試試 setLayerType(LAYER_TYPE_SOFTWARE, null) 或保持 HARDWARE,二者擇一以實際效果為準。

性能 & 資源

  • 方案A 基本不需要擔心性能(GPU 動畫,輕量);
  • 方案B 每幀重繪,盡量避免做額外開銷(如 Bitmap 頻繁創(chuàng)建)。
  • 圖片過大時請用 centerCrop 與合適分辨率,避免內存抖動。

七、項目詳細總結

  • 首選:屬性動畫 + cameraDistance,實現(xiàn)簡單、性能穩(wěn)、展示效果已足夠“3D”。
  • 進階Camera+Matrix 給你更高自由度(半區(qū)翻轉、復雜翻書效果),代價是自己管理繪制與兼容。
  • 通用建議:合理設置透視距離與動畫時長,注意生命周期暫停恢復,避免后臺白跑。

八、常見問題與解答(FAQ)

為什么我設置了 rotationY 但看不出 3D 透視?
→ 大概率是 cameraDistance 太大(透視過弱)或太?。ɑ儯=ㄗh在 8000~20000 * density 內調參。

Camera 效果在某些手機發(fā)虛/鋸齒?
→ 嘗試 setLayerType(LAYER_TYPE_SOFTWARE, null)HARDWARE 切換;另外避免在動畫中同時做大幅 scale。

如何只做 180° 卡片翻轉?
→ 把動畫區(qū)間調到 0~180,結束時替換圖片即可;或在 90° 時切換前后圖層。

如何讓旋轉更絲滑?
→ 使用 LinearInterpolator 勻速,時長 2.5~3.5s;圖片盡量使用與控件尺寸匹配的資源,減少 GPU 采樣壓力。

如何點擊暫停/繼續(xù)?
→ 方案A 直接 pause()/resume();方案B 對 ValueAnimator 調用相同方法或 cancel()/start()

九、擴展方向與優(yōu)化建議

  • 組合動效:在旋轉同時,疊加輕微 scale/alpha 做呼吸感。
  • 多圖輪播:配合 ViewPager2RecyclerView,在切換頁時加 3D 翻頁過渡。
  • 曲線速度:自定義 TimeInterpolator(如先快后慢)營造動勢。
  • 數(shù)據(jù)驅動:把 degree 暴露為可綁定屬性(DataBinding/Compose),做可控進度展示。
  • Compose 版本:用 Modifier.graphicsLayer { rotationY = ...; cameraDistance = ... } + rememberInfiniteTransition 實現(xiàn)同等效果。

以上就是基于Android實現(xiàn)三維效果的動態(tài)旋轉圖的詳細內容,更多關于Android動態(tài)旋轉圖的資料請關注腳本之家其它相關文章!

相關文章

最新評論

龙陵县| 威海市| 曲水县| 湖州市| 荔波县| 自治县| 定结县| 夹江县| 浏阳市| 汾阳市| 和硕县| 吉林市| 名山县| 青海省| 镇宁| 留坝县| 上高县| 黔江区| 临猗县| 常山县| 惠东县| 东兴市| 资阳市| 庆阳市| 嘉峪关市| 肥西县| 色达县| 始兴县| 郑州市| 泸水县| 蓬安县| 德兴市| 磴口县| 安康市| 灵宝市| 湖州市| 海原县| 浮梁县| 马龙县| 辰溪县| 买车|