0

I got the following Problem,

When using an AndroidPlot XYPlot inside a RecyclerView.

the AndroidPlot like this:

<com.androidplot.xy.XYPlot
android:id="@+id/mPlot"
style="@style/APDefacto.Light"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
androidplot.renderMode="use_background_thread"
android:visibility="visible"
ap:lineLabels="left"
android:minHeight="100dp" />

most important here is renderMode="use_background_thread" which sets that the Plot should be drawn in a background thread.

I have to set the ViewHolder as not RecycleAble

ViewHolder vh = new ViewHolder(v,parentContext);
vh.setIsRecyclable(true);

because the Plot becomes gray when only bind RecyclerView. Reason for this is that there are Problems, recalling the background Thread for drawing an element. By setting the Viewholders as not Recyclable, every time a new Item comes in Focus a new ViewHolder is created. Not really efficient, I but faster than using Plots without Backgroud_Thread. Link to my previous post

Then without Background Thread scrolling is really stagnant and not really beautiful.

Here the Stack-Trace:

java.lang.IllegalThreadStateException: Thread already started                                                             
at java.lang.Thread.checkNotStarted(Thread.java:849)                                                                               
at java.lang.Thread.start(Thread.java:1059)                                                                               
at com.androidplot.Plot.onSizeChanged(Plot.java:780)                                                                               
at android.view.View.sizeChange(View.java:16748)                                                                               
at android.view.View.setFrame(View.java:16710)                                                                               
at android.view.View.layout(View.java:16627)                                                                               
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)                                                                      
at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1732)                                                                               
at android.widget.LinearLayout.onLayout(LinearLayout.java:1497)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)                                                                               
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586)                                                                               
at android.widget.LinearLayout.onLayout(LinearLayout.java:1495)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)                                                                               
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)                                                                              
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)                                                                               
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586)                                                                               
at android.widget.LinearLayout.onLayout(LinearLayout.java:1495)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.support.v7.widget.RecyclerView$LayoutManager.layoutDecoratedWithMargins(RecyclerView.java:8968)                                                                               
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1614)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1516)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:608)                                                                               
at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3693)                                                                               
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3410)                                                                               
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3962)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)                                                                               
at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1732)                                                                               
at android.widget.LinearLayout.onLayout(LinearLayout.java:1497)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)                                                                               
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586)                                                                               
at android.widget.LinearLayout.onLayout(LinearLayout.java:1495)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)                                                                               
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                              
at android.support.v7.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:443)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)                                                                               
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743)                                                                               
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586)                                                                               
at android.widget.LinearLayout.onLayout(LinearLayout.java:1495)                                                                               
at android.view.View.layout(View.java:16630)                                                                               
at android.view.ViewGroup.layout(ViewGroup.java:5437)                                                                               
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)                                                                               
at android.widget.FrameLayout.onLayout(FrameLayout.java:273)                                                                            
at com.android.internal.policy.PhoneWi

Thanks for help,

Franzi

Franz
  • 358
  • 6
  • 18

1 Answers1

1

The Androidplot demo app features a listview example that demonstrates how to accomplish this. (Theres nothing particularly special or different about RecyclerView at this level.) I modified that code to use a RecyclerView instead and got a pretty smooth result without background threading even on an emulator (bear in mind this is a low framerate gif):

enter image description here

Might be worth knowing a little more about the size of your series data. Depending on how dense the data is, sometimes sampling is necessary.

FWIW, Here's the source of the modified listview demo activity:

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.androidplot.Plot;
import com.androidplot.ui.SeriesBundle;
import com.androidplot.util.PixelUtils;
import com.androidplot.xy.CatmullRomInterpolator;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.SimpleXYSeries;
import com.androidplot.xy.XYSeries;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class RecyclerViewActivity extends Activity {
    private static final int NUM_PLOTS = 10;
    private static final int NUM_POINTS_PER_SERIES = 10;
    private static final int NUM_SERIES_PER_PLOT = 5;
    private RecyclerView recyclerView;


    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recyclerview_example);
        PixelUtils.init(this);
        generateData();
        recyclerView = findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(new MyRecyclerViewAdapter(generateData()));
    }

    static class PlotViewHolder extends RecyclerView.ViewHolder {

        private Plot plot;

        public PlotViewHolder(View itemView) {
            super(itemView);
            this.plot = itemView.findViewById(R.id.xyplot);
        }

        public void bind(List<SeriesBundle<XYSeries, LineAndPointFormatter>> seriesBundles, String title) {
            plot.clear();
            plot.getTitle().setText(title);

            for(SeriesBundle<XYSeries, LineAndPointFormatter> bundle : seriesBundles) {
                plot.addSeries(bundle.getSeries(), bundle.getFormatter());
            }
            plot.redraw();
        }
    }

    static class MyRecyclerViewAdapter extends RecyclerView.Adapter<PlotViewHolder> {
        private List<List<SeriesBundle<XYSeries, LineAndPointFormatter>>> seriesData;
        public MyRecyclerViewAdapter(List<List<SeriesBundle<XYSeries, LineAndPointFormatter>>> seriesData) {
            this.seriesData = seriesData;
        }

        @NonNull
        @Override
        public PlotViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View item = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.listview_example_item, parent, false);
            return new PlotViewHolder(item);
        }

        @Override
        public void onBindViewHolder(@NonNull PlotViewHolder holder, int position) {
            holder.bind(seriesData.get(position), "S" + position);
        }

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

    static List<List<SeriesBundle<XYSeries, LineAndPointFormatter>>> generateData() {
        final List<List<SeriesBundle<XYSeries, LineAndPointFormatter>>> seriesData = new ArrayList<>(NUM_PLOTS);
        final Random generator = new Random();
        for(int i = 0; i < NUM_PLOTS; i++) {
            List<SeriesBundle<XYSeries, LineAndPointFormatter>> seriesList
                    = new ArrayList<>(NUM_SERIES_PER_PLOT);

            for (int k = 0; k < NUM_SERIES_PER_PLOT; k++) {
                ArrayList<Number> nums = new ArrayList<>();
                for (int j = 0; j < NUM_POINTS_PER_SERIES; j++) {
                    nums.add(generator.nextFloat());
                }

                double rl = Math.random();
                double gl = Math.random();
                double bl = Math.random();

                double rp = Math.random();
                double gp = Math.random();
                double bp = Math.random();

                LineAndPointFormatter format = new LineAndPointFormatter(
                        Color.rgb(Double.valueOf(rl * 255).intValue(),
                                Double.valueOf(gl * 255).intValue(), Double.valueOf(bl * 255).intValue()),
                        Color.rgb(Double.valueOf(rp * 255).intValue(),
                                Double.valueOf(gp * 255).intValue(), Double.valueOf(bp * 255).intValue()),
                        null, null);

                // for fun, configure interpolation on the formatter:
                format.setInterpolationParams(
                        new CatmullRomInterpolator.Params(20, CatmullRomInterpolator.Type.Centripetal));

                seriesList.add(new SeriesBundle<XYSeries, LineAndPointFormatter>(
                        new SimpleXYSeries(nums, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "S" + k),
                        format));
            }
            seriesData.add(seriesList);
        }
        return seriesData;
    }
}
Nick
  • 8,181
  • 4
  • 38
  • 63
  • Is there no possibility to use Background Thread, I use about 10 to 1000++ Elements – Franz Mar 09 '18 at 15:18
  • It seems your Function com.androidplot.Plot.onSizeChanged(Plot.java:780) tries to restart a already running Thread – Franz Mar 09 '18 at 15:20
  • Background thread rendering in general does not work will with listview or recyclerview. (Background rendering is mainly there for high performance dynamic data use cases, like an ecg or similar) There's work being done to resolve it but for the short term it's best to use the default render mode and optimize the series using sampling etc. if you have large amounts of data. – Nick Mar 09 '18 at 15:43
  • Hey @Nick can you please help me with this https://stackoverflow.com/questions/49952965/recyclerview-horizontal-scrolling-to-left?noredirect=1#comment87836903_49952965 –  May 18 '18 at 12:46