Friday, July 11, 2014

Creating a Reusable Dialog Class

The past couple of days I have been working on a task that I thought would be a much more common problem.  However, after much Google searching I couldn't find a single page that could help.  So, as always, I'm going to dump my code here in case it can help out someone in the future.

I was trying to make my own Dialog class that I could reuse throughout my app.  Since I had to use it in many different ways, I needed to be able to put a custom layout in the middle of the dialog, similar to how the AlertDialog class works.  This was the biggest issue; I wanted to completely change the way my dialogs looked, more than you can change by simply creating a style.  (Although after looking through the code, I realize that a style could have changed a lot more than I originally thought.)  Finally, I wanted to be able to change the colors used in the dialog on the fly.  This last one is not really related to the rest, but is just something I am trying to do everywhere in my app.  (I'm mentioning it now because my code would be pretty confusing otherwise.)  You can see my original concept over to the right, to get an idea of what I was aiming for.

So, here is all of my code:

I'm not going to talk about the color-changing aspect, because that's not really the point of the post.  There are a couple things I would like to talk about though.

Notice in the dialog's layout, I have a LinearLayout (id/dialog_background) within a FrameLayout, which I use to show the background drawable.  Why not just put the background on the FrameLayout and get rid of that LinearLayout?  Well, the background drawable automatically surrounds all of its children.  Since I wanted the title and close button to float a bit outside of the main dialog, I had to put those two elements outside of the layout that contains the rest of the "inner" dialog elements, so that they wouldn't be surrounded by the background.

The big thing here is the FrameLayout that holds the custom view.  This is actually the same method that is used for the AlertDialog class; I ended up looking through the source code for some inspiration.  And although I have a lot of code up there, it all really boils down to this:

This is how you can stick different views inside of the dialog, allowing you to use it in different places.   It seems pretty simple, but I bring it up because it took me much longer to figure out than I would care to admit.  In fact, that is the first of the big issues I ran in to:

Problems and bugs

1.) It took me way too long to figure out how to embed a custom layout within the dialog's layout.

 I tried a bunch of different ideas.  It seemed like a pretty simple task, but no matter what I tried I couldn't get it to work.  I couldn't find any examples online, except for one StackOverflow question.  I tried all of the different answers that were suggested, but no dice.  I even eventually went to the AlertDialog source code, but even after using the same method, I just couldn't get the inner view to show up!

Of course, the whole time I assumed that my code to display the view was at fault.  I took for granted some things that I assumed could never be the problem.  (I tend to do that a lot.)  In the end, the problem was NOT with my code, but with the layout I was trying to display.  When I was trying to test the dialog, I just grabbed a random layout that I was already using elsewhere in my app.  The layout that I chose contained only a single ListView.  Of course, if you don't set up an adapter for a ListView, it just shows up empty.  So my code was working the whole time, but I thought it wasn't because I was adding an empty layout.

2.) The backgrounds of my button drawables were turning black for no reason.

The link above explains the issue pretty well: My buttons were working fine, but for no reason whatsoever the backgrounds suddenly all turned black.  I still have absolutely no idea why this happened.  I was able to fix the problem by explicitly adding a transparent background to the drawable.

3.) The shadow of my "close" button would disappear after the dialog was shown the first time.

This is even stranger than the last one:  My dialog would look perfect, but only the first time it was displayed.  After that, the shadow of the close button would disappear.  (See the link above for a picture.)  I tried debugging to see what was different between the two states.  I tried Googling, but my search turned up empty.  I tried asking StackOverflow, but all I got was a single comment that didn't even address the issue.  As a last resort, I just tried changing things in the drawable to try to find the problem.  I knew that the title box didn't have any issues, and that was almost identical.  After much trial and error, I narrowed down the problem to the "padding" tag.  For whatever reason, the problem only happens when I am using that tag.  So I took it out and added position attributes to all of the other layers instead.  Again, I have no idea what caused this; I can only assume that it is actually an Android bug. 

Finally, here is an example of how to use the dialog:
And here is what the final result looks like:

Not too bad!  Obviously not perfect; it only has the functionality that I needed at the moment, so it's not nearly as robust as the original AlertDialog class.  But it's definitely a start!

No comments:

Post a Comment