在 RecyclerView 中加载 WebView 时显示进度

Show progress while loading WebViews inside RecyclerView

在我的 android activity 中,我使用的是 RecyclerView,其中包含 MathViews 个数。 MathView 是显示 LaTeX 内容的第三方库(这 有点类似于WebViewMathView 的实现可以在这个 android 项目中看到。 github.com/lingarajsankaravelu/Katex)。

问题是,渲染这个 MathView 的内容需要更长的时间。因为我在 RecycleView 中使用了很少的 MathView 组件并且渲染时间增加了更多。因此,当 Activity 启动时,首先在视图中,一些白色 space 显示几秒钟,然后呈现相关内容。

作为这个问题的解决方案,我需要显示一个进度条,直到 Activity 的所有布局内容完全渲染,渲染完成后显示 Activity。

相关源代码如下。

数学视图;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:auto="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="16dp"
    android:layout_marginRight="16dp"
    android:id="@+id/equation_item"

    android:clickable="true"
    android:foreground="?attr/selectableItemBackground">

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardCornerRadius="4dp">

        <katex.hourglass.in.mathlib.MathView
            android:id="@+id/math_view"
            android:clickable="true"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            app:setClickable="true"
            app:setTextColor="@color/colorPrimary"
            app:setTextSize="10sp"
            />
    </android.support.v7.widget.CardView>
    <include layout="@layout/item_divider"/>

</LinearLayout>

回收站视图;

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.a37moons.mathcheatsheets.ContentActivity"
    tools:showIn="@layout/activity_content">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view_equations"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
</android.support.v4.widget.NestedScrollView>

ViewHolder Class

public class ViewHolder extends RecyclerView.ViewHolder {

    public MathView mathView;

    public ViewHolder(View itemView) {
        super(itemView);

        mathView = itemView.findViewById(R.id.math_view);

    }
}

回收器适配器Class

   public class RecyclerAdapterEquations extends RecyclerView.Adapter<ViewHolder>{

    private List<Equation> equations;
    View view;

    public RecyclerAdapterEquations(List<Equation> equations){
        this.equations = equations;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.equation_view_item,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Equation sampleEquation = equations.get(position);
        holder.mathView.setDisplayText(sampleEquation.equationString);
       // holder.mathView2.setText(sampleEquation.equationString);
        Log.d("MATH_APP","position "+position+" mathview text set ");
    }

    @Override
    public int getItemCount() {
        return equations.size();
    }
}

终于实现了。

RecyclerView recyclerView;
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);

        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(new RecyclerAdapterEquations(sample));

不要打电话给

setContentView(layout);

像往常一样,首先在适配器中加载数据,然后调用

setContentView(layout);

之后将适配器设置为 recyclerview 。

我在描述中看不到问题本身,因此我将参考:

I just need a solution to show a progress bar until all the layout content of the Activity is completely rendered and then after rendering is completed, show up the Activity.

  1. 在 xml 文件中引入一个 ProgressBar 或类似的东西。此视图应在 RecyclerView 之后声明,以便绘制在 RecyclerView
  2. 之上
  3. 使 RecyclerView 不可见(通过 android:visibility="invisible"
  4. 现在 RecyclerView 将实际布局 不会显示在屏幕上。您需要一个回调,它会在稍后 RecyclerView 已设置时执行。在此回调中,您将隐藏进度条并将 RecyclerView 的可见性更改为 View.VISIBLE

问题归结为附加 "children of RecyclerView are initialized" 侦听器。

只要 katex.hourglass.in.mathlib.MathView is a subclass of android.webkit.WebView,这意味着,为了获得有关加载完成事件的通知,您应该在以下方法中注册 WebViewClient


    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Equation sampleEquation = equations.get(position);
        holder.mathView.setDisplayText(sampleEquation.equationString);
        holder.mathView.setWebViewClient(new WebViewClient() {
            public void onPageFinished(WebView view, String url) {
                // No longer interested in upcoming events - unregister
                view.setWebViewClient(null);
                // Now data is loaded, can make RecyclerView visible
            }
        });
        ...
    }

请注意,这将使 RecyclerView 在任何 MathView 加载后立即显示。您可以根据需要等待它们全部加载。

写的答案的帮助下,我找到了问题的答案。

那个里面提到了,就是这个过程;

  1. Introduce a or something similar inside xml file. This view should be declared after RecyclerView in order to be drawn on top of RecyclerView
  2. Make RecyclerView invisible (via android:visibility="invisible")
  3. Now RecyclerView will be actually laid out but not shown on the screen. You need a callback, that would be executed some time later when RecyclerView is already setup. Within this callback you will hide progress bar and change visibility of RecyclerView to View.VISIBLE.

现在 katex.hourglass.in.mathlib.MathView 是 android WebView 的子class,我们可以为此设置 WebChromeClient。然后,我们可以得到加载内容的进度百分比。

 int loadedPercentage = 0;
 boolean loaded = false;
 mathView.setWebChromeClient(new WebChromeClient(){
     public void onProgressChanged(WebView view, int newProgress) {
         super.onProgressChanged(view, newProgress);
         loadedPercentage = newProgress;
         if(newProgress==100) {
             //When the loading is 100% completed; todo
             loaded = true;
             Toast.makeText(getContext(), newProgress + "LOADED COMPLETELY", Toast.LENGTH_SHORT).show();
         }
     }
 });

我们可以实现一个进度条来显示loadedPercentage。当loadedPercentage为100时,可以看到相关内容加载完成。 所以我编辑了 MathView class 如下;

public class MyMathView extends WebView {
    private String TAG = "KhanAcademyKatexView";
    private static final float default_text_size = 18;
    private String display_text;
    private int text_color;
    private int text_size;
    private boolean clickable = false;

    private boolean loaded = false;
    private int loadedPercentage = 0;

    public MyMathView(Context context) {
        //...
    }

    public MyMathView(Context context, AttributeSet attrs) {
        //...
    }

    public boolean isLoaded(){
        return loaded;
    }

    public int getLoadedPercentage() {
        return loadedPercentage;
    }

    public void setViewBackgroundColor(int color)
    {
        //...
    }

    private void pixelSizeConversion(float dimension) {
        //...
    }

    private void configurationSettingWebView()
    {
        //...
    }


    public void setDisplayText(String formula_text) {
        this.display_text = formula_text;
        loadData();
    }

   private String getOfflineKatexConfig()
    {
        String offline_config = "<!DOCTYPE html>\n" +
                "<html>\n" +
                "    <head>\n" +
                "        <meta charset=\"UTF-8\">\n" +
                "        <title>Auto-render test</title>\n" +
                "        <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/katex/katex.min.css\">\n" +
                "        <link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/themes/style.css\">\n" +
                "        <script type=\"text/javascript\" src=\"file:///android_asset/katex/katex.min.js\"></script>\n" +
                "        <script type=\"text/javascript\" src=\"file:///android_asset/katex/contrib/auto-render.min.js\"></script>\n" +
                " <style type='text/css'>"+
                "body {"+
                "margin: 0px;"+
                "padding: 0px;"+
                "font-size:" +this.text_size+"px;"+
                "color:"+getHexColor(this.text_color)+";"+
                " }"+
                " </style>"+
                "    </head>\n" +
                "    <body>\n" +
                "        {formula}\n" +
                "        <script>\n" +
                "          renderMathInElement(\n" +
                "              document.body\n" +
                "          );\n" +
                "        </script>\n" +
                "    </body>\n" +
                "</html>";
        String start = "<html><head><meta http-equiv='Content-Type' content='text/html' charset='UTF-8' /><style> body {"+
       " white-space: nowrap;}</style></head><body>";

        String end = "</body></html>";

        //return   start+offline_config.replace("{formula}",this.display_text)+end;
        return offline_config.replace("{formula}",this.display_text);

    }


    private void loadData()
    {
        if (this.display_text!=null)
        {
            loadedPercentage = 0;
            loaded = false;

            this.setWebChromeClient(new WebChromeClient(){
                public void onProgressChanged(WebView view, int newProgress) {
                    super.onProgressChanged(view, newProgress);
                    loadedPercentage = newProgress;
                    if(newProgress==100) {
                        loaded = true;
                        Toast.makeText(getContext(), newProgress + "LOADED", Toast.LENGTH_SHORT).show();
                    }
                }
            });
            this.loadDataWithBaseURL("null",getOfflineKatexConfig(),"text/html","UTF-8","about:blank");
        }
    }


    public void setTextSize(int size)
    {
       //...
    }
    public void setTextColor(int color)
    {
       //...
    }
    private String getHexColor(int intColor)
    {
       //...
    }


    private void setDefaultTextColor(Context context) {
       //...
    }

    private void setDefaultTextSize() {
        //...
    }

}