Android: загрузка вывода WebView в App Widget

Хотя я понимаю, что виджет App Widget не поддерживает WebView напрямую, возможно ли вообще использовать ImageView (который поддерживается) и метод, описанный здесь для создания образа для ImageView ? WebView не будет использоваться напрямую, а используется только в фоновом режиме, чтобы обеспечить изображение для ImageView .

Это возможно, по-видимому, по крайней мере на моем устройстве, хотя ваш пробег может отличаться.

В манифесте мы регистрируем <receiver> Widget как обычно, а также регистрируем наш 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 x 2.

 <?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 в этом примере переопределяет только метод onUpdate() , поскольку мы используем тот же ACTION_APPWIDGET_UPDATE чтобы запускать наши собственные обновления с помощью AlarmManager . Обратите внимание, что пример будильника срабатывает каждые 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); } } 

В WebView мы WebView и добавляем его в FrameLayout , который, в свою очередь, добавляется в WindowManager с нулевым значением для его параметров ширины и высоты. Затем мы загружаем тестовый URL-адрес в WebView , и когда страница заканчивается, мы вынуждаем WebView соответствовать соответствующему размеру и WebView его на наш целевой Bitmap через объект Canvas . Это делается после небольшой задержки, чтобы WebView мог полностью отобразить себя, чтобы мы не WebView пустое белое изображение. Затем Bitmap устанавливается на объект RemoteViews для обновления ImageView в макете виджетов. Я оставил Toast в коде для тестирования, так как наш пример Widget может обновляться чаще, чем обновление первой страницы Stack Overflow.

 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((px < py ? py : px), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((px < py ? px : py), 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

Добавляя к вышеуказанному ответу,

Для людей, которые получают эту ошибку – невозможно добавить окно. Permission denied для этого типа окна, по крайней мере, в моем случае с устройствами, использующими зефир, тип должен быть изменен.

Просто замените

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY – либо

WindowManager.LayoutParams.TYPE_TOAST (или)

WindowManager.LayoutParams.TYPE_APPLICATION_PANEL

И он будет работать отлично.

Примечание. Проверено и подтверждено, что Nexus6p работает с последней версией marsmallow.

Для WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY требуется разрешение android.permission.INTERNAL_SYSTEM_WINDOW, которое может быть добавлено только для приложений, связанных с системой Android.

Также не забудьте изменить высоту и ширину веб-представления, как только страница закончит загрузку на onPageFinished () для людей, которые ищут синтаксический анализ данных с помощью Javascript.

Я полностью забыл о важности этого в вышеупомянутом ответе и закончил тратить много времени.

Первоначально мое предположение было, поскольку мое требование состояло в том, чтобы не отображать содержимое в веб-представлении виджета через изображение, это не требовалось.

Как выясняется, javascript не может анализировать данные с веб-представления, если ширина и высота представления не равны нулю.