web-dev-qa-db-ja.com

Android:WebView出力をアプリウィジェットにロードする

App WidgetWebViewを直接サポートしていませんが、ImageView(サポートされています)と次のような手法を使用することは可能ですか?説明 ここImageViewの画像を生成するには? WebViewは直接使用されませんが、ImageViewの画像を提供するためにバックグラウンドでのみ使用されます。

17
drmrbrewer

あなたのマイレージは変わるかもしれませんが、これは明らかに、少なくとも私のデバイスでは可能です。

マニフェストでは、ウィジェットの_<receiver>_を通常どおりに登録し、Serviceとその画像キャプチャを処理するWebViewも登録します。

_<manifest ... >
    ...

    <uses-permission Android:name="Android.permission.INTERNET" />
    <uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW" />

    <application ... >
        ...

        <receiver
            Android:name=".WebWidgetProvider"
            Android:label="@string/widget_name" >
            <intent-filter>
                <action Android:name="Android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                Android:name="Android.appwidget.provider"
                Android:resource="@xml/widget_info" />
        </receiver>

        <service Android:name=".WebShotService" />

    </application>

</manifest>
_

ウィジェットの情報ファイル_widget_info.xml_で、最小サイズを4 x2に設定しました。

_<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:initialLayout="@layout/widget_layout"
    Android:minWidth="292dip"
    Android:minHeight="146dip" />
_

この例では、ウィジェットのレイアウト_widget_layout.xml_は単に_ImageView._です。

_<ImageView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/widget_image"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:scaleType="fitXY"
    Android:src="@drawable/ic_launcher" />
_

この例のAppWidgetProviderは、同じ_ACTION_APPWIDGET_UPDATE_を使用してAlarmManagerで独自の更新をトリガーしているため、onUpdate()メソッドのみをオーバーライドしています。アクティブなウィジェットがある限り、サンプルのアラームは30秒ごとに発生することに注意してください。ウィジェットの更新にはかなりのコストがかかる可能性があるため、テスト以外でこれを実行することはおそらく望ましくありません。

_public class WebWidgetProvider extends AppWidgetProvider {
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // We can't trust the appWidgetIds param here, as we're using
        // ACTION_APPWIDGET_UPDATE to trigger our own updates, and
        // Widgets might've been removed/added since the alarm was last set.
        final int[] currentIds = appWidgetManager.getAppWidgetIds(
            new ComponentName(context, WebWidgetProvider.class));

        if (currentIds.length < 1) {
            return;
        }

        // We attach the current Widget IDs to the alarm Intent to ensure its
        // broadcast is correctly routed to onUpdate() when our AppWidgetProvider
        // next receives it.
        Intent iWidget = new Intent(context, WebWidgetProvider.class)
            .setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
            .putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, currentIds);
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, iWidget, 0);

        ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
            .setExact(AlarmManager.RTC, System.currentTimeMillis() + 30000, pi);

        Intent iService = new Intent(context, WebShotService.class);
        context.startService(iService);
    }
}
_

Serviceで、WebViewをインスタンス化し、それをFrameLayoutに追加します。これは、幅がゼロのWindowManagerに追加されます。高さパラメータ。次に、テストURLをWebViewにロードし、ページが終了したら、WebViewを強制的に適切なサイズにレイアウトし、それをターゲットのBitmapに描画します。 Canvasオブジェクト。これは、WebViewが完全にレンダリングされるように少し遅れて行われ、空白の白い画像になってしまうことはありません。次に、BitmapRemoteViewsオブジェクトに設定され、ウィジェットのレイアウトのImageViewが更新されます。この例のウィジェットはStackOverflowのフロントページが更新されるよりも頻繁に更新される可能性があるため、テスト目的でコードにToastを残しました。

_public class WebShotService extends Service {
    private WebView webView;
    private WindowManager winManager;

    public int onStartCommand(Intent intent, int flags, int startId) {
        winManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        webView = new WebView(this);
        webView.setVerticalScrollBarEnabled(false);
        webView.setWebViewClient(client);

        final WindowManager.LayoutParams params =
            new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
                                           WindowManager.LayoutParams.WRAP_CONTENT,
                                           WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                                           WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                                           PixelFormat.TRANSLUCENT);
        params.x = 0;
        params.y = 0;
        params.width = 0;
        params.height = 0;

        final FrameLayout frame = new FrameLayout(this);
        frame.addView(webView);
        winManager.addView(frame, params);

        webView.loadUrl("http://stackoverflow.com");

        return START_STICKY;
    }

    private final WebViewClient client = new WebViewClient() {
        public void onPageFinished(WebView view, String url) {
            final Point p = new Point();
            winManager.getDefaultDisplay().getSize(p);

            webView.measure(MeasureSpec.makeMeasureSpec((p.x < p.y ? p.y : p.x),
                                MeasureSpec.EXACTLY), 
                            MeasureSpec.makeMeasureSpec((p.x < p.y ? p.x : p.y),
                                MeasureSpec.EXACTLY));
            webView.layout(0, 0, webView.getMeasuredWidth(), webView.getMeasuredHeight());

            webView.postDelayed(capture, 1000);
        }
    };

    private final Runnable capture = new Runnable() {
        @Override
        public void run() {
            try {
                final Bitmap bmp = Bitmap.createBitmap(webView.getWidth(),
                    webView.getHeight(), Bitmap.Config.ARGB_8888);
                final Canvas c = new Canvas(bmp);
                webView.draw(c);

                updateWidgets(bmp);
            }
            catch (IllegalArgumentException e) {
                e.printStackTrace();
            }

            stopSelf();
        }
    };

    private void updateWidgets(Bitmap bmp) {
        final AppWidgetManager widgetManager = AppWidgetManager.getInstance(this);
        final int[] ids = widgetManager.getAppWidgetIds(
            new ComponentName(this, WebWidgetProvider.class));

        if (ids.length < 1) {
            return;
        }

        final RemoteViews views = new RemoteViews(getPackageName(), R.layout.widget_layout);
        views.setImageViewBitmap(R.id.widget_image, bmp);
        widgetManager.updateAppWidget(ids, views);

        Toast.makeText(this, "WebWidget Update", 0).show();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
_

これがウィジェットの良さのスクリーンショットです。

WebWidget screenshot

31
Mike M.

上記の答えに加えて、

このエラーが発生した場合-ウィンドウを追加できません。このウィンドウタイプのアクセスが拒否されました少なくとも、Marshmallowを実行しているデバイスでは、タイプを変更する必要があります。

単に交換する

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAYいずれかに

WindowManager.LayoutParams.TYPE_TOAST(または)

WindowManager.LayoutParams.TYPE_APPLICATION_PANEL

そしてそれは完全にうまくいくでしょう。

注:最新バージョンのmarsmallowを実行しているNexus6pで確認および確認されました。

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAYには権限が必要ですAndroid.permission.INTERNAL_SYSTEM_WINDOWこれは、Androidシステム関連のアプリにのみ追加できます。

また、Javascriptを使用してデータを解析しようとしている人のために、onPageFinished()でページの読み込みが完了したら、Webビューの高さと幅を変更することを忘れないでください。

上記の回答でこれの重要性を完全に見落とし、多くの時間を無駄にしてしまいました。

当初、私の想定は、Webビューのコンテンツを画像ビューを介してウィジェットに表示することではなかったため、これは必須ではありませんでした。

結局のところ、ビューの幅と高さがゼロでない限り、javascriptはWebビューからのデータを解析できません。

2
Vikram Ezhil

WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY Android Mの後にのみ機能します。また、権限の設定が必要ですSettings.canDrawOverlays(context)

0
hc2hunter