2013-06-14 2 views
13

На некоторых устройствах, таких как Nexus 7 или Samsung Galaxy Nexus (возможно, другие), я заметил эту проблему (см. Рисунок). Это выполняется в режиме ускорения аппаратного обеспечения в WebView. Однако, когда я перехожу к режиму рендеринга программного обеспечения, он отображается в хорошем состоянии, но немного замедляет работу. Я хотел бы узнать, как исправить эту проблему, и использовать только аппаратное ускорение в WebViews, а не в программном режиме.Android WebView Hardware Rendering Weird Artifact Issue

Bad рендеринга (работает на Samsung Galaxy Nexus): Image showing weird rending issue.

Правильный рендеринг (работает на Motorola Bionic): How it should render.

Android Manifest:

<uses-sdk 
     android:minSdkVersion="10" 
     android:targetSdkVersion="17" /> 

    <uses-permission android:name="android.permission.INTERNET" /> 
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> 
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

    <supports-screens 
     android:resizeable="true" 
     android:smallScreens="true" 
     android:normalScreens="true" 
     android:largeScreens="true" 
     android:xlargeScreens="true" 
     android:anyDensity="true" /> 

    <application 
     android:allowBackup="true" 
     android:hardwareAccelerated="true" 
     android:icon="@drawable/ic_launcher" 
     android:label="@string/app_name" 
     android:theme="@style/AppTheme" > 
     .... 
    </application> 
</manifest> 

Java код:

.... 
@TargetApi(VERSION_CODES.HONEYCOMB) 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     try{ 

      db = new DatabaseHandler(this); 
      tts = new TextToSpeech(this, this); 
      handler = new Handler(); 

      frame = new FrameLayout(this); 
      frame.setBackgroundColor(Color.GRAY); 

      wv = new WebView(this); 
      wv.setScrollBarStyle(WebView.SCROLLBARS_INSIDE_OVERLAY); 

      frame.addView(wv); 

      wv.setVisibility(WebView.INVISIBLE); 

      iv = new ImageView(this); 
      Drawable d = Drawable.createFromStream(getAssets().open("www/img/loading.jpg"), null); 
      iv.setImageDrawable(d); 

      FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 
        FrameLayout.LayoutParams.MATCH_PARENT); 
      iv.setLayoutParams(params); 

      frame.addView(iv); 

      WebSettings ws = wv.getSettings(); 
      ws.setRenderPriority(RenderPriority.HIGH); 
      ws.setJavaScriptEnabled(true); 
      ws.setAllowFileAccess(true); 
      if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) 
        ws.setAllowUniversalAccessFromFileURLs(true); 
      ws.setCacheMode(WebSettings.LOAD_NO_CACHE); 
      SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); 
      if(Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB && !pref.getBoolean("prefHardware", true)){ 
       wv.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 
      } 
      wv.setWebChromeClient(new WebChromeClient(){ 
       //int id = 0; 
       @Override 
       public boolean onConsoleMessage(ConsoleMessage consoleMessage) { 
        // TODO Auto-generated method stub 
//     Log.d("JS Console", consoleMessage.message() + " at " + consoleMessage.sourceId() + ":" + consoleMessage.lineNumber()); 
        //Toast.makeText(TaskRunner.this, consoleMessage.message() + " at " + consoleMessage.sourceId() + ":" + consoleMessage.lineNumber(), Toast.LENGTH_LONG).show(); 
//     NotificationCompat.Builder mBuilder = 
//       new NotificationCompat.Builder(TaskRunner.this) 
//       .setSmallIcon(R.drawable.ic_launcher) 
//       .setContentTitle("Console Message") 
//       .setContentText(consoleMessage.message() + " at " + consoleMessage.sourceId() + ":" + consoleMessage.lineNumber()); 
////     mBuilder.setContentIntent(null); 
//     NotificationManager mNotificationManager = 
//      (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 
//     // mId allows you to update the notification later on. 
//     mNotificationManager.notify(id++, mBuilder.build()); 
        return super.onConsoleMessage(consoleMessage); 
       } 
      }); 

      wv.setWebViewClient(new WebViewClient(){ 
       @Override 
       public void onPageFinished(WebView view, String url) { 
        // TODO Auto-generated method stub 
        //Log.i("TEST", "TEST"); 
        //hideLoading(); 
        super.onPageFinished(view, url); 
       } 

       @Override 
       public void onPageStarted(WebView view, String url, 
         Bitmap favicon) { 
        // TODO Auto-generated method stub 
        if(frame.getChildAt(frame.getChildCount()-1) != iv){ 
         frame.addView(iv); 
         wv.setVisibility(WebView.INVISIBLE); 
        } 
        super.onPageStarted(view, url, favicon); 
       } 

       @Override 
       public void onReceivedError(WebView view, int errorCode, 
         String description, String failingUrl) { 
        // TODO Auto-generated method stub 
        Log.e("ERROR", description); 
        super.onReceivedError(view, errorCode, description, failingUrl); 
       } 
      }); 
      MyJavascriptInterface ji = new MyJavascriptInterface(); 
      wv.addJavascriptInterface(ji, "ji"); 

      setOrientation(); 

      wv.loadUrl("file:///android_asset/www/index.html"); 

      setContentView(frame); 

     } 
     catch(Exception e){ 

     } 

    } 
    .... 

HTML файл:

<!DOCTYPE html> 
<html> 
<head> 
    <title>Task Player</title> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
    <meta name="format-detection" content="telephone=no" /> 
    ...... 
</head> 
<body> 
    <div id="gamespacer"> 
     <div id="instructions-block"> 
      <div id="explaination"> 
      </div> 
      <div id="instructions-close"> 
       <br /><br /> 
       <div class="btn btn-large btn-info" id="speak-tts">Speak</div> 
       <br /><br /> 
       <input type='checkbox' id='auto-speak' value="yes" checked="checked" />Auto speak 
      </div> 

      <div id="drag-container"> 
       <div id='drag'> 
        <span id='au'><img src="img/arrow-up.png" alt="" width="45" /></span> 
        <span id='ad'><img src="img/arrow-down.png" alt="" width="45"/></span> 
       </div> 
      </div> 
     </div> 
    </div> 
    <div id="play-container"> 
      <canvas> 
      </canvas> 
    </div> 
    <div class="centerMe" id="rotate" style="display: none;"><p style="font-size: 24px; margin-top: 20px">Rotate the device to play the game.</p></div> 
    <div id="winScreen"> 
     <div id="points"> 
      You Got<br/> 
      <div id="mypoints">0</div> 
      Points 
     </div> 
     <a href="#" id='done' class="btn btn-large btn-inverse">Done</a> 
    </div> 
.... 
</body> 
</html> 

CSS:

#winScreen{ 
    display: none; 
    width: 100%; 
    height: 100%; 
    text-align: center; 
    background-color: green; 
    font-size: 24px; 
    margin: 0 auto; 
    position: absolute; 
    left: 0px; 
    top: 0px; 
} 
    #points { 
    padding-top: 110px; 
    color: yellow; 
    margin-bottom: 10px; 
    } 

    #mypoints{ 
    margin: 10px; 
    } 

    .button{ 
    margin: 0 auto; 
    margin-top: 50px; 
    } 

#play-container{ 
    width: 100%; 
    height: 100%; 
    margin: 0 auto; 
    text-align: center; 
} 

canvas { 
    background-color: white; 
    position: absolute; 
    top: 0px; 
    left: 0px; 
} 

#gamespacer { 
} 

     #instructions-block { 
      position: absolute; 
      left: 0px; 
      top: -144px; 
      width: 100%; 
      background-color: white; 
      z-index: 1; 
     } 

     #instructions-content { 
      margin-top: 5px; 
     } 

     #instructions-close { 
      text-align: center; 
      margin-bottom: 10px; 
     } 

     #explaination { 
      margin: 5px; 
      font-size: 24px; 
      line-height: 1; 
      position: relative; 
      width: 100% 
      height: 100% 
     } 


     #drag-container{ 
      width: 100%; 
     } 

     #drag { 
      position: absolute; 
      left: 92%; 
      margin-top: 5px; 
     } 

#au{ 
    display: none; 
    pointer-events: none; 
}  

#ad { 
    pointer-events: none; 
} 

body{ 

    -webkit-user-select: none; 
    user-select: none; 


    -webkit-touch-callout: none; 
    touch-callout: none; 

    -webkit-tap-highlight-color: rgba(0,0,0,0); 
    tap-highlight-color: rgba(0,0,0,0); 

} 

UPDATE 1

Так что я попытался следующий код, и что не работает, как хорошо.

wv.setLayerType(View.LAYER_TYPE_HARDWARE, null); 

UPDATE 2

Похоже, до сих пор, насколько мне известно, что это происходит только на Google сделал устройства (как в Nexus). Итак, может быть ошибка в их ядре или видеодрайвере? Или это может быть что-то не так с Android 4.2.2.

Если у вас есть какие-либо идеи относительно того, как исправить это, пожалуйста, дайте мне знать.

UPDATE 3

Я сделал тестовый проект для вас попробовать. Играйте с уровнем масштабирования и перемещением окна вокруг. Дай мне знать, если это будет что-то странное. Это до сих пор делает это с Android 4.2.2

http://jsbin.com/equtoq/2

Спасибо!

+1

Возможно, добавьте код? – Alice

+0

@DavidKnag Хорошо, я добавил код. –

+0

Любые предложения? –

ответ

5

Благодаря Деляну для решения проблемы. Как он сказал мне, чтобы решить это, нужно добавить -webkit-transform: translate3d(0,0,0) ко всем частям, которые перемещаются.

KUDOS DELYAN!

+0

Меньшее решение: -webkit-transform: translateZ (0); – robisaks

+1

Я думаю, что в зависимости от модели смартфона рендеринг может вызвать множество проблем при включении аппаратного ускорения. Предполагая, что вы хотите избежать странных серых форм при прокрутке, добавьте 'android: hardwareAccelerated =" false "' в тег приложения в AndroidManifest.xml http://www.chinabtp.com/webview-inside-viewpager-or-scrollview-weird- рендеринг-ошибка-на-андроида-3-0 / –

1

Убедитесь, что аппаратное ускорение включено в вашем манифесте XML.

<application android:hardwareAccelerated="true" ... /> 
+0

Я обновил свой вопрос с кодом, как вы видите, у меня уже есть этот флаг. Спасибо хоть! –

8

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

Основная идея заключается в том, что рендеринг артефактов будет отображаться только после определенного масштабирования, поэтому мы сможем безопасно использовать аппаратное рендеринг до того, как этот порог будет пересечен, и переключитесь на более медленное, но правильное рендеринг программного обеспечения при использовании большего масштабирования. Пороговое значение масштабирования составляет 1,67 на N7, хотя вам, возможно, придется проверить Galaxy Nexus, чтобы узнать, универсально ли это значение.В моем опыте есть целый ряд вопросов с WebView через каждую версию Android, так что это может быть лучшим решением в настоящее время, пока все не обновляются до 4,3 ...

webView.setWebViewClient(new WebViewClient() { 

       @Override 
       public void onScaleChanged(WebView view, float oldScale, 
         float newScale) { 
       //4.3 SDK required, or change Build.VERSION_CODES.JELLY_BEAN_MR2 to 18 
       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { 
        if (newScale > 1.7f) { 
         if (webView.getLayerType() != View.LAYER_TYPE_SOFTWARE) { 
          webView.setLayerType(View.LAYER_TYPE_SOFTWARE, 
            null); 
         } 
        } else { 
         if (webView.getLayerType() != View.LAYER_TYPE_HARDWARE) { 
          webView.setLayerType(View.LAYER_TYPE_HARDWARE, 
            null); 
         } 
        } 
       } 
       } 
}); 

Обратите внимание, что onScaleChanged() будет вызываться несколько раз, иногда скрещивая порог с помощью одного масштабирования (с помощью кнопки увеличения/уменьшения), поэтому вы должны добавить некоторую логику, чтобы уменьшить количество вызовов webView.setLayerType() для события «один за масштаб», чтобы уменьшить артефакты аппаратного и программного обеспечения, которые будет отображаться на короткий промежуток времени

+0

вызывается onScaleChanged(), когда пользователь увеличивает масштаб, поскольку я отключил масштабирование от пользователя. –

+0

Да, как я и думал. onScaleChanged не вызывается, потому что пользователь не может масштабировать. –

+0

Если вы отключили масштабирование, тогда это будет даже проще, вы можете просто настроить масштабирование на безопасное значение для аппаратного рендеринга для работы – Kai

0

Это ошибка гонки в WebView. Если рендеринг страницы становится несовместимым с фактическим просмотром страницы.

Обычная причина в том, что вы обрабатываете поворот с android:configChanges в своем теге <activity> в файле AndroidManifest.xml. Воссоздание WebView при вращении разрешит эту проблему, но в этом есть другие проблемы, связанные с ней.

Их два решения, о которых я знаю. Я бы не рекомендовал вам использовать android:configChanges, поскольку он обычно вреден, если вы действительно не знаете, что это означает для вашего приложения. Если вам абсолютно необходимо сохранить android:configChanges, есть решение. Вы можете создать свой собственный код View, который наследуется от WebView. Просто переопределите onConfigurationChanged и не пересылайте его на суперкласс .. в этом случае это WebView. В качестве примера я привел приведенный ниже код.

Но более правильный и правильный подход заключается в том, чтобы исправить сохранение и восстановление состояния WebView. Если вы будете охотиться в Интернете, вы найдете множество решений для этой проблемы. Здесь в целом хорошо. http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/

import android.content.Context; 
import android.content.res.Configuration; 
import android.util.AttributeSet; 
import android.webkit.WebView; 


public class MyWebView extends WebView 
{ 
    public MyWebView(Context context) 
    { 
     super(context); 
    } 

    public MyWebView(Context context, AttributeSet attrs) 
    { 
     super(context, attrs); 
    } 
    public MyWebView(Context context, AttributeSet attrs, int defStyle) 
    { 
     super(context, attrs, defStyle); 
    } 

    @Override 
    protected void onConfigurationChanged(Configuration newConfig) 
    { 
     // NOTE: We don't want to call this 
     // super.onConfigurationChanged(newConfig); 
    } 
}