Showing posts with label onclick. Show all posts
Showing posts with label onclick. Show all posts

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


05 November 2014

Android ripple effect in Lollipop

Just a quick how-to today.

So Google have made a big thing of the new ripple effect in Android Lollipop.
http://android-developers.blogspot.co.uk/2014/10/implementing-material-design-in-your.html

 To my eyes however it wasn't immediately clear how to implement it, particularly if you wanted your app to work in older versions of Android, which almost everyone will.

So the first point to note is android buttons automatically implement the ripple effect if you've got the material theme running. However I mostly used TextViews as buttons with custom drawables for their background and onClick state.

Second I'm not talking about getting ripple working in old versions of Android, just a graceful rollback to a pressed state.

So first create a drawable and a drawable-v21 folder under res, in each add a button_selector.xml

res/drawable/button_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Pressed -->
    <item android:state_pressed="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Selected -->
    <item android:state_selected="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Focus -->
    <item android:state_focused="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Default -->
    <item android:drawable="@color/colorPrimary"/>
</selector>

res/drawable-v21/button_selector.xml
<ripple
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/accent">
    <!-- Pressed -->
    <item android:state_pressed="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Selected -->
    <item android:state_selected="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Focus -->
    <item android:state_focused="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Default -->
    <item android:drawable="@color/colorPrimary"/>
</ripple>

now in your activity_main or wherever you need to add a button with a background

<TextView
    android:id="@+id/activity_main_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:background="@drawable/button_selector"
    android:padding="20dp"
    android:text="Button"/>

You'll notice the ripple xml basically incorporates the same drawables as the selector does, adding on the ripple effect.