【问题标题】:Show progress while loading WebViews inside RecyclerView在 RecyclerView 中加载 WebView 时显示进度
【发布时间】:2018-04-01 02:49:49
【问题描述】:

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

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

作为这个问题的解决方案,我需要显示一个进度条,直到 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 类

public class ViewHolder extends RecyclerView.ViewHolder {

    public MathView mathView;

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

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

    }
}

回收器适配器类

   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));

【问题讨论】:

  • 如果您有预期的复杂视图和布局。我不确定延迟是否是由系统引起的,如果您真的按照预期使用 RecyclerView,除非它运行在非常非常低的硬件配置文件中。听起来问题可能是您如何获取数据以填充 RV
  • 您所指的项目是用 JavaScript 编写的。你是在使用 React Native 还是类似的东西进行开发?
  • 除非您使用实际代码更新问题,否则不清楚您在问什么。
  • 我可以从你的代码中看到 RecyclerView 的实现是可以的。在 bindView() 方法中,您没有进行任何昂贵的操作。您的适配器简单明了。它与每个 recyclerView 项目中 MathView 项目的渲染有关。为了确保您可以用一些简单的 TextView 替换您的 MathView 并查看行为。如果行为看起来不错,您需要再次检查库以检查其行为缓慢的原因。
  • @AshikeeAbHi 如果我们使用 TextView,它会以正常方式运行并且不会延迟渲染。但是 MathView 本身需要一些时间来呈现其内容。这是 MathView 的正常行为。

标签: java android android-layout android-webview android-view


【解决方案1】:

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

正如answer中提到的,这是程序;

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

现在katex.hourglass.in.mathlib.MathView 是android WebView 的子类,我们可以为此设置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类编辑如下;

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() {
        //...
    }

}

【讨论】:

    【解决方案2】:

    我在描述中看不到问题本身,因此我将参考your statement in comments section:

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

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

    问题归结为附加 “RecyclerView 的子代已初始化” 监听器。

    只要katex.hourglass.in.mathlib.MathView 是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 在任何MathViews 加载后立即显示。您可以根据自己的要求等待全部加载完毕。

    【讨论】:

    • 看来你已经明白我的问题了。但是我尝试了您的解决方案;它不能解决我的问题。请您进一步解释答案。
    • 我认为最好还是分享你的代码,只要我已经分享了这个概念。不确定我可以添加什么额外内容。
    • 当我将 URL 加载到 MathView 时,此方法可以正常工作。但在这里它不起作用。
    • 那你在加载什么? sampleEquation.equationString 是什么,布局的最终​​外观是什么(可能是截图?)?
    • 感谢您的路径。我试过了,发现注册WebChromeClient可以解决这个问题
    【解决方案3】:

    不要打电话

    setContentView(layout);
    

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

    setContentView(layout);
    

    之后将适配器设置为 recyclerview 。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-02
      • 2021-05-07
      • 2018-08-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多