Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android仿天天果園Splash

Android仿天天果園Splash

編輯:關於Android編程

天天果園的App 開機Splash動畫有一個空心圓逐漸擴散的效果,仔細看天天果園的logo,在圓擴散期間還有一個漸變消失效果,建議大家使用surfaceView來實現這種對幀率由一定要求的動畫繪制,筆者也嘗試過重寫View的onDraw方法,結果在高分辨率Android手機上悲劇了。

先來看看要實現的效果

這裡寫圖片描述

首先用android studio打開react-native項目中android文件夾,新建SplashScreen,SlashScreenPackage兩個類
這裡寫圖片描述

SplashScreen繼承ReactContextBaseJavaModule,要求重寫getName()方法。
-
@Override
public String getName() {return "%JS調用類的名字%"; }

創建內部類MySurfaceView繼承SurfaceView

` class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private Thread thread; // SurfaceView通常需要自己單獨的線程來播放動畫
private Canvas canvas;
private SurfaceHolder surfaceHolder;

public MySurfaceView(Context c) {
    super(c);
    this.surfaceHolder = this.getHolder();
    this.surfaceHolder.addCallback(this);


}


@Override
public void run() {

    if (!firstRun) {

        while (true) {
            if (radius > MaxRaduis) {
             if(splashDialog.isShowing()){
                    splashDialog.dismiss();
                }
                break;
            }

            canvas = this.surfaceHolder.lockCanvas(null);    //通過lockCanvas加鎖並得到該SurfaceView的畫布


            Paint paint = new Paint();
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            if (canvas != null) {
                canvas.drawPaint(paint);
                canvas.clipRect(0, 0, ScreenWidth, ScreenHeight);
                int n = radius;
                Path mPath = new Path();
                mPath.addCircle(cX, cY, n, Path.Direction.CCW);
                canvas.clipPath(mPath, Region.Op.DIFFERENCE);
                canvas.drawColor(Color.GRAY);

            }

            radius += rate;
            if (canvas != null) {
                this.surfaceHolder.unlockCanvasAndPost(canvas); // 釋放鎖並提交畫布進行重繪
            }

        }

    } else {
        //第一次運行
        canvas = this.surfaceHolder.lockCanvas(null); // 通過lockCanvas加鎖並得到該SurfaceView的畫布

        if (canvas != null) {
            canvas.clipRect(0, 0, ScreenWidth, ScreenHeight);
            canvas.drawColor(Color.WHITE);
            canvas.drawBitmap(bmp, 0, 0, null);
            this.surfaceHolder.unlockCanvasAndPost(canvas);
        }
        firstRun = false;
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
    Log.i("Test","surfaceDestroyed");
}

@Override
public void surfaceCreated(SurfaceHolder arg0) {
    Log.i("Test","surfaceCreated");
    doSufaceAnimation();
}

public void doSufaceAnimation() {
    this.thread = new Thread(MySurfaceView.this);

    this.thread.start();

}

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    // 這裡是SurfaceView發生變化的時候觸發的部分

}

SplashScreen構造方法,需要傳入上下文環境

   public SplashScreen(ReactApplicationContext reactContext,Activity activity) {
        super(reactContext);
        this.activity = activity;

    }
開機顯示畫面,初始化控件
private void showSplashScreen() {
        Log.i("Test", "showSplashScreen");
        final int drawableId = getSplashId();
        getActivity().runOnUiThread(new Runnable() {
            public void run() {
                // Get reference to display
//                rl = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
                firstRun = true;
                Resources resources = activity.getResources();
                DisplayMetrics dm = resources.getDisplayMetrics();
                handler = new Handler();
                ScreenWidth = dm.widthPixels;
                ScreenHeight = dm.heightPixels;
                rate = 40;
                int a = ScreenWidth;
                int b = ScreenHeight;
                MaxRaduis = (int) Math.sqrt(a * a + b * b) / 2 + 100; // a*a+b*b開根

getBitmap(drawableId);//處理圖片
surfaceView = new MySurfaceView(activity.getApplicationContext());
ViewGroup.LayoutParams params =
                        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                ViewGroup.LayoutParams.MATCH_PARENT);
surfaceView.setLayoutParams(params);
                surfaceView.getLayoutParams().height=ScreenHeight - getStatusBarHeight();
                surfaceView.getLayoutParams().width=ScreenWidth;
surfaceView.setClickable(false);//動畫期間不能點擊,線程影響
surfaceView.setZOrderOnTop(true);//最上層surfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);//設置畫布  背景透明
//必須在dialog內添加surfaceView,RN內 
//RootView沒法添加SurfaceView
splashDialog = new Dialog(activity, android.R.style.Theme_Translucent_NoTitleBar);
                splashDialog.setContentView(surfaceView);
splashDialog.setCancelable(false);
splashDialog.show();

            }
        });


    }
處理圖片,適配屏幕,需要注意的是為了開機時候application加載圖片位置與之後顯示的圖片位置一致,這裡要計算狀態欄的高度。
    private void getBitmap(int drawableId) {
        radius = 12;

        cX = ScreenWidth / 2;
        cY = ScreenHeight / 2;//要加上狀態欄高度
        bitmap = BitmapFactory.decodeResource(activity.getResources(), drawableId);
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int oldwidth = bitmap.getWidth();
        int oldheight = bitmap.getHeight();
        int newWidth = ScreenWidth;
        int newHeight = ScreenHeight;
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);


        resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, oldwidth,
                oldheight, matrix, true);

        bitmap.recycle();//回收內存


        int h = getStatusBarHeight();// 裁切後所取的區域左上角y坐標 ,狀態欄高度
        int w = 0;// 裁切後所取的區域左上角x坐標
        bmp = Bitmap.createBitmap(resizedBitmap, w, h, resizedBitmap.getWidth(),
                resizedBitmap.getHeight() - h, null, false);
        resizedBitmap.recycle();//回收內存

    }
獲取狀態欄高度
public int getStatusBarHeight() {
        int result = 0;
        int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = activity.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }
圖片是使用的android本地圖片資源
 private int getSplashId() {
        int drawableId = getActivity().getResources().getIdentifier("%圖片文件名稱%", "drawable", getActivity().getClass().getPackage().getName());
        if (drawableId == 0) {
            drawableId = getActivity().getResources().getIdentifier("%圖片文件名稱%", "drawable", getActivity().getPackageName());
        }
        return drawableId;
    }
提供JS調用的兩個方法,需要標注@ReactMethod
  @ReactMethod
    public void showSplash(){
        showSplashScreen();//開始遮擋主屏幕
    }

    /**
     * 提供js點用方法,圓形展開動畫開始
     */
    @ReactMethod
    public void doAnimation() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        getActivity().runOnUiThread(new Runnable() {
            public void run() {
                if (splashDialog != null && splashDialog.isShowing()) {
                    handler = new Handler();
//                   rl = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
                   rl = (ViewGroup) ((ViewGroup) splashDialog.getWindow().getDecorView()).getChildAt(0);
                    ViewGroup.LayoutParams params =
                            new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                    ViewGroup.LayoutParams.MATCH_PARENT);

                    // 一個自定義的布局,作為顯示的內容
                    ImageView contentView = new ImageView(activity);

                    contentView.setLayoutParams(params);
                    contentView.setImageBitmap(bmp);
                    popupWindow = new PopupWindow(contentView,
                            ScreenWidth, ScreenHeight - getStatusBarHeight(), true);
                    popupWindow.setTouchable(false);
                    popupWindow.setBackgroundDrawable(null);
                    popupWindow.setAnimationStyle(R.style.PopupAnimation);
                    popupWindow.showAtLocation(rl, Gravity.CENTER_HORIZONTAL, cX, cY);


                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            surfaceView.doSufaceAnimation();
                        }
                    }, 144);//為了讓popwindow有足夠時間顯示圖片

                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            popupWindow.dismiss();

                        }
                    }, 500);//這裡執行的時間要與popwindow漸變消失的動畫時間一致,popwindow的動畫,就不詳述了,開始動畫也是可以做到漸變消失的,只要最後把popwindow移除久可以了


                }
            }
        });
SplashScreenPackage需要實現ReactPackage接口
public class SplashScreenPackage implements ReactPackage {
    private ReactActivity activity;
    private SplashScreen mModuleInstance;



    public SplashScreenPackage(ReactActivity activity) {
        super();
     this.activity=activity;

    }



    @Override
    public List createNativeModules(ReactApplicationContext reactContext) {
        mModuleInstance = new SplashScreen(reactContext,activity);
        return Arrays.asList(
                mModuleInstance
        );
    }


    @Override
    public List> createJSModules() {
        return Collections.emptyList();
    }


    @Override
    public List createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.asList();
    }
}
為了傳入上下文環境給SplashScreen,mainApplication與mainActivity需要部分修改
 //mainApplication中去掉了finally
 private ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List getPackages() {
      return Arrays.asList(
          new MainReactPackage()


      );
    }

  };
//添加set方法
    public void setmReactNativeHost(ReactNativeHost mReactNativeHost) {
        this.mReactNativeHost = mReactNativeHost;
    }```

 - mainActivity中需要重寫ReactRootView,因為這個方法執行後就會根據ApplicationReactPackage構建控件

@Override
protected ReactRootView createRootView() {
ma=(MainApplication)this.getApplication();
ma.setmReactNativeHost( new ReactNativeHost(ma) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

        @Override
        protected List getPackages() {
            return Arrays.asList(
                    new MainReactPackage(),
                    new SplashScreenPackage(MainActivity.this)

            );
        }

    });

    return super.createRootView();
}```

最後還有一個問題,執行popwindow彈出時,可能會產生閃屏,建議使用透明背景的圖片顯示在popwindow中,這樣閃屏效果不明顯。

最後完成的效果,模擬器有點卡。。
這裡寫圖片描述

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved