Showing posts with label view. Show all posts
Showing posts with label view. Show all posts

11 November 2015

Android layout with view fixed to bottom


I recently submitted a stack overflow question about a problem I was having. I didn't get the answer I was looking for so eventually I answered it myself.

The concept I wanted to achieve was to have a view stick to the bottom of the screen. Pretty easy in a relative layout with alignParentBottom="true". However the screen was a registration screen, so it had edit text boxes. When you click on an edit text box, the soft keyboard appears. The problem I then had of course was that my bottom view then popped up to the top of the keyboard and obscured most of the screen.



The above diagram goes a little way to representing what I was seeing, with three being the keyboard and two being the view I wanted stuck to the bottom. The overall container being a RelativeLayout with the blue being a ScrollView and the red alignParentBottom="true".

After much head scratching I finally stumbled across a solution, although it was a bit different to what I was expecting.

First I moved the red bottom view into the scrollview and added a stretcher view with a height of zero.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:isScrollContainer="false">

        <LinearLayout
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <EditText />
            <EditText />

            <View
                android:id="@+id/stretcher"
                android:layout_width="match_parent"
                android:layout_height="0dp" />

            <TextView
                android:id="@+id/2"
                android:layout_gravity="bottom"
                text="2" />
        </LinearLayout>
    </ScrollView>
</RelativeLayout>


The next step was to add a view tree observer.

    final View mainLayout = getView();
    final View mainContent = getView().findViewById(R.id.content);
    final View stretcherView = getView().findViewById(R.id.stretcher);

    //Main layout uses weight some, so we can't hard code the size of the circles.
    //We must dynamically re-size
    mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (android.os.Build.VERSION.SDK_INT >= 16) {
                mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            } else {
                mainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }

            //Calculate the desired height of the stretcher view to be the remainder between full screen and content.
            int stretchHeight = mainLayout.getHeight() - mainContent.getHeight();

            //Apply calculated height remainder to stretched view.
            //This enables our bottom box to be pushed to the bottom without obstructing the content when the keyboard appears.
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) stretcherView.getLayoutParams();
            params.height = stretchHeight;
            stretcherView.setLayoutParams(params);
        }
    });


This listener watches the entire screen and then measures the difference between the full screen and the scrollview. It then inflates the stretcher view by this difference. What we're effectively doing is nudging the bottom view to the bottom of the screen. Now when we load the page the bottom view sits nicely on the bottom of the screen, but when the keyboard moves the bottom view stays underneath and appears in the scroll.

Hope this helps somebody else!

27 October 2015

Android custom view, interesting lessons


So here's an interesting one, I recently created a custom view in Android. Basically a specific shape that a client wanted to frame some text. To begin with the custom view was very simple and I used a relative layout to hold the view in place and the centred brand name inside.


The java comprised of a Paint with a fill in the view initializer and in the onDraw method a Path drawn on the canvas with drawPath().

Unfortunately vertical centre was of the whole view, I needed it to be centred by the left side (a). Remember the view is more than what you can see and in this case it includes the invisible part of the rectangle above the slope. My first impulse was to push it down with some marginTop, but this would of course not work consistently on smaller devices.
So what I needed to do was measure a and centre the text accordingly. I grabbed height and width in onMeasure using MeasureSpec.getSize. Then used the following code to I found on stack overflow:

int yPos = (int) ((mHeight / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
canvas.drawText("", 20, yPos, mTextPaint);

This allowed me to centre the text as required. I added a paint object with some formatting:

        //Text font
        mTextToShow = getContext().getResources().getString(R.string.some_text);
        Typeface ty = Typeface.createFromAsset(getContext().getAssets(), "fonts/some_font.otf");

        //Text size
        Resources resources = getContext().getResources();
        float scale = resources.getDisplayMetrics().density;

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize((int) (20 * scale));
        mTextPaint.setTypeface(ty);
        mTextPaint.setColor(getResources().getColor(R.color.primary_white));


I now needed to do one more thing, the view was to be a link. I'm a huge fan of using Android's state to change text colour when its clickable. This helps the user know they've clicked something and it looks good too. However I had no idea how to do this, if I'd made an xml layout for this view I could do it, but using drawText meant I had no xml. Hmmm.

A few Google searches and even the mighty stack overflow didn't get me very far. Then I started to think, don't all view's have a onPressed or an onStateChanged method? I hit Ctrl Space and scanned through my options. OnPressed was there but I needed more... drawableStateChanged. Sounds promising. A few minutes later I had a working guess and it was building. Hey preseto! It worked, check this out:
    @Override
    protected void drawableStateChanged() {
        if(isPressed()){
            mTextPaint.setColor(getResources().getColor(R.color.primary));
        }else{
            mTextPaint.setColor(getResources().getColor(R.color.primary_white));
        }
        invalidate();

        super.drawableStateChanged();
    }

Hope this helps, happy coding