I decided to write an article at my leisure on how to write applications for Android. Write will be a simple time manager. In this part, I will write the program itself, attach the interface with animation to it, and in the next one, if interested, write a service to it, so that the program also works in the background.
What will be considered:
RelativeLayout for implementing the program interface with overlapping images on each other.
Timer to implement a time counting algorithm.
Animation for whistlers beautiful applet interface.
For clarity, added a screencast how all this stuff works. ')
So, we write a simple time manager for Android. Suppose that the SDK + Eclipse is already installed.
Launch Eclipse, and go to File - New - Other. In the window that opens, select Android - Android Project.
This window will open. We drive in parameters: Project Name is the name of the project, it must be unique in the current Workspace. BuildTarget - OS version for which the project is being built. It should be remembered that when collecting a project for 1.5, it will run on both 1.6 and 2.1, and if you build for 1.6, then on 1.5 it will not. Application Name - the name of the application. It will be displayed in the main menu, from where it can be launched. Package Name - com. <Office name>. <Applet name> Create Activity - the name of the Activity to launch the application. Since the screen was made after writing the project, you can notice the error “There is already a file ...” on top of me because a project with that name has already been created. Click Finish.
Interface
This is the kind of view that should be brought to it, while the text size of the timers should be the same, increase / decrease due to Animation. To do this, use RelativeLayout with three children: ImageView, for the shadow at the top of the applet, and two LinearLayouts, one for the timers and one for the buttons at the bottom. If you have time and opportunity you can draw everything yourself, but for every fireman I attach an archivist with all the graphic elements.
TimeTracker.zip (194Kb) Unpack and throw the bg.png , * .9.png , icon.png and topshadow.png files into thedrawabledirectory . 9.png is a 9-patch drawable. You can read more about it at developer.android.com/guide/developing/tools/draw9patch.html , I note only that these files have the ability to stretch only certain parts of the image if necessary. So buttons work for me. If the content text is larger than the original image, or the button has the layout_width / height = fill_parent attribute, then android will not stretch the entire bitmap, but only the marked areas.
When I started to do it I did not take into account, but for the future I will note that these images should have a minimum length and width, I have the same width - 100px, so I will not make the button a hundred pixels anymore.
Now you need to let the android know how to stretch the background image, header, and change the images from the button state. To do this, create in the folder drawable files: background.xml, greebutton.xml, redbutton.xml and header.xml.
Specify bitmap for each button state. You can, for example, add a picture for the state of focused.
For redbutton.xml, everything is the same as in greenbutton.xml, with the exception of bitmaps. At Habré people are smart, I think you will understand what you need to write there. For header.xml, too, everything is the same as in background.xml, only with a different bitmap.
Moving to the daddy layout - there are xml files that describe the interface of the applet. In our case, there is a single file, open it and bring it to this form:
RelativeLayout allows you to place elements relative to yourself or each other. Thanks to this we will be able to impose timers on the header. I described the following parameters: orientation - orientation of the children of this layout. For example, if you place two buttons in LinearLayout with the orientation = "vertical" parameter, then they will be under each other, and with the orientation = "horizontal" parameter, one after another. layout_width and layout_height - width and height. Three types of values are accepted: fill_parent (accept the parent parameter), wrap_content (the minimum value where the content will not be truncated), or a numeric value. background - background. A little bit higher we created background.xml in the drawable folder, so we are now turning to it. I will explain, "@ <folder in the folder res> / <file in this folder>".In the same way identifiers are set, but more on that later. Then inside RelativeLayout, we create 3 more elements, which I mentioned just above. ImageView
src is the path to header.xml that we created recently, which contains the parameters for the shadow at the top of the applet. scaleType - how to stretch it. In principle, it was possible to do without header.xml, and immediately specify the path to topshadow.png. layout_alignParentTop - each type of RelativeView has parameters of this type, and indicate where to place the element. Specifically, this suggests that it is necessary to draw at the upper border of the parent, ie RelativeView.
id - the id of the widget. To create a new identifier, use the "@ + id / <name>", to access an existing one, for example, in the settings you need to make the item dependent on the choice of another item "@ id / <name>". textColor - text color. It is set either as #RRGGBB or #AARRGGBB, where AA is the hex alpha channel (transparency), RR, GG and BB are the hex of each color. singleLine - text in one line. gravity - alignment of the text itself. text - try it yourself to guess XD.
Here, everything is clear in principle, I note only that the text is not set as a line, but as a link to a line in the file with lines ... some kind of taftalogy ... I will write about this a little later, for the time being, leave it like that.
Animation
The principle is as follows. We press the button, one timer decreases, the other increases. Create an anim subfolder in the res folder. We shove 4 xml files there: magnify_rest.xml, shrink_rest.xml, magnify_work.xml and shrink_work.xml. magnify - increase the timer. shrink - decrease the timer.
fromXScale and fromYScale - size values before the animation begins. If you put 2.0, then the text will first suddenly double, and from this value it will change its size. toXScale and toYScale are the size values at the end of the animation. I have 2.0, that is, the text will increase twice from the initial value specified in the layout, and not from fromXScale and fromYScale. pivotX and pivotY - the points from which the animation originates. For example: if you put in both by 50%, then the text will be increased in all directions, if pivotX = 0%, then the text will increase to the right, 100% - to the left. startOffset - the gap before the start of the animation. duration - animation time in milliseconds.
two timers on top of each other, aligned to the vertical center of the parent. I press the button, the top increases up, the bottom decreases too up. I press on another, and they decrease and increase accordingly, only already down. All this is done so that they do not overlap each other. Therefore, in the upper two pivot'y indicated as the lower center point. In the other two files everything is the same, except for the fact that the pivots point to the upper center point, that is, 50% and 0%.
Strings and localization
Go to res / values / strings.xml and bring it to this form:
<? xml version = "1.0" encoding = "utf-8"?>
<resources>
<string name = "hello"> Hello World, Main! </ string>
<string name = "app_name"> Time Manager </ string>
<string name = "button_work"> Work </ string>
<string name = "button_rest"> Rest </ string>
</ resources>
name - I mentioned it a little earlier, so the string / hello construction will give us Hello World, Main! This file contains all the lines for use in the program.
For localization, create the values- <locale> directory, in our case, values-ru, and copy the strings.xml file there, then open it and translate the string values into Russian.
Program
Things are easy. It remains to write the program itself in order for all this beauty to move, stop and enter. Open the java file in the src folder. He should be there alone. At the top we see the name of the package, in my case, com.nixan.timetracker. Then there are imports of other classes. Then the declaration of the class itself:
public class Main extends Activity
Instead of Main, you can have something else, namely, what we asked in create activity when creating a project. Activity is ... an analogue of forms under Windows, let's say. There is also a ListActivity - a list of some elements to be combed, PreferenceActivity - a window with settings, all of which are defined in a separate xml file and there is no need to invent a layout for all this beauty, but this is not the case. This class has an onCreate () method that is called when the window is created. There are several such methods. For more details, see developer.android.com/guide/topics/fundamentals.html#actlife
rest_button and work_button - work / rest switching buttons. rest_timer and work_timer - text that displays the current values of the timer. magnify_rest, shrink_rest, magnify_work and shrink_work - animations that we asked in the xml file. counter - what the timer will do. timer - the timer itself. resting and working are variables in which current states are stored. rest_time and work_time - how many seconds actually worked and rested. stats_editor - settings, they store spent and rested seconds, in order to close the program, the timer is not reset.
So let's go!
First of all, we check if there is a parent onCreate call.
super.onCreate (savedInstanceState);
If not, then append.
Then we say to this Activity which layout should be drawn.
setContentView (R.layout.main);
R.layout.main is an analogue of "@ layout / main", where main is main.xml in the layout folder. If your layout is called not main, correct the call. Before setContentView ();You can add more requestWindowFeature ();, in which you can pass in parameters, for example Window.FEATURE_NO_TITLE, to remove the gray title at the top of the window.
In my case, I converted the seconds to the string HH: MM: SS in two different ways. Thank you in advance for my bad Java, but it seems to me that the first one eats less memory, but it works more slowly, and the other way around, so don’t scold me much.
private String getTime (int time)
{
String result = "";
int hours = time / 3600;
int minutes = (time - (hours * 3600)) / 60;
int seconds = (time - (hours * 3600) - (minutes * 60));
result = String.valueOf (hours) + ":";
if (minutes <10)
{
result + = "0" + String.valueOf (minutes) + ":";
}
else
{
result + = String.valueOf (minutes) + ":";
}
if (seconds <10)
{
result + = "0" + String.valueOf (seconds);
}
else
{
result + = String.valueOf (seconds);
}
return result;
}
I will say it again, I don’t know how correct and competent it is, but it seems to work.
And right after the initialization of the variables, add:
Thus we set the previously calculated seconds in the text of the timers.
Create a task for the timer:
counter = new TimerTask ()
{
@Override
public void run () {
}
};
Before Override variables that are responsible for seconds, minutes and hours of the timer. Since I have implemented the whole process in one timer, then by recruiting for rest and work:
int seconds_r = 0;
int hours_r = 0;
int minutes_r = 0;
String seconds_ind_r;
String minutes_ind_r;
String hours_ind_r;
int seconds_w = 0;
int hours_w = 0;
int minutes_w = 0;
String seconds_ind_w;
String minutes_ind_w;
String hours_ind_w;
resting is a boolean variable indicating that we are resting. runOnUiThread () - executes a piece of code in a stream with an interface. Above - a piece of code for rest, for work just after that a similar code is added, only with a set of seconds, minutes and hours for work, the first if changes to if (working) and the text we set is not for rest_timer, but for work_timer 'but.
Things are easy: handling button presses.
rest_button.setOnClickListener (new OnClickListener ()
{
public void onClick (View v) {
if (! resting)
{
rest_timer.startAnimation (magnify_rest);
resting = true;
}
else
{
rest_timer.startAnimation (shrink_rest);
resting = false;
stats_editor.putInt ("key_rest_time", rest_time);
}
if (working)
{
work_timer.startAnimation (shrink_work);
working = false;
stats_editor.putInt ("key_work_time", work_time);
}
stats_editor.commit ();
}
});
If at the moment we do not rest, then we begin to rest. If we rest, we stop to rest. If it works, then stop working. In this case, only the logical variables working and resting are changed, which are checked every second in the timer, and if they are turned on, the time begins to increase.
Thus, by pressing the button twice, we can switch off the timer altogether, without switching on the operating mode.
For the work button, everything is similar.
Saving, connect the handset and run the application.
And immediately find the first bug. If the timer is running and we turn the screen, the timer stops.
Find:
resting = false;
working = false;
And change to:
resting = saved_stats.getBoolean ("key_resting", false);
working = saved_stats.getBoolean ("key_working", false);
if (resting)
{
rest_timer.startAnimation (magnify_rest);
}
if (working)
{
work_timer.startAnimation (magnify_work);
}