androidandroidplot

androidplot with new android-full-screen activity does not fill screen


there seem to be two definitions for full-screen

android full screen = no taskbar, no status bar or buttons, simply fill the screen

android plot full-screen = no legends left and right and on the bottom, simply a full-screen graph widget

this question is related to the Android full-screen with the swipe action to make the taskbar and button-bar hid (full-screen) or displayed ... it does not extend (fill) the graph portion down to the legend at the bottom

the following is based on Android plot 1.4.3

creating a new android studio 2.3.2 project with an empty activity and copying the Android plot demo app's "simple xy plot" java code into onCreate and the xml portion into the layout xml file works fine and the graph portion is expanded all the way to the bottom legend

creating a new android studio 2.3.2 project with a full-screen activity (with swipe action to hide/show taskbar and button-bar) setup fails to expand the graph portion down to the bottom legend

the android studio generated default activity code framework is now more complex as compared to the first example with an empty activity

lines are shown with "// zzz ..." indicate deviations from android studio generated code

full-screen activity java code start

package com.efilabs.plotxynew;

import android.annotation.SuppressLint;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;

import com.androidplot.xy.XYPlot;

/**
 * An example full-screen activity that shows and hides the system UI                 (i.e.
 * status bar and navigation/system bar) with user interaction.
 */
public class ActPlotNew0 extends AppCompatActivity
{
  /**
   * Whether or not the system UI should be auto-hidden after
   * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
   */
  private static final boolean AUTO_HIDE = true;

  /**
   * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after
   * user interaction before hiding the system UI.
   */
  private static final int AUTO_HIDE_DELAY_MILLIS = 3000;

  /**
   * Some older devices needs a small delay between UI widget updates
   * and a change of the status and navigation bar.
   */
  private static final int UI_ANIMATION_DELAY = 300;
  private final Handler mHideHandler = new Handler ();
  private View   mContentView;      // zzz
//private XYPlot mContentView;      // zzz
      private final Runnable mHidePart2Runnable = new Runnable ()
      {
    @SuppressLint ("InlinedApi")
    @Override
    public void run ()
    {
      // Delayed removal of status and navigation bar

      // Note that some of these constants are new as of API 16 (Jelly Bean)
      // and API 19 (KitKat). It is safe to use them, as they are inclined
      // at compile-time and do nothing on earlier devices.
      mContentView.setSystemUiVisibility (View.SYSTEM_UI_FLAG_LOW_PROFILE
          | View.SYSTEM_UI_FLAG_FULLSCREEN
          | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
          | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
          | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
          | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    }
  };
  private View mControlsView;
  private final Runnable mShowPart2Runnable = new Runnable ()
  {
    @Override
    public void run ()
    {
      // Delayed display of UI elements
      ActionBar actionBar = getSupportActionBar ();
      if (actionBar != null) {
    actionBar.show ();
      }
      mControlsView.setVisibility (View.VISIBLE);
    }
  };
  private boolean mVisible;
  private final Runnable mHideRunnable = new Runnable ()
  {
    @Override
    public void run ()
    {
      hide ();
    }
  };
  /**
   * Touch listener to use for in-layout UI controls to delay hiding the
   * system UI. This is to prevent the jarring behavior of controls going away
   * while interacting with activity UI.
   */
  private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener ()
  {
    @Override
    public boolean onTouch (View view, MotionEvent motionEvent)
    {
      if (AUTO_HIDE) {
    delayedHide (AUTO_HIDE_DELAY_MILLIS);
      }
      return false;
    }
  };

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

    setContentView (R.layout.act_plotnew0);

    mVisible = true;
    mControlsView = findViewById (R.id.fullscreen_content_controls);
    mContentView =      findViewById (R.id.fullscreen_content); // zzz
//  mContentView = (XYPlot) findViewById (R.id.fullscreen_content); // zzz


    // Set up the user interaction to manually show or hide the system UI.
    mContentView.setOnClickListener (new View.OnClickListener ()
    {
      @Override
      public void onClick (View view)
      {
    toggle ();
      }
    });

    // Upon interacting with UI controls, delay any scheduled hide()
    // operations to prevent the jarring behavior of controls going away
    // while interacting with the UI.
    findViewById (R.id.dummy_button).setOnTouchListener     (mDelayHideTouchListener);
  }

  @Override
  protected void onPostCreate (Bundle savedInstanceState)
  {
    super.onPostCreate (savedInstanceState);

    // Trigger the initial hide() shortly after the activity has been
    // created, to briefly hint to the user that UI controls
    // are available.
    delayedHide (100);
  }

  private void toggle ()
  {
    if (mVisible) {
      hide ();
    } else {
      show ();
    }
  }

  private void hide ()
  {
    // Hide UI first
    ActionBar actionBar = getSupportActionBar ();
    if (actionBar != null) {
      actionBar.hide ();
    }
    mControlsView.setVisibility (View.GONE);
    mVisible = false;

    // Schedule a runnable to remove the status and navigation bar after a delay
    mHideHandler.removeCallbacks (mShowPart2Runnable);
    mHideHandler.postDelayed (mHidePart2Runnable, UI_ANIMATION_DELAY);
  }

  @SuppressLint ("InlinedApi")
  private void show ()
  {
    // Show the system bar
    mContentView.setSystemUiVisibility     (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    mVisible = true;

    // Schedule a runnable to display UI elements after a delay
    mHideHandler.removeCallbacks (mHidePart2Runnable);
    mHideHandler.postDelayed (mShowPart2Runnable, UI_ANIMATION_DELAY);
  }

  /**
   * Schedules a call to hide() in [delay] milliseconds, canceling any
   * previously scheduled calls.
   */
  private void delayedHide (int delayMillis)
  {
    mHideHandler.removeCallbacks (mHideRunnable);
    mHideHandler.postDelayed (mHideRunnable, delayMillis);
  }
}

full-screen activity java code end

XML layout contains inserted android plot view portion

full-screen activity layout XML start

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     xmlns:ap="http://schemas.android.com/apk/res-auto"
     android:background="#0099cc"
     tools:context="com.efilabs.plotxynew.ActPlotNew0">

  <!-- The primary full-screen view. This can be replaced with whatever view
         is needed to present your content, e.g. VideoView, SurfaceView,
         TextureView, etc. -->
  <!--TextView
      android:id="@+id/fullscreen_content"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:gravity="center"
      android:keepScreenOn="true"
      android:text="@string/dummy_content"
      android:textColor="#33b5e5"
      android:textSize="50sp"
      android:textStyle="bold"/-->

  <com.androidplot.xy.XYPlot
      style="@style/APDefacto.Light"
      android:id="@+id/fullscreen_content"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      ap:title="A Simple XY Plot"
      ap:rangeTitle="range"
      ap:domainTitle="domain"
      ap:lineLabels="left|bottom"
      ap:lineLabelRotationBottom="-45"/>

  <!-- This FrameLayout insets its children based on system windows using
         android:fitsSystemWindows. -->
      <FrameLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent"
      android:fitsSystemWindows="true">

    <LinearLayout
        android:id="@+id/fullscreen_content_controls"
        style="?metaButtonBarStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:background="@color/black_overlay"
        android:orientation="horizontal"
        tools:ignore="UselessParent">

      <Button
          android:id="@+id/dummy_button"
          style="?metaButtonBarButtonStyle"
          android:layout_width="0dp"
          android:layout_height="wrap_content"
          android:layout_weight="1"
          android:text="@string/dummy_button"/>

    </LinearLayout>
  </FrameLayout>

</FrameLayout>

full-screen activity layout XML end

To not mess with the Android generated java code, I created a 2nd java activity class extending the Android generated java activity code and placed the "simple XY plot" into it

extended activity code start

package com.efilabs.plotxynew;

import android.graphics.DashPathEffect;
import android.os.Bundle;
import android.support.v7.app.ActionBar;

import com.androidplot.util.PixelUtils;
import com.androidplot.xy.CatmullRomInterpolator;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.SimpleXYSeries;
import com.androidplot.xy.XYGraphWidget;
import com.androidplot.xy.XYPlot;
import com.androidplot.xy.XYSeries;

import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.Arrays;

public class ActPlotNew0a extends ActPlotNew0
{
  private XYPlot plot;

  @Override
  protected void onCreate (Bundle savedInstanceState)
  {
    super.onCreate (savedInstanceState);
//  setContentView (R.layout.act_plotnew0); // xxx provides full screen but swipe doesn't work anylonger

    ActionBar bar  = getSupportActionBar () ;
    if       (bar != null) bar.hide () ;

    // initialize our XYPlot reference:
    plot = (XYPlot) findViewById(R.id.fullscreen_content);

    // create a couple arrays of y-values to plot:
    final Number[] domainLabels = {1, 2, 3, 6, 7, 8, 9, 10, 13, 14};
    Number[] series1Numbers = {1, 4, 2, 8, 4, 16, 8, 32, 16, 64};
    Number[] series2Numbers = {5, 2, 10, 5, 20, 10, 40, 20, 80, 40};

    // turn the above arrays into XYSeries':
    // (Y_VALS_ONLY means use the element index as the x value)
    XYSeries series1 = new SimpleXYSeries (
            Arrays.asList(series1Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series1");
    XYSeries series2 = new SimpleXYSeries(
            Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series2");

    // create formatters to use for drawing a series using LineAndPointRenderer
    // and configure them from xml:
    LineAndPointFormatter series1Format =
            new LineAndPointFormatter(this, R.xml.line_point_formatter_with_labels);

    LineAndPointFormatter series2Format =
            new LineAndPointFormatter(this, R.xml.line_point_formatter_with_labels_2);

    // add an "dash" effect to the series2 line:
    series2Format.getLinePaint().setPathEffect(new DashPathEffect (new float[]
            {
                    // always use DP when specifying pixel sizes, to keep things consistent across devices:
                    PixelUtils.dpToPix(20),
                    PixelUtils.dpToPix(15)
            }, 0));

    // (optional) add some smoothing to the lines:
    // see: http://androidplot.com/smooth-curves-and-androidplot/
    series1Format.setInterpolationParams(
            new CatmullRomInterpolator.Params(10, CatmullRomInterpolator.Type.Centripetal));

    series2Format.setInterpolationParams(
            new CatmullRomInterpolator.Params(10, CatmullRomInterpolator.Type.Centripetal));

    // add a new series' to the xyplot:
    plot.addSeries(series1, series1Format);
    plot.addSeries(series2, series2Format);

    plot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.BOTTOM).setFormat(new Format ()
    {
      @Override
      public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos)
      {
        int i = Math.round(((Number) obj).floatValue());
        return toAppendTo.append(domainLabels[i]);
      }
      @Override
      public Object parseObject(String source, ParsePosition pos)
      {
        return null;
      }
    });
  }
}

extended activity code end

the funny part is, un-commenting line 28 containing "... // xxx ..." makes the full screen work, but now the swipe gesture to switch between hiding/show taskbar and button-bar" does not work anymore

I'm not experienced enough to resolve this issue and would need some serious help

Note: the above scenario would work fine with Android plot 0.9.8 and would provide a full-screen Android plot graph portion filling down to the bottom legend

this picture shows the additional rectangle portion marked in red which should be filled by the graph widget for full screen


Solution

  • The root cause of the issue is the call to setSystemUiVisibility(...) after the plot has been laid out and drawn. This was not a problem in 0.9.8 because XYGraphWidget would manually re-calculate it's dimensions on every draw. This is an expensive and (usually) unnecessary operation though and was removed in later releases for performance reasons.

    I know this doesn't address your use case (which is a valid one) but if you get rid of the postDelayed and move your full screen adjustment code into onCreate, then fullscreen mode will display correctly.

    I'm working on an alternate implementation of the graph render logic that will hopefully avoid the performance issue while supporting your use case, for the 1.4.5 release. It should not require any code changes on your end.

    In the mean time, an easy fix is to do the following immediately after setSystemUiVisibility:

    plot.getGraph().setGridRect(null);
    plot.redraw();