Search code examples
androidandroid-canvasandroid-version

No line drawn with Canvas paint in android OS versions greater then 4.4


I am drawing a line segment with the specified start and stop x,y coordinates, using the specified paint on the following Bitmap image for each day dynamically -

chart_by_rule (Unexpected in >= 4.4) -

enter image description here

chart_by_rule (Expected) -

enter image description here

The following code should draw the line in the above bitmap. The lines are only drawn in Android versions < 4.4.

This is the code -

@Override
public View onCreateView(LayoutInflater inflater, 
ViewGroup container, Bundle savedInstanceState) 
{
    getActivity().runOnUiThread(new Runnable() 
    {
        @Override
        public void run() 
        {
            getChartLoaded();
        }
    });

}

public void getChartLoaded()
{
    if(dates_array.indexOf(today) == -1 || (redrawChart != null && redrawChart.equals("popback"))  || (getSetting("ZoneIDChanged","0").equals("1"))) 
    {
        redrawChartView(scal, zeroPadding, zeroPaddingDate);
        setSetting("ZoneIDChanged","0");
    } 
    else 
    {
        addChartViews(scal, zeroPadding, zeroPaddingDate);
    }

}

public void redrawChartView(Calendar scal, 
String zeroPadding, String zeroPaddingDate)
{
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(driverTimeZone));
    while (scal.compareTo(cal) < 0) {
    cv = new ChartView();
    LinearLayout layout = cv.getChartView(i);
    i++;
    linearLayoutMap.put(date, layout);
    views.add(layout);
    }
}

public class ChartView 
{
    private Paint cPaint;
    float startHours = 0, endHours = 0, 
    startHeight = 0, endHeight = 0,
    scale = 0;

    public LinearLayout getChartView(final int number) 
    {
       getActivity().runOnUiThread(new Runnable() 
       {

          @Override
          public void run() {

          cPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
          cPaint.setStyle(Paint.Style.STROKE);
          cPaint.setColor(Color.BLACK);
          cPaint.setStrokeWidth(3);

          scale = getActivity().getBaseContext().getResources()
          .getDisplayMetrics().density;

          linearlayout = new LinearLayout(getActivity());

          final ImageView img = new ImageView(getActivity());

          BitmapFactory.Options options = new BitmapFactory.Options();
          options.inPurgeable = true;
          newBitmap = BitmapFactory.decodeResource(getResources(),
          chart_by_rule).copy(Bitmap.Config.ARGB_4444, true);
          canvas = new Canvas(newBitmap);
          img.setImageBitmap(newBitmap);
          img.setScaleType(ScaleType.FIT_XY);
          img.setTag(number);       
          float wFactor = (15 * scale);
          float hFactor = (float) ((19.35) * scale);
          float x1 = startHours * wFactor;
          float x2 = endHours * wFactor;
          float y1 = (2 * startHeight - 1) * hFactor;
          float y2 = (2 * endHeight - 1) * hFactor;

          canvas.drawLine(x1, y1, x2, y2, cPaint);
          canvas.save(Canvas.ALL_SAVE_FLAG);
          linearlayout.addView(img);
        }
     });
        return linearlayout;
   }
} 

This code is working fine in all the android versions smaller then 4.4.

In Android OS >= 4.4, I am getting no line drawn with the canvas on my linear layout that contains the image view.

Is there any change in these later OS versions regarding Canvas OR the problem can be somewhere else ?

More: I have different layout files for all the screen layouts.

This was checked with Motorola XT907.

This is the phone specification - http://wiki.cyanogenmod.org/w/Xt907_Info.


Solution

  • It is the problem related to DiskLruCache to cache the pages.

    NOTE: Where you created the instance of it is important.

    DiskLruCache

    Java implementation of a Disk-based LRU cache which specifically targets Android compatibility.

    The DiskLruCache doc mentions,

    This class is tolerant of some I/O errors. If files are missing from the filesystem, the corresponding entries will be dropped from the cache. If an error occurs while writing a cache value, the edit will fail silently. Callers should handle other problems by catching IOException and responding appropriately.

    Solution:

    I had -

    final int DISK_CACHE_SIZE = 1024 * 1024 * memClass / 10;
    
    cache = new DiskLruImageCache(getActivity(), DISK_CACHE_SUBDIR,
            DISK_CACHE_SIZE, CompressFormat.JPEG, 100);
    

    where, DiskLruImageCache is a class.

    This cache instance code was at onCreate() method of the fragment.

    Removed the code from onCreate() and put it at onCreateView() method of fragment.

    Now android 4.4 and greater versions are also showing the bitmap with drawn lines.

    That was weird but there seems to be some changes in this respect that created such an issue.

    Reuse Bitmaps

    Then I found the article to reuse bitmap at whats-changed-in-android-4-4-kitkat.

    As stated,

    Bitmap.reconfigure() is included to modify an existing instance to fit a new dimension and pixel configuration. BitmapFactory has been updated as well for this reconfiguration behavior to reuse in Bitmap.

    You could safely reconfigure an instance from 200×200 at ARGB_8888 to 100×100 at ARGB_8888, and then again to 300×300 at RGB_565, because all of those fit inside the initial allocation. If I tried to reconfigure it to 300×300 at ARGB_8888, however, an exception would be thrown. It is also important to note that you cannot reconfigure a Bitmap while it is attached to a view. This should be done after the element has been detached or moved off-screen.

    So if your application can determine a suitable size for a single instance, even if it may be larger than the pixels needed at any given time, you can still reduce memory usage. A single 360KB Bitmap reused for both a 300×300 and 200×200 image (obviously not shown simultaneously) is better for everyone than needing 520KB (360KB + 160KB) for two separate instances.

    I hope anyone who may face this issue later will get help from here.