1

ok, so before I've asked this question, I've looked at:

How to remove unwanted overlapping in android

Tabs at bottom overlapping with the list view

Android bottom navigation bar overlapping Spinner. Set Spinner dropdown height / margin

Bottom button bar overlaps the last element of Listview!

However I haven't seen what I would think to be a fix for my particular situation. So here's my problem: I'm writing a custom SurfaceView that will display an image on-screen. In that SurfaceView I am drawing an image to the bottom right-hand corner. Here is the code:

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.os.Build;
import android.os.Bundle;
import android.view.Display;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

public class demosf extends Activity {

    OurView v;
    int Measuredwidth;
    int Measuredheight;
    WindowManager w;
    Bitmap whatever;

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

        Measuredwidth = 0;
        Measuredheight = 0;
        getScreenWidthAndHeight();
        whatever = BitmapFactory.decodeResource(getResources(), R.raw.dragarrow);
        v = new OurView(this);
        setContentView(v);
    }

    public class OurView extends SurfaceView implements Runnable {

        Thread t = null;
        SurfaceHolder holder;
        boolean isItOK;
        public OurView(Context context) {
            super(context);
            holder = getHolder();
        }

        @Override
        public void run() {
            while (isItOK) {
                try {
                    Thread.sleep((long) 50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (!holder.getSurface().isValid()) {
                    continue;
                }
                Canvas c = holder.lockCanvas();
                c.drawARGB(255, 0, 0, 0);
                c.drawBitmap(whatever, ((float) Measuredwidth - (float) whatever.getWidth()), ((float) Measuredheight - (float) whatever.getHeight()), null);
                holder.unlockCanvasAndPost(c);
            }
        }
        public void pause() {
            isItOK = false;
            while (true) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                break;
            }
            t = null;
        }

        public void resume() {
            isItOK = true;
            t = new Thread(this);
            t.start();
        }
    }
    @Override
    protected void onPause() {
        super.onPause();
        v.pause();
    }
    @Override
    protected void onResume() {
        super.onResume();
        v.resume();
    }

    @SuppressLint("NewApi")
    @SuppressWarnings("deprecation")
    public void getScreenWidthAndHeight() {
        Point size = new Point();
        w = getWindowManager();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
            w.getDefaultDisplay().getSize(size);

            Measuredwidth = size.x;
            Measuredheight = size.y;
        } else {
            Display d = w.getDefaultDisplay();
            Measuredwidth = d.getWidth();
            Measuredheight = d.getHeight();

        }
    }
}

and just to be thorough, here's the manifest too:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.something"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.something.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name="com.example.something.demosf"
            android:label="@string/app_name"
            android:screenOrientation="landscape"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
            >
            <intent-filter>
                <action android:name="com.example.something.DEMO" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

    </application>

</manifest>

now the problem I seem to be having, is that on my Nexus 7 this works out exactly as I'd expect. However on my friends Toshiba Thrive, the bottom of the image is cut off by the bar on the bottom. Here's the comparison: problem The Nexus7 is on the right, the Thrive is on the left. So my question is: what exactly is happening, and how can I fix this? (preferably for all Android versions) Oh, side question: what exactly is that bottom bar even called? lol

EDIT: I'm aware that it's not the best code, I'm simply using this code to demonstrate what's happening and perhaps find a different approach to my "getScreenWidthAndHeight()" method that accounts for this odd overlap in the devices it occurs in

Community
  • 1
  • 1
codingNewb
  • 470
  • 1
  • 8
  • 23
  • It's called the System Bar, FYI. – Mike P. Oct 07 '13 at 00:56
  • Good to know, much appreciated =) Any idea how to account for this then? I've found a "getStatusBarHeight()" method at http://stackoverflow.com/questions/3407256/height-of-status-bar-in-android however since my Nexus 7 doesn't seem to account for that bar while the thrive does, I'm still hoping for someone to have some sort of solution – codingNewb Oct 07 '13 at 01:01
  • The Status Bar is not the same thing as the System Bar; System Bar consists of the (usually on the bottom) set of controls that are/were touch buttons on most devices. The Status Bar is the (usually on top) bar that contains your notifications, battery life, etc. – Mike P. Oct 07 '13 at 01:20

1 Answers1

2

Instead of:

c.drawBitmap(whatever, ((float) Measuredwidth - (float) whatever.getWidth()), 
          ((float) Measuredheight - (float) whatever.getHeight()), null);

use:

c.drawBitmap(whatever, ((float) this.getWidth() - (float) whatever.getWidth()), 
          ((float) this.getHeight() - (float) whatever.getHeight()), null);

Reason:

Your method of positioning the Bitmap assumes that no screen decorations are present at the bottom. In fact, it does not account for decor views at all. You should be handling positioning using dimensions of your Activity's view.

Edit:

In your activity's case, the following will get you the view's dimensions:

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

    Measuredwidth = 0;
    Measuredheight = 0;
    getScreenWidthAndHeight();
    whatever = BitmapFactory.decodeResource(getResources(), R.raw.dragarrow);
    v = new OurView(this);

    v.post(new Runnable() {

        @Override
        public void run() {
            Measuredwidth = v.getWidth();
            Measuredheight = v.getHeight();    
        }
    });

    setContentView(v);
}

Edit 2:

LinearLayout llMain;

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

    Measuredwidth = 0;
    Measuredheight = 0;
    //getScreenWidthAndHeight();
    whatever = BitmapFactory.decodeResource(getResources(), R.raw.dragarrow);

    llMain = new LinearLayout(this);

    setContentView(llMain);

    llMain.post(new Runnable() {

        @Override
        public void run() {

            Measuredwidth = llMain.getWidth();
            Measuredheight = llMain.getHeight();    

            scaleAndTrimImages();

            v = new OurView(demosf.this);

            llMain.addView(v);                
        }
    });

}

public void scaleAndTrimImages() {

    // Use Measuredwidth and Measuredheight
    // Since you are calling this method from onCreate(Bundle),
    // it runs only once.

}
Vikram
  • 51,313
  • 11
  • 93
  • 122
  • 1
    Ooo looks promising, I'll try it out ^_^ – codingNewb Oct 07 '13 at 01:49
  • So how would I get the size in the method itself though? I actually require those measurements to be discovered in the onCreate method at the same point that the getScreenWidthAndHeight() method is used in my example. You said use the activities view, which is set to "v", but when I use v.getWidth() to set the width I get returned 0 – codingNewb Oct 07 '13 at 01:58
  • @codingNewb Thanks. A solid approach to positioning child views is using your parent view's bounding rect. Create a `Rect` variable: `Rect r = new Rect();`. Get parent view's bounds: `this.getGlobalVisibleRect(r);`. Now, use: `c.drawBitmap(whatever, ((float) r.right - (float) whatever.getWidth()), ((float) r.bottom - (float) whatever.getHeight()), null);`. Good luck! – Vikram Oct 07 '13 at 02:02
  • I'm trying to use "this.getGlobalVisibleRect(r);" with the onCreate with no success, this approach works in the run method above drawing the bitmap, however I really need the dimensions in the onCreate method, and "this.getGlobalVisibleRect(r);" doesn't seem to work there – codingNewb Oct 07 '13 at 02:07
  • @codingNewb A view's dimensions aren't accessible at the time of its creation. See my edit for a way to get a view's dimensions as soon as they are available. – Vikram Oct 07 '13 at 02:10
  • @codingNewb In `this.getGlobalVisibleRect(r);`, this refers to the SurfaceView. If you place this call in `onCreate(Bundle)`, this would refer to `demosf` and `getGlobalVisibleRect(r)` isn't defined for an Activity. – Vikram Oct 07 '13 at 02:13
  • ok, so when trying within the v.post Runnable, I'm able to get the proper values, however after that method and before the setContentView(v), if I run log.d for Measuredheight at that point, the value still returns as 0, so unfortunately the other Runnable to get the value doesn't seem to help, so still unable to get that data in the OnCreate and so still unfortunately stuck =( – codingNewb Oct 07 '13 at 02:33
  • @codingNewb `View.post(Runnable)` does not work that way. The Runnable is posted to the view's message queue and processed by turn. When you log the values, the Runnable hasn't been run. May I ask why you need those values in onCreate(Bundle)? Dimensions of a view that hasn't (yet) been drawn will always be zero. Posting a Runnable _is_ the sure way of knowing the dimensions as soon as they are available. – Vikram Oct 07 '13 at 02:40
  • scaling/trimming of multiple images mainly, I have a few images that need to be displayed on the SurfaceView screen, in order to save memory I'm scaling/trimming images to the proper height in the OnCreate before actually drawing them, rather than attempting to trim/scale each time through before it's drawn, and drawing multiple bitmaps on top of each other, a single bitmap of the proper size is created during the OnCreate and that alone is displayed. It appears I'll need to make a condition that only runs on the first time run() is called to account for this – codingNewb Oct 07 '13 at 02:47
  • @codingNewb That's fine. You can place the code for `scaling/trimming` in a method and call it from `OurView#run()`. You have a 50 ms delay `(Thread.sleep(50))` before you access `Measuredwidth` and `Measuredheight`. This should be enough for the values to be updated in the Runnable that you post in onCreate(Bundle). Adding another approach/edit to my answer. – Vikram Oct 07 '13 at 03:10
  • 1
    after working through a couple of my own errors, and finally being able to implement it into my actual app, I must say a MASSIVE thank you for all of your help, you have been awesome ^_^ – codingNewb Oct 07 '13 at 07:03