Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show progress while loading WebViews inside RecyclerView

In my android activity I am using a RecyclerView which contains number of MathViews. MathView is a third-party library which shows LaTeX contents (This is somewhat similar to WebView. Implementation of MathView can be seen on this android project. github.com/lingarajsankaravelu/Katex).

The problem is, for rendering the content of this MathView, it takes a little bit longer time. As I have used few MathView components inside a RecycleView and the rendering time increases more. Therefore when the Activity started, at first in the view, some white space are shown for few seconds and then the relevant content is rendered.

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

The relavent source codes are shown below.

MathView;

<?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>

Recycler View;

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

    }
}

Recycler Adapter 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();
    }
}

Finally the implementation.

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

        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(new RecyclerAdapterEquations(sample));
like image 293
Tharindu Sathischandra Avatar asked Apr 01 '18 02:04

Tharindu Sathischandra


1 Answers

With the help of the answer of written by azizbekian, I found the answer to my question.

As mentioned in that answer, this is the procedure;

  1. Introduce a ProgressBar 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.

Now as the katex.hourglass.in.mathlib.MathView is a subclass of android WebView, we can set a WebChromeClient to this. Then, we can get the percentage of progress of loading the content.

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

We can implement a progress bar to show the loadedPercentage. When the loadedPercentage is 100, we can see the relavent content is completely loaded. So I editted the MathView class as follows;

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

}
like image 157
Tharindu Sathischandra Avatar answered Sep 28 '22 00:09

Tharindu Sathischandra