主頁 > 移動端開發 > 《Android開發藝術探索》第7章- Android 影片深入分析讀書筆記

《Android開發藝術探索》第7章- Android 影片深入分析讀書筆記

2021-11-08 10:18:45 移動端開發

目錄

  • 1. View 影片
    • 1.1 Android 影片的分類有哪些?
    • 1.2 Android 影片的特點是什么?
    • 1.3 Tween Animation 補間影片中的軸點是什么作用?
    • 1.4 自定義 View 影片的步驟是什么?
  • 2. View 影片的特殊使用場景
    • 2.1 如何控制 ViewGroup 中子元素的出場效果?
    • 2.2 如何自定義 Activity 的切換效果?
    • 2.3 如何自定義 Fragment 的切換效果?
  • 3. 屬性影片
    • 3.1 插值器和估值器的作用分別是什么?
    • 3.2 如何使用屬性影片對任意屬性做影片?
  • 4. 使用影片的注意事項
  • 參考

1. View 影片

1.1 Android 影片的分類有哪些?

總共有兩類影片:View Animation(視圖影片) 和 Property Animation(屬性影片),其中,View Animation 又包括 Tween Animation(補間影片)和 Frame Animation(逐幀影片);Property Animation 又包括 ValueAnimator 和 ObjectAnimator,

<style>#mermaid-svg-JEiT0zcdjO4blM7A .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-JEiT0zcdjO4blM7A .label text{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .node rect,#mermaid-svg-JEiT0zcdjO4blM7A .node circle,#mermaid-svg-JEiT0zcdjO4blM7A .node ellipse,#mermaid-svg-JEiT0zcdjO4blM7A .node polygon,#mermaid-svg-JEiT0zcdjO4blM7A .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-JEiT0zcdjO4blM7A .node .label{text-align:center;fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .node.clickable{cursor:pointer}#mermaid-svg-JEiT0zcdjO4blM7A .arrowheadPath{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-JEiT0zcdjO4blM7A .flowchart-link{stroke:#333;fill:none}#mermaid-svg-JEiT0zcdjO4blM7A .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-JEiT0zcdjO4blM7A .edgeLabel rect{opacity:0.9}#mermaid-svg-JEiT0zcdjO4blM7A .edgeLabel span{color:#333}#mermaid-svg-JEiT0zcdjO4blM7A .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-JEiT0zcdjO4blM7A .cluster text{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-JEiT0zcdjO4blM7A .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-JEiT0zcdjO4blM7A text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-JEiT0zcdjO4blM7A .actor-line{stroke:grey}#mermaid-svg-JEiT0zcdjO4blM7A .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-JEiT0zcdjO4blM7A .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-JEiT0zcdjO4blM7A #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-JEiT0zcdjO4blM7A .sequenceNumber{fill:#fff}#mermaid-svg-JEiT0zcdjO4blM7A #sequencenumber{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A #crosshead path{fill:#333;stroke:#333}#mermaid-svg-JEiT0zcdjO4blM7A .messageText{fill:#333;stroke:#333}#mermaid-svg-JEiT0zcdjO4blM7A .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-JEiT0zcdjO4blM7A .labelText,#mermaid-svg-JEiT0zcdjO4blM7A .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-JEiT0zcdjO4blM7A .loopText,#mermaid-svg-JEiT0zcdjO4blM7A .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-JEiT0zcdjO4blM7A .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-JEiT0zcdjO4blM7A .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-JEiT0zcdjO4blM7A .noteText,#mermaid-svg-JEiT0zcdjO4blM7A .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-JEiT0zcdjO4blM7A .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-JEiT0zcdjO4blM7A .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-JEiT0zcdjO4blM7A .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-JEiT0zcdjO4blM7A .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A .section{stroke:none;opacity:0.2}#mermaid-svg-JEiT0zcdjO4blM7A .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-JEiT0zcdjO4blM7A .section2{fill:#fff400}#mermaid-svg-JEiT0zcdjO4blM7A .section1,#mermaid-svg-JEiT0zcdjO4blM7A .section3{fill:#fff;opacity:0.2}#mermaid-svg-JEiT0zcdjO4blM7A .sectionTitle0{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .sectionTitle1{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .sectionTitle2{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .sectionTitle3{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-JEiT0zcdjO4blM7A .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A .grid path{stroke-width:0}#mermaid-svg-JEiT0zcdjO4blM7A .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-JEiT0zcdjO4blM7A .task{stroke-width:2}#mermaid-svg-JEiT0zcdjO4blM7A .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A .taskText:not([font-size]){font-size:11px}#mermaid-svg-JEiT0zcdjO4blM7A .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-JEiT0zcdjO4blM7A .task.clickable{cursor:pointer}#mermaid-svg-JEiT0zcdjO4blM7A .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-JEiT0zcdjO4blM7A .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-JEiT0zcdjO4blM7A .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-JEiT0zcdjO4blM7A .taskText0,#mermaid-svg-JEiT0zcdjO4blM7A .taskText1,#mermaid-svg-JEiT0zcdjO4blM7A .taskText2,#mermaid-svg-JEiT0zcdjO4blM7A .taskText3{fill:#fff}#mermaid-svg-JEiT0zcdjO4blM7A .task0,#mermaid-svg-JEiT0zcdjO4blM7A .task1,#mermaid-svg-JEiT0zcdjO4blM7A .task2,#mermaid-svg-JEiT0zcdjO4blM7A .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-JEiT0zcdjO4blM7A .taskTextOutside0,#mermaid-svg-JEiT0zcdjO4blM7A .taskTextOutside2{fill:#000}#mermaid-svg-JEiT0zcdjO4blM7A .taskTextOutside1,#mermaid-svg-JEiT0zcdjO4blM7A .taskTextOutside3{fill:#000}#mermaid-svg-JEiT0zcdjO4blM7A .active0,#mermaid-svg-JEiT0zcdjO4blM7A .active1,#mermaid-svg-JEiT0zcdjO4blM7A .active2,#mermaid-svg-JEiT0zcdjO4blM7A .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-JEiT0zcdjO4blM7A .activeText0,#mermaid-svg-JEiT0zcdjO4blM7A .activeText1,#mermaid-svg-JEiT0zcdjO4blM7A .activeText2,#mermaid-svg-JEiT0zcdjO4blM7A .activeText3{fill:#000 !important}#mermaid-svg-JEiT0zcdjO4blM7A .done0,#mermaid-svg-JEiT0zcdjO4blM7A .done1,#mermaid-svg-JEiT0zcdjO4blM7A .done2,#mermaid-svg-JEiT0zcdjO4blM7A .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-JEiT0zcdjO4blM7A .doneText0,#mermaid-svg-JEiT0zcdjO4blM7A .doneText1,#mermaid-svg-JEiT0zcdjO4blM7A .doneText2,#mermaid-svg-JEiT0zcdjO4blM7A .doneText3{fill:#000 !important}#mermaid-svg-JEiT0zcdjO4blM7A .crit0,#mermaid-svg-JEiT0zcdjO4blM7A .crit1,#mermaid-svg-JEiT0zcdjO4blM7A .crit2,#mermaid-svg-JEiT0zcdjO4blM7A .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-JEiT0zcdjO4blM7A .activeCrit0,#mermaid-svg-JEiT0zcdjO4blM7A .activeCrit1,#mermaid-svg-JEiT0zcdjO4blM7A .activeCrit2,#mermaid-svg-JEiT0zcdjO4blM7A .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-JEiT0zcdjO4blM7A .doneCrit0,#mermaid-svg-JEiT0zcdjO4blM7A .doneCrit1,#mermaid-svg-JEiT0zcdjO4blM7A .doneCrit2,#mermaid-svg-JEiT0zcdjO4blM7A .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-JEiT0zcdjO4blM7A .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-JEiT0zcdjO4blM7A .milestoneText{font-style:italic}#mermaid-svg-JEiT0zcdjO4blM7A .doneCritText0,#mermaid-svg-JEiT0zcdjO4blM7A .doneCritText1,#mermaid-svg-JEiT0zcdjO4blM7A .doneCritText2,#mermaid-svg-JEiT0zcdjO4blM7A .doneCritText3{fill:#000 !important}#mermaid-svg-JEiT0zcdjO4blM7A .activeCritText0,#mermaid-svg-JEiT0zcdjO4blM7A .activeCritText1,#mermaid-svg-JEiT0zcdjO4blM7A .activeCritText2,#mermaid-svg-JEiT0zcdjO4blM7A .activeCritText3{fill:#000 !important}#mermaid-svg-JEiT0zcdjO4blM7A .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-JEiT0zcdjO4blM7A g.classGroup text .title{font-weight:bolder}#mermaid-svg-JEiT0zcdjO4blM7A g.clickable{cursor:pointer}#mermaid-svg-JEiT0zcdjO4blM7A g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-JEiT0zcdjO4blM7A g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-JEiT0zcdjO4blM7A .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-JEiT0zcdjO4blM7A .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-JEiT0zcdjO4blM7A .dashed-line{stroke-dasharray:3}#mermaid-svg-JEiT0zcdjO4blM7A #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A .commit-id,#mermaid-svg-JEiT0zcdjO4blM7A .commit-msg,#mermaid-svg-JEiT0zcdjO4blM7A .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-JEiT0zcdjO4blM7A g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-JEiT0zcdjO4blM7A g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-JEiT0zcdjO4blM7A g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-JEiT0zcdjO4blM7A .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-JEiT0zcdjO4blM7A .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-JEiT0zcdjO4blM7A .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-JEiT0zcdjO4blM7A .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-JEiT0zcdjO4blM7A .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-JEiT0zcdjO4blM7A .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-JEiT0zcdjO4blM7A .edgeLabel text{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JEiT0zcdjO4blM7A .node circle.state-start{fill:black;stroke:black}#mermaid-svg-JEiT0zcdjO4blM7A .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-JEiT0zcdjO4blM7A #statediagram-barbEnd{fill:#9370db}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-state .divider{stroke:#9370db}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-JEiT0zcdjO4blM7A .note-edge{stroke-dasharray:5}#mermaid-svg-JEiT0zcdjO4blM7A .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-JEiT0zcdjO4blM7A .error-icon{fill:#522}#mermaid-svg-JEiT0zcdjO4blM7A .error-text{fill:#522;stroke:#522}#mermaid-svg-JEiT0zcdjO4blM7A .edge-thickness-normal{stroke-width:2px}#mermaid-svg-JEiT0zcdjO4blM7A .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-JEiT0zcdjO4blM7A .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-JEiT0zcdjO4blM7A .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-JEiT0zcdjO4blM7A .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-JEiT0zcdjO4blM7A .marker{fill:#333}#mermaid-svg-JEiT0zcdjO4blM7A .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;}</style> <style>#mermaid-svg-JEiT0zcdjO4blM7A { color: rgba(0, 0, 0, 0.75); font: ; }</style>
Android影片
ViewAnimation-視圖影片-API1引入
PropertyAnimation-屬性影片-API1引入
TweenAnimation-補間影片
FrameAnimation-逐幀影片
ValueAnimator
ObjectAnimator

1.2 Android 影片的特點是什么?

影片特點
Tween Animation(補間影片)通過對控制元件不斷地執行影像變換(平移、縮放、旋轉、透明度)從而產生影片,是一種漸進式影片,支持自定義
Frame Animation(逐幀影片)通過順序地播放一系列影像從而產生影片效果
ValueAnimator不會對控制元件執行任何操作,只會在監聽回呼中回傳值得漸變程序
ObjectAnimator通過動態地改變控制元件得屬性從而達到影片效果

1.3 Tween Animation 補間影片中的軸點是什么作用?

在縮放影片和旋轉影片中有軸點的概念,默認情況下軸點是 View 的中心點,

對于縮放影片來來說,如果軸點是 View 的中心點,在水平方向上會導致 View 向左右兩個方向同時縮放;如果把軸點設為 View 的右邊界,那么 View 只會向左邊進行縮放,

對于旋轉影片來說,View 是圍繞著軸點進行旋轉的,如果軸點是 View 的中心點,那么 View 的旋轉看起來就是穩定地旋轉,否則會形成一種偏心旋轉效果,

1.4 自定義 View 影片的步驟是什么?

  1. 集成 Animation 抽象類;
  2. 重寫它的 initializeapplyTransformation 方法;
  3. initialize 方法中做一些初始化作業;
  4. applyTransformation 方法中進行相應的矩陣變換,

這里展示一個來自 ApiDemos 里的翻轉影片效果:

public class FlipAnimation extends Animation {
    private float mStartDegree;
    private float mEndDegree;
    private float mCenterX;
    private float mCenterY;
    private Camera mCamera;
    private boolean mFlag;
    private float mZDistance = 400f;
    public FlipAnimation(float startDegree, float endDegree, float centerX, float centerY, boolean flag) {
        mStartDegree = startDegree;
        mEndDegree = endDegree;
        mCenterX = centerX;
        mCenterY = centerY;
        mFlag = flag;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCamera = new Camera();
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        final float startDegree = mStartDegree;
        final float endDegree = mEndDegree;
        final float zDistance = mZDistance;
        final float centerX = mCenterX;
        final float centerY = mCenterY;
        float currDegree = startDegree + interpolatedTime * (endDegree - startDegree);
        Camera camera = mCamera;
        Matrix matrix = t.getMatrix();
        camera.save();
        if (mFlag) {
            camera.translate(0, 0, interpolatedTime * zDistance);
        } else {
            camera.translate(0,0, (1 - interpolatedTime) * zDistance);
        }
        camera.rotateX(currDegree);
        camera.getMatrix(matrix);
        camera.restore();
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

頁面布局如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/rl"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/cab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/junk_cab"
        android:layout_centerHorizontal="true" />

    <ImageView
        android:id="@+id/junk_ok"
        android:visibility="invisible"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:src="@drawable/ok"
        android:layout_below="@id/cab"
        android:layout_centerHorizontal="true" />

    <ImageView
        android:id="@+id/bin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:src="@drawable/junk_bin"
        android:layout_below="@id/cab"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

頁面代碼如下:

public class CustomFlipAnimationActivity extends Activity {

    private RelativeLayout mRl;
    private ImageView mIvCab;
    private ImageView mIvJunkOk;
    private ImageView mIvBin;

    public static void start(Context context) {
        Intent starter = new Intent(context, CustomFlipAnimationActivity.class);
        context.startActivity(starter);
    }

    private boolean mInvisToVis = true;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_flip_animation);

        initViews();

        mRl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Rect rect = new Rect();
                mRl.getHitRect(rect);
                final float centerX = rect.centerX();
                final float centerY = rect.centerY();
                FlipAnimation flipAnimation = new FlipAnimation(0.0F, 90.0F, centerX, centerY, true);
                flipAnimation.setDuration(600L);
                flipAnimation.setInterpolator(new AccelerateInterpolator());
                flipAnimation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public final void onAnimationStart(Animation paramAnonymousAnimation) {
                    }

                    @Override
                    public final void onAnimationEnd(Animation paramAnonymousAnimation) {
                        if (mInvisToVis) {
                            mIvJunkOk.setVisibility(View.VISIBLE);
                            mIvCab.setVisibility(View.INVISIBLE);
                            mIvBin.setVisibility(View.INVISIBLE);
                        } else {
                            mIvJunkOk.setVisibility(View.INVISIBLE);
                            mIvCab.setVisibility(View.VISIBLE);
                            mIvBin.setVisibility(View.VISIBLE);
                        }
                        mInvisToVis = !mInvisToVis;
                        FlipAnimation localb = new FlipAnimation(270.0F, 360.0F, centerX, centerY, false);
                        localb.setDuration(600L);
                        localb.setFillAfter(true);
                        localb.setInterpolator(new DecelerateInterpolator());
                        localb.setAnimationListener(null);
                        mRl.startAnimation(localb);
                    }

                    @Override
                    public final void onAnimationRepeat(Animation paramAnonymousAnimation) {
                    }
                });
                mRl.startAnimation(flipAnimation);
            }
        });
    }

    private void initViews() {
        mRl = (RelativeLayout) findViewById(R.id.rl);
        mIvCab = (ImageView) findViewById(R.id.cab);
        mIvJunkOk = (ImageView) findViewById(R.id.junk_ok);
        mIvBin = (ImageView) findViewById(R.id.bin);
    }
}

翻轉效果如下:
在這里插入圖片描述

2. View 影片的特殊使用場景

2.1 如何控制 ViewGroup 中子元素的出場效果?

使用 <layoutAnimation>,一般用于 ListView

2.2 如何自定義 Activity 的切換效果?

使用 overridePendingTransition(int enterAnim, int exitAnim) 方法,這個方法必須在 startActivity(Intent intent) 或者 finish() 之后被呼叫才有效,

當啟動一個 Activity 時,overridePendingTransition(int enterAnim, int exitAnim) 方法中的 enterAnim 表示給新打開的 Actiivty 添加入場影片效果,exitAnim 表示給當前的 Activity 添加出場影片效果,

當關閉一個 Activity 時,overridePendingTransition(int enterAnim, int exitAnim) 方法中的 enterAnim 表示恢復到前臺的 Actiivty 添加入場影片效果,exitAnim 表示給關閉的 Activity 添加出場影片效果,

也就是說,overridePendingTransition(int enterAnim, int exitAnim) 為即將到來的 Activity 添加入場影片效果,為即將退出的 Activity 添加出場影片效果,

需要注意的是,enterAnimexitAnim 都是定義在 anim 目錄下的影片資源檔案的 id,如果不希望有任何頁面切換效果,可以把這個引數都傳入 0 即可,

2.3 如何自定義 Fragment 的切換效果?

使用 setCustomAnimations(@AnimRes int enter, @AnimRes int exit) 這個方法,這里同樣是定義在 anim 目錄下的影片資源檔案的 id,

3. 屬性影片

3.1 插值器和估值器的作用分別是什么?

插值器(或者說時間插值器)需要實作 Interpolator 介面或者 TimeInterpolator 介面:

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}
public interface Interpolator extends TimeInterpolator {
    // A new interface, TimeInterpolator, was introduced for the new android.animation
    // package. This older Interpolator interface extends TimeInterpolator so that users of
    // the new Animator-based animations can use either the old Interpolator implementations or
    // new classes that implement TimeInterpolator directly.
}

需要說明的是:

getInterpolation 方法的 input 引數與我們設定的任何值都沒有關系,只與時間有關,隨著時間的推移,影片的進度也自然地增加,這個值也會從 0 增加到 1.0,所以,這個值的取值范圍是 [0,1.0],其中 0 表示影片剛開始,1.0 表示影片結束了,0.5 表示影片進行到一半了,

getInterpolation 方法的回傳值表示當前實際想要顯示的進度,這個值可以超過 1.0,也可以小于 0,超過 1.0 表示超過了目標值,小于 0 表示小于開始位置,

通過 setInterpolator(TimeInterpolator value) 方法設定插值器,這是策略模式的應用了,

估值器要實作 TypeEvaluator 介面:

/**
 * Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
 * allow developers to create animations on arbitrary property types, by allowing them to supply
 * custom evaluators for types that are not automatically understood and used by the animation
 * system.
 *
 * @see ValueAnimator#setEvaluator(TypeEvaluator)
 */
public interface TypeEvaluator<T> {

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
     * and <code>t</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value.
     * @param endValue   The end value.
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public T evaluate(float fraction, T startValue, T endValue);

}

根據當前實際顯示的進度,影片開始屬性值,影片結束屬性值,來計算出當前要顯示的屬性值,其中,fraction 的值就是插值器的 getInterpolation 方法的回傳值,

3.2 如何使用屬性影片對任意屬性做影片?

  1. 嘗試給控制元件提供該屬性的 get 和 set 方法,這樣屬性影片會根據外界傳遞的該屬性的初始值和最終值,以影片的效果多次去呼叫 set 方法,每次傳遞給 set 方法的值都不一樣,這樣隨著時間的推移,所傳遞的值越來越接近最終值,

    需要說明的是,set 方法是必須的,get 方法只有在影片沒有傳遞初始值的時候才需要,因為此時系統需要通過 get 方法去獲取初始值,如果沒有 get 方法則會取影片引數型別的默認值作為初始值,當無法獲取影片引數型別的默認值時,則會直接崩潰,

  2. 如果不能給直接給控制元件添加該屬性的 get 和 set 方法,則可以使用一個類來包裝原始的控制元件物件,間接地為其提供 get 和 set 方法;

  3. 采用 ValueAnimator,監聽影片程序,自己實作屬性的改變,

下面使用具體的例子來說明:

使用影片在 5s 內把 Button 的寬度增加到 500 px,

我們先采用繼承 Button 來自定義一個 MyButton 來實作:

public class MyButton extends Button {

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setBreadth(int width) {
        getLayoutParams().width = width;
        requestLayout();
    }
}

使用屬性影片來實作影片效果,代碼如下:

ObjectAnimator.ofInt(mBtn5, "breadth", 500).setDuration(2000).start();

效果如下:
在這里插入圖片描述
可以看到,影片開始的寬度是從 0 開始的,這是因為我們并沒有提供 breadth 屬性的 get 方法,而 breadth 引數的默認值是 0,所以影片開始的寬度是 0,那該怎么辦呢?添加對應的 get 方法即可,

public class MyButton extends Button {

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setBreadth(int width) {
        getLayoutParams().width = width;
        requestLayout();
    }

    public int getBreadth() {
        return getWidth();
    }
}

代碼不用改動,再次查看效果:
在這里插入圖片描述
我們還可以使用一個類包裝 Button 物件來實作:

private static class ViewWrapper {
    private View mTarget;
    private ViewWrapper(View target) {
        mTarget = target;
    }
    public int getBreadth() {
        return mTarget.getLayoutParams().width;
    }
    public void setBreadth(int width) {
        mTarget.getLayoutParams().width = width;
        mTarget.requestLayout();
    }
}

點擊按鈕時的代碼如下:

ViewWrapper viewWrapper = new ViewWrapper(mBtn3);
ObjectAnimator.ofInt(viewWrapper, "breadth", mBtn3.getWidth(), 500).setDuration(2000).start();

同樣可以實作效果,

現在需求變化了:使用影片在 5s 內把 Button 的寬度和高度都增加到 500 px,

public class MyButton extends Button {

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    public void setSize(PointF point) {
        getLayoutParams().width = (int) point.x;
        getLayoutParams().height = (int) point.y;
        requestLayout();
    }
}

點擊按鈕的代碼如下:

ObjectAnimator.ofObject(mBtn6, "size", new PointFEvaluator(), new PointF(500F,500F)).setDuration(2000).start();

運行程式,崩潰了,日志如下:

W/PropertyValuesHolder( 6601): Method getSize() with type null not found on target class class com.wzc.chapter_7.MyButton
D/AndroidRuntime( 6601): Shutting down VM
E/AndroidRuntime( 6601): FATAL EXCEPTION: main
E/AndroidRuntime( 6601): Process: com.wzc.chapter_7, PID: 6601
E/AndroidRuntime( 6601): java.lang.NullPointerException: Attempt to read from field 'float android.graphics.PointF.x' on a null object reference
E/AndroidRuntime( 6601): 	at android.animation.PointFEvaluator.evaluate(PointFEvaluator.java:73)
E/AndroidRuntime( 6601): 	at android.animation.PointFEvaluator.evaluate(PointFEvaluator.java:23)
E/AndroidRuntime( 6601): 	at android.animation.KeyframeSet.getValue(KeyframeSet.java:202)
E/AndroidRuntime( 6601): 	at android.animation.PropertyValuesHolder.calculateValue(PropertyValuesHolder.java:1017)
E/AndroidRuntime( 6601): 	at android.animation.ValueAnimator.animateValue(ValueAnimator.java:1561)
E/AndroidRuntime( 6601): 	at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:987)
E/AndroidRuntime( 6601): 	at android.animation.ValueAnimator.setCurrentFraction(ValueAnimator.java:692)
E/AndroidRuntime( 6601): 	at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:655)
E/AndroidRuntime( 6601): 	at android.animation.ValueAnimator.start(ValueAnimator.java:1087)
E/AndroidRuntime( 6601): 	at android.animation.ValueAnimator.start(ValueAnimator.java:1106)
E/AndroidRuntime( 6601): 	at android.animation.ObjectAnimator.start(ObjectAnimator.java:852)
E/AndroidRuntime( 6601): 	at com.wzc.chapter_7.PropertyAnimationActivity$6.onClick(PropertyAnimationActivity.java:104)
E/AndroidRuntime( 6601): 	at android.view.View.performClick(View.java:7509)
E/AndroidRuntime( 6601): 	at android.view.View.performClickInternal(View.java:7486)
E/AndroidRuntime( 6601): 	at android.view.View.access$3600(View.java:841)
E/AndroidRuntime( 6601): 	at android.view.View$PerformClick.run(View.java:28720)
E/AndroidRuntime( 6601): 	at android.os.Handler.handleCallback(Handler.java:938)
E/AndroidRuntime( 6601): 	at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 6601): 	at android.os.Looper.loop(Looper.java:236)
E/AndroidRuntime( 6601): 	at android.app.ActivityThread.main(ActivityThread.java:8059)
E/AndroidRuntime( 6601): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 6601): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
E/AndroidRuntime( 6601): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)

從日志可以看到,是因為缺少 getSize 方法,

添加 getSize 方法:

public class MyButton extends Button {

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setSize(PointF point) {
        getLayoutParams().width = (int) point.x;
        getLayoutParams().height = (int) point.y;
        requestLayout();
    }

    public PointF getSize() {
        return new PointF(getWidth(), getHeight());
    }
}

運行程式,效果如下:
在這里插入圖片描述

4. 使用影片的注意事項

  1. 幀影片 OOM 問題:對于幀影片來說,當圖片數量較多且圖片尺寸較大時就很容易出現 OOM 問題,這時我們要減少圖片數量,減小圖片尺寸,或者采用其他影片來實作,
  2. 無限回圈屬性影片記憶體泄漏問題:無限回圈的屬性影片,在 Activity 退出時要及時取消影片,否則影片會無限回圈,從而導致 View 控制元件無法釋放,進一步導致整個 Activity 無法釋放,最終引起記憶體泄漏,作為對比,View 影片不存在這種問題,
  3. View 影片完成后 View 無法隱藏問題:View 影片是對 View 的影像做影片,并不是真正地改變 View 的狀態,所以會出現影片完成后設定 setVisibility(View.GONE) 失效的問題,這個時候只要呼叫 View.clearAnimation() 清除 View 影片即可解決此問題,
  4. 使用 px 影片在不同設備上影片效果不同的問題:盡量使用 dp,而不要使用 px,
  5. View 影片平移后,控制元件點擊事件仍在原位置的問題:可以改用屬性影片來實作,
  6. 影片不流暢問題:建議開啟硬體加速,這樣可以提供影片的流暢性,
  7. onAnimationEnd沒有回呼的問題onAnimationEnd 可能因各種例外沒被回呼,建議加上超時保護或者通過 postDelay 替代 onAnimationEnd,參考:onAnimationEnd is not getting called, onAnimationStart works fine,

參考

  • Android setVisibility(View.GONE)無效的問題及原因分析

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/352255.html

標籤:其他

上一篇:小程式仿instagram互動(附帶長串列性能優化處理)

下一篇:反編譯獲取apk安裝包源代碼步驟

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more