10 useful solutions for the Android Developer

  1. Entry
  2. About the adapters list ( ListView ) with subcategories (for grouping items in the list)
  3. About the lists that contain any of the (list items do anything complicated or alter themselves for selection)
  4. About forced invalidation of the lists
  5. About caching images for ListView (for lists with images)
  6. About adapters, iterates through the cursor (to support pagination on the list)
  7. About authorization via OAuth to Android
  8. About using MediaPlayer and bufferring video feed over HTTP
  9. About the turn of a few AsyncTask (alternate to perform background tasks)
  10. About changing illumination of the selected item in ListView
  11. About adding QuickActions in project
  12. Three more small solving

1. Entry

Last summer (already), I had a great desire to write a client for Android-web service Vimeo . I like this service, and as for me, it would be convenient to watch for updates in subscriptions to the video device.

I conceived this project for myself as an academic (in the sense that I'm learning), but the result was to make quite a tangible part (you can see screenshots of what is done ), but it has not yet been completed. This client does not just me, his version is almost the same time (he was the first) began to make me makotosan and his version so far, it seems, also has done).

In any case, the process of writing project, I gained some knowledge base, which I hasten to share. Not all threads exclusive, but some see the subtleties are not disclosed on the Internet or buried pretty deep in its bowels. I will give examples of additional codes iskhodnyh vimeoid, this will allow you podcmotret as the topic under consideration operates in real time (NB: some links lead to specific line in the code).

2. Lists with subcategories

Aside from the usual use of ListView , is often required to make a list in which items are grouped into several sections in the form header section, paragraph, item, item, ..., a section heading, paragraph, item, ..., a section heading, paragraph, point, and so on ... In the picture are sections «Statistics» and «Information».

(Hereafter I will use the word as an analogue to the English point item, to distinguish between a list item, which may be heading, and the point from point customary element, which can not be a title)

List partitioning

Headers should not react to clicks and the selection and must have their own kind. This can be achieved by redefining except getView method getItemViewType , getViewTypeCount and isEnabled adapter of this list and it otnasledovav, for example, BaseAdapter .

 public class SectionedItemsAdapter extends BaseAdapter {.  .  .
Primarily infest constants that uniquely identify the item type, one for the header, the second for the item (that is, types may be greater than two, but in this case it is better to use the enum to a set of identifiers):

 public static final int ITEM_VIEW_TYPE = 0; \ \ item
 public static final int SECTION_VIEW_TYPE = 1 \ \ section

Then constant containing the number of types of elements (in our case - two):

 public static final int VIEW_TYPES_COUNT = SECTION_VIEW_TYPE + 1;

The adapter will contain information on all elements, so the implementation getCount , getItem and getItemIddepend on your situation.

Method getItemViewType should return the type of the constant element in his position. For an undetermined type of element in the class Adapter there is a constant IGNORE_ITEM_VIEW_TYPE .

 public int getItemViewType (int position) {
    if (...) return ITEM_VIEW_TYPE;
    if (...) return SECTION_VIEW_TYPE;

In my case I keep the adapter list of sections that contain them owned items. Thus, each partition can be asked how many items within it at the cost of this to know the type.

This method can now be used to override the getView :

 public View getView (int position, View convertView, ViewGroup parent) {
     final int viewType = getItemViewType (position);
     if (viewType == IGNORE_ITEM_VIEW_TYPE) throw new IllegalStateException ("Failed to get object at position" + position);
     if (viewType == SECTION_VIEW_TYPE) { 
         convertView =.  .  .  / / Here you can get through LayoutInflater Layout for the section title
     } Else if (viewType == ITEM_VIEW_TYPE) {
         convertView =.  .  .  / / Here you can get through LayoutInflater Layout for the item
     return convertView;    

The method isEnabled should return false for the items that can not be pressed and that can not move the cursor, and true for others. Here, again, will getItemViewType :

 public boolean isEnabled (int position) { 
     return getItemViewType (position)! = SECTION_VIEW_TYPE};

Method getViewTypeCount returns the most constant, the number of possible types of elements:

 public int getViewTypeCount () {return VIEW_TYPES_COUNT;}

By the way, you can store a reference to LayoutInflater in the adapter and get it from that created it aktiviti through the constructor.

That's all you need to work with a list of sections, if necessary - look over here, but first give some explanations.

The example I use a structure for storing data of the sections and paragraphs. The structure of the partition is stored identifier section, its title and structure of the items contained therein. Structure keeps a pointer to point to the parent structure of the section heading, paragraph, the path to the icon and the click handler on the point (about it in the next chapter). The designers of both structures are available only for adapters:So I simplified the adding of groups and items in the list. The adapter has methods:

 public int addSection (String title);
 public LActionItem addItem (int section, int icon, String title);

Method addSection returns a group ID, which can then be used to add items to this group:

 final int suitsSection = adapter.addSection ("Suits");
 adapter.addItem (suitsSection, R.drawable.heart, "Hearts");
 adapter.addItem (suitsSection, R.drawable.diamond, "Diamonds");
 adapter.addItem (suitsSection, R.drawable.spade, "Spades");
 adapter.addItem (suitsSection, R.drawable.cross, "Crosses");
 final int figuresSection = adapter.addSection ("Figures");
 adapter.addItem (figuresSection, R.drawable.king, "King");
 adapter.addItem (figuresSection, R.drawable.queen, "Queen");
 .  .  .

3. Lists with responsive elements

Sometimes you need to when you click on the item list, it changed its status and / or switched aktiviti. For example, the "Follow" in the list of actions on account of Twitter may contain an icon with a minus, if you have not follovili this person and change the icon on the plus and come after you for confirmation of follovinge. You can handle the selected item in the current ListActivity and depending on the position to make a decision, but if the list is contained somewhere within the normal Activity , then perhaps the choice is easier to handle in the adapter.If you agree with that, your adapter can implement the interface OnItemClickListener :

 public class ActionsAdapter extends.  .  .  implements OnItemClickListener

And using it aktiviti you can do:

 final ListView actionsList = (ListView) findViewById (R.id.actionsList);
 final SectionedActionsAdapter actionsAdapter = new ActionsAdapter (...);
 .  .  .  / / Fill the adapter values
 actionsList.setAdapter (actionsAdapter);
 actionsList.setOnItemClickListener (actionsAdapter);

In my case, for points in each section serve some kind of action - go to aktiviti or change the view point of the request to the server. So I chose to make the structure of the publicly available properties for sections and paragraphs, and the structure of the items contain handler OnClick which takes View on which there was a choice, so you can change the View directly from them. Thanks to this adapter, you can simply pass the handler:

 public void onItemClick (AdapterView <?> parent, View view, int position, long id) {
     final LActionItem item = (LActionItem) getItem (position);
     if (item.onClick! = null) item.onClick (view);

Using the above method addItem can install the handler:

 final LActionItem heartsItem = adapter.addItem (suitsSection, R.drawable.heart, "Hearts");
 heartsItem.onClick = new OnClickListener () {public void onClick (View view) {.  .  .  }};

4. Forced invalidation of the lists

ListView in Android, as we know, are arranged with the trick, this trick - ListView Recycler . Prinitsip Recycler'and, if briefly, is that if the list of items than fit on the screen as you scroll down the list of kinds of new elements are created and reused the old forms - in this implementation work prinitsipe getView in adapters.

If at some point need to upgrade (invalidirovat) The specific form of the element well-known (or even a child form) of the list at a time when it is visible on the screen, you can call ListView.invalidate() orAdapter.notifyDataSetChanged() , but sometimes these irrational methods update and neighboring species, if at all, all visible (especially if the layout is built correctly ). Is there a way to get the current form of a list item using the method ListView.getChildAt(position) . However, the position in this case is not an index entry in the list, as would be expected, and the index relative to visible on-screen forms. So useful are these methods:

 public static View getItemViewIfVisible (AdapterView <?> holder, int itemPos) {
     int firstPosition = holder.getFirstVisiblePosition ();
     int wantedChild = itemPos - firstPosition;
     if (wantedChild <0 | | wantedChild> = holder.getChildCount ()) return null;
     return holder.getChildAt (wantedChild);

 public static void invalidateByPos (AdapterView <?> parent, int position) {
     final View itemView = getItemViewIfVisible (parent, position);
     if (itemView! = null) itemView.invalidate ();        

invalidateByPos update form only if it is visible on the screen (by force causing getView adapter), and if the item does not see - getView adapter will be automatically called when the form appears in the sight when you scroll through the list. To update a child form of the element, you can use the getViewIsVisible , it will return the form element from which you can access its child types and null , if the view is not visible to the user and updating is not necessary.
  • Methods described in class: Utils

5. About caching images for lists

List with pictures

If you create a list of ListView , which contains an image downloaded from the network, this chapter is for you.It is unwise to be on every call getView adapter to get the image on the URL again - of course it would be better to a) cache b) request only if the form with the image visible to the user. At the moment this problem is so often the programmers would get up on Android, there is already a lot of her decisions .

My version from the same source, this solution Fedor Vlasov , corrected for my needs. First, I made ​​a directory for storing cached images of static - that is, once it is created during the lifetime of the application and consistently clears the call clearCache (this method is useful to call in onDestroy() in aktiviti using ImageLoader orfinalize() for using it adapter), slightly changed the way the creation of this catalog (seeUtils.createCacheDir() ). Second, the designer can pass ids of images to be displayed on site Pictures in the process of downloading and / or download if it failed. In the third a couple of small changes. In general, this class could be done and Singleton, changing settings before using it, but it's up to you. In my case, one by one instance is created for each running ListActivity and passed to each adapter in need ListView (or created in the adapter itself, if the ListView is inside the regular Activity ). The basic method - displayImage(String url, ImageView view) , the definition speaks for itself.

6. Adapters, iterates through the cursors

This chapter deals with the pagination in the ListView . That is, the user sees the first n items, scrolls the list up to n -matrix element, and only after a request is made ​​for the next n elements of the database or the server.Then, the user scrolls down the list until the element 2n , and we request the following pack sizes n , etc. Invimeoid I make the following query when you click on footerView that says "Load more ..." from the list: do not automatically, but the technique is roughly the same. </ pHere, a more complex hierarchy of classes, loading each page through a special AsyncTask , after which the background Vimeo API call informs the caller aktiviti, remained there still elements and not the last page whether it is, and aktiviti updates its forms in accordance with these data.In order to provide paged, you can just keep a list of the containers for pages (eg, cursor) in the adapter, and the getView() , if requested by one of the last items, run a query on the following page (preferably - AsyncTask ), which in receiving a new container will add it to the adapter and the adapter can cause notifyDataSetChanged() .Like this:

 private final Page [] pages = new Page [MAX_PAGES_COUNT];

 public View getView (final int position, View convertView, ViewGroup parent) {

     if (! waitingNextPage & & 
         (Pages.length <MAX_PAGES_COUNT) & & 
         (Position> = ((pages.length * PER_PAGE) - 2))) {

         final AsyncTask <Integer,.  .  .> NextPageTask =.  .  .;
         nextPageTask.execute (pages.length);
         / / NextPageTask is addSource, when it receives a new page

         waitingNextPage = true;

     .  .  .


 public void addSource (Page page) {
    if (pages.length> = MAX_PAGES_COUNT) return; 
    pages [pages.length] = page;
    waitingNextPage = false;
    notifyDataSetChanged ();

EasyCursorsAdapter - a good example, where as a counterpart to Page appears Cursor . Surely there are alternatives would be happy if they mentioned in the comments.

7. Sign in via OAuth to Android

If you write a client for any complex web service - you are confronted with the problem of authorization, the vast number of web services for its implementation now uses OAuth and Vimeo just among those.

It is not necessary to write the implementation itself is somewhat thankless task, since there is already a great library signpost and better alternatives as far as I know, yet.First you need to get a unique key for your application from a Web service and specify the Web service URL, which will vozvraschatsya user when authorization is successful (eg, vimeoid://oauth.done ) (but in the case of Android it pass when asked to /request_token ). Usually this is done via the web interface of the service.

The algorithm first authorization to Android is the following:
  1. Specify the signpost where the service entry points are located in OAuth
  2. Request to /request_token get a couple of token / secret annex for unauthorized requests for this key (kolbek-URL vimeoid://oauth.done pass here): provider.retrieveRequestToken(Uri callbackUri) . NB:retrieveRequestToken not return a token, and immediately Uri , same authUri by which we must turn to next paragraph
  3. Aktiviti run a browser, go to /authorize , by passing a token application and, if necessary, adding additional parameters to the required rights: startActivity(new Intent(Intent.ACTION_VIEW, authUri + ...))
  4. The user will see the page in the style of "Allow this application to access your account?" (When he logged out of service, he will offer to login). If it allows access, the browser is redirected to the address kolbeka vimeoid://oauth.done?... , but since your AndroidManifest.xml to capture such a special URL described aktiviti, Android user returns to your application, it is the opening aktiviti - ReceiveCredentials
  5. In aktiviti ReceiveCredentials you get the user token in the parameters of Uri uri = getIntent().getData() , now on the token to obtain the secret through a request to /access_token :provider.retrieveAccessToken(Uri uri)
  6. Now you can save the token and secret user, for example, in private SharedPreferences :consumer.getToken() , consumer.getTokenSecret()
You can then sign each request to Web Service API token obtained: consumer.sign(Object request) . If your application has been restarted, before any requests can be checked if there are any tokens SharedPreferences , if there is - to remind them signpost 'in: consumer.setTokenWithSecret(String token, String secret) , and if not - ask the secret user again (or refresh tokens, if the Web service allows it).

Important note: on Android signpost works only with CommonsHttpOAuthConsumer / CommonsHttpOAuthProvider .Classes DefaultOAuth* do not work.

8. Media Player buffering and video via HTTP

MediaPlayer as it turned out, it is very difficult to get to work just as you want, when playing videos. To get the video I had to perform an unusual request from the HTTP-specific headers, so getting the stream and had to write by hand bufferizirovanie. Streaming on analogue samples for audio files , I did not work, so for now I just load the video completely and start playing when it is already loaded (if your card is not enough space, I warn the user). When you close the player or playing I failed to clear the cache.

Still, the behavior VideoView / SurfaceView switching within a single species leyauta also works very mixed (black screen every other time), so we had to leave in the layout banal single VideoView and show ProgressDialog over him while the video loads. Again, if you know something about the streaming video means MediaPlayer (or the receipt of the chunks by hand), write in the comments.

Therefore, if in your case, you have enough call MediaPlayer.setDataSource(Uri uri) , you can skip the next paragraph for more in her and does not tell.

If you also had to get the flow manually, I will turn your attention to a couple of points, the rest just show you the code, it should tell it all himself:Load flow better using AsyncTask . I just aggregate MediaPlayer inside ...PlayingTask for convenience, you can choose any other way, but it's definitely better to get flow through AsyncTask . In this case, the methodonPreExecute you can prepare your player and customize it to doInBackground get the video stream and return the stream to onPostExecute , where and start playing. Again, it is convenient to display the download progress percentage, because doInBackground known quantity of the data.

If the boot stream is thrown, the message it has shown through runOnUiThread , because the task was interrupted.

Running getWindow().setFormat(PixelFormat.TRANSPARENT); intended to displayed over player types do not go over it after hiding it. Although if you want to use ViewSwitcher , it still does not help.

The code of the stream on the URL something like this:

 public static InputStream getVideoStream (long videoId) 
        throws FailedToGetVideoStreamException, VideoLinkRequestException {
     try {           
         final HttpClient client = new DefaultHttpClient ();
         .  .  .
         HttpResponse response = client.execute (request);
         if ((response == null) | | (response.getEntity () == null))
             throw new FailedToGetVideoStreamException ("Failed to get video stream");
         lastContentLength = response.getEntity (). getContentLength ();
         return response.getEntity (). getContent ();
     } Catch (URISyntaxException use) {
         throw new VideoLinkRequestException ("URI creation failed:" + use.getLocalizedMessage ());
     } Catch (ClientProtocolException cpe) {
         throw new VideoLinkRequestException ("Client call failed:" + cpe.getLocalizedMessage ());
     } Catch (IOException ioe) {
         throw new VideoLinkRequestException ("Connection failed:" + ioe.getLocalizedMessage ());

9. Queues of AsyncTask

If you often have to perform several background tasks one by one (when completed one - to run the next), this free pattern that hides itself in the transitions to the linked list, you will do. For example, you might want to run at startup Activity consecutive multiple requests to the API of a Web service or database. The main thing is that the parameter types and result of all these problems have always been the same.

Here is the interface problem, which knows that it has the following problem:

 public interface HasNextTask <Params> {
     public int getId ();
     void setNextTask (HasNextTask <Params> task);
     public HasNextTask <Parames> getNextTask ();
     public AsyncTask <?,?,?> execute (Params. .. params); 
                          / / Overlap with AsyncTask <Params, ...>

An interface that monitors all the moments when the task successfully or unsuccessfully performed:

 public interface PerformHandler <Params, Result> {
     public void onPerfomed (int taskId, Result result, HasNextTask <Params> nextTask);
     public void onError (Exception e, String description);

The interface HasNextTask . What is represented by dots, can be taken in the child class or the class itself to make the abstract, that the methods doInBackground / onPostExecute implemented directly in createTask queue:

 public class TaskInQueue <Params, Result> extends AsyncTask <Params, Void, Result> 
                                          implements HasNextTask <Params> {

     private final int taskId;
     private HasNextTask <Params> nextTask = null;
     private final PerformHandler <Params, Result> listener;

     public TaskInQueue (PerformHandler <Params, Result> listener, int taskId) {
         this.taskId = taskId;
         this.listener = listener;

     @ Override
     public Result doInBackground (Params. .. params) {.  .  .  / * Task * /}

     @ Override
     protected void onPostExecute (Result result) {
         .  .  .  / / Handle the result if necessary
         listener.onPerformed (taskId, result, nextTask);

     @ Override public int getId () {return taskId;}

     @ Override 
     public void setNextTask (HasNextTask <Params> nextTask) {
         if (this.nextTask! = null) 
             throw new IllegalStateException ("Next task is already set");
         this.nextTask = nextTask;

     @ Override
     public HasNextTask <Params> getNextTask () {return nextTask;};


And most importantly, the implementation of the queue:

 public abstract class TasksQueue <Params, Result> 
                 implements PerformHandler <Params, Result>, Runnable {

     public static final String TAG = "TasksQueue";

     private HasNextTask <Params> firstTask = null;
     private HasNextTask <Params> lastTask = null;
     private Map <Integer, Params> tasksParams = null;
     private int currentTask = -1;
     private boolean running = false; / / now runs one of the tasks
     private boolean started = false; / / turn launched
     private int size = 0;

     protected HasNextTask <Params> createTask (int taskId) {/ / you can override
         return new TaskInQueue <Params, Result> (this, taskId);

     @ Override
     public HasNextTask <Params> add (int taskId, Params params) {
         Log.d (TAG, "Adding task" + taskId);
         final HasNextTask <Params> = createTask (taskId);
         if (isEmpty ()) {
             firstTask = task;
             lastTask = task;
             tasksParams = new HashMap <Integer, Params> ();
         } Else {
             lastTask.setNextTask (task);
             lastTask = task;
         tasksParams.put (task.getId (), params);
         size + = 1;
         return task;

     @ Override
     public void run () {
         Log.d (TAG, "Running first task");
         if (! isEmpty ())
             try {
                 started = true;
                 execute (firstTask);
             } Catch (Exception e) {
                 onError (e, e.getLocalizedMessage ());
                 finish ();
         else throw new IllegalStateException ("Queue is empty");

     @ Override
     public void onPerfomed (int taskId, Result result, HasNextTask <Params> nextTask) {
         Log.d (TAG, "Task" + taskId + "performed");
         if (taskId! = currentTask) 
             throw new IllegalStateException ("Tasks queue desynchronized");
         running = false;
         try {
             if (nextTask! = null) {
                 execute (nextTask);
             } Else finish ();
         } Catch (Exception e) {
             onError (e, "Error while executing task" + 
                        ((NextTask! = Null)? NextTask.getId (): taskId));
             finish ();                

     protected void execute (HasNextTask <Result> task) throws Exception {
         Log.d (TAG, "Trying to run task" + task.getId ());
         if (running) throw new IllegalStateException ("Tasks queue desynchronized");
         currentTask = task.getId ();
         running = true;
         Log.d (TAG, "Running task" + task.getId ());
         task.execute (tasksParams.get (task.getId ())). get (); / / wait for result

     protected void finish () {
         firstTask = null;
         lastTask = null;
         if (tasksParams! = null) tasksParams.clear ();
         tasksParams = null;
         currentTask = -1;
         running = false;
         started = false;
         size = 0;

     public boolean isEmpty () {return (firstTask == null);}

     public boolean started () {return started;}

     public boolean running () {return running;}

     public int size () {return size;}        


Now in your aktiviti at any time you can easily create all background tasks:

 protected final TasksQueue secondaryTasks;

 private final int TASK_1 = 0;
 private final int TASK_2 = 1;
 private final int TASK_3 = 2;

 public ... Activity () {/ / constructor

     secondaryTasks = new TasksQueue <..., ...>() {

         / / Here you can override createTask 

         @ Override public void onPerfomed (int taskId, ... result) throws JSONException {
             super.onPerfomed (taskId, result);
             onSecondaryTaskPerfomed (taskId, result);

         @ Override public void onError (Exception e, String message) {
             Log.e (TAG, message + "/" + e.getLocalizedMessage ());
             Dialogs.makeExceptionToast (ItemsListActivity.this, message, e);


     secondaryTasks.add (TASK_1, ...);
     secondaryTasks.add (TASK_2, ...);
     secondaryTasks.add (TASK_3, ...);


 protected void someMethod () {
     .  .  .
     if (! secondaryTasks.isEmpty ()) secondaryTasks.run ();
     .  .  .

 protected void onSecondaryTaskPerfomed (int taskId, ... result) {
     switch (taskId) {
         case TASK_1:.  .  .
         case TASK_2:.  .  .
         case TASK_3:.  .  .
         .  .  .

By the way, thanks to the interface Runnable such a queue can be run in a separate thread:

 new Thread (secondaryTasks, "Tasks Queue"). start ();

10. Backlight selection in ListView

The selected entry in the list

In the picture shows a blue band, this custom highlight the selected item, it has four states - pressed, which has a focus that is prohibited by and pressed the animation of the transition from a jammed for a long tapa. The first three and squeezed state - the so-called 9-patch , you've probably heard about them , the animation - xml -file animations.

In order to describe the state to highlight the selection in the layout, specifyandroid:listSelector="@drawable/selector_bg" for your ListView .

selector_bg.xml - this is another xml -file, a set of rules about how the backlit, depending on conditions. The system goes through each rule and as soon as the first rule is matched, it is true, but these are ignored. The algorithm is simple, but the build rules in the right order do not always work out right away. See examples:Editor nine-patch

From 9-patch is a cunning, too, just what is wrong in the layout - and they drive around and goes around the entire list, too. The main rule is - check the above description of the ListView , make sure that layout_width andlayout_height installed fill_parent and also double-check the items above in the hierarchy. Then, if you did not help, you can correct the nine-patch. Thin black lines denote the top and left of the images that will be stretched if the content is not got a picture. Thin black lines (optional) on the right and bottom indicate the region in which the content itself will be written. Find the right position, too, turns out not once, have to experiment. Do not even think to create a nine-patch without an editor of the box, it is unnecessary removal of the brain - areas highlighted in the editor for content and errors, and even when everything seems right, do not always perceived layout infleyterom as expected.

Prohibited conditionThe state of focusPressed stateSqueezed state

11. Add QuickActions

Example QuickActions

QuickActions - a small library for pop-up dialogs with activities such as in the figure (and not only these, because their design can be changed easily.) They have become a new popular trend of the appearance of an official twitter client. Must be other implementation, in vimeoid I use this, and it also touched up a bit for their needs.

In order to display a dialog instead of the context menu when you long to Tape element on the list, enough to override a method onCreateContextMenu in ListActivity as follows:

 public void onCreateContextMenu (ContextMenu menu, View v, ContextMenuInfo menuInfo) {      
     .  .  .
     final AdapterView.AdapterContextMenuInfo info = extractMenuInfo (menuInfo);
     final QuickAction quickAction = 
           createQuickActions (info.position, getItem (info.position), info.targetView);
     if (quickAction! = null) quickAction.show ();

 protected QuickAction createQuickActions (final int position, final ... item, View view) {
     QuickAction qa = new QuickAction (view);
     qa.addActionItem (getString (R.string ...), 
                      getResources (). getDrawable (R.drawable ...), 
             new QActionClickListener () {
                 @ Override public void onClick (View v, QActionItem item) {
                     .  .  .
     .  .  .
     return qa;
About adding an external library in the Eclipse project described in this article . In short, it is enough to create a separate library for Android-source project, set the checkbox isLibrary see Android in the properties of this project, and in the main project add to a project with a library point of Library -> Add from the same section. In this case R -file from the project with the library will be added to the main project.

12. Three more small solving

12a. A single place to call various activity

If your application aktiviti many different and they are invoked in a similar way, it might be convenient to transfer their calls including filling Extras in a separate class:
  • An example of vimeoid: Invoke

12b. About placeholder in localization

Maybe this is obvious, but the rows of strings.xml pleyshodery can use to substitute some value independent of the locale inside the lines, for example: <string name="image_info">Image size: {width}x{height}</string> . This function will format , so you can call: format(getString(R.string.image_info), "width", String.valueOf(600), "height", String.valueOf(800)) :

 public static String format (String source, String ... params) {
     String result = source;
     int pos = 0;
     while (pos <params.length) {
         result = result.replaceAll ("\ \ {" + params [pos + +] + "\ \}", params [pos ++]);
     return result;

Upd. It turned out as I thought it was a bicycle: There is a standard function getString(int resId, Object... formatArgs) . Thank zochek .

12c. About incorrect layout

Be sure to read these articles in infleyter android really very sensitive to the complex structures and if you write a complex application, leyauty sooner or later will have to optimize:My pererenderivayuschiesya leyauty often at one point and failed getView adapter has called almost every second (and still sometimes is, but already much less). After replacing many nested slozhnostrukturirovannyhLinearLayout s to less invested and elegant RelativeLayout , infleyteru was clearly easier and myself too, because the hierarchy has become shorter and make small changes to become easier. I have not yet had time to replace everywhere, but now otnoschus to leyautam carefully. Also watch out for, so thatwidth/height=wrap_content used only possible for simple elements, the use of wrap_content as parameters the width / height LinearLayout and other complex types can lead to difficult consequences.