澳门金莎娱乐网站:截图功能源码解析,Android源

作者: 数据库信息  发布:2019-12-23

几日前那篇小说大家注重讲一下Android系统中的截屏事件管理流程。用过android系统手提式有线电话机的同桌应该都精通,平日的android手提式有线电话机按下音量收缩键和电源开关就能够触发截屏事件(国内定制机做个改过的这里就不做思谋了)。那么这里的截屏事件是如何触发的吗?触发之后android系统是什么样贯彻截屏操作的呢?带着那多个难题,起头我们的源码阅读流程。

诚如未有改造rom的android原生系统截图效能的组合键是高低减 开机键;前些天大家从源码角度来深入分析截图功效是什么样在源码中达成的。

大家领略这里的截屏事件是经过大家的开关操作触发的,所以这里就须求我们从android系统的按钮触发模块开首看起,由于大家在分裂的App页面,操作音量减少键和电源键都会触发系统的截屏管理,所以那边的按钮触发逻辑应该是Android系统的全局按钮管理逻辑。

在android系统中,由于大家的每一个Android分界面都是贰个Activity,而分界面的展示都以因此Window对象实现的,每种Window对象实际都是PhoneWindow的实例,而各种PhoneWindow对象都对应叁个PhoneWindowManager对象,当大家在Activity分界面推行按钮操作的时候,在将开关的管理操作分发到App早前,首先会回调PhoneWindowManager中的dispatchUnhandledKey方法,该方法重要用以实行当前App处理开关在此之前的操作,大家切实看一下该措施的贯彻。

在android系统中,由于大家的每八个Android分界面都以一个Activity,而分界面包车型大巴彰显都是通过Window对象达成的,各类Window对象实际都以PhoneWindow的实例,而各类PhoneWindow对象都一个PhoneWindowManager对象,当大家在Activity分界面施行开关操作的时候,在将按钮的拍卖操作分发到App在此以前,首先会回调PhoneWindowManager中的dispatchUnhandledKey方法,该办法首要用来执行当前App管理开关此前的操作,大家具体看一下该方法的完毕。

/** {@inheritDoc} */
    @Override
    public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
        ...
        KeyEvent fallbackEvent = null;
        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            final KeyCharacterMap kcm = event.getKeyCharacterMap();
            final int keyCode = event.getKeyCode();
            final int metaState = event.getMetaState();
            final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
                    && event.getRepeatCount() == 0;

            // Check for fallback actions specified by the key character map.
            final FallbackAction fallbackAction;
            if (initialDown) {
                fallbackAction = kcm.getFallbackAction(keyCode, metaState);
            } else {
                fallbackAction = mFallbackActions.get(keyCode);
            }

            if (fallbackAction != null) {
                ...
                final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
                fallbackEvent = KeyEvent.obtain(
                        event.getDownTime(), event.getEventTime(),
                        event.getAction(), fallbackAction.keyCode,
                        event.getRepeatCount(), fallbackAction.metaState,
                        event.getDeviceId(), event.getScanCode(),
                        flags, event.getSource(), null);

                if (!interceptFallback(win, fallbackEvent, policyFlags)) {
                    fallbackEvent.recycle();
                    fallbackEvent = null;
                }

                if (initialDown) {
                    mFallbackActions.put(keyCode, fallbackAction);
                } else if (event.getAction() == KeyEvent.ACTION_UP) {
                    mFallbackActions.remove(keyCode);
                    fallbackAction.recycle();
                }
            }
        }

        ...
        return fallbackEvent;
    }
/** {@inheritDoc} */
 @Override
 public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
 ...
 KeyEvent fallbackEvent = null;
 if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
 final KeyCharacterMap kcm = event.getKeyCharacterMap();
 final int keyCode = event.getKeyCode();
 final int metaState = event.getMetaState();
 final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
 && event.getRepeatCount() == 0;

 // Check for fallback actions specified by the key character map.
 final FallbackAction fallbackAction;
 if (initialDown) {
 fallbackAction = kcm.getFallbackAction(keyCode, metaState);
 } else {
 fallbackAction = mFallbackActions.get(keyCode);
 }

 if (fallbackAction != null) {
 ...
 final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
 fallbackEvent = KeyEvent.obtain(
 event.getDownTime(), event.getEventTime(),
 event.getAction(), fallbackAction.keyCode,
 event.getRepeatCount(), fallbackAction.metaState,
 event.getDeviceId(), event.getScanCode(),
 flags, event.getSource(), null);

 if (!interceptFallback(win, fallbackEvent, policyFlags)) {
 fallbackEvent.recycle();
 fallbackEvent = null;
 }

 if (initialDown) {
 mFallbackActions.put(keyCode, fallbackAction);
 } else if (event.getAction() == KeyEvent.ACTION_UP) {
 mFallbackActions.remove(keyCode);
 fallbackAction.recycle();
 }
 }
 }
 ...
 return fallbackEvent;
 }

此间大家关注一下方法体中调用的:interceptFallback方法,通过调用该情势将管理按钮的操作下发到该方法中,大家继续看一下该办法的兑现逻辑。

那边大家关注一下方法体中调用的:interceptFallback方法,通过调用该形式将管理按键的操作下发到该方法中,大家继续看一下该办法的兑现逻辑。

private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
        int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
        if ((actions & ACTION_PASS_TO_USER) != 0) {
            long delayMillis = interceptKeyBeforeDispatching(
                    win, fallbackEvent, policyFlags);
            if (delayMillis == 0) {
                return true;
            }
        }
        return false;
    }
private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
 int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
 if ((actions & ACTION_PASS_TO_USER) != 0) {
 long delayMillis = interceptKeyBeforeDispatching(
 win, fallbackEvent, policyFlags);
 if (delayMillis == 0) {
 return true;
 }
 }
 return false;
 }

下一场大家看见在interceptFallback方法中大家调用了interceptKeyBeforeQueueing方法,通过翻阅大家大家领略该方式主要实现了对截屏开关的拍卖流程,那样咱们一连看一下interceptKeyBeforeWueueing方法的拍卖:

下一场大家见到在interceptFallback方法中我们调用了interceptKeyBeforeQueueing方法,通过翻阅大家大家领略该方法主要达成了对截屏开关的管理流程,那样我们世襲看一下interceptKeyBeforeWueueing方法的拍卖:

@Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        if (!mSystemBooted) {
            // If we have not yet booted, don't let key events do anything.
            return 0;
        }

        ...
        // Handle special keys.
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                if (mUseTvRouting) {
                    // On TVs volume keys never go to the foreground app
                    result &= ~ACTION_PASS_TO_USER;
                }
                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                    if (down) {
                        if (interactive && !mScreenshotChordVolumeDownKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mScreenshotChordVolumeDownKeyTriggered = true;
                            mScreenshotChordVolumeDownKeyTime = event.getDownTime();
                            mScreenshotChordVolumeDownKeyConsumed = false;
                            cancelPendingPowerKeyAction();
                            interceptScreenshotChord();
                        }
                    } else {
                        mScreenshotChordVolumeDownKeyTriggered = false;
                        cancelPendingScreenshotChordAction();
                    }
                }
                ...

        return result;
    }
@Override
 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
 if (!mSystemBooted) {
 // If we have not yet booted, don't let key events do anything.
 return 0;
 }

 ...
 // Handle special keys.
 switch (keyCode) {
 case KeyEvent.KEYCODE_VOLUME_DOWN:
 case KeyEvent.KEYCODE_VOLUME_UP:
 case KeyEvent.KEYCODE_VOLUME_MUTE: {
 if (mUseTvRouting) {
 // On TVs volume keys never go to the foreground app
 result &= ~ACTION_PASS_TO_USER;
 }
 if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
 if (down) {
 if (interactive && !mScreenshotChordVolumeDownKeyTriggered
 && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
 mScreenshotChordVolumeDownKeyTriggered = true;
 mScreenshotChordVolumeDownKeyTime = event.getDownTime();
 mScreenshotChordVolumeDownKeyConsumed = false;
 cancelPendingPowerKeyAction();
 interceptScreenshotChord();
 }
 } else {
 mScreenshotChordVolumeDownKeyTriggered = false;
 cancelPendingScreenshotChordAction();
 }
 }
 ...

 return result;
 }

能够窥见此处首先推断当前系统是不是业已boot实现,若未有运行完结,则怀有的按钮操作都将失效,若运营完结,则实行后续的操作,这里我们只是关切音量收缩按钮和电源开关组合的处理事件。别的这里多说一句想安卓系统的HOME按钮事件,MENU开关事件,进度列表按钮事件等等都以在这里地完毕的,后续中大家会陆陆续续介绍那上头的开始和结果。

能够窥见此处首先判定当前系统是或不是早就boot完结,若未有运维完成,则有所的按钮操作都将失效,若运营成功,则实行后续的操作,这里大家只是关切音量裁减开关和电源按钮组合的处总管件。此外这里多说一句想安卓系统的HOME开关事件,MENU按钮事件,进度列表开关事件等等都以在那间达成的,后续中大家会陆陆续续介绍那地方的情节。

回到大家的interceptKeyBeforeQueueing方法,当本身用按下音量降低按钮的时候回步向到:case KeyEvent.KEYCODE_VOLUME_MUTE分支并推行相应的逻辑,然后还要剖断客商是不是按下了电源键,若同一时候按下了电源键,则奉行:

重临大家的interceptKeyBeforeQueueing方法,当笔者用按下音量裁减按钮的时候回步入到:case KeyEvent.KEYCODE_VOLUME_MUTE分支并履行相应的逻辑,然后还要决断客户是还是不是按下了电源键,若同时按下了电源键,则推行:

if (interactive && !mScreenshotChordVolumeDownKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mScreenshotChordVolumeDownKeyTriggered = true;
                            mScreenshotChordVolumeDownKeyTime = event.getDownTime();
                            mScreenshotChordVolumeDownKeyConsumed = false;
                            cancelPendingPowerKeyAction();
                            interceptScreenshotChord();
                        }
if (interactive && !mScreenshotChordVolumeDownKeyTriggered
 && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
 mScreenshotChordVolumeDownKeyTriggered = true;
 mScreenshotChordVolumeDownKeyTime = event.getDownTime();
 mScreenshotChordVolumeDownKeyConsumed = false;
 cancelPendingPowerKeyAction();
 interceptScreenshotChord();
 }

能够窥见此处的interceptScreenshotChrod方法就是系统思考起初执行截屏操作的发端,大家后续看一下interceptcreenshotChord方法的贯彻。

能够窥见此处的interceptScreenshotChrod方法便是系统打算开头实施截屏操作的始发,大家延续看一下interceptcreenshotChord方法的贯彻。

private void interceptScreenshotChord() {
        if (mScreenshotChordEnabled
                && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
                && !mScreenshotChordVolumeUpKeyTriggered) {
            final long now = SystemClock.uptimeMillis();
            if (now <= mScreenshotChordVolumeDownKeyTime   SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
                    && now <= mScreenshotChordPowerKeyTime
                              SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
                mScreenshotChordVolumeDownKeyConsumed = true;
                cancelPendingPowerKeyAction();

                mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
            }
        }
    }
private void interceptScreenshotChord() {
 if (mScreenshotChordEnabled
 && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
 && !mScreenshotChordVolumeUpKeyTriggered) {
 final long now = SystemClock.uptimeMillis();
 if (now <= mScreenshotChordVolumeDownKeyTime   SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
 && now <= mScreenshotChordPowerKeyTime
   SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
 mScreenshotChordVolumeDownKeyConsumed = true;
 cancelPendingPowerKeyAction();

 mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
 }
 }
 }

在方法体中大家最后会进行发送多个推迟的异步音讯,诉求施行截屏的操作而那边的延时时间,若当前输入框是张开状态,则延时日子为输入框关闭时间累计系统陈设的按钮超时时间,若当前输入框未有张开则平昔是系统布署的按钮超时管理时间,可看一下getScreenshotChordLongPressDelay方法的切切实实落到实处。

在方法体中大家最后会试行发送一个延缓的异步信息,乞求施行截屏的操作而那边的延时时间,若当前输入框是展开状态,则延时时光为输入框关闭时间累积系统计划的开关超时时间,若当前输入框未有展开则从来是系统结构的开关超时管理时间,可看一下getScreenshotChordLongPressDelay方法的切实完成。

private long getScreenshotChordLongPressDelay() {
        if (mKeyguardDelegate.isShowing()) {
            // Double the time it takes to take a screenshot from the keyguard
            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
                    ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
        }
        return ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout();
    }
private long getScreenshotChordLongPressDelay() {
 if (mKeyguardDelegate.isShowing()) {
 // Double the time it takes to take a screenshot from the keyguard
 return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
 ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
 }
 return ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout();
 }

归来大家的interceptScreenshotChord方法,发送了异步音信随后系统末段会被咱们发送的Runnable对象的run方法奉行;那样我们看一下Runnable类型的mScreenshotRunnable的run方法的兑现:

回到咱们的interceptScreenshotChord方法,发送了异步音信之后系统末段会被大家发送的Runnable对象的run方法实行,这里关于异步新闻的逻辑可参照:android源码解析之(二)–>异步音讯机制

private final Runnable mScreenshotRunnable = new Runnable() {
        @Override
        public void run() {
            takeScreenshot();
        }
    };

那般大家看一下Runnable类型的mScreenshotRunnable的run方法的贯彻:

好呢,方法体中未有实践别的操作,直接正是调用了takeScreenshot方法,那样我们后续看一下takeScreenshot方法的落到实处。

private final Runnable mScreenshotRunnable = new Runnable() {
 @Override
 public void run() {
 takeScreenshot();
 }
 };
private void takeScreenshot() {
        synchronized (mScreenshotLock) {
            if (mScreenshotConnection != null) {
                return;
            }
            ComponentName cn = new ComponentName("com.android.systemui",
                    "com.android.systemui.screenshot.TakeScreenshotService");
            Intent intent = new Intent();
            intent.setComponent(cn);
            ServiceConnection conn = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    synchronized (mScreenshotLock) {
                        if (mScreenshotConnection != this) {
                            return;
                        }
                        Messenger messenger = new Messenger(service);
                        Message msg = Message.obtain(null, 1);
                        final ServiceConnection myConn = this;
                        Handler h = new Handler(mHandler.getLooper()) {
                            @Override
                            public void handleMessage(Message msg) {
                                synchronized (mScreenshotLock) {
                                    if (mScreenshotConnection == myConn) {
                                        mContext.unbindService(mScreenshotConnection);
                                        mScreenshotConnection = null;
                                        mHandler.removeCallbacks(mScreenshotTimeout);
                                    }
                                }
                            }
                        };
                        msg.replyTo = new Messenger(h);
                        msg.arg1 = msg.arg2 = 0;
                        if (mStatusBar != null && mStatusBar.isVisibleLw())
                            msg.arg1 = 1;
                        if (mNavigationBar != null && mNavigationBar.isVisibleLw())
                            msg.arg2 = 1;
                        try {
                            messenger.send(msg);
                        } catch (RemoteException e) {
                        }
                    }
                }
                @Override
                public void onServiceDisconnected(ComponentName name) {}
            };
            if (mContext.bindServiceAsUser(
                    intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
                mScreenshotConnection = conn;
                mHandler.postDelayed(mScreenshotTimeout, 10000);
            }
        }
    }

好啊,方法体中绝非实行此外操作,直接正是调用了takeScreenshot方法,这样我们一而再看一下takeScreenshot方法的贯彻。

能够开掘这里经过反射机制成立了三个TakeScreenshotService对象然后调用了bindServiceAsUser,那样就创办了TakeScreenshotService服务并在劳动成立之后发送了四个异步音信。好了,我们看一下TakeScreenshotService的得以完毕逻辑。

private void takeScreenshot() {
 synchronized (mScreenshotLock) {
 if (mScreenshotConnection != null) {
 return;
 }
 ComponentName cn = new ComponentName("com.android.systemui",
 "com.android.systemui.screenshot.TakeScreenshotService");
 Intent intent = new Intent();
 intent.setComponent(cn);
 ServiceConnection conn = new ServiceConnection() {
 @Override
 public void onServiceConnected(ComponentName name, IBinder service) {
 synchronized (mScreenshotLock) {
 if (mScreenshotConnection != this) {
 return;
 }
 Messenger messenger = new Messenger(service);
 Message msg = Message.obtain(null, 1);
 final ServiceConnection myConn = this;
 Handler h = new Handler(mHandler.getLooper()) {
 @Override
 public void handleMessage(Message msg) {
 synchronized (mScreenshotLock) {
  if (mScreenshotConnection == myConn) {
  mContext.unbindService(mScreenshotConnection);
  mScreenshotConnection = null;
  mHandler.removeCallbacks(mScreenshotTimeout);
  }
 }
 }
 };
 msg.replyTo = new Messenger(h);
 msg.arg1 = msg.arg2 = 0;
 if (mStatusBar != null && mStatusBar.isVisibleLw())
 msg.arg1 = 1;
 if (mNavigationBar != null && mNavigationBar.isVisibleLw())
 msg.arg2 = 1;
 try {
 messenger.send(msg);
 } catch (RemoteException e) {
 }
 }
 }
 @Override
 public void onServiceDisconnected(ComponentName name) {}
 };
 if (mContext.bindServiceAsUser(
 intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
 mScreenshotConnection = conn;
 mHandler.postDelayed(mScreenshotTimeout, 10000);
 }
 }
 }
public class TakeScreenshotService extends Service {
    private static final String TAG = "TakeScreenshotService";

    private static GlobalScreenshot mScreenshot;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    final Messenger callback = msg.replyTo;
                    if (mScreenshot == null) {
                        mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
                    }
                    mScreenshot.takeScreenshot(new Runnable() {
                        @Override public void run() {
                            Message reply = Message.obtain(null, 1);
                            try {
                                callback.send(reply);
                            } catch (RemoteException e) {
                            }
                        }
                    }, msg.arg1 > 0, msg.arg2 > 0);
            }
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return new Messenger(mHandler).getBinder();
    }
}

能够窥见此处经过反射机制创造了贰个TakeScreenshotService对象然后调用了bindServiceAsUser,那样就创制了TakeScreenshotService服务并在劳动创设之后发送了二个异步新闻。好了,我们看一下TakeScreenshot瑟维斯的贯彻逻辑。

能够窥见在在TakeScreenshotService类的概念中有叁个Handler成员变量,而我们在开发银行TakeScreentshowService的时候回发送二个异步音讯,那样就能实践mHandler的handleMessage方法,然后在handleMessage方法中大家成立了叁个GlobalScreenshow对象,然后实践了takeScreenshot方法,好吧,继续看一下takeScreentshot方法的实践逻辑。

public class TakeScreenshotService extends Service {
 private static final String TAG = "TakeScreenshotService";

 private static GlobalScreenshot mScreenshot;

 private Handler mHandler = new Handler() {
 @Override
 public void handleMessage(Message msg) {
 switch (msg.what) {
 case 1:
 final Messenger callback = msg.replyTo;
 if (mScreenshot == null) {
 mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
 }
 mScreenshot.takeScreenshot(new Runnable() {
 @Override public void run() {
 Message reply = Message.obtain(null, 1);
 try {
 callback.send(reply);
 } catch (RemoteException e) {
 }
 }
 }, msg.arg1 > 0, msg.arg2 > 0);
 }
 }
 };

 @Override
 public IBinder onBind(Intent intent) {
 return new Messenger(mHandler).getBinder();
 }
}
/**
     * Takes a screenshot of the current display and shows an animation.
     */
    void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
        // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
        // only in the natural orientation of the device :!)
        mDisplay.getRealMetrics(mDisplayMetrics);
        float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
        float degrees = getDegreesForRotation(mDisplay.getRotation());
        boolean requiresRotation = (degrees > 0);
        if (requiresRotation) {
            // Get the dimensions of the device in its native orientation
            mDisplayMatrix.reset();
            mDisplayMatrix.preRotate(-degrees);
            mDisplayMatrix.mapPoints(dims);
            dims[0] = Math.abs(dims[0]);
            dims[1] = Math.abs(dims[1]);
        }

        // Take the screenshot
        mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
        if (mScreenBitmap == null) {
            notifyScreenshotError(mContext, mNotificationManager);
            finisher.run();
            return;
        }

        if (requiresRotation) {
            // Rotate the screenshot to the current orientation
            Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
                    mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(ss);
            c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
            c.rotate(degrees);
            c.translate(-dims[0] / 2, -dims[1] / 2);
            c.drawBitmap(mScreenBitmap, 0, 0, null);
            c.setBitmap(null);
            // Recycle the previous bitmap
            mScreenBitmap.recycle();
            mScreenBitmap = ss;
        }

        // Optimizations
        mScreenBitmap.setHasAlpha(false);
        mScreenBitmap.prepareToDraw();

        // Start the post-screenshot animation
        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
                statusBarVisible, navBarVisible);
    }

能够窥见在在TakeScreenshotService类的概念中有三个Handler成员变量,而大家在开发银行TakeScreentshowService的时候回发送四个异步音讯,那样就能够施行mHandler的handleMessage方法,然后在handleMessage方法中大家创设了三个GlobalScreenshow对象,然后试行了takeScreenshot方法,好吧,继续看一下takeScreentshot方法的实践逻辑。

能够看看这里后七个参数:statusBarVisible,navBarVisible是还是不是可知,而那三个参数在大家PhoneWindowManager.takeScreenshot方法传递的:

/**
 * Takes a screenshot of the current display and shows an animation.
 */
 void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
 // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
 // only in the natural orientation of the device :!)
 mDisplay.getRealMetrics(mDisplayMetrics);
 float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
 float degrees = getDegreesForRotation(mDisplay.getRotation());
 boolean requiresRotation = (degrees > 0);
 if (requiresRotation) {
 // Get the dimensions of the device in its native orientation
 mDisplayMatrix.reset();
 mDisplayMatrix.preRotate(-degrees);
 mDisplayMatrix.mapPoints(dims);
 dims[0] = Math.abs(dims[0]);
 dims[1] = Math.abs(dims[1]);
 }

 // Take the screenshot
 mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
 if (mScreenBitmap == null) {
 notifyScreenshotError(mContext, mNotificationManager);
 finisher.run();
 return;
 }

 if (requiresRotation) {
 // Rotate the screenshot to the current orientation
 Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
 mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
 Canvas c = new Canvas(ss);
 c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
 c.rotate(degrees);
 c.translate(-dims[0] / 2, -dims[1] / 2);
 c.drawBitmap(mScreenBitmap, 0, 0, null);
 c.setBitmap(null);
 // Recycle the previous bitmap
 mScreenBitmap.recycle();
 mScreenBitmap = ss;
 }

 // Optimizations
 mScreenBitmap.setHasAlpha(false);
 mScreenBitmap.prepareToDraw();

 // Start the post-screenshot animation
 startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
 statusBarVisible, navBarVisible);
 }
if (mStatusBar != null && mStatusBar.isVisibleLw())
                            msg.arg1 = 1;
                        if (mNavigationBar != null && mNavigationBar.isVisibleLw())
                            msg.arg2 = 1;

能够看来此间后七个参数:statusBarVisible,navBarVisible是不是可以见到,而那三个参数在大家PhoneWindowManager.takeScreenshot方法传递的:

可以知道若果mStatusBar可以知道,则传递的statusBarVisible为true,若mNavigationBar可以知道,则传递的navBarVisible为true。然后大家在截屏的时等候法庭判果决nStatusBar是还是不是可以看到,mNavigationBar是还是不是可以预知,若可知的时候则截屏同样将其截屏出来。继续回来我们的takeScreenshot方法,然后调用了:

if (mStatusBar != null && mStatusBar.isVisibleLw())
 msg.arg1 = 1;
 if (mNavigationBar != null && mNavigationBar.isVisibleLw())
 msg.arg2 = 1;
// Take the screenshot
mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);

看得出若果mStatusBar可以看到,则传递的statusBarVisible为true,若mNavigationBar可知,则传递的navBarVisible为true。然后大家在截屏的时等候法庭判果断nStatusBar是或不是可知,mNavigationBar是不是可以预知,若可以知道的时候则截屏相同将其截屏出来。继续回来我们的takeScreenshot方法,然后调用了:

方法,看注释,这里正是实施截屏事件的具体操作了,然后笔者看一下SurfaceControl.screenshot方法的切实可行落实,其它这里必要专一的是,截屏之后回来的是二个Bitmap对象,其实熟谙android绘制机制的童鞋应该清楚android中保有展现能够显示的事物,在内部存款和储蓄器中表现都以Bitmap对象。

本文由金沙澳门官网发布于数据库信息,转载请注明出处:澳门金莎娱乐网站:截图功能源码解析,Android源

关键词: 金沙澳门官网